onclick Ereignis bei "normalen" Widgets!!!

Alles rund um die Programmierung mit Qt
fedderle
Beiträge: 10
Registriert: 19. Oktober 2005 18:23

onclick Ereignis bei "normalen" Widgets!!!

Beitrag von fedderle »

habe da ein kleines Problem.

Teste noch ein wenig in QT herum! Ich brauche später ein Polygon, was mit einer Farbe gefüllt ist und was man wenn man es anklickt dann seine Farbe ändert!

Dachte mir, dass ich das dann mit QObject::connect mache!

nur brauch ich dann ja das SIGNAL(clicked()) welches aber nur bei Buttons existiert und nicht bei einem Polygon oder ähnlichem!

Jetzt sie Frage: Wie kann ich so ein clicked() SIGNAL schreiben???? Also wie erkennt man denn das auf das Objekt geklickt wurde?
ArneStocker
Beiträge: 300
Registriert: 3. November 2004 16:15
Wohnort: Berlin

Beitrag von ArneStocker »

in einer eigenen abgeleisteten Klasse (z.B. MyPolygonClass)

Code: Alles auswählen

class MyPolygonClass  : public QObject
{
    Q_OBJECT
    ...
    signals:
        void clicked();
    ...
}
um das signal zu senden rufst Du innerhalb von MyPolygonClass

Code: Alles auswählen

emit clicked();
auf. Das connect natürlich nicht vergessen.

Gruss Arne
fedderle
Beiträge: 10
Registriert: 19. Oktober 2005 18:23

Beitrag von fedderle »

Danke!
Das oben ist klar!

Aber: Das mit dem

Code: Alles auswählen

emit clicked()
versteh ich irgendwie nicht so ganz! Wenn ich das richtig verstehe muss ich genau das ja schreiben nachdem ich festgestellt habe, das auf das Polygon geklickt wurde! Jetzt ist nur die Sache dass ich das genau ja nicht weiß!

Oder versteh ich hier gerade etwas total falsch?

Hier der aktuelle Code von meinem Programm! Arbeite da zur Zeit zwar nicth mit einem Polygon, aber das dürfte ja keinen Unterschied machen.

Code: Alles auswählen

#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QLinearGradient>
#include <QObject>
#include <QPushButton>

class MyWidget : public QWidget
{
    Q_OBJECT
    public:
        // Funktion PaintEvent muss ueberladen werden
        MyWidget(QWidget *parent = 0);
        void paintEvent(QPaintEvent *);
        
    signals:
        void clicked();
};

MyWidget::MyWidget(QWidget *parent):QWidget(parent)
{}

void MyWidget::paintEvent(QPaintEvent *)
{
    // Zeichenstift auf dieses Widget
    QPainter painter(this);
    
    
    QLinearGradient gradient(0, 0, 150, 150);
    gradient.setColorAt(0, Qt::red);
    gradient.setColorAt(0.5, Qt::green);
    gradient.setColorAt(1, Qt::blue);
    painter.setBrush(gradient);
    painter.drawRect(0, 0, 150, 150);
}

int main(int argc, char* * argv)
{
    // Anwendung
    QApplication app(argc, argv);
    
    MyWidget zeichenbrett;
    zeichenbrett.resize(150,300);
    
    QPushButton button("quit", &zeichenbrett);
    button.setGeometry(0, 150, 100, 30);
    
    QObject::connect(&button, SIGNAL(clicked()), &app, SLOT(quit()));
    
    // wenn man auf das Brett klcikt schliesst sich das Programm
    QObject::connect(&zeichenbrett, SIGNAL(clicked()), &app, SLOT(quit()));
    
    QPainter painter;
    
    zeichenbrett.show();
    button.show();
    
    return app.exec();
}

fedderle!!!
Goos
Beiträge: 360
Registriert: 17. August 2004 10:10
Wohnort: Black Forest

Beitrag von Goos »

Du musst halt ueber QMouseEvent deiner Klasse die Mausklicks abfangen und ueberprüfen, ob sich der Mauszeiger derzeit in deinem Rechteck befindet.

Goos
fedderle
Beiträge: 10
Registriert: 19. Oktober 2005 18:23

Beitrag von fedderle »

Jetzt bin ich irgendwie total verwirrt!

Also! Was brauche ich denn jetzt alles um erkennen zu koennen, ob auf das Objekt geklickt wurde!

nur dieses ...

Code: Alles auswählen

class MyPolygon : public QPolygonF, public QObject
{
     Q_OBJECT
     ...
  signals:
     void clicked();
};
und dann irgendwo dieses emit!
Wobei ich hier nicht ganz verstehe wie dass dann gehen soll, weil egal wo ich dieses

