Basic Drawing Beispiel

Das Basic-Drawing-Beispiel zeigt, wie grundlegende Grafikprimitive in einer Vielzahl von Stilen mit der Klasse QPainter angezeigt werden können.

QPainter führt Low-Level-Malerei auf Widgets und anderen Malgeräten durch. Die Klasse kann alles zeichnen, von einfachen Linien bis hin zu komplexen Formen wie Torten und Akkorde. Sie kann auch ausgerichteten Text und Pixmaps zeichnen. Normalerweise zeichnet sie in einem "natürlichen" Koordinatensystem, aber sie kann auch Ansichts- und Welttransformationen durchführen.

Das Beispiel stellt einen Renderbereich zur Verfügung, der die gerade aktive Form anzeigt, und lässt den Benutzer die gerenderte Form und ihr Aussehen mit den Parametern von QPainter manipulieren: Der Benutzer kann die aktive Form (Shape) ändern und den Stift (Pen Width, Pen Style, Pen Cap, Pen Join), den Pinsel (Brush Style) und die Render-Hinweise (Antialiasing) von QPainter ändern. Darüber hinaus kann der Benutzer eine Form drehen (Transformations); hinter den Kulissen nutzen wir die Fähigkeit von QPainter, das Koordinatensystem zu manipulieren, um die Drehung durchzuführen.

Das Basic Drawing Beispiel besteht aus zwei Klassen:

  • RenderArea ist ein benutzerdefiniertes Widget, das mehrere Kopien der gerade aktiven Form darstellt.
  • Window ist das Hauptfenster der Anwendung, das ein RenderArea Widget sowie mehrere Parameter-Widgets anzeigt.

Zunächst wird die Klasse Window besprochen, dann wird die Klasse RenderArea betrachtet.

Definition der Klasse Window

Die Klasse Window erbt von QWidget und ist das Hauptfenster der Anwendung, in dem neben mehreren Parameter-Widgets auch ein RenderArea -Widget angezeigt wird.

class Window : public QWidget
{
    Q_OBJECT

public:
    Window();

private slots:
    void shapeChanged();
    void penChanged();
    void brushChanged();

private:
    RenderArea *renderArea;
    QLabel *shapeLabel;
    QLabel *penWidthLabel;
    QLabel *penStyleLabel;
    QLabel *penCapLabel;
    QLabel *penJoinLabel;
    QLabel *brushStyleLabel;
    QLabel *otherOptionsLabel;
    QComboBox *shapeComboBox;
    QSpinBox *penWidthSpinBox;
    QComboBox *penStyleComboBox;
    QComboBox *penCapComboBox;
    QComboBox *penJoinComboBox;
    QComboBox *brushStyleComboBox;
    QCheckBox *antialiasingCheckBox;
    QCheckBox *transformationsCheckBox;
};

Wir deklarieren die verschiedenen Widgets und drei private Slots, die das RenderArea Widget aktualisieren: Der Slot shapeChanged() aktualisiert das Widget RenderArea, wenn der Benutzer die derzeit aktive Form ändert. Wir rufen den Slot penChanged() auf, wenn sich einer der Stiftparameter von QPainter ändert. Und der brushChanged() -Slot aktualisiert das RenderArea -Widget, wenn der Benutzer den Pinselstil des Malers ändert.

Implementierung der Fensterklasse

Im Konstruktor erstellen und initialisieren wir die verschiedenen Widgets, die im Hauptfenster der Anwendung erscheinen.

