Anzeige:
Seite 2 von 2 ErsteErste 12
Ergebnis 16 bis 28 von 28

Thema: [Qt] Linien dynamisch auf Label zeichnen

  1. #16
    Registrierter Benutzer
    Registriert seit
    23.05.2004
    Beiträge
    592
    Du darfst nicht ausserhalb eines Paintevent auf ein Widget zeichnen. Das geht nur mit einer speziellen Option, und auch dann nur auf X-Windows. Also nicht Windows oder Mac. Siehe dazu QPainter-Doku bzw. QWidget:: paintEvent. Das sollte es eigentlich schon sein. Wenn nach umschreiben die Fehlermeldung immer noch kommt, melde dich nochmal.

  2. #17
    Registrierter Benutzer
    Registriert seit
    12.03.2009
    Beiträge
    36
    Hallo,
    ich habe ein bisschen rumgetüftelt und habe jetzt genau das Problem, das ich schon kommen gesehen habe Es wird gezeichnet, aber immer. Ich hätte gerne, dass auf ein Bild, welches auf dem Label dargestellt wird, gezeichnet wird, wenn ich z.B. einen Button drücke (der z.B. den SLOT zeichnen() auslöst) und das die Zeichnung wieder verschwindet, wenn ich das Bild auf das Label neu zeichne.
    Hier mal mein ergänzender Quellcode

    Änderung in MainWindow.cpp:
    Code:
    imageLabel = new MyLabel(this->centralWidget());
    Neu -> MyLabel.h:
    Code:
    #ifndef MYLABEL_H
    #define MYLABEL_H
    
    #include <Qt/QLabel.h>
    //#include <Qt/QMainWindow.h>
    
    class MyLabel: public QLabel
    {
       Q_OBJECT
    
    //signals:
    
    public:
       MyLabel(QWidget * parent = 0, Qt::WindowFlags f = 0);
    
    protected:
       void paintEvent(QPaintEvent *event);
    
    };
    
    #endif //MYLABEL
    Neu -> MyLabel.cpp:
    Code:
    #include "MyLabel.h"
    #include <QtGui/QMouseEvent>
    #include <QtGui/QtGui>
    
    
    MyLabel::MyLabel(QWidget * parent, Qt::WindowFlags f):
       QLabel(parent, f)
    {}
    
    void MyLabel::paintEvent(QPaintEvent *event)
    {
        QPainter painter(this);
        static const QPointF points[3] = {
         QPointF(10.0, 80.0),
         QPointF(20.0, 10.0),
         QPointF(80.0, 30.0),
        };
    
        painter.drawPolygon(points, 3);
    
    }
    Für jede Hilfe bin ich sehr dankbar!

  3. #18
    Administrator Avatar von anda_skoa
    Registriert seit
    17.11.2001
    Ort
    Graz, Österreich
    Beiträge
    5.477
    Wenn du den den Inhalt des QLabel haben willst, einfach an dessen paintEvent() Method delegieren.

    z.B:

    Code:
    void MyLabel::paintEvent(QPaintEvent *event)
    {
         // zuerst den eigentlichen Labelinhalt zeichen
        QLabel::paintEvent(event);
    
        // dann darauf das eigene
        QPainter painter(this);
        static const QPointF points[3] = {
         QPointF(10.0, 80.0),
         QPointF(20.0, 10.0),
         QPointF(80.0, 30.0),
        };
    
        painter.drawPolygon(points, 3);
    }
    Ciao,
    _
    Qt/KDE Entwickler
    Debian Benutzer

  4. #19
    Registrierter Benutzer
    Registriert seit
    12.03.2009
    Beiträge
    36
    Hallo,

    ich habe es leider immer noch nicht verstanden... Wenn ich es so mache, wie anda_skoa vorschlägt, dann zeichne ich doch immer das Dreieck. Ich möchte aber nur einmal kurz (nach einem Button-Klick z.B.) das Dreieck zeichnen und während der sonstigen Programmlaufzeit gar nicht. Geht das nicht?

  5. #20
    Registrierter Benutzer
    Registriert seit
    23.05.2004
    Beiträge
    592
    Möchtest du
    a) Das Dreieck anzeigen nachdem der Benutzer auf einen Button geklickt, solange bis der Button nochmal geklickt wird
    oder
    b) Nach einem Klick für eine bestimmte Zeit anzeigen, und dann automatisch verschwinden lassen?

    Um Alternative a zu implementieren kannst du z.B.
    1. Einen QPushbutton anlegen
    2. Den Button "checkable" machen
    3. Einen Slot in deiner Klasse anlegen, der ein Repaint auslöst.
    4. Das "clicked(bool)" Signal des Buttons mit deinem Slot verbinden.
    5. In der überschriebenen paintEvent-Funktion abfragen in welchem Zustand sich der Button befindet.
    6. Davon abhängig das Dreieck zeichnen oder nicht.

    Für Alternative b:
    Bei klicken des Buttons (der dann nicht "checkable" sein sollte) irgendeinen Timer starten, das Dreieck zeichnen und bei "feuern" des Timers das Dreieck verschwinden lassen.

  6. #21
    Registrierter Benutzer
    Registriert seit
    12.03.2009
    Beiträge
    36
    Hallo locus vivendi,
    ich habe schon befürchtet, dass ich es so in der Art machen muss. Danke für den Tipp. Ich werde mich mal dransetzen und mein Glück versuchen!

  7. #22
    Registrierter Benutzer
    Registriert seit
    12.03.2009
    Beiträge
    36
    Mhh, was ich jetzt noch nicht verstehe: Mein paintEvent verändere ich in der MyLabel Klasse, den Menü-Button (checkable) binde ich aber in meiner MainWindow Klasse ein. Wie kann ich jetzt auf den Wert von dem Button in der MyLabel Klasse zugreifen. Vielleicht stehe ich auch total auf dem Schlauch

  8. #23
    Administrator Avatar von anda_skoa
    Registriert seit
    17.11.2001
    Ort
    Graz, Österreich
    Beiträge
    5.477
    Eine Möglichkeit ist, in MyLabel einen Slot zu machen und den mit dem Button zu verbinden.

    Hat den Vorteil, dass du ein Update des Labels anfordern kannst

    Code:
    class MyLabel : public QLabel
    {
        Q_OBJECT
    
    //...
    
    public Q_SLOTS:
        void toggleOverlay( bool on );
    
    private:
        bool mShowOverlay;
    };
    Code:
    void MyLabel::toggleOverlay( bool on )
    {
        if ( on != mShowOverlay ) {
            mShowOverlay = on;
            update();
        }
    }
    Ciao,
    _
    Qt/KDE Entwickler
    Debian Benutzer

  9. #24
    Registrierter Benutzer
    Registriert seit
    12.03.2009
    Beiträge
    36
    Hallo anda_skoa,

    vielen Dank für die tolle Idee. Konnte es umsetzen.

    Für mein Programm wäre es sinnvoll, wenn ich die Koordinaten des Dreieckes im Hauptprogramm festlegen könnte. Hat jemand von euch eine Idee, wie ich die Koordinaten aus MainWindow.cpp zeitgleich mit dem bool-Wert, der kennzeichnet, dass gezeichnet werden kann, in MyLabel.cpp schubsen kann? Signals und Slots kann ich - soweit ich durchblicke - ja wohl nur für QObject Klassen verwenden und das ist ein QPointF Array ja nicht.

  10. #25
    Registrierter Benutzer
    Registriert seit
    23.05.2004
    Beiträge
    592
    [...]
    Signals und Slots kann ich - soweit ich durchblicke - ja wohl nur für QObject Klassen verwenden und das ist ein QPointF Array ja nicht.
    Aber du kannst ein QPointF-Objekte durchaus über eine Signal-Slotverbindung senden.
    Code:
    public Q_SLOTS:
    void xyz(QPointF p) { ... }

  11. #26
    Registrierter Benutzer
    Registriert seit
    12.03.2009
    Beiträge
    36
    Hallo ,

    wie sieht denn dann das Signal aus? Ich hatte mir sowas in der Art überlegt:

    MainWindow.h
    Code:
    class MainWindow : public QMainWindow, protected Ui_MainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = 0);
    
    private:
        QImage GenerateQImage(int value);
    
        void setPoints();
    
        MyLabel *imageLabel;
        QPointF *p_pts;
    
    signals:
        void pointsChanged(QPointF *new_p_pts);
    
    };
    MainWindow.cpp
    Code:
    ...
    MainWindow::MainWindow(QWidget *parent)
    	: QMainWindow(parent)
    {
    ...
    	setPoints();
    	connect(action_Beenden, SIGNAL(triggered()), this, SLOT(close()));
    	connect(WAS MUSS HIER HIN???, SIGNAL(pointsChanged(QPointF *)), imageLabel, SLOT(setPointArray(QPointF *)));
    	connect(actionAnzeigen, SIGNAL(triggered(bool)), imageLabel, SLOT(toggleOverlay(bool)));
    }
    
    void MainWindow::setPoints()
    {
    	QPointF pts[3] = {
         QPointF(10.0, 80.0),
         QPointF(20.0, 10.0),
         QPointF(80.0, 30.0),
        };
    
    	if(&pts[0] != p_pts)
    	{
    		p_pts = pts;
    		emit pointsChanged(p_pts);
    	}
    }
    Und in der MyLabel Klasse

    MyLabel.h
    Code:
    class MyLabel: public QLabel
    {
       Q_OBJECT
    
    //signals:
    
    public:
       MyLabel(QWidget * parent = 0, Qt::WindowFlags f = 0);
    
    protected:
       void paintEvent(QPaintEvent *event);
    
    public Q_SLOTS:
        void toggleOverlay(bool on);
        void setPointArray(QPointF *pts);
    
    private:
        bool mShowOverlay;
        QPointF *points;
    
    };
    MyLabel.cpp
    Code:
    ...
    void MyLabel::setPointArray(QPointF *pts)
    {
        points = pts;
    }
    Ich bin mir aber nicht sicher, ob das der richtige Weg ist und wie gesagt weiß ich nicht, wer der Sender des Signals sein muss...

  12. #27
    Registrierter Benutzer
    Registriert seit
    23.05.2004
    Beiträge
    592
    Wenn du nur einen Zeiger auf die Punkte speicherst wirst du unter Umständen Speicherfehler bekommen. So wie der Code in deinem letzten Post steht, sogar ganz bestimmt.

    Ich habe mal zwei Dateien angehängt, als kleines Beispiel wie du die Verbindung zwischen Label und Mainwindow herstellen kannst.

    Erst "moc -o xw.moc.cc xw.hh" aufrufen. Dann xw.cc kompilieren. Die Ausgabe von moc wird in xw.cc unten eingebunden, es ist also nicht notwendig noch irgendwelche .o-Dateien einzubinden.

    Okay... Dateien anhängen geht aus irgendeinem Grund nicht, also hier direkt:
    xw.hh:
    Code:
    #ifndef XW_HH
    #define XW_HH
    
    #include "QtCore/QObject"
    #include "QtCore/QPointF"
    #include "QtGui/QApplication"
    #include "QtGui/QColor"
    #include "QtGui/QHBoxLayout"
    #include "QtGui/QPainter"
    #include "QtGui/QPen"
    #include "QtGui/QPushButton"
    #include "QtGui/QWidget"
    
    class X : public QWidget // Dies ist die "Label"-Klasse, welche das Dreieck anzeigt.
    {
      Q_OBJECT
    
    public:
      X(QWidget* parent) : QWidget(parent), do_highlight(false)
      {
        setMinimumSize(400, 300);
        setAutoFillBackground(true);
      }
    
    public Q_SLOTS:
      void highlight(bool yes_or_no, QPointF a, QPointF b, QPointF c)
      {
        do_highlight = yes_or_no;
        points[0] = a; points[1] = b; points[2] = c;
        update();
      }
    
    protected:
      virtual void paintEvent(QPaintEvent*)
      {
        if(do_highlight) {
          QPainter painter(this);
          QPen pen(QColor(255, 0, 0));
          pen.setWidth(3);
          painter.setPen(pen);
          painter.drawConvexPolygon(points, 3);
        }
      }
    
    private:
      bool do_highlight;
      QPointF points[3];
    };
    
    class W : public QWidget // Dies ist die "Mainwindow"-Klasse.
    {
      Q_OBJECT
    
    public:
      W() : layout(this), show_button("Press!", this), exit_button("Exit!", this), triangle(this)
      {
        setLayout(&layout);
        layout.addWidget(&show_button);
        layout.addWidget(&exit_button);
        layout.addWidget(&triangle);
        show_button.setCheckable(true); // Die letzten fünf Zeilen waren Vorarbeiten..
        QObject::connect(&show_button, SIGNAL(clicked(bool)), this, SLOT(button_clicked(bool))); // Interne Verbindung.
        QObject::connect(&exit_button, SIGNAL(pressed()), QApplication::instance(), SLOT(quit())); // Beendet Programm auf Klick.
        QObject::connect(this, SIGNAL(have_points(bool, QPointF, QPointF, QPointF)), &triangle, SLOT(highlight(bool, QPointF, QPointF, QPointF)));
        // Die letzte Zeile verbindet die beiden Klassen miteinander.
      }
    
    Q_SIGNALS:
    void have_points(bool yes_or_no, QPointF a, QPointF b, QPointF c); // Dieses ist das Signal welches die beiden Klassen verbindet.
    
    private Q_SLOTS:
    void button_clicked(bool checked) { // Dies ist ein "interner" Slot.
      QPointF p1(10.0, 10.0), p2(100.0, 100.0), p3(190.0, 10.0); // Diese Punkte dienen nur als Beispiel.
      // Folgende Zeile ist die "Magie": Hier wird das clicked()-Signal des Pushbuttons auf die richige Signatur erweitert.
      Q_EMIT have_points(checked, p1, p2, p3);
      // Der Pushbutton löst also indirekt have_points(bool, QPointF, QPointF, QPointF) aus.
    }
    
    private:
      QHBoxLayout layout;
      QPushButton show_button, exit_button;
      X triangle;
    };
    
    #endif
    xw.cc:
    Code:
    #include "xw.hh"
    
    int main(int argc, char* argv[])
    {
      QApplication app(argc, argv);
      W w;
      w.show();
      return app.exec();
    }
    
    #include "xw.moc.cc"

  13. #28
    Administrator Avatar von anda_skoa
    Registriert seit
    17.11.2001
    Ort
    Graz, Österreich
    Beiträge
    5.477
    Der erste Parameter im connect ist immer der Sender.
    D.h. eine Instanz der Klasse, die das Signal anbietet. In deinem Fall also "this".

    An irgendeiner Stelle in deinem Mainwindow, wenn du eben neue Punkte haben willst, machst du dann

    Code:
    emit pointsChanged( p_pts );
    Also das Signal mit aktuellen Werten aussenden.

    Was du allerdings ändern solltest, ist den Signalparametertyp.
    Pointer sind nicht sehr gut, weil man dann leicht vergisst, wer der "Besitzer" ist (also wer den Pointer löschen muss).

    Mein Vorschlag wäre QVector, also

    Code:
    // ....
    
       QVector<QPointF> p_pts;
    
    signals:
        void pointsChanged( QVector<QPointF> new_p_pts );
    QVector hat hier zusätzlich den Vorteil, dass du eine sichere Länge hast, die du abfragen kannst.

    Ciao,
    _
    Qt/KDE Entwickler
    Debian Benutzer

Lesezeichen

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •