1

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

  • Click 'check_paint_cnt' - status bar shows "paint_cnt = 1" (so paintEvent is called)
  • Click 'call_update'
  • Click 'check_paint_cnt' again - status bar shows "paint_cnt = 1", but should be 2... (no call to paintEvent happen)
  • Resizing the window does increase paint_cnt, so the function is overriden successefully and is called sometimes, but not with update or repaint.

(Small test app - below. The 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 tested 'repaint' instead of 'update' - it gives the same result.

Question: Documentation says QTableWidget::update should trigger QTableWidget::paintEvent, but it doesn't What am I doing wrong?

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

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

Good workaround is:

inline void wa_widget_update(QWidget* w)
    {
    if(auto a = dynamic_cast<QAbstractScrollArea*>(w))
            a->viewport()->update();
    else
            w->update();
    };

instead of w->update() use wa_widget_update(w). A bit ugly but works. Reported this bug to qt project: Link to bugtracker

Full sources:

#--------------------------------------------------
# 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();
    };   
Yuri Yaryshev
  • 991
  • 6
  • 24
  • QWidget::setUpdatesEnabled() is default to true, is it not? – macetw Apr 10 '14 at 19:30
  • Ensure you have called show() on the widget, and that you are within the QApplication::exec() event loop. You are probably all those things if you get to click on that check_paint_cnt() action. – macetw Apr 10 '14 at 19:34
  • There may be copy-paste errors here or elsewhere. Make sure you compile it under a C++11 compiler, in the .pro file add `CONFIG += c++11`, and declare the event handler as `void paintEvent(QPaintEvent*) override;`. Make sure it still compiles - if it doesn't, you're not overriding the function that you think you are :) – Kuba hasn't forgotten Monica Apr 11 '14 at 00:58
  • Besides, what is it that you're trying to do? Why override a paint event of a very complex class like that? – Kuba hasn't forgotten Monica Apr 11 '14 at 00:59
  • - Widget is visible - I can see it on the screen. – Yuri Yaryshev Apr 11 '14 at 03:12
  • - "QWidget::setUpdatesEnabled() is default to true" - Yes, I added "setUpdatesEnabled(true)", just to be sure. - "override mismatch" - it can't be, since I see the counter = 1, not 0. If it didn't matched the name the function would never be called (so the counter would be 0 forever). – Yuri Yaryshev Apr 11 '14 at 03:17
  • "What I'm trying to do?" - this is just a simple unit test app, the real case is: I'm trying to refresh the table's data only if the table is visible (not visible()==true, but is really not overlayed by any other window/widget/tab etc). So I just want to check if the data is changed using paint event (it should fire only when the widget is visible). – Yuri Yaryshev Apr 11 '14 at 03:23

1 Answers1

3

QTableWidget is derived from QAbstractScrollArea and uses a viewport widget. If you call the update() method of the viewport widget, the paintEvent will get triggered:

void MainWindow::on_call_update()
{
    table_widget_ex->viewport()->update();
}

More info also at: update() or repaint() fails to trigger paintEvent()

Community
  • 1
  • 1
Steffen
  • 2,888
  • 19
  • 19
  • 1
    The part about it was derived from `QAbstractScrollArea` solved my problem. I instantiate `QPainter` this way `QPainter painter(viewport());`, taken from http://stackoverflow.com/questions/6515695/qt-qtableview-draw-border-around-active-cells. Thanks Steffen! – swdev May 25 '14 at 23:19