Window::Window()
{
    renderArea = new RenderArea;

    shapeComboBox = new QComboBox;
    shapeComboBox->addItem(tr("Polygon"), RenderArea::Polygon);
    shapeComboBox->addItem(tr("Rectangle"), RenderArea::Rect);
    shapeComboBox->addItem(tr("Rounded Rectangle"), RenderArea::RoundedRect);
    shapeComboBox->addItem(tr("Ellipse"), RenderArea::Ellipse);
    shapeComboBox->addItem(tr("Pie"), RenderArea::Pie);
    shapeComboBox->addItem(tr("Chord"), RenderArea::Chord);
    shapeComboBox->addItem(tr("Path"), RenderArea::Path);
    shapeComboBox->addItem(tr("Line"), RenderArea::Line);
    shapeComboBox->addItem(tr("Polyline"), RenderArea::Polyline);
    shapeComboBox->addItem(tr("Arc"), RenderArea::Arc);
    shapeComboBox->addItem(tr("Points"), RenderArea::Points);
    shapeComboBox->addItem(tr("Text"), RenderArea::Text);
    shapeComboBox->addItem(tr("Pixmap"), RenderArea::Pixmap);

    shapeLabel = new QLabel(tr("&Shape:"));
    shapeLabel->setBuddy(shapeComboBox);

Zuerst erstellen wir das RenderArea Widget, das die derzeit aktive Form darstellt. Dann erstellen wir die Shape Combobox und fügen die zugehörigen Elemente hinzu (d.h. die verschiedenen Formen, die QPainter zeichnen kann).

    penWidthSpinBox = new QSpinBox;
    penWidthSpinBox->setRange(0, 20);
    penWidthSpinBox->setSpecialValueText(tr("0 (cosmetic pen)"));

    penWidthLabel = new QLabel(tr("Pen &Width:"));
    penWidthLabel->setBuddy(penWidthSpinBox);

QPainterDer Stift ist ein QPen Objekt; die QPen Klasse definiert, wie ein Maler Linien und Umrisse von Formen zeichnen sollte. Ein Stift hat mehrere Eigenschaften: Breite, Stil, Kappe und Verbindung.

Die Breite eines Stifts kann null oder größer sein, aber die häufigste Breite ist null. Dies bedeutet nicht 0 Pixel, sondern impliziert, dass die Form so gleichmäßig wie möglich gezeichnet wird, auch wenn dies vielleicht nicht mathematisch korrekt ist.

Wir erstellen eine QSpinBox für den Parameter Pen Width.

    penStyleComboBox = new QComboBox;
    penStyleComboBox->addItem(tr("Solid"), static_cast<int>(Qt::SolidLine));
    penStyleComboBox->addItem(tr("Dash"), static_cast<int>(Qt::DashLine));
    penStyleComboBox->addItem(tr("Dot"), static_cast<int>(Qt::DotLine));
    penStyleComboBox->addItem(tr("Dash Dot"), static_cast<int>(Qt::DashDotLine));
    penStyleComboBox->addItem(tr("Dash Dot Dot"), static_cast<int>(Qt::DashDotDotLine));
    penStyleComboBox->addItem(tr("None"), static_cast<int>(Qt::NoPen));

    penStyleLabel = new QLabel(tr("&Pen Style:"));
    penStyleLabel->setBuddy(penStyleComboBox);

    penCapComboBox = new QComboBox;
    penCapComboBox->addItem(tr("Flat"), Qt::FlatCap);
    penCapComboBox->addItem(tr("Square"), Qt::SquareCap);
    penCapComboBox->addItem(tr("Round"), Qt::RoundCap);

    penCapLabel = new QLabel(tr("Pen &Cap:"));
    penCapLabel->setBuddy(penCapComboBox);

    penJoinComboBox = new QComboBox;
    penJoinComboBox->addItem(tr("Miter"), Qt::MiterJoin);
    penJoinComboBox->addItem(tr("Bevel"), Qt::BevelJoin);
    penJoinComboBox->addItem(tr("Round"), Qt::RoundJoin);

    penJoinLabel = new QLabel(tr("Pen &Join:"));
    penJoinLabel->setBuddy(penJoinComboBox);

Der Stiftstil definiert den Linientyp. Der Standardstil ist solid (Qt::SolidLine). Wenn Sie den Stil auf "none" (Qt::NoPen) setzen, wird der Zeichner angewiesen, keine Linien oder Umrisse zu zeichnen. Die Stiftkappe definiert, wie die Endpunkte der Linien gezeichnet werden. Und die Stiftverbindung legt fest, wie zwei Linien verbunden werden, wenn mehrere verbundene Linien gezeichnet werden. Die Kappe und die Verbindung gelten nur für Linien mit einer Breite von 1 Pixel oder mehr.

Wir erstellen QComboBoxes für jeden der Parameter Pen Style, Pen Cap und Pen Join und fügen die zugehörigen Elemente hinzu (d. h. die Werte der Enums Qt::PenStyle, Qt::PenCapStyle bzw. Qt::PenJoinStyle ).

    brushStyleComboBox = new QComboBox;
    brushStyleComboBox->addItem(tr("Linear Gradient"),
            static_cast<int>(Qt::LinearGradientPattern));
    brushStyleComboBox->addItem(tr("Radial Gradient"),
            static_cast<int>(Qt::RadialGradientPattern));
    brushStyleComboBox->addItem(tr("Conical Gradient"),
            static_cast<int>(Qt::ConicalGradientPattern));
    brushStyleComboBox->addItem(tr("Texture"), static_cast<int>(Qt::TexturePattern));
    brushStyleComboBox->addItem(tr("Solid"), static_cast<int>(Qt::SolidPattern));
    brushStyleComboBox->addItem(tr("Horizontal"), static_cast<int>(Qt::HorPattern));
    brushStyleComboBox->addItem(tr("Vertical"), static_cast<int>(Qt::VerPattern));
    brushStyleComboBox->addItem(tr("Cross"), static_cast<int>(Qt::CrossPattern));
    brushStyleComboBox->addItem(tr("Backward Diagonal"), static_cast<int>(Qt::BDiagPattern));
    brushStyleComboBox->addItem(tr("Forward Diagonal"), static_cast<int>(Qt::FDiagPattern));
    brushStyleComboBox->addItem(tr("Diagonal Cross"), static_cast<int>(Qt::DiagCrossPattern));
    brushStyleComboBox->addItem(tr("Dense 1"), static_cast<int>(Qt::Dense1Pattern));
    brushStyleComboBox->addItem(tr("Dense 2"), static_cast<int>(Qt::Dense2Pattern));
    brushStyleComboBox->addItem(tr("Dense 3"), static_cast<int>(Qt::Dense3Pattern));
    brushStyleComboBox->addItem(tr("Dense 4"), static_cast<int>(Qt::Dense4Pattern));
    brushStyleComboBox->addItem(tr("Dense 5"), static_cast<int>(Qt::Dense5Pattern));
    brushStyleComboBox->addItem(tr("Dense 6"), static_cast<int>(Qt::Dense6Pattern));
    brushStyleComboBox->addItem(tr("Dense 7"), static_cast<int>(Qt::Dense7Pattern));
    brushStyleComboBox->addItem(tr("None"), static_cast<int>(Qt::NoBrush));

    brushStyleLabel = new QLabel(tr("&Brush:"));
    brushStyleLabel->setBuddy(brushStyleComboBox);

Die Klasse QBrush definiert das Füllmuster von Formen, die von einem QPainter gezeichnet werden. Der Standard-Pinselstil ist Qt::NoBrush. Dieser Stil weist den Maler an, Formen nicht zu füllen. Der Standardstil für das Füllen ist Qt::SolidPattern.

Wir erstellen ein QComboBox für den Parameter Brush Style und fügen die zugehörigen Elemente hinzu (d. h. die Werte der Aufzählung Qt::BrushStyle ).

    otherOptionsLabel = new QLabel(tr("Options:"));
    antialiasingCheckBox = new QCheckBox(tr("&Antialiasing"));

Antialiasing ist eine Funktion, die die Pixel "glättet", um gleichmäßigere und weniger gezackte Linien zu erzeugen, und kann mit den Render-Hinweisen von QPainter angewendet werden. QPainter::RenderHints wird verwendet, um Flags für QPainter anzugeben, die von einer bestimmten Engine beachtet werden können oder nicht.

Wir erstellen einfach ein QCheckBox für die Option Antialiasing.

    transformationsCheckBox = new QCheckBox(tr("&Transformations"));

Die Option Transformations impliziert eine Manipulation des Koordinatensystems, die so aussieht, als ob die gerenderte Form in drei Dimensionen gedreht wäre.

Wir verwenden die Funktionen QPainter::translate(), QPainter::rotate() und QPainter::scale(), um diese Funktion zu implementieren, die im Hauptanwendungsfenster durch ein einfaches QCheckBox dargestellt wird.

    connect(shapeComboBox, &QComboBox::activated,
            this, &Window::shapeChanged);
    connect(penWidthSpinBox, &QSpinBox::valueChanged,
            this, &Window::penChanged);
    connect(penStyleComboBox, &QComboBox::activated,
            this, &Window::penChanged);
    connect(penCapComboBox, &QComboBox::activated,
            this, &Window::penChanged);
    connect(penJoinComboBox, &QComboBox::activated,
            this, &Window::penChanged);
    connect(brushStyleComboBox, &QComboBox::activated,
            this, &Window::brushChanged);
    connect(antialiasingCheckBox, &QAbstractButton::toggled,
            renderArea, &RenderArea::setAntialiased);
    connect(transformationsCheckBox, &QAbstractButton::toggled,
            renderArea, &RenderArea::setTransformed);

Dann verbinden wir die Parameter-Widgets mit den zugehörigen Slots mit Hilfe der statischen Funktion QObject::connect(), um sicherzustellen, dass das Widget RenderArea immer dann aktualisiert wird, wenn der Benutzer die Form oder einen der anderen Parameter ändert.

    QGridLayout *mainLayout = new QGridLayout;
    mainLayout->setColumnStretch(0, 1);
    mainLayout->setColumnStretch(3, 1);
    mainLayout->addWidget(renderArea, 0, 0, 1, 4);
    mainLayout->addWidget(shapeLabel, 2, 0, Qt::AlignRight);
    mainLayout->addWidget(shapeComboBox, 2, 1);
    mainLayout->addWidget(penWidthLabel, 3, 0, Qt::AlignRight);
    mainLayout->addWidget(penWidthSpinBox, 3, 1);
    mainLayout->addWidget(penStyleLabel, 4, 0, Qt::AlignRight);
    mainLayout->addWidget(penStyleComboBox, 4, 1);
    mainLayout->addWidget(penCapLabel, 3, 2, Qt::AlignRight);
    mainLayout->addWidget(penCapComboBox, 3, 3);
    mainLayout->addWidget(penJoinLabel, 2, 2, Qt::AlignRight);
    mainLayout->addWidget(penJoinComboBox, 2, 3);
    mainLayout->addWidget(brushStyleLabel, 4, 2, Qt::AlignRight);
    mainLayout->addWidget(brushStyleComboBox, 4, 3);
    mainLayout->addWidget(otherOptionsLabel, 5, 0, Qt::AlignRight);
    mainLayout->addWidget(antialiasingCheckBox, 5, 1, 1, 1, Qt::AlignRight);
    mainLayout->addWidget(transformationsCheckBox, 5, 2, 1, 2, Qt::AlignRight);
    setLayout(mainLayout);

    shapeChanged();
    penChanged();
    brushChanged();
    antialiasingCheckBox->setChecked(true);

    setWindowTitle(tr("Basic Drawing"));
}

