Grafik in Qt
Willemers Informatik-Ecke
Weitere Themen


QGraphicsView und QGraphicsScene

Im Qt-Designer wählt man ein QGraphicsView aus und positioniert das Element. Für eine QGraphicsView realisiert QGraphicsScene das Modell. Eine Scene kann auch für verschiedene Views als Modell dienen.

QGraphicsScene wiederum dient als Container für Grafikobjekte, die von QGraphicsItem abgeleitet werden. Dazu dient die Funktion QGraphicsItem::addItem(). Allerdings gibt es noch ein paar spezialisierte Funktionen, die mit add beginnen und dann die Art des Grafikobjekts nennen, wie beispielsweise addLine, um eine Linie zu ziehen.

Alternativ kann mit addWidget der QGraphicsScene ein Element der Klasse QWidget übergeben werden. Dann wird vergleichbar mit klassischen Oberflächen die Funktion paintEvent überschrieben und über einen Painter die Zeichenprimitive aufgerufen.

Der Unterschied zwischen diesen Ansätzen ist, dass im ersten Ansatz die gezeichneten Items als Objekte nach dem Zeichnen zur Verfügung stehen und eigene Objekte sind. Der zweite Ansatz eignet sich eher bei einfachen Grafiken.

Einbau eines 2-D-Widgets im Qt-Designer

Im Qt-Creator kann ein Element vom Typ QGraphicsView eingefügt werden. Das Objekt erhält beispielsweise den Namen graphview. Nun kann im Konstruktor des Hauptfensters eine eigene Scene mit der folgenden Zeile eingebettet werden:
QGraphicsScene *scene = new MeinGraphScene(this);
ui->graphview->setScene(scene);
MeinGraphScene wird von QGraphicsScene abgeleitet. Wenn mit diesen Parametern eine neue Klasse über den Qt-Creator angelegt wird, wird automatisch eine solche Headerdatei angelegt:
#ifndef MEINGRAPHSCENE_H
#define MEINGRAPHSCENE_H

#include <QGraphicsScene>

class MeinGraphScene : public QGraphicsScene
{
    Q_OBJECT
public:
    explicit MeinGraphScene(QObject *parent = 0);

protected:
    void paint(QPainter *painter, const QRectF &rect);

signals:

public slots:

};

#endif // MEINGRAPHSCENE_H

Items in der Scene

Im Konstruktor von MeinGraphScene wiederum können nun die einzelnen Elemente eingebettet werden. Im folgenden Beispiel wird ein Rechteck angelegt.
MeinGraphScene::MeinGraphScene(QObject *parent) : QGraphicsScene(0,0,1000,500)

{
    QColor color(255, 0, 0);
    QBrush brush(color);
    QPen pen(color);
    QGraphicsRectItem *rect = addRect(10, 10, 100, 100, pen, brush);
}
Der Aufruf des Konstruktors hinter dem Doppelpunkt bewirkt, dass die Scene auf ein Koordinatensystem von 1000x500 definiert wird.

Über den Zeiger rect kann das Item später jederzeit zugegriffen werden.

Neben dem Rechteck gibt es verschiedene weitere Elemente .

Verwendung eines QWidgets

Statt die Scene mit Items zu füllen, kann ihr mit der Funktion addWidget auch ein von QWidget abgeleitetes Objekt übergeben werden.
MeinGraphScene::MeinGraphScene(QObject *parent) : QGraphicsScene(0,0,1000,500)
{
    addWidget(new BelegWidget());
}
Nun wird eine neue Klasse erzeugt und man gibt dem Qt-Creator an, dass er eine Klasse erzeugt, die von QWidget abgeleitet wird. Er erstellt daraufhin Header und CPP-Datei mit einem Rahmen.

Das neue QWidget kann nun die Funktion paintEvent überschreiben, um die Grafik aufzubauen und beispielsweise mousePressEvent überschreiben, um die Mausaktionen zu überwachen.

#ifndef BELEGWIDGET_H
#define BELEGWIDGET_H

#include 

class BelegWidget : public QWidget
{
    Q_OBJECT
public:
    explicit BelegWidget(QWidget *parent = 0);

signals:

public slots:
    void paintEvent(QPaintEvent *event);

protected:
    void mousePressEvent(QMouseEvent *event);
};

#endif // BELEGWIDGET_H
Die Funktion paintEvent wird immer dann aufgerufen, wenn der Widget-Inhalt dargestellt wird. Das ist das erste Mal dann, wenn das Widget angelegt wird und später bei jeder aufgedeckten Überlappung durch andere Fenster.
void BelegWidget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    QFontMetrics metrics = painter.fontMetrics();
    int boxwidth = metrics.width('0')*2 + 2;
    int boxheight = metrics.height() + metrics.descent();
    // Farbenvorbereitungen für Brush und Pen
    QColor brushcolor(0, 255, 255);
    QBrush brush(brushcolor);
    QColor pencolor(0,0,0);
    QPen pen(pencolor);
    painter.setPen(pen);
    for (int tag=0; tag<monatsTage; ++tag)
    {
        painter.fillRect(tag*boxwidth, 0, boxwidth, boxheight, brush);
        painter.drawRect(tag*boxwidth, 0, boxwidth, boxheight);
        QString str = QString::number(tag+1);
        painter.drawText(tag*boxwidth+1, boxheight-1, str);
    }
    ...
}

Mausklick in der Scene

Damit eine Scene einen Mausklick auswerten kann, wird die die virtuelle Funktion mouseDoubleClickEvent, mouseMoveEvent, mousePressEvent oder mouseReleaseEvent überschrieben. Über den Parameter vom Typ QGraphicsSceneMouseEvent erhält die Funktion alle Informationen über das eingetretene Mausereignis.

event->scenePos() liefert QPointF, dessen Funktionen x() und y() die Position der Maus angeben.

void MeinGraphScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{

    QPointF pos = event->scenePos();
    Qt::MouseButton button = event->button();
    switch (button) {
    case Qt::LeftButton:
        butStr = "left";
        break;
    case Qt::RightButton:
        butStr = "right";
        break;
    }
    std::cout << "mouse" << pos.x() <<", " << pos.y()
              << " button: " << butStr.toStdString() << std::endl;
}

Mausklick auf ein QWidget

Ein QWidget empfängt die Mausereignisse über eigene mousePressEvents. Der Parameter ist hier ein Zeiger auf QMouseEvent.
void BelegWidget::mousePressEvent(QMouseEvent *event)
{

    QPointF pos = event->pos();
    Qt::MouseButton button = event->button();
    QString butStr;
    switch (button) {
    case Qt::LeftButton:
        butStr = "left";
        break;
    case Qt::RightButton:
        butStr = "right";
        break;
    }
    std::cout << "widget mouse" << pos.x()<<","<<pos.y()
              << " button: " << butStr.toStdString() << std::endl;
}

OpenGL

Ein Tutorial zur Beschreibung von OpenGL unter Qt:

Qt5 Tutorial OpenGL with QGLWidget