Understanding QRegion::cend() for Custom Shapes in Qt GUI


What is QRegion?

  • It's commonly used for various purposes in GUI applications, such as:
    • Defining custom shapes for widgets (e.g., rounded corners, buttons with non-rectangular outlines).
    • Clipping regions to limit the area where painting occurs, improving performance.
    • Defining complex shapes for hit detection (e.g., determining if a mouse click falls within a specific area).
  • In Qt, QRegion is a class that represents a two-dimensional shape composed of non-overlapping rectangles.

What is QRegion::cend()?

  • In essence, it provides a way to iterate through the individual rectangles that define the region.
  • It's a const iterator that points to the end of the range of non-overlapping rectangles that make up the QRegion.
  • QRegion::cend() is a method introduced in Qt 5.8.

How to Use QRegion::cend()?

    • You can use various constructors like QRegion(const QRect& rect), QRegion(const QPolygon& polygon, Qt::FillRule rule), or others to create a QRegion based on your desired shape.
  1. Iterate through rectangles

    • Use a QRegion::const_iterator to iterate through the rectangles. You can get an iterator pointing to the beginning using QRegion::begin() and the end using QRegion::cend():
    QRegion myRegion;
    // ... (create the region using a constructor)
    
    QRegion::const_iterator it = myRegion.begin();
    while (it != myRegion.cend()) {
        QRect currentRect = *it;
        // Access properties of the current rectangle, e.g., currentRect.x(), currentRect.y()
        // ... (perform operations on the rectangle)
        ++it;
    }
    

Key Points

  • For iterating in reverse order (from end to beginning), use QRegion::rbegin() and QRegion::rend().
  • QRegion::cend() is a const iterator, meaning you cannot modify the rectangles within the region through it. However, you can access their properties and perform other read-only operations.

Beyond QRegion::cend()

  • QRegion offers various other methods for working with regions, such as:
    • contains(const QPoint& point): Checks if a point lies within the region.
    • contains(const QRect& rect): Checks if a rectangle is entirely contained within the region.
    • unite(const QRegion& otherRegion): Creates a new region that combines the current region with another region.
    • subtract(const QRegion& otherRegion): Creates a new region that excludes the area of another region from the current region.


Example 1: Displaying Rectangles of a Region

This code creates a QRegion with two rectangles, iterates through them using cend(), and displays their coordinates in a message box:

#include <QApplication>
#include <QMessageBox>
#include <QRegion>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    // Create a region with two rectangles
    QRegion myRegion;
    myRegion += QRect(10, 20, 50, 30);
    myRegion += QRect(80, 50, 70, 40);

    QString message;
    QRegion::const_iterator it = myRegion.begin();

    while (it != myRegion.cend()) {
        QRect currentRect = *it;
        message += QString("Rectangle: (%1, %2) - (%3, %4)\n")
                   .arg(currentRect.x())
                   .arg(currentRect.y())
                   .arg(currentRect.x() + currentRect.width())
                   .arg(currentRect.y() + currentRect.height());
        ++it;
    }

    QMessageBox::information(nullptr, "Region Rectangles", message);

    return app.exec();
}

Example 2: Custom Widget with a Rounded Corner

This example creates a custom widget class with a rounded corner using a QRegion and QPainter:

#include <QWidget>
#include <QPainter>
#include <QRegion>

class RoundedCornerWidget : public QWidget {
    Q_OBJECT

public:
    RoundedCornerWidget(QWidget *parent = nullptr) : QWidget(parent) {}

protected:
    void paintEvent(QPaintEvent *event) override {
        int cornerRadius = 20; // Adjust for desired corner radius

        QRegion region;
        region += rect();
        region -= QRegion(QRect(cornerRadius, cornerRadius, width() - 2 * cornerRadius, height() - 2 * cornerRadius));

        QPainter painter(this);
        painter.setClipRegion(region);
        // Draw your widget content here using painter
        // ...
    }
};
  • You can then draw your widget's content within the clipping region using the painter object.
  • The painter.setClipRegion(region) sets the clipping region, ensuring only areas within the region are painted.
  • This creates a "hole" in the overall region, effectively shaping the widget's visible area with rounded corners.
  • Another QRegion is created representing a rectangle with rounded corners by subtracting a square region with the specified cornerRadius from the full rectangle.
  • A QRegion is created that represents the entire widget rectangle (rect()).
  • A cornerRadius variable is set to define the roundness of the corners.
  • The RoundedCornerWidget class defines a paintEvent that overrides the default painting behavior.


Using QRegion::rectCount() and at()

  • This approach involves getting the total number of rectangles in the region using QRegion::rectCount() and then accessing them individually using QRegion::at(int index).
int numRects = myRegion.rectCount();
for (int i = 0; i < numRects; ++i) {
  QRect currentRect = myRegion.at(i);
  // ... (perform operations on the rectangle)
}

This method is useful if you know the exact number of rectangles beforehand or if you need random access to specific rectangles. However, it requires an extra loop and might be less efficient for large regions compared to iterators.

Using a Standard Loop with begin() and end()

  • This approach leverages standard C++ loop constructs with the begin() and end() methods of QRegion.
for (QRegion::const_iterator it = myRegion.begin(); it != myRegion.end(); ++it) {
  QRect currentRect = *it;
  // ... (perform operations on the rectangle)
}

This is functionally equivalent to using cend() but might be more familiar to programmers accustomed to standard C++ iterators.

  • For familiarity with standard C++ loops, using begin() and end() could be a viable choice.
  • If you require random access or prior knowledge of the number of rectangles, rectCount() and at() could be suitable.
  • If you need to iterate through all rectangles in order, QRegion::cend() remains the most concise and efficient option.