Drawing Polygonal Shapes in Qt GUI with QPainterPath::addPolygon()


Purpose

  • The QPainterPath::addPolygon() function in Qt's GUI framework allows you to create a polygonal shape within a QPainterPath object. This path can then be used for various drawing operations, such as filling or outlining the polygon.

Key Points

  • Customization
    You can further customize how the polygon is drawn by:
    • Setting the fill rule using setFillRule() (e.g., Qt::OddEvenFill or Qt::WindingFill) to determine how enclosed areas are filled.
    • Using a QPainter object and its methods like fillPath() or strokePath() to fill or outline the polygon based on the path.
  • Subpath Creation
    It adds an unclosed subpath to the current QPainterPath. This means that the line segments will be drawn from one vertex to the next, but the path won't automatically connect back to the starting point to form a closed shape.
  • Input
    It takes a QPolygon object as an argument, which defines the vertices (points) that make up the polygon.

Example

#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QPolygon>

class MyWidget : public QWidget {
protected:
    void paintEvent(QPaintEvent *event) override {
        QPainter painter(this);

        // Create a polygon with vertices
        QPolygon polygon;
        polygon << QPoint(50, 50);
        polygon << QPoint(150, 100);
        polygon << QPoint(100, 150);

        // Add the polygon to the painter path
        QPainterPath path;
        path.addPolygon(polygon);

        // Set the fill rule (optional)
        path.setFillRule(Qt::OddEvenFill);

        // Fill the polygon with a blue color
        painter.setPen(Qt::NoPen);
        painter.setBrush(QColor(Qt::blue));
        painter.fillPath(path, QBrush(Qt::blue));

        // Optionally, outline the polygon with a black pen
        // painter.setPen(QPen(Qt::black));
        // painter.strokePath(path, QPen(Qt::black));
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    MyWidget widget;
    widget.show();
    return app.exec();
}

In this example:

  1. We create a QPolygon object with three vertices.
  2. We create a QPainterPath object.
  3. We call addPolygon() on the path, passing the polygon object.
  4. We optionally set the fill rule.
  5. We use a QPainter object to fill the polygon with a blue brush.
  6. (Optional) We could also use strokePath() to draw an outline around the polygon.
  • It's particularly useful for creating complex shapes that can't be easily achieved with built-in shapes like rectangles or ellipses.
  • When you need to draw a custom polygonal shape in your Qt GUI application.


Drawing a Star

#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QPolygon>
#include <cmath>

class MyWidget : public QWidget {
protected:
    void paintEvent(QPaintEvent *event) override {
        QPainter painter(this);

        // Calculate points for a five-pointed star
        int numPoints = 5;
        double outerRadius = 50;
        double innerRadius = outerRadius * 0.382;
        QPolygon polygon;
        for (int i = 0; i < 2 * numPoints; ++i) {
            double angle = 2 * M_PI * i / (2 * numPoints);
            double radius = (i % 2 == 0) ? outerRadius : innerRadius;
            polygon << QPoint(radius * cos(angle) + width() / 2,
                              radius * sin(angle) + height() / 2);
        }

        // Add the polygon to the path and fill it with yellow
        QPainterPath path;
        path.addPolygon(polygon);
        painter.setBrush(QColor(Qt::yellow));
        painter.fillPath(path, QBrush(Qt::yellow));
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    MyWidget widget;
    widget.show();
    return app.exec();
}

This code creates a pentagon with alternating larger and smaller points to form a star shape.

Drawing an Arrow

#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QPolygon>

class MyWidget : public QWidget {
protected:
    void paintEvent(QPaintEvent *event) override {
        QPainter painter(this);

        // Create a polygon for the arrow head and body
        QPolygon head, body;
        head << QPoint(0, 0) << QPoint(10, 5) << QPoint(5, 0);
        body << QPoint(0, 0) << QPoint(20, 0) << QPoint(15, 5);

        // Move and combine head and body polygons
        QTransform transform;
        transform.translate(width() / 2, height() / 2);
        head = transform.map(head);
        body = transform.map(body);
        QPolygon arrow;
        arrow << body.at(0) << body.at(1) << head.at(1) << head.at(2) << head.at(0) << body.at(2);

        // Add the polygon to the path and stroke it with black
        QPainterPath path;
        path.addPolygon(arrow);
        painter.setPen(QPen(Qt::black, 2));
        painter.strokePath(path, QPen(Qt::black, 2));
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    MyWidget widget;
    widget.show();
    return app.exec();
}

This code creates two separate polygons, one for the arrow head and one for the body. It then combines them into a single polygon representing the entire arrow and strokes the path with a black pen.



Using Built-in Shapes

  • Qt provides built-in classes for common shapes like rectangles (QRect), ellipses (QRectF), and lines (QLine). These can be more efficient for simple shapes and offer properties for customization (e.g., size, position, color).
#include <QApplication>
#include <QWidget>
#include <QPainter>

class MyWidget : public QWidget {
protected:
    void paintEvent(QPaintEvent *event) override {
        QPainter painter(this);

        // Draw a rectangle with rounded corners
        painter.setBrush(QColor(Qt::lightgray));
        painter.setPen(QPen(Qt::black));
        painter.drawRoundedRect(QRect(50, 50, 100, 70), 15, 15);

        // Draw an ellipse with a blue outline
        painter.setBrush(Qt::NoBrush);
        painter.setPen(QPen(Qt::blue, 2));
        painter.drawEllipse(QRectF(170, 30, 80, 120));
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    MyWidget widget;
    widget.show();
    return app.exec();
}

QPainter::drawConvexPolygon()

  • If you have a convex polygon (all angles turn in the same direction), you can use QPainter::drawConvexPolygon(). This method can be slightly more efficient for convex shapes compared to QPainterPath::addPolygon().
#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QPolygon>

class MyWidget : public QWidget {
protected:
    void paintEvent(QPaintEvent *event) override {
        QPainter painter(this);

        // Create a convex polygon
        QPolygon polygon;
        polygon << QPoint(30, 50) << QPoint(100, 10) << QPoint(170, 80);

        // Fill the polygon with green
        painter.setBrush(QColor(Qt::green));
        painter.setPen(Qt::NoPen);
        painter.drawConvexPolygon(polygon);
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    MyWidget widget;
    widget.show();
    return app.exec();
}

Custom Drawing with QPainter

  • For maximum flexibility, you can directly use the drawing methods of QPainter like drawLine(), drawArc(), and drawBezierCurve() to construct your shapes. This approach requires more code but gives you complete control over every aspect of the drawing process.

Consider these factors when choosing an alternative:

  • Control
    Custom drawing with QPainter provides the most control but requires more coding effort.
  • Performance
    For simple convex shapes, QPainter::drawConvexPolygon() might be slightly faster.
  • Shape Complexity
    Built-in shapes are ideal for simple geometries. QPainterPath::addPolygon() offers more flexibility for complex shapes.