QMetaObject::invokeMethod() problem
-
Given:
public: void Progress1(const QString& szText, int lAchieved1); protected slots: virtual void slotProgress1(const QString& szText, int lAchieved1);and:
void ProgressBase::Progress1(const QString& text, int achieved) { QMetaObject::invokeMethod(this, "slotProgress1", Qt::QueuedConnection, Q_ARG(const QString&, text), Q_ARG(int, achieved)); } void ProgressBase::slotProgress1(const QString& szText, int lAchieved1) { qDebug() << __FUNCTION__; // etc.If Progress1 is called from a long running non-GUI thread, all is well and the slot is driven.
If however Progress1 is called from long running code on the GUI thread, then the slot isn't driven because the event dispatcher doesn't get to handle it.
What's the correct way to ensure the slot is driven regardless of which thread Progess1 is called from? Calling QCoreApplication::processEvents() inside Progress1 immediately after invokeMethod() when the code isn't the GUI thread doesn't feel like it's the correct answer.
-
P Perdrix deleted this topic on
-
P Perdrix restored this topic on
-
Given:
public: void Progress1(const QString& szText, int lAchieved1); protected slots: virtual void slotProgress1(const QString& szText, int lAchieved1);and:
void ProgressBase::Progress1(const QString& text, int achieved) { QMetaObject::invokeMethod(this, "slotProgress1", Qt::QueuedConnection, Q_ARG(const QString&, text), Q_ARG(int, achieved)); } void ProgressBase::slotProgress1(const QString& szText, int lAchieved1) { qDebug() << __FUNCTION__; // etc.If Progress1 is called from a long running non-GUI thread, all is well and the slot is driven.
If however Progress1 is called from long running code on the GUI thread, then the slot isn't driven because the event dispatcher doesn't get to handle it.
What's the correct way to ensure the slot is driven regardless of which thread Progess1 is called from? Calling QCoreApplication::processEvents() inside Progress1 immediately after invokeMethod() when the code isn't the GUI thread doesn't feel like it's the correct answer.
-
@Perdrix I often use QMetaObject::invokeMethod to trigger a signal. Since slotProgress1 is a slot, you may try to invoke a signal which connects to it in a GUI thread.
@JoeCFD Maybe I need to ask how can I determine if the thread that invokes Progress1 is the GUI thread and invoke QCoreApplication::processEvents() IF it is is the GUI thread??
If Progress1 is called from a non-GUI thread then no need to call processEvents() I think?
D.
-
@JoeCFD Maybe I need to ask how can I determine if the thread that invokes Progress1 is the GUI thread and invoke QCoreApplication::processEvents() IF it is is the GUI thread??
If Progress1 is called from a non-GUI thread then no need to call processEvents() I think?
D.
@Perdrix if you invoke a signal, you do not need to call processEvents() ; slot slotProgress1 is a function and can be called because of a signal or QMetaObject::invokeMethod or direct call. You may check which sender the caller is by calling sender() function inside the slot slotProgress1() .
void slotProgress1(const QString& szText, int lAchieved1) { auto which_sender = sender(); } -
Given:
public: void Progress1(const QString& szText, int lAchieved1); protected slots: virtual void slotProgress1(const QString& szText, int lAchieved1);and:
void ProgressBase::Progress1(const QString& text, int achieved) { QMetaObject::invokeMethod(this, "slotProgress1", Qt::QueuedConnection, Q_ARG(const QString&, text), Q_ARG(int, achieved)); } void ProgressBase::slotProgress1(const QString& szText, int lAchieved1) { qDebug() << __FUNCTION__; // etc.If Progress1 is called from a long running non-GUI thread, all is well and the slot is driven.
If however Progress1 is called from long running code on the GUI thread, then the slot isn't driven because the event dispatcher doesn't get to handle it.
What's the correct way to ensure the slot is driven regardless of which thread Progess1 is called from? Calling QCoreApplication::processEvents() inside Progress1 immediately after invokeMethod() when the code isn't the GUI thread doesn't feel like it's the correct answer.
@Perdrix said in QMetaObject::invokeMethod() problem:
If however Progress1 is called from long running code on the GUI thread, then the slot isn't driven because the event dispatcher doesn't get to handle it.
Then don't use Qt::QueuedConnection so it get's called immediatelly.
-
@Perdrix if you invoke a signal, you do not need to call processEvents() ; slot slotProgress1 is a function and can be called because of a signal or QMetaObject::invokeMethod or direct call. You may check which sender the caller is by calling sender() function inside the slot slotProgress1() .
void slotProgress1(const QString& szText, int lAchieved1) { auto which_sender = sender(); } -
@JoeCFD said in QMetaObject::invokeMethod() problem:
sender
So are you saying that Progress1 should simply be declared as a signal ? I can certainly do that ...
@Perdrix I did not mean that. I saw that you are trying to call slotProgress1 with QMetaObject::invokeMethod. What I meant was you may have defined a signal which connects to slot slotProgress1. Instead of trying to trigger the slotProgress1, you can also trigger this signal with QMetaObject::invokeMethod and then slotProgress1 will be called.
-
@JoeCFD said in QMetaObject::invokeMethod() problem:
sender
So are you saying that Progress1 should simply be declared as a signal ? I can certainly do that ...
@Perdrix As @Christian-Ehrlicher said just remove the
Qt::QueuedConnectionparameter. It forces the invocation to be queued, whether it's on the same thread or not. If you omit this parameter an auto connection type will be used, meaning that the slot will be called immediately if the target object is in the same thread or queued if on a different one.As a side note - if your problem is that the GUI thread is blocked then you're doing it wrong. You should never block GUI thread and long running tasks should always be executed in worker threads.
CallingQCoreApplication::processEvents()is a hack indicating bad app flow design. -
@Perdrix As @Christian-Ehrlicher said just remove the
Qt::QueuedConnectionparameter. It forces the invocation to be queued, whether it's on the same thread or not. If you omit this parameter an auto connection type will be used, meaning that the slot will be called immediately if the target object is in the same thread or queued if on a different one.As a side note - if your problem is that the GUI thread is blocked then you're doing it wrong. You should never block GUI thread and long running tasks should always be executed in worker threads.
CallingQCoreApplication::processEvents()is a hack indicating bad app flow design.@Chris-Kawa >Long running tasks should always be executed in worker threads...
I couldn't agree more but when dealing with inherited code, you need to cope with the cases where this isn't being done as they're not always simple to move into a worker thread. That's why this code has to be written to handle both cases.
-
@Chris-Kawa >Long running tasks should always be executed in worker threads...
I couldn't agree more but when dealing with inherited code, you need to cope with the cases where this isn't being done as they're not always simple to move into a worker thread. That's why this code has to be written to handle both cases.
@Perdrix Ah yes, inherited code. The bane of all existence :)
Well, auto connection should work as you want. It will do a direct function call when in the same thread, so it doesn't matter that the event loop is blocked.