Code: Alles auswählen

emit clicked()
innnerhalb der Klasse hinschreibe kommt folgende compiler fehlermeldung
undefined reference to 'MyPolygon::clicked()'
Oder muss ich beides machen also diese QMouseEvent ebenfalls mit reinmachen?????

feddderle
ArneStocker
Beiträge: 300
Registriert: 3. November 2004 16:15
Wohnort: Berlin

Beitrag von ArneStocker »

Innerhalb Deiner Polygonklasse musst Du schon die events auslesen. SIGNAL und SLOT ist eigentlich als Kommunikation zwischen verschiedenen Objekten gedacht (also z.B. einem QButton Objekt und dem QDialog Objekt).

In der Standard - Implementation von QButton wird z.B. auf ein Klick reagiert (wie die das machen kann ich nur vermuten, nämlich über event) und dann das Signal clicked() gesendet.

Wenn Du innerhalb Deiner Polygonklasse auf ein Klickevent reagieren willst, musst Du die events abfangen. Um von Deiner Polygonklasse eine Nachricht an z.B. die Dialogklasse zu versenden nimmst du das emit clicked().

Nur zur Klarstellung : Polygon (Deine Klasse) versendet nicht automatisch ein clicked() event, wenn Du draufklickst (das macht aber z.B. QButton). Das Mausereigniss über deinem Objekt führt von sich aus nicht zum Versenden der Nachricht clicked. Umgekehrt kannst ein clicked() SIGNAL auch versenden, wenn gar nichts geklickt wurde. Das emit clicked() rufst Du also in der eventmethode der Polygon - Klasse auf (nämlich dann wenn auf Dein Objekt geklickt wurde).

Anschliessend werden alle Objekte, die Du mit connect verbunden hast, über ihren SLOT von dem SIGNAL benachrichtigt.

Alle Klarheiten beseitigt ? :-)

Gruss Arne

PS.: wegen des Compiler - Fehlers könnte es sein, dass Du (falls das nicht automatisch von qmake gemacht wird) den moc(Meta - objekt Compiler über Deine Klassendeklaration laufen lassen musst). Wenn Du nachträglich ein Q_OBJECT eingefügt hast, ist m.E. ein erneuter Aufruf von qmake fällig.
fedderle
Beiträge: 10
Registriert: 19. Oktober 2005 18:23

Beitrag von fedderle »

So weit in der theorie klar!
Also ich hab noch mal von vorne angefangen! Ich versuche das ganze Vorhaben jetzt mal genau zu erklären! Insgesammt soll das ganze später ne Landkarte werden, wo man die einzelnen Länder anklicken kann und das aktuell angeklickte Land dann die Farbe ändert!

Ich hab mir dazu gedacht erstellt ein Widget, wo die ganzen einzelnen Länder (Polygone) drauf dargestellt werden! Klingt eigentlich in der Theorie verdammt einfach!

Ich probier jetzt nur schon seit Tagen hier rum! Dachte erst,das kann ja nicht so schwer sein, mach einfach mal ne neue Klasse und schreib die Methode woid paintEvent() um!

Und jetzt kommen wir wieder zu meinem alten Problem!

Habe folgenden Quelltext aktuell mit dem ich herumhantiere:

Code: Alles auswählen

#include <QApplication>
#include <QPushButton>
#include <QWidget>
#include <QPolygonF>
#include <QPainter>
#include <QObject>
#include <QPaintEvent>

class MyQWidget : public QWidget
{
    Q_OBJECT
            
    public:
        void paintEvent(QPaintEvent *);
        
    signals:
        void clicked();
};

void MyQWidget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    
    QPolygonF polygon;
    polygon << QPointF(20,50) << QPointF (40, 40) << QPointF(120, 100) << QPointF(55, 230);
    
    painter.setBrush(Qt::blue);
    painter.drawPolygon(polygon, Qt::OddEvenFill);
}

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    
    MyQWidget widget;
    widget.show();
    
    return app.exec();
}
Wie gesagt habe noch einmal von vorne angefangen! Treffe aber jetzt hier schon auf erste Probleme beim kompililieren!

Sobald ich oben in der eigenen Klasse das Stichwort Q_OBJECT eingebe und irgendwo ein Objekt der Klasse erzeugen (oben geschieht dies in der int main()) möchte, kommt diese Fehlermeldung:

