Beispiel für grundlegende Zeichnungen
Das Beispiel „Grundlegende Zeichnung“ zeigt, wie man mit der Klasse „ QPainter “ grundlegende Grafikprimitive in verschiedenen Stilen darstellen kann.
QPainter führt Low-Level-Zeichnungen auf Widgets und anderen Zeichenobjekten durch. Die Klasse kann alles zeichnen, von einfachen Linien bis hin zu komplexen Formen wie Kreisen und Sehnen. Außerdem kann sie ausgerichteten Text und Pixmaps zeichnen. Normalerweise zeichnet sie in einem „natürlichen“ Koordinatensystem, kann aber zusätzlich Ansichts- und Welttransformationen durchführen.

Das Beispiel stellt einen Darstellungsbereich bereit, in dem die aktuell aktive Form angezeigt wird, und ermöglicht es dem Benutzer, die gerenderte Form und ihr Erscheinungsbild mithilfe der „ QPainter “-Parameter zu bearbeiten: Der Benutzer kann die aktive Form ändern (Shape) und den Stift (Pen Width, Pen Style, Pen Cap, Pen Join), den Pinsel (Brush Style) sowie die Render-Hinweise (Antialiasing) des „ QPainter “ anpassen. Zudem kann der Benutzer eine Form drehen (Transformations); im Hintergrund nutzen wir die Fähigkeit von „ QPainter “, das Koordinatensystem zu manipulieren, um die Drehung durchzuführen.
Das Beispiel „Basic Drawing“ besteht aus zwei Klassen:
RenderAreaist ein benutzerdefiniertes Widget, das mehrere Kopien der aktuell aktiven Form rendert.Windowist das Hauptfenster der Anwendung, das neben mehreren Parameter-Widgets ein „RenderArea“-Widget anzeigt.
Zunächst werden wir die Klasse „ Window “ betrachten, anschließend werfen wir einen Blick auf die Klasse „ RenderArea “.
Definition der Klasse „Window“
Die Klasse „Window“ erbt von „ QWidget “ und stellt das Hauptfenster der Anwendung dar, in dem neben mehreren Parameter-Widgets 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 sowie drei private Slots, die das Widget „ RenderArea “ aktualisieren: Der Slot „ shapeChanged() “ aktualisiert das Widget „ RenderArea “, wenn der Benutzer die aktuell aktive Form ändert. Wir rufen den Slot „ penChanged() “ auf, wenn sich einer der Stiftparameter von „ QPainter “ ändert. Und der Slot „ brushChanged() “ aktualisiert das Widget „ RenderArea “, wenn der Benutzer den Pinselstil des Malers ändert.
Implementierung der Fensterklasse
Im Konstruktor erstellen und initialisieren wir die verschiedenen Widgets, die im Hauptanwendungsfenster 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);Zunächst erstellen wir das Widget „ RenderArea “, das die aktuell aktive Form darstellt. Anschließend erstellen wir die Combobox „ Shape “ und fügen die zugehörigen Einträge hinzu (d. h. die verschiedenen Formen, die ein „ 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 Klasse „ QPen “ definiert, wie ein „Painter“ Linien und Umrisse von Formen zeichnen soll. Ein Stift verfügt über mehrere Eigenschaften: Breite, Stil, Endkappe und Verbindung.
Die Breite eines Stifts kann null oder größer sein, am häufigsten ist jedoch die Breite null. Beachten Sie, dass dies nicht 0 Pixel bedeutet, sondern impliziert, dass die Form so glatt wie möglich gezeichnet wird, auch wenn dies mathematisch vielleicht nicht korrekt ist.
Wir erstellen ein „ 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 der Stil auf „none“ (Qt::NoPen) gesetzt wird, wird der Zeichner angewiesen, keine Linien oder Konturen zu zeichnen. Die Stiftkappe definiert, wie die Endpunkte von Linien gezeichnet werden. Und die Stiftverbindung definiert, wie zwei Linien miteinander verbunden werden, wenn mehrere miteinander verbundene Linien gezeichnet werden. Die Einstellungen für „cap“ und „join“ gelten nur für Linien mit einer Breite von mindestens 1 Pixel.
Wir erstellen für jeden der Parameter „ Pen Style “, „ Pen Cap “ und „ Pen Join “ ein „ QComboBox“ 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 mit 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 eine ` QComboBox ` für den Parameter ` Brush Style ` und fügen die zugehörigen Elemente hinzu (d. h. die Werte der Enumeration ` 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 mithilfe der Render-Hinweise von „ QPainter “ angewendet werden. „ QPainter::RenderHints “ werden verwendet, um Flags für „ QPainter “ anzugeben, die von einer bestimmten Engine berücksichtigt werden können oder auch 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 den Anschein erweckt, als wäre die gerenderte Form dreidimensional gedreht.
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);Anschließend verbinden wir die Parameter-Widgets mithilfe der statischen Funktion ` QObject::connect()` mit den zugehörigen Slots und stellen so sicher, dass das Widget ` RenderArea ` aktualisiert wird, sobald 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"));
}Abschließend fügen wir die verschiedenen Widgets einem Layout hinzu und rufen die Slots ` shapeChanged()`, ` penChanged()` und ` brushChanged() ` auf, um die Anwendung zu initialisieren. Außerdem aktivieren wir das Antialiasing.
void Window::shapeChanged()
{
RenderArea::Shape shape = RenderArea::Shape(shapeComboBox->itemData(
shapeComboBox->currentIndex(), IdRole).toInt());
renderArea->setShape(shape);
}Der Slot „ shapeChanged() “ wird aufgerufen, sobald der Benutzer die aktuell aktive Form ändert.
Zunächst rufen wir die vom Benutzer ausgewählte Form mithilfe der Funktion ` QComboBox::itemData()` ab. Diese Funktion gibt die Daten für die angegebene Rolle im angegebenen Index der Combobox zurück. Wir verwenden ` QComboBox::currentIndex()` zum Abrufen des Index der Form, wobei die Rolle durch die Enumeration ` Qt::ItemDataRole ` definiert ist; ` 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 unterschiedliche Daten unter demselben Index speichern müssen, können Sie verschiedene Rollen verwenden, indem Sie einfach den Wert von „ Qt::UserRole “ erhöhen, zum Beispiel: „Qt::UserRole + 1“ und „Qt::UserRole + 2“. Es entspricht jedoch guter Programmierpraxis, jeder Rolle einen eigenen Namen zu geben: „myFirstRole = Qt::UserRole + 1“ und „mySecondRole = Qt::UserRole + 2“. Auch wenn wir in diesem speziellen Beispiel nur eine einzige Rolle benötigen, fügen wir die folgende Codezeile am Anfang der Datei „ window.cpp “ hinzu.
const int IdRole = Qt::UserRole;Die Funktion „ QComboBox::itemData()“ gibt die Daten als „ QVariant “ zurück, daher müssen wir die Daten in „ RenderArea::Shape “ umwandeln. Wenn für die angegebene Rolle keine Daten vorhanden sind, gibt die Funktion „ QMetaType::UnknownType “ zurück.
Abschließend rufen wir den Slot „ RenderArea::setShape() “ auf, um das Widget „ RenderArea “ 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 Slot „ penChanged() “ auf, sobald der Benutzer einen der Stiftparameter ändert. Auch hier verwenden wir die Funktion „ QComboBox::itemData()“, um die Parameter abzurufen, und rufen anschließend den Slot „ RenderArea::setPen() “ auf, um das Widget „ RenderArea “ zu aktualisieren.
Der Slot „brushChanged()“ wird aufgerufen, sobald 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);Wenn es sich bei dem Pinselparameter um eine Farbverlaufsfüllung handelt, sind besondere Maßnahmen erforderlich.
Die Klasse „ QGradient “ wird in Kombination mit „ QBrush “ verwendet, um Farbverlaufsfüllungen festzulegen. Qt unterstützt derzeit drei Arten von Farbverlaufsfüllungen: linear, radial und konisch. Jede davon wird durch eine Unterklasse von „ QGradient “ dargestellt: „ QLinearGradient “, „ QRadialGradient “ und „ QConicalGradient “.
Wenn der Pinselstil also „ Qt::LinearGradientPattern “ lautet, erstellen wir zunächst ein ` QLinearGradient `-Objekt mit einem Interpolationsbereich zwischen den Koordinaten, die als Argumente an den Konstruktor übergeben werden. Die Positionen werden mithilfe logischer Koordinaten angegeben. Anschließend legen wir die Farben des Farbverlaufs mithilfe der Funktion ` QGradient::setColorAt()` fest. Die Farben werden mithilfe von Haltepunkten definiert, die sich aus einer Position (zwischen 0 und 1) und einem „ QColor “ zusammensetzen. Die Menge der Haltepunkte beschreibt, wie der Farbverlaufsbereich gefüllt werden soll. Ein Farbverlauf kann eine beliebige Anzahl von Haltepunkten haben.
Abschließend rufen wir den Slot „ 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 Handlungsmuster wie bei „ QLinearGradient “ wird auch bei „ Qt::RadialGradientPattern “ und „ Qt::ConicalGradientPattern “ verwendet.
Der einzige Unterschied besteht in den an den Konstruktor übergebenen Argumenten: Beim Konstruktor von ` QRadialGradient ` ist das erste Argument der Mittelpunkt und das zweite der Radius des radialen Farbverlaufs. Das dritte Argument ist optional, kann jedoch verwendet werden, um den Brennpunkt des Farbverlaufs innerhalb des Kreises zu definieren (der Standardbrennpunkt ist der Kreismittelpunkt). Beim 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 “ lautet, erstellen wir einen „ QBrush “ aus einem „ QPixmap “. Anschließend rufen wir den Slot „ RenderArea::setBrush() “ auf, um das Widget „ RenderArea “ mit dem neu erstellten Pinsel zu aktualisieren.
Andernfalls erstellen wir einfach einen Pinsel mit dem angegebenen Stil und der Farbe Grün und rufen dann den Slot „ RenderArea::setBrush() “ auf, um das Widget „ RenderArea “ mit dem neu erstellten Pinsel zu aktualisieren.
Definition der Klasse „RenderArea“
Die Klasse „ RenderArea “ erbt von „ QWidget “ und rendert mithilfe eines „ QPainter “ mehrere Kopien der aktuell aktiven Form.
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 eine öffentliche Enumeration „ Shape “, um die verschiedenen Formen zu erfassen, die vom Widget gerendert werden können (d. h. die Formen, die von einem „ QPainter “ gerendert werden können). Anschließend implementieren wir den Konstruktor sowie zwei der öffentlichen Funktionen von „ QWidget “ neu: „ minimumSizeHint()“ und „ sizeHint()“.
Außerdem implementieren wir die Funktion ` QWidget::paintEvent()` neu, um die aktuell aktive Form gemäß den angegebenen Parametern zeichnen zu können.
Wir deklarieren mehrere private Slots: Der Slot „ setShape() “ ändert die Form des „ 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 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 legen seine Form als „ Polygon “ fest, setzen die Eigenschaft „antialiased“ auf „false“ und laden ein Bild in die Variable „pixmap“ des Widgets. Abschließend legen wir die Hintergrundrolle des Widgets fest und definieren den Pinsel aus dem „ palette “ des Widgets, der zum Rendern des Hintergrunds verwendet wird. „ QPalette::Base “ ist in der Regel weiß.
Die „ RenderArea “ erbt die Eigenschaft „ sizeHint “ von „ QWidget “, die die empfohlene Größe für das Widget enthält. Ist der Wert dieser Eigenschaft eine ungültige Größe, wird keine Größe empfohlen.
Die Standardimplementierung der Funktion ` QWidget::sizeHint()` gibt eine ungültige Größe zurück, wenn kein Layout für das Widget vorhanden ist, und andernfalls die bevorzugte Größe des Layouts.
Unsere Neuimplementierung der Funktion gibt ein ` QSize ` mit einer Breite von 400 Pixeln und einer Höhe von 200 Pixeln zurück.
RenderArea erbt zudem die Eigenschaft ` minimumSizeHint ` von ` QWidget`, die die empfohlene Mindestgröße für das Widget enthält. Auch hier gilt: Ist der Wert dieser Eigenschaft eine ungültige Größe, wird keine Größe empfohlen.
Die Standardimplementierung von ` QWidget::minimumSizeHint()` gibt eine ungültige Größe zurück, wenn kein Layout für das Widget vorhanden ist, und andernfalls die Mindestgröße des Layouts.
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 „ RenderArea “-Widgets ändern möchten. Wir legen die Form, den Stift oder den Pinsel entsprechend dem Slot-Parameter fest und rufen „ QWidget::update()“ auf, um die Änderungen im „ RenderArea “-Widget sichtbar zu machen.
Der Slot ` QWidget::update()` löst keine sofortige Neuzeichnung aus; stattdessen plant er ein Zeichenereignis zur Verarbeitung ein, sobald Qt zur Haupt-Ereignisschleife 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;Anschließend 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 ` QPoint`-Objekten. Mit diesem Vektor rendern wir die Formen ` Points`, ` Polyline ` und ` Polygon `. Anschließend erstellen wir ein ` QRect`, das ein Rechteck in der Ebene definiert, das wir als Begrenzungsrechteck für alle Formen mit Ausnahme von ` Path ` und ` Pixmap` verwenden.
Außerdem erstellen wir einen „ QPainterPath “. Die Klasse „ QPainterPath “ stellt einen Container für Zeichenoperationen bereit, der es ermöglicht, grafische Formen zu konstruieren und wiederzuverwenden. Ein „Painter Path“ ist ein Objekt, das aus einer Reihe grafischer Bausteine wie Rechtecken, Ellipsen, Linien und Kurven besteht. Weitere Informationen zur Klasse „ QPainterPath “ finden Sie im Beispiel „Painter Paths “. In diesem Beispiel erstellen wir einen „Painter Path“, der aus einer geraden Linie und einer Bézier-Kurve besteht.
Außerdem definieren wir einen Startwinkel und eine Bogenlänge, 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 einen „ QPainter “ für das „ RenderArea “-Widget und legen den Stift und den Pinsel des Painters entsprechend dem Stift und dem Pinsel des „ RenderArea “ fest. Wenn die Parameteroption „ Antialiasing “ aktiviert ist, legen wir auch die Render-Hinweise des Painters fest. „ QPainter::Antialiasing “ gibt an, dass die Engine die Kanten von Primitiven nach Möglichkeit glätten soll.
for (int x = 0; x < width(); x += 100) {
for (int y = 0; y < height(); y += 100) {
QPainterStateGuard guard(&painter);
painter.translate(x, y);Schließlich rendern wir die mehreren Kopien der Form des „ RenderArea “. Die Anzahl der Kopien hängt von der Größe des „ RenderArea “-Widgets ab, und wir berechnen ihre Positionen mithilfe von zwei „ for “-Schleifen sowie der Höhe und Breite des Widgets.
Für jede Kopie speichern wir zunächst den aktuellen Malzustand, indem wir ein ` QPainterStateGuard` instanziieren. Anschließend verschieben wir das Koordinatensystem mithilfe der Funktion ` QPainter::translate()` an die Position, die durch die Variablen der ` for `-Schleifen bestimmt wird. Wenn wir diese Verschiebung des Koordinatensystems weglassen, werden alle Kopien der Form übereinander in der oberen linken 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);
}Ist die Parameteroption „ Transformations “ aktiviert, führen wir eine zusätzliche Verschiebung des Koordinatensystems durch, bevor wir das Koordinatensystem mithilfe der Funktion ` QPainter::rotate()` um 60 Grad im Uhrzeigersinn drehen und mit der Funktion ` QPainter::scale()` verkleinern. Abschließend verschieben wir das Koordinatensystem wieder an die Position zurück, an der es sich vor der Drehung und Skalierung befand.
Wenn die Form nun gerendert wird, erscheint sie so, als wäre sie dreidimensional gedreht worden.
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 „ RenderArea “ und rendern sie mithilfe der zugehörigen Zeichenfunktion ` QPainter `:
- QPainter::drawLine(),
- QPainter::drawPoints(),
- QPainter::drawPolyline(),
- QPainter::drawPolygon(),
- QPainter::drawRect(),
- QPainter::drawRoundedRect(),
- QPainter::drawEllipse(),
- QPainter::drawArc(),
- QPainter::drawChord(),
- QPainter::drawPie(),
- QPainter::drawPath(),
- QPainter::drawText() oder
- QPainter::drawPixmap()
Bevor wir mit dem Rendern begonnen haben, haben wir den aktuellen Malzustand gespeichert (indem wir den Zustand auf einen Stapel gelegt haben). Der Grund dafür ist, dass wir die Position jeder Formkopie relativ zu demselben Punkt im Koordinatensystem berechnen. Bei der Verschiebung des Koordinatensystems geht die Information über diesen Punkt verloren, es sei denn, wir speichern den aktuellen Malzustand, bevor wir mit der Verschiebung beginnen.
}
}
painter.setRenderHint(QPainter::Antialiasing, false);
painter.setPen(palette().dark().color());
painter.setBrush(Qt::NoBrush);
painter.drawRect(QRect(0, 0, width() - 1, height() - 1));
}Sobald wir das Rendern einer Kopie der Form abgeschlossen haben, wird der ursprüngliche Malzustand durch ` QPainterStateGuard` wiederhergestellt, das die Funktion ` QPainter::restore()` aufruft. 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ücktranslieren, anstatt den „Painter State“ zu speichern. Da wir jedoch zusätzlich zur Translation des Koordinatensystems (wenn die Option ` Transformation ` aktiviert ist) das Koordinatensystem sowohl drehen als auch skalieren, ist die einfachste Lösung, den aktuellen „Painter State“ zu speichern.
© 2026 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.