Schließlich fügen wir die verschiedenen Widgets zu einem Layout hinzu und rufen die Slots shapeChanged(), penChanged() und brushChanged() auf, um die Anwendung zu initialisieren. Außerdem schalten wir die Antialiasing-Funktion ein.

void Window::shapeChanged()
{
    RenderArea::Shape shape = RenderArea::Shape(shapeComboBox->itemData(
            shapeComboBox->currentIndex(), IdRole).toInt());
    renderArea->setShape(shape);
}

Der Slot shapeChanged() wird immer dann aufgerufen, wenn der Benutzer die derzeit aktive Form ändert.

Zunächst wird die vom Benutzer gewählte Form mit der Funktion QComboBox::itemData() abgerufen. Diese Funktion gibt die Daten für die angegebene Rolle im angegebenen Index in der Combobox zurück. Wir verwenden QComboBox::currentIndex(), um den Index der Form abzurufen, und die Rolle wird durch das Qt::ItemDataRole enum definiert; IdRole ist ein Alias für Qt::UserRole.

Beachten Sie, dass Qt::UserRole nur die erste Rolle ist, die für anwendungsspezifische Zwecke verwendet werden kann. Wenn Sie verschiedene Daten im selben Index speichern müssen, können Sie verschiedene Rollen verwenden, indem Sie einfach den Wert von Qt::UserRole inkrementieren, zum Beispiel: "Qt::UserRole + 1" und "Qt::UserRole + 2". Es ist jedoch eine gute Programmierpraxis, jeder Rolle einen eigenen Namen zu geben: "myFirstRole = Qt::UserRole + 1" und "mySecondRole = Qt::UserRole + 2". Obwohl wir in diesem Beispiel nur eine einzige Rolle benötigen, fügen wir die folgende Codezeile am Anfang der Datei window.cpp ein.

