Your MyProcessor
should indicate any property changes as they happen. Then it's a trivial matter for the GUI objects to react to the changes and adjust its state:
#include <QtWidgets>
class MyProcessor : public QObject {
Q_OBJECT
Q_PROPERTY(int value READ value NOTIFY valueChanged)
QBasicTimer m_timer;
int m_value;
void timerEvent(QTimerEvent * ev) {
if (ev->timerId() != m_timer.timerId()) return;
m_value = m_value == 42 ? 0 : 42;
emit valueChanged(m_value);
}
public:
MyProcessor(QObject * parent = 0) : QObject(parent) { m_timer.start(1000, this); }
Q_SIGNAL void valueChanged(int);
int value() const { return m_value; }
};
class Gui : public QObject {
Q_OBJECT
QLabel m_label;
public:
Gui(QObject * parent = 0) : QObject(parent) {
m_label.setMinimumWidth(qApp->fontMetrics().averageCharWidth() * 30);
m_label.show();
}
Q_SLOT void newValue(int value) {
m_label.setText(value == 42 ? "OK, correct value" : "Oops, wrong value");
}
};
// We must fix the broken-by-design QThread
struct Thread : public QThread { ~Thread() { quit(); wait(); } };
int main(int argc, char ** argv) {
QApplication app(argc, argv);
Gui gui;
MyProcessor proc;
Thread procThread;
proc.moveToThread(&procThread);
procThread.start();
QObject::connect(&proc, &MyProcessor::valueChanged, &gui, &Gui::newValue);
return app.exec();
}
#include "main.moc"
Only if both MyProcessor
does not signal property changes, and you cannot modify it to do so, would you poll for up-to-date values. You can either poll periodically, or at predetermined moments - this depends on the design of your system. In the example below, I'll assume that one must poll periodically.
The correct implementation is to break the call up into two parts: a request, and a response handler. Your goal is to invoke some code in another object, and then invoke some code in the source object when the response is available.
You should fully leverage C++11's lambda syntax to keep the code concise and retain the locality needed for a human maintainer to follow what's going on. The value is acquired in the target object, and then processed in the requesting object.
The only changes, compared to the first, preferred solution, are:
MyProcessor
does not notify of property changes.
- The
Gui
thread-safely, and in a non-blocking fashion polls the processor for property values.
// USE ONLY IF MyProcessor PROPERTIES HAVE NO CHANGE NOTIFIERS
#include <QtWidgets>
class MyProcessor : public QObject {
Q_OBJECT
Q_PROPERTY(int value READ value)
QBasicTimer m_timer;
int m_value;
void timerEvent(QTimerEvent * ev) {
if (ev->timerId() != m_timer.timerId()) return;
m_value = m_value == 42 ? 0 : 42;
}
public:
MyProcessor(QObject * parent = 0) : QObject(parent) { m_timer.start(1000, this); }
int value() const { return m_value; }
};
// See http://stackoverflow.com/a/21653558/1329652
template <typename F>
void postTo(QObject * obj, F && fun) {
if (!obj) return;
if (obj->thread() != QThread::currentThread()) {
QObject signalSource;
QObject::connect(&signalSource, &QObject::destroyed, obj, std::forward<F>(fun));
} else
fun();
}
class Gui : public QObject {
Q_OBJECT
QBasicTimer m_poll;
QLabel m_label;
QPointer<MyProcessor> m_proc;
void timerEvent(QTimerEvent * ev) {
if (ev->timerId() != m_poll.timerId()) return;
postTo(m_proc, [=]{
// runs in target's thread
auto value = m_proc->value();
postTo(this, [=]{ newValue(value); /* runs in our thread */ });
});
}
public:
Gui(MyProcessor * proc, QObject * parent = 0) : QObject(parent), m_proc(proc) {
m_label.setMinimumWidth(qApp->fontMetrics().averageCharWidth() * 30);
m_label.show();
m_poll.start(900, this);
}
Q_SLOT void newValue(int value) {
m_label.setText(value == 42 ? "OK, correct value" : "Oops, wrong value");
}
};
// We must fix the broken-by-design QThread
struct Thread : public QThread { ~Thread() { quit(); wait(); } };
int main(int argc, char ** argv) {
QApplication app(argc, argv);
MyProcessor proc;
Thread procThread;
proc.moveToThread(&procThread);
procThread.start();
Gui gui(&proc);
return app.exec();
}
#include "main.moc"
Finally, as a quick hack you could use blocking queued connections to call slots in other threads. This works only if the property read accessors are slots - it is a hack, after all. The GUI thread will block while waiting for the results, but you can safely read some values for debugging purposes that way. If you ship such code to your users, they will hate you.
// DO NOT USE THIS CODE IN PRODUCTION!
// THIS IS A HORRIBLE USABILITY BUG
// ONLY USE FOR DEBUGGING!!
#include <QtWidgets>
class MyProcessor : public QObject {
Q_OBJECT
Q_PROPERTY(int value READ value)
QBasicTimer m_timer;
int m_value;
void timerEvent(QTimerEvent * ev) {
if (ev->timerId() != m_timer.timerId()) return;
m_value = m_value == 42 ? 0 : 42;
}
public:
MyProcessor(QObject * parent = 0) : QObject(parent) { m_timer.start(1000, this); }
Q_SLOT int value() const { return m_value; }
};
class Gui : public QObject {
Q_OBJECT
QBasicTimer m_poll;
QLabel m_label;
QPointer<MyProcessor> m_proc;
void timerEvent(QTimerEvent * ev) {
if (ev->timerId() != m_poll.timerId()) return;
newValue(getValue());
}
public:
Gui(MyProcessor * proc, QObject * parent = 0) : QObject(parent), m_proc(proc) {
m_label.setMinimumWidth(qApp->fontMetrics().averageCharWidth() * 30);
m_label.show();
m_poll.start(900, this);
}
Q_SIGNAL int getValue();
Q_SLOT void newValue(int value) {
m_label.setText(value == 42 ? "OK, correct value" : "Oops, wrong value");
}
};
// We must fix the broken-by-design QThread
struct Thread : public QThread { ~Thread() { quit(); wait(); } };
int main(int argc, char ** argv) {
QApplication app(argc, argv);
MyProcessor proc;
Thread procThread;
proc.moveToThread(&procThread);
procThread.start();
Gui gui(&proc);
QObject::connect(&gui, &Gui::getValue, &proc, &MyProcessor::value, Qt::BlockingQueuedConnection);
return app.exec();
}
#include "main.moc"