У меня следующая проблема:
когда я вызываю update()
для QListView
, его paintEvent()
не запускается, если над виджетом не происходит какое-либо другое событие (перемещение мыши, получение фокуса....)
Я использую Qt 4.8.3, и если это определенно не ошибка в версии, я бы предпочел не обновлять (по моему опыту, обновления приносят больше проблем, чем преимуществ).
Вопрос: Как сделать так, чтобы QListView
(и Q...View
) обновлялись в следующий раз, когда основной цикл получает управление?
Небольшая предыстория, что я решаю, если это поможет:
Имеется в виду однопоточное приложение.
Внизу находится некоторая независимая (не Qt) модель, которая является иерархической, и потребители запрашивают подэлементы. Элементы в нижней части иерархии могут быть изменены.
При модификации потребитель запрашивает W(ritable)Item. В этот момент части модели, на которые влияют отчеты об изменениях, "модифицируются" через подход наблюдателя. Таким образом, наблюдатели уведомляются в начале изменения (модель возвращает доступный для записи объект, не имеет контроля или понятия, когда изменение заканчивается).
Ожидается, что потребитель завершит модификацию до возврата из функции/метода, начавшей модификацию.
Ожидается, что модифицирующие методы/функции будут вызываться из основного потока, поэтому в следующий раз, когда основной поток возится с графическим интерфейсом, модель находится в согласованном состоянии, и потребители могут обновляться.
QModel
выполняются для предоставления данных из приведенной ниже модели в формате, доступном для Qt.
Далее идут QWidget
(списки/текстовые поля/метки), визуализирующие данные для пользователя, они изменены для поддержки метода Desync()
, который помечает визуализированные данные как несинхронизированные, и переопределяют paintEvent
, который проверяет состояние inSync
. Для простых QWidget
, таких как метки, при синхронизации вызывается обратный вызов, который просто заполняет данные. Для Q...View
я предполагал заставить модели выдавать modelReset
, поэтому список перезагружает количество строк и содержимое видимых.
Сверху находится класс, собирающий все это вместе в своем регионе, который подключается к наблюдателям, а по сообщениям об изменениях — Desync
соответствующих виджетов.
Все методы, изменяющие что-либо, связаны через сигнал/слот Qt с кнопками/выпадающими списками/другими элементами графического интерфейса, поэтому я предполагаю, что все это выполняется в основном потоке.
Идея изменения:
- Событие, вызванное графическим интерфейсом, основной поток начинает обработку метода изменения потребителя
- потребитель получает необходимые предметы для сдачи
- потребитель получает элементы, доступные для записи
- Отчеты реальная модель изменены для наблюдателей
- наблюдатели отмечают (
Desync
) соответствующиеQWidget
s как несинхронизированные QWidget
помечены как несинхронизированные и запланированы для обновления, но не пытаются получить доступ к чему-либо, так как мы работаем в основном потоке.- потребитель выполняет изменение, во время которого реальная модель может быть даже несовместимой
- consumer возвращает управление тому, кто его вызвал
- основной цикл выполняет обновления, которые переопределяются для синхронизации виджетов.
* Что я заметил: *
update()
приводит кpaintEvent
для большинства виджетов, у которых вообще нет модели (метка/текстовое поле...)update()
не приводит кpaintEvent
дляQListView
- repaint() не помогает (была просто дикая попытка)
- перемещение мыши над виджетом приводит к
paintEvent
, аQWidget
синхронизируется - trying to
visible(false); update(); visible(true);
repaints immediately- that is wrong, as
QWidget
synchronizes before consumer performs the change
- that is wrong, as
- переключение окон (например, визуальная студия) или обратно приводит к вызову
paintEvent
Упрощенные источники, откуда можно узнать о поведении:
мойСписок.h
#ifndef __myList_h__
#define __myList_h__
#include <qlistview.h>
class myList : public QListView
{
bool inSync;
void sync();
protected:
virtual void paintEvent(QPaintEvent * event) override;
public:
myList(QWidget * parent);
void Desync();
virtual ~myList();
};
#endif
myList.cpp
#include "myList.h"
#include "myModel.h"
void myList::sync()
{
if (inSync)
return;
inSync = true; //< set early, to prevent loops
((myModel*)model())->ResetModel();
}
void myList::paintEvent(QPaintEvent * event)
{
sync();
QListView::paintEvent(event);
}
myList::myList(QWidget * parent) : QListView(parent), inSync(false)
{}
void myList::Desync()
{
inSync = false;
update();
}
myList::~myList()
{}
мояМодель.h
#ifndef __myModel_h__
#define __myModel_h__
#include <QAbstractListModel>
class myModel : public QAbstractListModel
{
Q_OBJECT;
int & externalComplexData;
public:
myModel(int & externalComplexData);
virtual int rowCount(QModelIndex const & parent = QModelIndex()) const override;
virtual QVariant data(QModelIndex const & index, int role) const override;
void ResetModel();
virtual ~myModel();
};
#endif
myModel.cpp
#include "myModel.h"
myModel::myModel(int & externalComplexData) : externalComplexData(externalComplexData)
{}
int myModel::rowCount(QModelIndex const & parent) const
{
return 1;
}
QVariant myModel::data(QModelIndex const & index, int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
return QString::number(externalComplexData);
}
void myModel::ResetModel()
{
reset();
}
myModel::~myModel()
{}
tmp.h
#ifndef __Tmp_H__
#define __Tmp_H__
#include <QtGui/QMainWindow>
#include "ui_tmp.h"
class tmp : public QMainWindow
{
Q_OBJECT
public:
tmp(QWidget *parent = 0, Qt::WFlags flags = 0);
~tmp();
private:
Ui::tmpClass ui;
private slots:
void clicked();
};
#endif
tmp.cpp
#include "tmp.h"
#include "myModel.h"
int localComplexData = 0;
tmp::tmp(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
ui.setupUi(this);
ui.lst->setModel(new myModel(localComplexData));
connect(ui.btn, SIGNAL(clicked()), this, SLOT(clicked()));
}
void tmp::clicked()
{
ui.lst->Desync();
++localComplexData;
}
tmp::~tmp()
{}
Поведение. При нажатии на кнопку обновляется внешняя модель, но список не синхронизируется.
При перемещении мыши по списку он синхронизируется.
Ожидаемое поведение: Регистрация желания программиста на update()
и результат paintEvent
в следующий раз, когда основной цикл получит управление (или даже через несколько циклов).
repaint()
- person user3528438   schedule 26.05.2015