Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Clear background when implementing drawBackground



  • Hello everyone!
    In my application I have a class that inherits QGraphicsScene, in that class I have overridden drawBackground method to draw gridlines only if some flag is set. And when user clicks the button "Start", I need to reset the flag state and hide gridlines. For that I am doing:

    flag = false;
    QGraphicsScene::invalidate(sceneRect(), QGraphicsScene::AllLayers);
    

    in buttonClick handler for button "Start" and after that drawBackground has been called, but it didn't clear the existing drawings. So when the flag is set to false, gridlines are not drawed in drawBackground, but they still remain on the widgets background. The only way to hide them is to somehow force the whole window to repaint, e.g. resize it. So is there a way to clear the background at the beginning of the drawBackground method?



  • I found that in my example if I use update(), instead of invalidate(sceneRect()); everything works fine, but in my actual application (which is more complicated) it is still not working. I can't tell what is the problem with that, maybe with some connections between children and parent elements or something else, but I found another tricky solution to solve my problem. Instead of calling update, I am calling

    setBackgroundBrush(QBrush());
    

    since I don't use this brush, it is not changing my program's behavior, but as mentioned in the documentation this forces widget's background to redraw. And now everything works fine.

    Edit:
    Today I have found, that the reason why update() didn't worked for me is because BackgroundCache was on in .ui file for QGraphicsView. I turned it off, and now I can use update() and everything work as expected.


  • Lifetime Qt Champion

    Hi,

    What about painting a transparent rect when set to false ?



  • I have tried it, but transparent rectangle draws on top of existing gridlines (so gridlines still stay visible).


  • Lifetime Qt Champion

    And if you call the base class implementation when not drawing the grid ?



  • Still nothing ((


  • Lifetime Qt Champion

    Can you provide a minimal compilable example that shows this behaviour ?



  • This is minimum code (Qt Widget application with one layout with object name "layout" and one push button "pushButton").
    I recognized that if I remove line addText("Hello world"); everything works fine, so if there are no items on the scene everything is ok. But I have many items on the scene, so it is not an option for me, but this is a curious fact, maybe this is a bug ?

    mainwindow.h

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    #include <QMainWindow>
    #include <QObject>
    #include <QWidget>
    #include <QGraphicsScene>
    #include <QGraphicsView>
    #include <QColor>
    #include <QPen>
    #include <QPainter>
    QT_BEGIN_NAMESPACE
    namespace Ui { class MainWindow; }
    QT_END_NAMESPACE
    class scene : public QGraphicsScene
    {
        Q_OBJECT
    public:
        bool flag = true;
        scene(QObject *parent = 0);
        void drawBackground(QPainter *painter, const QRectF& rect) override;
        void back();
    };
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    public:
        MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
        QGraphicsView *mView;
        scene *mScene;
    private slots:
        void on_pushButton_clicked();
    private:
        Ui::MainWindow *ui;
    };
    #endif // MAINWINDOW_H
    
    

    mainwindow.cpp

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    static float gridStep_ = 40;
    scene::scene(QObject *parent) : QGraphicsScene(parent)
    {
        addText("Hello world");
    }
    void scene::drawBackground(QPainter *painter, const QRectF& rect)
    {
        if (flag) {
            QPen _pen;
            _pen.setColor(QColor(0, 0, 0, 255));
            painter->setPen(_pen);
    
            for (int _x = int(rect.left()) / gridStep_ * gridStep_; _x <= rect.right(); _x += gridStep_)
                for (int _y = int(rect.top()) / gridStep_ * gridStep_; _y <= rect.bottom(); _y += gridStep_)
                    painter->drawPoint(_x, _y);
        }
        QGraphicsScene::drawBackground(painter, rect);
    }
    void scene::back()
    {
        flag = !flag;
        invalidate(sceneRect());
    }
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        mScene = new scene();
        mView = new QGraphicsView(mScene);
        ui->layout->addWidget(mView);
    }
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    void MainWindow::on_pushButton_clicked()
    {
        mScene->back();
    }
    


  • I found that in my example if I use update(), instead of invalidate(sceneRect()); everything works fine, but in my actual application (which is more complicated) it is still not working. I can't tell what is the problem with that, maybe with some connections between children and parent elements or something else, but I found another tricky solution to solve my problem. Instead of calling update, I am calling

    setBackgroundBrush(QBrush());
    

    since I don't use this brush, it is not changing my program's behavior, but as mentioned in the documentation this forces widget's background to redraw. And now everything works fine.

    Edit:
    Today I have found, that the reason why update() didn't worked for me is because BackgroundCache was on in .ui file for QGraphicsView. I turned it off, and now I can use update() and everything work as expected.



  • That's interesting that the setBackground(QBrush()) works for you. Looking at the code, QGraphicsScene::drawBackground() doesn't do anything when the background brush is Qt::NoBrush (which is what QBrush() is). Not sure what Q_D(QGraphicsScene) does; it's called at the beginning of many QGraphicsScene methods but I didn't see where it was defined.

    This is the code from Qt 5.12:

    void QGraphicsScene::drawBackground(QPainter *painter, const QRectF &rect)
    {
        Q_D(QGraphicsScene);
    
        if (d->backgroundBrush.style() != Qt::NoBrush) {
            if (d->painterStateProtection)
                painter->save();
            painter->setBrushOrigin(0, 0);
            painter->fillRect(rect, backgroundBrush());
            if (d->painterStateProtection)
                painter->restore();
        }
    }
    

Log in to reply