Qt 5.2 calling QTableWidget::update does not result in QTableWidget::paintEvent



  • This Topic is simmilar to
    https://qt-project.org/forums/viewthread/21263
    and
    https://qt-project.org/forums/viewthread/28124,
    but there is no suitable answer: update and repaint isn't working.


    I have a problem: calling QTableWidget::update does not result in QTableWidget::paintEvent.

    Brief description:

    • QTableWidgetEx - a class derived from QTableWidget, with paintEvent overriden
    • Some code creating QTableWidgetEx and two menu items 'call_update' and 'check_paint_cnt'

    Testing sequence

    1. Click 'check_paint_cnt' - status bar shows "paint_cnt = 1" (so paintEvent is called)
    2. Click 'call_update'
    3. Click 'check_paint_cnt' again - status bar shows "paint_cnt = 1", but should be 2... (no call to paintEvent happen)
      Resizing the window do increase paint_cnt counter.

    (Small test app - below, full sources.
    Another test project as an empty Qt project, created by wizard (Qt 5.2): http://s000.tinyupload.com/index.php?file_id=57865294773039719910 - full source code.)

    I also tested, and have the same result (not working):

    • update(rect)
    • repaint, repaint(rect)
    • updateGeometry()
    • w->resize(w->width(), w->height());

    Workaround I found is:
    @inline void wa_widget_update(QWidget* w)
    {
    if(auto a = dynamic_cast<QAbstractScrollArea*>(w))
    a->viewport()->update();
    else
    w->update();
    };
    // Use wa_widget_update(widget) every time you'd use widget->update();
    @
    a bit ugly but works fine.

    Related bugtracker issue: https://bugreports.qt-project.org/browse/QTBUG-38286

    Question: Documentation says QTableWidget::update should trigger QTableWidget::paintEvent, but it doesn't. What am I doing wrong?
    Is there a correct way to toggle paintEvent?

    @ #--------------------------------------------------
    # Qt_Update_Test.pro contents
    #--------------------------------------------------
    QT += core gui
    greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
    TARGET = Qt_Update_Test
    TEMPLATE = app
    SOURCES += main.cpp
    HEADERS += main.h

    //--------------------------------------------------
    // main.h contents
    //--------------------------------------------------
    
    #ifndef MAIN_H
    #define MAIN_H
    
    #include <QApplication>
    #include <QMainWindow>
    #include <QTableWidget>
    #include <QPaintEvent>
    #include <QStatusBar>
    #include <QMenuBar>
    
    extern int paints_cnt;    // Global variable - paintEvent calls counter
    
    class QTableWidgetEx : public QTableWidget
        {
        Q_OBJECT
        public:
            inline explicit QTableWidgetEx(QWidget *parent = 0) : QTableWidget(parent) {};
        protected:
            virtual void paintEvent(QPaintEvent* e) override;
        };
    
    
    class MainWindow : public QMainWindow
        {
        Q_OBJECT
        public:
            QTableWidgetEx*        table_widget_ex;
            QMenuBar*              menuBar;
            QStatusBar*            statusBar;
    
            explicit MainWindow(QWidget *parent = 0);
            inline ~MainWindow(){};
        protected slots:
            void on_call_update();
            void on_check_paint_cnt();
        };
    
    #endif
    
    //--------------------------------------------------
    // main.cpp contents
    //--------------------------------------------------
    
    #include "main.h"
    int paints_cnt = 0;    // Global variable - paintEvent calls counter
    
    void QTableWidgetEx::paintEvent(QPaintEvent* e)
        {
        ++paints_cnt;
        QTableWidget::paintEvent(e);
        }
    
    void MainWindow::on_call_update()
        {
        table_widget_ex->update();
        };
    
    void MainWindow::on_check_paint_cnt()
        {
        statusBar->showMessage("paints_cnt = " + QString::number(paints_cnt));
        };
    

    // Below - Layout code, signal-slots, entry point, etc...
    MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
    {
    resize(400, 300);

        menuBar = new QMenuBar(this);
        setMenuBar(menuBar);
    
        table_widget_ex = new QTableWidgetEx(this);
        table_widget_ex->setUpdatesEnabled(true);
        setCentralWidget(table_widget_ex);
    
        statusBar = new QStatusBar(this);
        setStatusBar(statusBar);
    
        auto call_update = new QAction("call_update", this);
        connect(call_update, SIGNAL(triggered()), this, SLOT(on_call_update()));
        menuBar->addAction(call_update);
    
        auto check_paint_cnt = new QAction("check_paint_cnt", this);
        connect(check_paint_cnt, SIGNAL(triggered()), this, SLOT(on_check_paint_cnt()));
        menuBar->addAction(check_paint_cnt);
        };
    
    int main(int argc, char *argv[])
        {
        QApplication a(argc, argv);
        MainWindow w;
        w.show();    
        return a.exec(&#41;;
        };
    @

  • Lifetime Qt Champion

    Hi,

    AFAIK, if there was no changes (window move/notification from model etc…), Qt tries to avoid unneeded painting especially with update they are "compressed" to minimize the load



  • What is "a change"?
    I'd like to notify Qt that "I have a change here, please update my widget" - how do I do that?

    Also there is not a word about it in docs. Docs says "call update" it will notify about a change.
    Of cource I can resize a widget, or even recreate it to make "a change" for sure, but this causes other widgets to repaint due to layout changes. What I want is simply notify that some data under my widget is changed and it needs to be repainted. Also I'm not using Qt's model for some reason.

    So I'm asking what is "a change" not for fun, but to know how can I emit this "change".


  • Lifetime Qt Champion

    So you have a custom data structure that you use to feed the content of the QTableWidget ?

    Then add a signal to your "model" that will notify the change when it occurs and a slot in your MainWindow where you will update the content of the corresponding item. The the update of the QTableWidget will then be done for you.



  • The real case is a bit more complicated:
    I have dynamicaly changing data
    and 10 tables on 10 tabs,
    user can see only 1 table at a time.

    When data changes I don't want to instantly compute new representation for all 10 tables, instead I want to update only one visible, and mark other 9 as stale (marking stale using QWidget::update).

    Later, when the user goes to the other tab - the table will be updated (paintEvent should be called).

    Also I don't want the widget to be repainted if the user uses another app (so the widget is overlayed by it and no painting is requred).

    Also I'd like to make no updates if the data isn't changed.
    Please also note that some computation is requred to show this table, not just painting.
    For now I trigger this computation in paintEvent and cache it till next data change, so painting itself is fast.
    When the data is changed I use my workaround above to mark widget as stale, so if it is visible, then paintEvent is called and computation is done, if it's overlayed (by other tab or other app) recomputing is suspended till it's painted next time.

    Due to this complexity I've decided to make this simple test app to show my problem on a simple case.



  • For now I have a problem with this topic, it is shown that this topic has 4 replies, but among this replies there is no solution.

    So when the other members will try to find unanswered topics, they'll see this one is answered, but it's really not.

    What should I do now? Make a new topic?


  • Lifetime Qt Champion

    It's correct, I've answered twice an you also so it was four. Now it should be 6. The number of replies has nothing to do with the solved/unsolved state. So no, please don't open a new thread with the same question.

    To make it short: you don't need to worry about repainting. Just update the value of the item you need to. Qt will handle the rest for your.

    What you must change however is the computation part. It should not be done in the paintEvent. paintEvent is for all things painting not for data grinding. If you need some computation, do it in the slot I suggested to use earlier and then set the result value on the item.

    Unless you have some heavy computation (or a high data rate transmission between your model and the QTableWidget) you should be fine. Otherwise other techniques can be used.



  • I use paintEvent to check out if I need to do this computation: if the widget is not visible (overlayed by other) - I don't need to do computation.

    The approach is:

    • User opens the table
    • Than he goes to MS Word for example
    • My app refreshes the data, and it is changed
    • My app calls update() on table
    • There can be 100 more data changes while user is still in MS Word, I don't want any computation at this time
    • User goes back to my app and looks on the table
    • paintEvent is called and computation is triggered and cached
    • User resizes the window paintEvent is call, but no data changes, so use cached computation

    As you can see my main goal why I use paintEvent is to find out when I need to do some computation.

    when = (widget is not overlayed && data is changed);

    The best place for it is paintEvent.


    Anyway for now I've found a suitable workaround, and I found put why that happens:
    that's because QTableWidget is derived from QAbstractScrollView.
    There is some kind of bug or it's a feature, anyway to emit paint event I should call w->viewport()->update() instead of just w->update(). This works great, so I added this workaround to my post above.


  • Lifetime Qt Champion

    It's still not a good idea to have computations done in paintEvent.

    Why not just check the widget visibility with isVisible when you are getting new data ?

    You can also act on the showEvent and trigger the computation.



  • Will "showEvent " or "isVisible" show anything if a widget is overlayed by another window, but not hidden explicitly by hide()? - No!
    As the docs say: "A widget that happens to be obscured by other windows on the screen is considered to be visible. "

    In paintEvent i have something like:
    if(m_is_changed) compute();

    "It’s still not a good idea to have computations done in paintEvent." - I'd like not to diskuss this, because this will take lots of time (too much details) and also my english isn't good enough to describe all of them well and quick.
    In my task it's ok.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.