const int IdRole = Qt::UserRole;

Die Funktion QComboBox::itemData() gibt die Daten als QVariant zurück, also müssen wir die Daten in RenderArea::Shape umwandeln. Wenn es keine Daten für die angegebene Rolle gibt, gibt die Funktion QVariant::Invalid zurück.

Zum Schluss rufen wir den RenderArea::setShape() Slot auf, um das RenderArea Widget zu aktualisieren.

void Window::penChanged()
{
    int width = penWidthSpinBox->value();
    Qt::PenStyle style = Qt::PenStyle(penStyleComboBox->itemData(
            penStyleComboBox->currentIndex(), IdRole).toInt());
    Qt::PenCapStyle cap = Qt::PenCapStyle(penCapComboBox->itemData(
            penCapComboBox->currentIndex(), IdRole).toInt());
    Qt::PenJoinStyle join = Qt::PenJoinStyle(penJoinComboBox->itemData(
            penJoinComboBox->currentIndex(), IdRole).toInt());

    renderArea->setPen(QPen(Qt::blue, width, style, cap, join));
}

Wir rufen den penChanged() Slot immer dann auf, wenn der Benutzer einen der Stiftparameter ändert. Auch hier verwenden wir die Funktion QComboBox::itemData(), um die Parameter abzurufen, und rufen dann den Slot RenderArea::setPen() auf, um das Widget RenderArea zu aktualisieren.

