robbmj
robbmj

Reputation: 16526

Erasing painted areas from translucent widgets in Qt

I am faced with the problem of having to erase previously painted areas on a Qt widget.

The basic idea is, the user selects an area of the screen by clicking and dragging the mouse and a rectangle is drawn over the selected area.

The header

class ClearBack : public QWidget
{
    Q_OBJECT
public:
    explicit ClearBack(const QPoint &startingPos);

    bool eventFilter(QObject *obj, QEvent *event);
    void paintEvent(QPaintEvent *);
    void mouseMoveEvent(QMouseEvent *event);

signals:
    void regionSelected(const QRect &);

private:
    QRect currentRegion;
};

The Implementation

ClearBack::ClearBack(const QPoint &startingPos)
{
    setBackgroundRole(QPalette::Base);
    installEventFilter(this);
    currentRegion.setTopLeft(startingPos);
    currentRegion.setBottomRight(startingPos);
    this->setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint);
    this->showMaximized();
}

void ClearBack::paintEvent(QPaintEvent * event)
{
    Q_UNUSED(event);
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setPen(Qt::black);
    painter.drawRect(currentRegion);
}

void ClearBack::mouseMoveEvent(QMouseEvent *event)
{
    QPoint currentPos(event->globalX(), event->globalY());   
    currentRegion.setBottomRight(currentPos);
    this->repaint();
}

On a widget that has a solid background the effect works quite nicely, producing a single rectangle.

enter image description here

However, when the background is set to setAttribute(Qt::WA_TranslucentBackground); the following occurs.

enter image description here

The rectangles that were drawn previously are not "erased"

Is there a way to erase the previously painted rectangles on a translucent background, and if so, how?

Also for "bonus points" why does this effect occur on a translucent background and not on a solid one?

Upvotes: 2

Views: 2811

Answers (1)

Widgets with WA_TranslucentBackground attribute do not clear their backgrounds automatically. You have to:

  1. Change the composition mode from the default SourceOver to Source,

  2. Explicitly clear the old rectangle with a transparent brush,

  3. Paint the new rectangle.

Below is a working example, tested under Qt 5. You have to press the mouse to draw the initial rectangle and drag it around; the program exits when you release the mouse.

#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QMouseEvent>

class ClearBack : public QWidget
{
    Q_OBJECT
    QRect m_currentRegion, m_lastRegion;
public:
    explicit ClearBack(const QPoint &startingPos) :
        m_currentRegion(startingPos, startingPos)
    {
        setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint);
        setAttribute(Qt::WA_TranslucentBackground);
        showMaximized();
    }
    Q_SIGNAL void regionSelected(const QRect &);
protected:
    void paintEvent(QPaintEvent *) {
        QPainter painter(this);
        painter.setCompositionMode(QPainter::CompositionMode_Source);
        painter.setRenderHint(QPainter::Antialiasing);
        painter.setPen(QPen(Qt::transparent, 3));
        painter.drawRect(m_lastRegion);
        m_lastRegion = m_currentRegion;
        painter.setPen(Qt::black);
        painter.drawRect(m_currentRegion);
    }
    void mouseMoveEvent(QMouseEvent *event) {
        m_currentRegion.setBottomRight(event->globalPos());
        update();
    }
    void mouseReleaseEvent(QMouseEvent *) {
        emit regionSelected(m_currentRegion);
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    ClearBack back(QPoint(200,200));
    a.connect(&back, SIGNAL(regionSelected(QRect)), SLOT(quit()));
    return a.exec();
}

#include "main.moc"

Upvotes: 3

Related Questions