Search code examples
c++qtgridrect

QPainter fillRect over geometry of widget in grid cell


I've gone about customizing the QPushButton widget such that an animated flash of white appears above the widget when clicked. This works fine for the button I have located in cell (0, 0) of my grid. As for my button located at (2, 0) it appears that it doesn't paint a rect at all. This behavior can be seen in this video I've uploaded, with the corresponding qDebug output (button name & geometry). The flash appears over "Registry" when clicked, but not "Exit": https://youtu.be/j5gE_5jeMtg

Now for the code (if you see something that can be optimized, please feel free to provide me some tips):

pushbutton.h

#ifndef PUSHBUTTON
#define PUSHBUTTON

#include <QtCore>
#include <QPushButton>
#include <QPainter>

class PushButton : public QPushButton
{
    Q_OBJECT

public:
    explicit PushButton(QString text, QWidget *parent = 0);

protected:
    virtual void paintEvent(QPaintEvent *);

public slots:
    void OnClick();
    void OnTick();

private:
    enum States
    {
        NONE = 0,
        RESET,
        DRAW
    } state = NONE;

    QTimer *timer;

    float delta = 0.0f;
};

#endif

pushbutton.cpp

#include "pushbutton.h"

PushButton::PushButton(QString text, QWidget *parent) : QPushButton(text, parent)
{
    timer = new QTimer(this);

    QObject::connect(this, SIGNAL(clicked(bool)), this, SLOT(OnClick()));
    QObject::connect(timer, SIGNAL(timeout()), this, SLOT(OnTick()));
}

void PushButton::paintEvent(QPaintEvent *e)
{
    QPushButton::paintEvent(e);

    if(state == States::NONE)
    {
        return;
    }

    QPainter painter(this);

    if(state == States::RESET)
    {
        state = States::NONE;

        painter.eraseRect(geometry());

        update();
    }
    else
    {
        painter.fillRect(geometry(), QColor(200, 200, 200, 200 * (delta < 1.0f ? delta : 2.0f - delta)));
    }
}

void PushButton::OnClick()
{
    qDebug() << text() << geometry();

    state = States::DRAW;

    delta = 0.0f;

    timer->start(20);
}

void PushButton::OnTick()
{
    delta += 0.2f;

    if(delta > 2.0f)
    {
        state = States::RESET;

        timer->stop();
    }

    update();
}

mainwindow.cpp

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    registryButton = new PushButton("Registry");
    exitButton = new PushButton("Exit");

    registryButton->setStyleSheet("* { font-size: 16pt; font-family: Segoe360;"
                                  "border: none;"
                                  "color: rgba(240, 240, 240, 255);"
                                  "background: qlineargradient(x1:0 y1:0, x2:0 y2:1, stop:0 rgba(160, 169, 212, 255), stop:1 rgba(137, 151, 216, 255)); }");

    exitButton->setStyleSheet("* { font-size: 16pt; font-family: Segoe360;"
                                  "border: none;"
                                  "color: rgba(240, 240, 240, 255);"
                                  "background: qlineargradient(x1:0 y1:0, x2:0 y2:1, stop:0 rgba(191, 78, 78, 255), stop:1 rgba(199, 53, 53, 255)); }");

    ui->gridLayout->addWidget(registryButton, 0, 0, Qt::AlignTop);
    ui->gridLayout->addWidget(exitButton, 2, 0, Qt::AlignBottom);
}

My only assumption toward this behavior is that there is more to working with grids than I'm aware of.


Solution

  • Check the difference between geometry() and e->rect(). It works with e->rect().

    qDebug() << "paint" << text() << geometry() << e->rect();
    painter.fillRect(e->rect(), QColor(200, 200, 200, 200 * (delta < 1.0f ? delta : 2.0f - delta)));
    

    Output:

    paint "Registry" QRect(0,0 381x24) QRect(0,0 381x24)
    paint "Exit" QRect(0,217 381x24) QRect(0,0 381x24)
    

    QPaintEvent::rect(): Returns the rectangle that needs to be updated.

    QWidget::geometry(): This property holds the geometry of the widget relative to its parent and excluding the window frame.