void Window::brushChanged()
{
    Qt::BrushStyle style = Qt::BrushStyle(brushStyleComboBox->itemData(

Der brushChanged()-Slot wird immer dann aufgerufen, wenn der Benutzer den Pinselparameter ändert, den wir wie zuvor mit der Funktion QComboBox::itemData() abrufen.

    if (style == Qt::LinearGradientPattern) {
        QLinearGradient linearGradient(0, 0, 100, 100);
        linearGradient.setColorAt(0.0, Qt::white);
        linearGradient.setColorAt(0.2, Qt::green);
        linearGradient.setColorAt(1.0, Qt::black);
        renderArea->setBrush(linearGradient);

Handelt es sich bei dem Pinselparameter um eine Farbverlaufsfüllung, sind spezielle Aktionen erforderlich.

Die Klasse QGradient wird in Kombination mit QBrush verwendet, um Gradientenfüllungen zu spezifizieren. Qt unterstützt derzeit drei Arten von Farbverläufen: linear, radial und konisch. Jeder dieser Typen wird durch eine Unterklasse von QGradient repräsentiert: QLinearGradient, QRadialGradient und QConicalGradient dargestellt.

Wenn der Pinselstil also Qt::LinearGradientPattern ist, erstellen wir zunächst ein QLinearGradient Objekt mit einem Interpolationsbereich zwischen den Koordinaten, die als Argumente an den Konstruktor übergeben werden. Die Positionen werden mit logischen Koordinaten angegeben. Dann legen wir die Farben des Farbverlaufs mit der Funktion QGradient::setColorAt() fest. Die Farben werden mit Hilfe von Haltepunkten definiert, die aus einer Position (zwischen 0 und 1) und einem QColor bestehen. Die Menge der Haltepunkte beschreibt, wie der Bereich des Farbverlaufs gefüllt werden soll. Ein Farbverlauf kann eine beliebige Anzahl von Haltepunkten haben.

Zum Schluss rufen wir RenderArea::setBrush() auf, um den Pinsel des Widgets RenderArea mit dem Objekt QLinearGradient zu aktualisieren.

    } else if (style == Qt::RadialGradientPattern) {
        QRadialGradient radialGradient(50, 50, 50, 70, 70);
        radialGradient.setColorAt(0.0, Qt::white);
        radialGradient.setColorAt(0.2, Qt::green);
        radialGradient.setColorAt(1.0, Qt::black);
        renderArea->setBrush(radialGradient);
    } else if (style == Qt::ConicalGradientPattern) {
        QConicalGradient conicalGradient(50, 50, 150);
        conicalGradient.setColorAt(0.0, Qt::white);
        conicalGradient.setColorAt(0.2, Qt::green);
        conicalGradient.setColorAt(1.0, Qt::black);
        renderArea->setBrush(conicalGradient);

Ein ähnliches Aktionsmuster wie bei QLinearGradient wird in den Fällen Qt::RadialGradientPattern und Qt::ConicalGradientPattern verwendet.

Der einzige Unterschied besteht in den Argumenten, die dem Konstruktor übergeben werden: Beim QRadialGradient -Konstruktor ist das erste Argument der Mittelpunkt und das zweite der Radius des Radialgradienten. Das dritte Argument ist optional, kann aber verwendet werden, um den Brennpunkt des Gradienten innerhalb des Kreises zu definieren (der Standardbrennpunkt ist der Kreismittelpunkt). Für den Konstruktor QConicalGradient gibt das erste Argument den Mittelpunkt des Kegels an, das zweite den Startwinkel der Interpolation.

    } else if (style == Qt::TexturePattern) {
        renderArea->setBrush(QBrush(QPixmap(":/images/brick.png")));

Wenn der Pinselstil Qt::TexturePattern ist, erstellen wir ein QBrush aus einem QPixmap. Dann rufen wir RenderArea::setBrush() auf, um das Widget RenderArea mit dem neu erstellten Pinsel zu aktualisieren.

    } else {
        renderArea->setBrush(QBrush(Qt::green, style));
    }
}

Andernfalls erstellen wir einfach einen Pinsel mit dem angegebenen Stil und einer grünen Farbe, und rufen dann RenderArea::setBrush() auf, um das RenderArea Widget mit dem neu erstellten Pinsel zu aktualisieren.

Definition der Klasse RenderArea

Die Klasse RenderArea erbt QWidget und rendert mehrere Kopien der aktuell aktiven Form unter Verwendung von QPainter.

class RenderArea : public QWidget
{
    Q_OBJECT

public:
    enum Shape { Line, Points, Polyline, Polygon, Rect, RoundedRect, Ellipse, Arc,
                 Chord, Pie, Path, Text, Pixmap };

    explicit RenderArea(QWidget *parent = nullptr);

    QSize minimumSizeHint() const override;
    QSize sizeHint() const override;

public slots:
    void setShape(Shape shape);
    void setPen(const QPen &pen);
    void setBrush(const QBrush &brush);
    void setAntialiased(bool antialiased);
    void setTransformed(bool transformed);

protected:
    void paintEvent(QPaintEvent *event) override;

private:
    Shape shape;
    QPen pen;
    QBrush brush;
    bool antialiased;
    bool transformed;
    QPixmap pixmap;
};

Zunächst definieren wir ein öffentliches Shape enum, um die verschiedenen Formen festzuhalten, die von dem Widget gerendert werden können (d.h. die Formen, die von einem QPainter gerendert werden können). Dann implementieren wir den Konstruktor sowie zwei der öffentlichen Funktionen von QWidget neu: minimumSizeHint() und sizeHint().

Außerdem wird die Funktion QWidget::paintEvent() neu implementiert, um die derzeit aktive Form entsprechend den angegebenen Parametern zeichnen zu können.

Wir deklarieren mehrere private Slots: Der Slot setShape() ändert die Form von RenderArea, die Slots setPen() und setBrush() ändern den Stift und den Pinsel des Widgets, und die Slots setAntialiased() und setTransformed() ändern die jeweiligen Eigenschaften des Widgets.

Implementierung der Klasse RenderArea

Im Konstruktor initialisieren wir einige der Variablen des Widgets.

RenderArea::RenderArea(QWidget *parent)
    : QWidget(parent)
{
    shape = Polygon;
    antialiased = false;
    transformed = false;
    pixmap.load(":/images/qt-logo.png");

    setBackgroundRole(QPalette::Base);
    setAutoFillBackground(true);
}

Wir setzen seine Form auf Polygon, seine Antialiasing-Eigenschaft auf false und laden ein Bild in die pixmap-Variable des Widgets. Zum Schluss legen wir die Hintergrundrolle des Widgets fest, indem wir den Pinsel aus dem palette des Widgets definieren, der zum Rendern des Hintergrunds verwendet wird. QPalette::Base ist normalerweise weiß.

QSize RenderArea::sizeHint() const
{
    return QSize(400, 200);
}

RenderArea erbt die Eigenschaft sizeHint von QWidget, die die empfohlene Größe für das Widget enthält. Wenn der Wert dieser Eigenschaft eine ungültige Größe ist, wird keine Größe empfohlen.

Die Standardimplementierung der Funktion QWidget::sizeHint() gibt eine ungültige Größe zurück, wenn es kein Layout für das Widget gibt, und gibt ansonsten die bevorzugte Größe des Layouts zurück.

Unsere Neuimplementierung der Funktion gibt ein QSize mit einer Breite von 400 Pixeln und einer Höhe von 200 Pixeln zurück.

QSize RenderArea::minimumSizeHint() const
{
    return QSize(100, 100);
}

RenderArea Die Funktion QWidget erbt auch die Eigenschaft minimumSizeHint, die die empfohlene Mindestgröße für das Widget enthält. Auch hier wird keine Größe empfohlen, wenn der Wert dieser Eigenschaft eine ungültige Größe ist.

Die Standardimplementierung von QWidget::minimumSizeHint() gibt eine ungültige Größe zurück, wenn es kein Layout für das Widget gibt, und gibt ansonsten die Mindestgröße des Layouts zurück.

Unsere Neuimplementierung der Funktion gibt ein QSize mit einer Breite von 100 Pixeln und einer Höhe von 100 Pixeln zurück.

void RenderArea::setShape(Shape shape)
{
    this->shape = shape;
    update();
}

void RenderArea::setPen(const QPen &pen)
{
    this->pen = pen;
    update();
}

void RenderArea::setBrush(const QBrush &brush)
{
    this->brush = brush;
    update();
}

Die öffentlichen Slots setShape(), setPen() und setBrush() werden immer dann aufgerufen, wenn wir die Form, den Stift oder den Pinsel eines Widgets RenderArea ändern wollen. Wir setzen die Form, den Stift oder den Pinsel entsprechend dem Slot-Parameter und rufen QWidget::update() auf, um die Änderungen im RenderArea Widget sichtbar zu machen.

Der Slot QWidget::update() führt nicht zu einem sofortigen Neuanstrich, sondern plant ein Malereignis für die Verarbeitung, wenn Qt zur Hauptereignisschleife zurückkehrt.

void RenderArea::setAntialiased(bool antialiased)
{
    this->antialiased = antialiased;
    update();
}

void RenderArea::setTransformed(bool transformed)
{
    this->transformed = transformed;
    update();
}

Mit den Slots setAntialiased() und setTransformed() ändern wir den Zustand der Eigenschaften entsprechend dem Slot-Parameter und rufen den Slot QWidget::update() auf, um die Änderungen im Widget RenderArea sichtbar zu machen.

void RenderArea::paintEvent(QPaintEvent * /* event */)
{
    static const QPoint points[4] = {
        QPoint(10, 80),
        QPoint(20, 10),
        QPoint(80, 30),
        QPoint(90, 70)
    };

    QRect rect(10, 20, 80, 60);

    QPainterPath path;
    path.moveTo(20, 80);
    path.lineTo(20, 30);
    path.cubicTo(80, 0, 50, 50, 80, 80);

    int startAngle = 20 * 16;
    int arcLength = 120 * 16;

Dann implementieren wir die Funktion QWidget::paintEvent() neu. Als erstes erstellen wir die grafischen Objekte, die wir zum Zeichnen der verschiedenen Formen benötigen.

Wir erstellen einen Vektor aus vier QPoints. Wir verwenden diesen Vektor, um die Formen Points, Polyline und Polygon darzustellen. Dann erstellen wir ein QRect, das ein Rechteck in der Ebene definiert, das wir als Begrenzungsrechteck für alle Formen außer Path und Pixmap verwenden.

Außerdem erstellen wir eine QPainterPath. Die Klasse QPainterPath stellt einen Container für Maloperationen bereit, mit dem grafische Formen konstruiert und wiederverwendet werden können. Ein Malpfad ist ein Objekt, das aus einer Reihe von grafischen Bausteinen wie Rechtecken, Ellipsen, Linien und Kurven besteht. Weitere Informationen über die Klasse QPainterPath finden Sie im Beispiel Painter Paths. In diesem Beispiel erstellen wir einen Malerpfad, der aus einer geraden Linie und einer Bezier-Kurve besteht.

Außerdem legen wir einen Startwinkel und eine Bogenlänge fest, die wir beim Zeichnen der Formen Arc, Chord und Pie verwenden werden.

    QPainter painter(this);
    painter.setPen(pen);
    painter.setBrush(brush);
    if (antialiased)
        painter.setRenderHint(QPainter::Antialiasing, true);

Wir erstellen ein QPainter für das Widget RenderArea und stellen den Stift und den Pinsel des Malers entsprechend dem Stift und dem Pinsel von RenderArea ein. Wenn die Parameteroption Antialiasing aktiviert ist, setzen wir auch die Rendering-Hinweise des Malers. QPainter::Antialiasing gibt an, dass die Engine die Kanten von Primitiven wenn möglich antialias machen soll.

    for (int x = 0; x < width(); x += 100) {
        for (int y = 0; y < height(); y += 100) {
            painter.save();
            painter.translate(x, y);

Schließlich werden mehrere Kopien der Form von RenderArea gerendert. Die Anzahl der Kopien hängt von der Größe des RenderArea Widgets ab, und wir berechnen ihre Positionen mithilfe von zwei for Schleifen und der Höhe und Breite des Widgets.

Für jede Kopie wird zunächst der aktuelle Zustand des Malers gespeichert (der Zustand wird auf einen Stapel geschoben). Dann übersetzen wir das Koordinatensystem mit Hilfe der Funktion QPainter::translate() in die Position, die durch die Variablen der for -Schleifen bestimmt wird. Wenn wir diese Verschiebung des Koordinatensystems auslassen, werden alle Kopien der Form übereinander in der linken oberen Ecke des RenderArea Widgets gerendert.

            if (transformed) {
                painter.translate(50, 50);
                painter.rotate(60.0);
                painter.scale(0.6, 0.9);
                painter.translate(-50, -50);
            }

Wenn die Option Transformations aktiviert ist, wird eine zusätzliche Übersetzung des Koordinatensystems vorgenommen, bevor das Koordinatensystem mit der Funktion QPainter::rotate() um 60 Grad im Uhrzeigersinn gedreht und mit der Funktion QPainter::scale() verkleinert wird. Am Ende wird das Koordinatensystem wieder auf die Position vor der Drehung und Skalierung zurückgesetzt.

Jetzt wird die Form beim Rendern so aussehen, als ob sie in drei Dimensionen gedreht worden wäre.

            switch (shape) {
            case Line:
                painter.drawLine(rect.bottomLeft(), rect.topRight());
                break;
            case Points:
                painter.drawPoints(points, 4);
                break;
            case Polyline:
                painter.drawPolyline(points, 4);
                break;
            case Polygon:
                painter.drawPolygon(points, 4);
                break;
            case Rect:
                painter.drawRect(rect);
                break;
            case RoundedRect:
                painter.drawRoundedRect(rect, 25, 25, Qt::RelativeSize);
                break;
            case Ellipse:
                painter.drawEllipse(rect);
                break;
            case Arc:
                painter.drawArc(rect, startAngle, arcLength);
                break;
            case Chord:
                painter.drawChord(rect, startAngle, arcLength);
                break;
            case Pie:
                painter.drawPie(rect, startAngle, arcLength);
                break;
            case Path:
                painter.drawPath(path);
                break;
            case Text:
                painter.drawText(rect,
                                 Qt::AlignCenter,
                                 tr("Qt by\nThe Qt Company"));
                break;
            case Pixmap:
                painter.drawPixmap(10, 10, pixmap);
            }

Als nächstes identifizieren wir die Form von RenderArea und rendern sie mit der zugehörigen Zeichenfunktion QPainter:

Bevor wir mit dem Rendern begonnen haben, haben wir den aktuellen Zustand des Malers gespeichert (den Zustand auf einen Stapel geschoben). Der Grund dafür ist, dass wir die Position jeder Formkopie relativ zu demselben Punkt im Koordinatensystem berechnen. Beim Verschieben des Koordinatensystems geht die Kenntnis dieses Punktes verloren, es sei denn, wir speichern den aktuellen Painter-Status , bevor wir den Verschiebevorgang starten.

            painter.restore();
        }
    }

    painter.setRenderHint(QPainter::Antialiasing, false);
    painter.setPen(palette().dark().color());
    painter.setBrush(Qt::NoBrush);
    painter.drawRect(QRect(0, 0, width() - 1, height() - 1));
}

Wenn wir dann mit dem Rendern einer Kopie der Form fertig sind, können wir den ursprünglichen Malerstatus mit dem zugehörigen Koordinatensystem mit der Funktion QPainter::restore() wiederherstellen. Auf diese Weise stellen wir sicher, dass die nächste Formkopie an der richtigen Position gerendert wird.

Wir könnten das Koordinatensystem mit QPainter::translate() zurückübersetzen, anstatt den Painter-Status zu speichern. Da wir aber zusätzlich zur Übersetzung des Koordinatensystems (wenn die Transformation Parameteroption aktiviert ist) das Koordinatensystem sowohl rotieren als auch skalieren, ist die einfachste Lösung, den aktuellen Painter-Status zu speichern.

Beispielprojekt @ code.qt.io

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.