Qt 5.2, вызывающий QTableWidget::update, не приводит к QTableWidget::paintEvent

У меня проблема: вызов QTableWidget::update не приводит к QTableWidget::paintEvent.

Краткое описание: - QTableWidgetEx - класс, производный от QTableWidget, с переопределением paintEvent. — Некоторый код, создающий QTableWidgetEx и два пункта меню «call_update» и «check_paint_cnt».

Последовательность тестирования

  • Нажмите «check_paint_cnt» — в строке состояния отображается «paint_cnt = 1» (поэтому вызывается paintEvent)
  • Нажмите «call_update»
  • Нажмите «check_paint_cnt» еще раз — в строке состояния отображается «paint_cnt = 1», но должно быть 2... (вызов paintEvent не происходит)
  • Изменение размера окна увеличивает paint_cnt, поэтому функция успешно переопределяется и иногда вызывается, но не с обновлением или перерисовкой.

(Небольшое тестовое приложение — ниже. Тестовый проект как пустой проект Qt, созданный мастером (Qt 5.2): http://s000.tinyupload.com/index.php?file_id=57865294773039719910 — полный исходный код.)

Я пробовал «перекрасить» вместо «обновить» — результат тот же.

Вопрос: В документации сказано, что QTableWidget::update должен запускать QTableWidget::paintEvent, но этого не происходит. Что я делаю неправильно?

Я также проверил и получил тот же результат (не работает):

  • обновление (прямо)
  • перекрасить, перекрасить (прямо)
  • обновитьгеометрию()
  • w->resize(w->width(), w->height());

Хороший обходной путь:

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

вместо w->update() используйте wa_widget_update(w). Немного некрасиво, но работает. Сообщил об этой ошибке в проект qt: Ссылка на систему отслеживания ошибок

Полные исходники:

#--------------------------------------------------
# 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();
    };   

person Yuri Yaryshev    schedule 10.04.2014    source источник
comment
QWidget::setUpdatesEnabled() по умолчанию имеет значение true, не так ли?   -  person macetw    schedule 10.04.2014
comment
Убедитесь, что вы вызвали show() для виджета и находитесь в цикле обработки событий QApplication::exec(). Вы, вероятно, все эти вещи, если вы можете нажать на это действие check_paint_cnt().   -  person macetw    schedule 10.04.2014
comment
Здесь или где-то еще могут быть ошибки копирования-вставки. Убедитесь, что вы скомпилировали его под компилятором C++11, в файле .pro добавьте CONFIG += c++11 и объявите обработчик события как void paintEvent(QPaintEvent*) override;. Убедитесь, что он все еще компилируется - если это не так, вы не переопределяете функцию, которую вы считаете :)   -  person Kuba hasn't forgotten Monica    schedule 11.04.2014
comment
Кроме того, что ты пытаешься сделать? Зачем переопределять событие рисования очень сложного класса?   -  person Kuba hasn't forgotten Monica    schedule 11.04.2014
comment
- Виджет виден - я его вижу на экране.   -  person Yuri Yaryshev    schedule 11.04.2014
comment
- QWidget::setUpdatesEnabled() по умолчанию имеет значение true - Да, я добавил setUpdatesEnabled(true), просто чтобы быть уверенным. - переопределить несоответствие - этого не может быть, так как я вижу, что счетчик = 1, а не 0. Если бы он не совпадал с именем, функция никогда не вызывалась бы (поэтому счетчик всегда был бы равен 0).   -  person Yuri Yaryshev    schedule 11.04.2014
comment
Что я пытаюсь сделать? - это просто приложение для модульного тестирования, реальный случай: я пытаюсь обновить данные таблицы, только если таблица видна (не видна()==true, но на самом деле не перекрывается никаким другим окном/виджетом /вкладка и т. д.). Поэтому я просто хочу проверить, изменились ли данные с помощью события рисования (оно должно срабатывать только тогда, когда виджет виден).   -  person Yuri Yaryshev    schedule 11.04.2014


Ответы (1)


QTableWidget является производным от QAbstractScrollArea и использует виджет окна просмотра. Если вы вызовете метод update() виджета окна просмотра, сработает событие paintEvent:

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

Дополнительная информация также по адресу: update() или repaint() не может вызвать paintEvent ()

person Steffen    schedule 11.04.2014
comment
Часть об этом была получена из QAbstractScrollArea решенной моей проблемы. Я создаю экземпляр QPainter таким образом QPainter painter(viewport());, взятый из stackoverflow.com/questions/6515695/. Спасибо Штеффен! - person swdev; 26.05.2014