Trouble of using QBrush in different threads
-
Hello!
I have problem. I want to make some color animation in my app. In my case I want to use a separate thread that performs some calculations and defines a brush that transmitted to main thread and then sets to the widgets palette. That is code (main.cpp)
@
#include <QPalette>
#include <QThread>
#include <QEvent>
#include <QQueue>
#include <QWidget>
#include <QApplication>
#include <QLabel>
#include <QMetaProperty>
#include <QDebug>int g_animevent = QEvent::registerEventType();
bool g_blink = false;struct str
{
str(QWidget* widget, const QBrush& brush) :
widget(widget), value(brush) {}str(const str& other) { this->widget = other.widget; this->value = other.value; } QWidget* widget; QBrush value;
};
class PalEvent : public QEvent
{
public:
PalEvent(QQueue<str>* q) : QEvent((QEvent::Type)g_animevent), queue(q) {}
QQueue<str>* queue;
};class PalThread : public QThread
{
public:
PalThread(QWidget* m, const QList<QWidget*>& lw) : main(m), widgets(lw) {}protected:
void run()
{
QQueue<str>* queue = new QQueue<str>();
bool flag;
for(;;)
{
flag = g_blink;
foreach(QWidget* widget, widgets)
{
QPalette pal(widget->palette());
QBrush b(Qt::red);
if (flag)
b.setColor(Qt::blue);
else
b.setColor(Qt::yellow);const QBrush& bb = pal.brush(QPalette::WindowText); if (bb != b) { str s(widget, b); queue->enqueue(s); } } if (!queue->isEmpty()) { QCoreApplication::postEvent(main, new PalEvent(queue), Qt::HighEventPriority); queue = new QQueue<str>(); } QThread::msleep(10); } }
private:
QList<QWidget*> widgets;
QWidget* main;
};class Main : public QWidget
{
public:
Main()
{
for (int i = 0; i < 1000; ++i)
{
QLabel* lbl = new QLabel("L" + QString::number(i + 1), this);
lbl->move((i % 25) * 40, (i / 25) * 16);
widgets.append(lbl);
}
PalThread* pt = new PalThread(this, widgets);
pt->start();
startTimer(500);
}bool event(QEvent *e) { if (e->type() == g_animevent) { QQueue<str> *qp = ((PalEvent*)e)->queue; while (!qp->isEmpty()) { str pd = qp->dequeue(); QPalette pal = pd.widget->palette(); pal.setBrush(QPalette::WindowText, pd.value); pd.widget->setPalette(pal); } delete qp; return true; } else if (e->type() == QEvent::Timer) { g_blink = !g_blink; /* bool flag = g_blink; QQueue<str>* queue = new QQueue<str>(); foreach(QWidget* widget, widgets) { QPalette pal(widget->palette()); QBrush b(Qt::red); if (flag) b.setColor(Qt::blue); else b.setColor(Qt::yellow); const QBrush& bb = pal.brush(QPalette::WindowText); if (bb != b) { str s(widget, b); queue->enqueue(s); } } if (queue->isEmpty()) delete queue; else QCoreApplication::postEvent(this, new PalEvent(queue), Qt::HighEventPriority); */ } return QWidget::event(e); }
private:
QList<QWidget*> widgets;
};int main(int argc, char** argv)
{
QApplication a(argc, argv);
Main m;
m.show();
return a.exec();
}
@
But after little time since start execution of application it crashes at 'memory access error'. I can't understand what is the reason of trouble at present time and I hope someone can help me.
Besides, I defined that in case of using single thread app all executing normal (as you can see in commented block of code). -
You should consider creating smaller examples. It's hard to read through all this. It's also my impression that you needlessly complicated this. For example why so many dynamic allocations? One thread allocates queue with new, packs it into another dynamic event and it is expected that another thread will successful get that, unpack and delete queue... this is scetchy at best.
But anyway...
I don't see any thread synchronization done in your code? You access g_blink in both threads but there's no mutex guarding it. Consider a QMutex or QAtomicInt there.
Another thing is that you access widget->palette() from the worker thread. Widgets should only be accessed from the main thread and even if they weren't you're again not guarding it with any synchronization primitives. -
Hi,
A QWidget must only be accessed from the thread that instantiates QApplication.
-
Chris Kawa, It's, of course, simplified version of real application that desribes my problem and using QMutex-access g_blink is not critical in my case.
I understand that I can't write Widget properties in other threads.
But do not I even read Widget's state in other thread?
Or in this case I must use some synchronization? -
You can't assume anything about access to the widgets in other threads. It might be platform dependent. It might be system settings dependent, it might be Qt version dependent, it might be specific member function dependent etc.
If any widget member function is thread safe it is marked so in the docs. Most are not.
The rule of thumb is: don't try it.If you need to access some widget data in another thread retrieve it in the ui thread and pass it via synchronized data.