I have a background thread and the thread calls some methods that update the UI (in order to show progress bars and show additional info in text areas).
If I modify some UI widget values, a "Cannot send events to objects owned by a different thread" assertion error is raised.
Looking at forums, I read that I could use QMetaObject::invokeMethod method but it just works if I pass it the Qt::DirectConnection flag that actually raises the same error shown above.
If I use Qt::QueuedConnection or Qt::AutoConnection, the invokeMethod returns false.
My code looks similar to this:
.h:
class A : public QMainWindow
{
Q_OBJECT
QProgressBar* pb;
public slots:
bool m(bool, int);
};
class B
{
A* a;
public:
void handleEvent();
};
.cpp:
bool A::m(bool x, int y)
{
pb->setValue(y);
return x;
}
void B::handleEvent()
{
//a->m(true, 12); //raises an assertion error
bool r;
//bool ret = QMetaObject::invokeMethod(a, "m", Qt::DirectConnection, Q_RETURN_ARG(bool, r), Q_ARG(bool, true), Q_ARG(int, 12)); //raises the same assertion error error
bool ret = QMetaObject::invokeMethod(a, "m", Qt::AutoConnection, Q_RETURN_ARG(bool, r), Q_ARG(bool, true), Q_ARG(int, 12)); //is ignored and ret contains false.
}
Do you know what is going on or what i am doing wrong? or maybe, can someone suggest me another approach to deal with my newbie problem?
Thanks in advance,
Ernesto
I haven't used invokeMethod()
myself, but to do this, I usually just use signals and slots. For instance, you could create a signal as a member of class B
that is connected to the slot in class A
that updates the progress:
class B : public QObject
{
Q_OBJECT
A* a;
signals:
void update_signal(bool, int);
public:
void handleEvent();
};
B::B()
{
//assuming a already points to the correct place...
connect(this, SIGNAL(update_signal(bool,int),
a, SLOT(m(bool,int)), Qt::QueuedConnection);
}
void B::handleEvent()
{
emit update_signal(true, 12);
}
A::m()
will have to return void in this case but that is not a problem because when using a queued connection, you cannot get a return value anyway since the call is asynchronous (emit update_signal(true,12)
may return before the slot function is called making it impossible to have a return value ready).
You can actually perform this connection anywhere as long as you have pointers to an object of type A
and an object of type B
. This makes the signals and slots very flexible since you could completely decouple A
from B
, yet still allow them to communicate through signals and slots. For instance:
class B : public QObject
{
Q_OBJECT
signals:
void update_signal(bool, int);
public:
void handleEvent();
};
void B::handleEvent()
{
emit update_signal(true, 12);
}
class A : public QMainWindow
{
Q_OBJECT
QProgressBar* pb;
public slots:
void m(bool, int);
};
void A::m(bool x, int y)
{
pb->setValue(y);
}
int main()
{
A* a = new A();
B* b = new B();
QObject::connect(b, SIGNAL(update_signal(bool, int)),
a, SLOT(m(bool, int)), Qt::QueuedConnection);
//...
}
In this case, b
does not have to store a pointer or know anything about a
yet they can communicate through a thin well-defined channel.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With