src/testpolygone1.cpp:29: undefined reference to `vtable for MyQWidget'
Wenn ich kein Obejekt erstelle kommt die Meldung nicht! Wenn ich ein Obejkt erstelle aber das Q_OBJECT nicht hinschreibe kommt auch keine fehlermeldung! Aber ich brauch das Q_OBJECT ja!

Dazu noch! Ja ich nutze vor dem kompielieren das qmake!

Also meine eingaben sind da dann immer:
1. qmake -project
2. qmake
3. make


ist doch richtig so!!!!? Oder hab ich was vergessen?
Wäre echt dankbar, wenn mir da irgendjemand helfen könnte!

fedderle
lepsai
Beiträge: 573
Registriert: 14. September 2004 21:33
Wohnort: Berlin
Kontaktdaten:

Beitrag von lepsai »

Es heisst :

Code: Alles auswählen

protected: 
        void paintEvent(QPaintEvent *);
ArneStocker
Beiträge: 300
Registriert: 3. November 2004 16:15
Wohnort: Berlin

Beitrag von ArneStocker »

das protected ist m.E. nicht schuld. Es muss zulässig sein, eine Methode in einer anderen
Klasse mit anderen Berechtigungen zu implementieren

Aber paintEvent ist eine virtuelle Methode und wird zur Laufzeit gebunden. Es sollte daher heissen
virtual void paintEvent(QPaintEvent *pEvent);
Nur zum Verständnis: Dein Programm lässt sich doch übersetzen (sonst könntest Du kein Objekt erzeugen). Erhälst Du die Meldung während des Debuggens in der Konsole ?

Gruss Arne
lepsai
Beiträge: 573
Registriert: 14. September 2004 21:33
Wohnort: Berlin
Kontaktdaten:

Beitrag von lepsai »

Nein, mein Lieber, genau das ist falsch. Eine Funktion bleibt in allen abgeleiteten Klassen virtuell. Also muss es nicht vitual void ... heissen. Dagegen ist es wichtig, dass die Funktion, die ich überlade, in der gleichen Access-Klasse bleibt. Das ist auch irgendwo der Sinn der objekt-orientierten Herangehensweise.
ArneStocker
Beiträge: 300
Registriert: 3. November 2004 16:15
Wohnort: Berlin

Beitrag von ArneStocker »

ich hab's nochmal nachgelesen, Du hast recht.. ups ! Zum Glück hat das virtual vor den Methoden nicht geschadet :-)

Gruss Arne
fedderle
Beiträge: 10
Registriert: 19. Oktober 2005 18:23

Beitrag von fedderle »

Ok Leute hatte ein wenig gedauert!

Hatte immer noch Probleme, das ich nicht kompilieren konnte wenn ich Q_OBJECT in den Code einfüge! Bis ich irgendwann herausgefunden habe, dass das ja nur in headerdateien stehen darf nicht in cpp dateien!

Habe es jetzt folgendermaßen gemacht:

habe eine MyQwidget Klasse von QWidget abgeleitet!

Dort habe ich das siganl void clicked() definiert!

Jetzt nur noch mal um Nummer sicher zu gehen, damit iches auch wirklich richtig verstanden habe!

Jetzt muss ich ne Funktion schreiben/finden, die ermittlet, wann aufd den Button geklickt wurde! und dort das emit clicked() einbinden!!!!??

seh ich das richtig???

Gruß fedderle!
FlorianBecker
Beiträge: 1213
Registriert: 2. Dezember 2004 10:54
Kontaktdaten:

Beitrag von FlorianBecker »

Ja
fedderle
Beiträge: 10
Registriert: 19. Oktober 2005 18:23

Beitrag von fedderle »

OK! Danke! Dann wäre meine Frage beantwortet!
AngelD
Beiträge: 2
Registriert: 1. Dezember 2005 11:04

Beitrag von AngelD »

Hi! Ich habe so ziemlich das selbe Problem, habe das auch so gemacht wie ihr es beschrieben hattet, nur irgendwie will es nicht funktionieren. :(

Code: Alles auswählen

class HPKarte : public QWidget
{
    Q_OBJECT
    
    protected:
        void mousePressEvent(QMouseEvent *event);
        
    signals: 
        void clicked(HPKarte *); // Signal das auf das Widget geklickt wurde 
        
    public slots:
        void cardClicked(HPKarte *);
}

void HPKarte::mousePressEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
        emit clicked(this);
}
und in der main:

Code: Alles auswählen

HPKarte *muh = new HPKarte(HPKarte::Figur(1));
    QObject::connect(muh, SIGNAL(clicked(HPKarte *)), muh, SLOT(cardClicked(HPPolygon *)));
    muh->show();
Also es soll diese Karte angeklickt werden, und dann z.b. ein anderes Bild oder ähnliches geladen werden.

Dies ist auch fast der selbe Code den federle benutzt, nur das es bei ihm funktioniert und bei mir nicht.

Kann mir jemand sagen warum??

Gruß, AngelDust
Antworten