I have following problem:
when I call update()
on QListView
, its paintEvent()
is not triggered unless some other event occurs over the widget (mouse move, got focus....)
I am using Qt 4.8.3, and unless this is definitely bug in the version, I would prefer not upgrading (as from my experience upgrades bears more trouble than benefits).
The question:
How to make QListView
(and Q...View
) to update next time main loop gets control?
Some background as what I am solving if it helps:
Meant as single threaded application.
At the bottom is some independent (no Qt) model, which is hierarchical, and consumers request subitems. Items at the bottom of hierarchy may be modified.
On modification, consumer requests W(ritable)Item. At that point, Model parts influenced by the change reports "modified" via observer approach. So observers are notified at the start of the change (model returns writable object, has no control or idea when change ends).
Consumer is expected to finish modification before returning from function/method which started modification.
Modifying methods/functions are expected to be called from main thread, so next time main thread tinkers with GUI, model is in consistent state, and consumers can refresh.
QModel
s are done to provide data from model below in Qt accessible format.
Next are QWidget
s (lists/textboxes/labels) visualising data to user,
these are modified to support Desync()
method, which mark visualised data to be out of sync, and overridden paintEvent
, which checks for inSync
state. For simple QWidget
s like labels, on synchronization, callback is called, which just fills in the data. For Q...View
, I assumed to force models to emit modelReset
, so list reloads number of rows and content of visible ones.
On the top is class collecting it all together under its region, which is hooked to observers, and on reported changes Desync
relevant widgets.
All methods changing anything are connected via signal/slot Qt thingie to buttons/comboboxes/other GUI elements, so I assume it all runs under main thread.
Idea of change:
Desync
) relevant QWidget
s as out of syncQWidget
s are marked as out of sync, and scheduled to update, yet do not attempt to access anything, as we run under main thread* What I observed: *
update()
results in paintEvent
for most widgets that do not have model at all (label/ textbox...)update()
does not result in paintEvent
for QListView
paintEvent
, and QWidget
synchronizesvisible(false); update(); visible(true);
repaints immediately
QWidget
synchronizes before consumer performs the changepaintEvent
being calledSimplified sources where to get the behavior:
myList.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()
{}
myModel.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()
{}
Behavior: Clicking button updates external model, yet the list is not synchronized.
When moving mouse over the list, it synchronizes.
Expected Behavior:
Registering programmer's wish to update()
, and result in paintEvent
next time main loop gets the control (or even few loops later).
You did it wrong.
Do not touch QListView
you don't have to. Just fix data model (Qt) and rest will work out of box.
Your model myModel
should simply invoke proper methods when data are changing. This model should observe source of real data.
When something will happen to the data:
If you do this properly nothing else is needed.