Deciding between signal-slot mechanism versus traditional loop - c++

I want to update many objects. I have the option to do so with traditional loop like:
void ParentClass::updateItems(const float factor)
{
for (Item *item : items()) {
item->update(factor)
}
}
... or I can do it with signal-slot mechanism like:
class ParentClass : public QObject
{
Q_OBJECT
Q_SIGNALS:
// The signal which will be emitted by parent:
void updateNeeded(const float factor);
private:
void updateItems(const float factor);
}
// Signal is emitted here
void ParentClass::updateItems(const float factor)
{
emit updateNeeded(factor);
}
class Item : public QObject
{
Q_OBJECT
Item(ParentClass *parent) : QObject()
, m_parent(parent)
{
// Connect parent signal to each item slot at each item constructor
QObject::connect(m_parent, &ParentClass::UpdateNeeded,
this, &Item::handleUpdate);
}
public Q_SLOTS:
void handleUpdate(const float factor);
private:
ParentClass *m_parent;
}
// The slot which handles emitted signal:
void Item::handleUpdate(const float factor)
{
this->update(factor);
}
I have tested the loop approach and it works.
I'm thinking on signal-slot mechanism, maybe it has some benefits for complex code.
I wonder which approach is how it should be done or is there a better way?
After writing my question, I found this post which is really helpful but I"m not sure if it is directly related to my question.

Use the loop based approach whenever and whereever you can, KISS
signal-slot is a very loose-coupled method of interfacing - while this can be great sometimes (mostly when other options are not possible..) it is much more brittle than regular function calls.

Related

Performance degrade from using QThread and multiple signals-and-slots connections

I am having trouble trying to figure out where this performance degrade in my codes comes from. My program is quite big so I'll simplify using some examples. Say I have a MainUI class subclassed from QMainWindow, which has two member objects, a Worker (subclassed from QThread) and a DisplayPanel (subclassed from QGLWidget). Then a connection is established between a signal from this Worker object (emitted from its run method) and a slot in the DisplayPanel object. And I see performance degrade in the run method if I increase the number of connections.
Basically what I am describing is (very simplified):
class Worker : public QThread {
Q_OBJECT
signals:
void updateDisplay();
protected:
void startWorking() {
// some setup codes
this->start();
}
void run() {
while (1) {
// some processing codes
emit updateDisplay();
}
}
};
class DisplayPanel : public QOpenGLWidget, protected QOpenGLFunctions_5_0_Core {
Q_OBJECT
public slots:
void refresh() {
update();
}
};
class MainUI : public QMainWindow {
Q_OBJECT
public:
explicit MainUI(QWidget *parent = 0) {
// some Ui setup codes
worker = new Worker();
num_panels = 5 // arbitrary number
panels.resize(num_panels);
for (auto p : panels) {
p = new DisplayPanel();
connect(worker, SIGNAL(updateDisplay()), p, SLOT(refresh()));
}
}
void process() {
worker->startWorking();
}
private:
unsigned num_panels;
Worker* worker;
std::vector<DisplayPanel*> panels;
};
Imagine MainUI::process() was called from the GUI.
What confuses me is that there is some noticeable performance degrade in the while loop of Worker::run() if I increase MainUI::num_panels. From my understanding, the increased number of signals-and-slots connections should not affect the performance of Worker very much because the queued requests to DisplayPanel::update() should run on a different thread.
Any insights would be appreciated!

Q_PROPERTY System not working as expected

I have this class I intended to use in the context of a qml engine so in order to use property binding I setted up these Q_PROPERY macros. I want to use the MEMBER keyword and have the notify signal emitted automatically.
class InterfaceBackend : public QObject
{
Q_OBJECT
Q_PROPERTY(quint8 current_view MEMBER m_current_view NOTIFY sCurrentViewChanged)
Q_PROPERTY(quint8 future_view MEMBER m_future_view NOTIFY sFutureViewChanged)
public:
explicit InterfaceBackend(QObject *parent = 0);
~InterfaceBackend();
quint8 getCurrentView() { return this->m_current_view; }
quint8 getFutureView() { return this->m_future_view; }
private:
quint8 m_current_view;
quint8 m_future_view;
QByteArray m_selected_language;
public slots:
void onLanguageSelected(QByteArray language);
private slots:
signals:
void sCurrentViewChanged(quint8 current_view);
void sFutureViewChanged(quint8 future_view);
};
InterfaceBackend::InterfaceBackend(QObject *parent) : QObject(parent)
{
this->setObjectName("backend");
QObject::connect(this, &InterfaceBackend::sFutureViewChanged, []() {qDebug() << "sFutureViewChanged";});
this->m_current_view=1;
this->m_future_view=1;
}
InterfaceBackend::~InterfaceBackend()
{
}
void InterfaceBackend::onLanguageSelected(QByteArray language)
{
this->m_selected_language=language;
this->m_future_view=2;
}
qt docs say:
A NOTIFY signal is optional. If defined, it should specify one existing signal in that class that is emitted whenever the value of the property changes. NOTIFY signals for MEMBER variables must take zero or one parameter, which must be of the same type as the property. The parameter will take the new value of the property. The NOTIFY signal should only be emitted when the property has really been changed, to avoid bindings being unnecessarily re-evaluated in QML, for example. Qt emits automatically that signal when needed for MEMBER properties that do not have an explicit setter
But whenever I call the slot the signals never gets called nor the property is updated in the qml model, what's wrong!?
To give an answer that is technically more accurate:
The MEMBER in Q_PROPERTY will tell the moc (Meta object compiler) that when accessing the property via the meta object it should use the member directly instead of a getter or setter method. So the moc will the generate a setter method internally that sets the member and emits the signal - it basically just does the work of writing getters/setters for you. Since changing a member needs to emit the change signal, this is automatically done when the property is written from the meta object system. So, calling:
backend->setProperty("future_view", future_view);
will correctly emit the changed signal. This is the only guarantee that is given when using MEMBER. Changes, that are done via the meta property will trigger the change signal. This means if you would set future_view from QML directly, without the onLanguageSelected method, it would actually work.
In your example however you directly write a value to the member inside a special method - This will not trigger the signal automatically! (I mean, how should Qt even know you did that). So what you need to do is whenever you change the value of your member you need to emit the change signal yourself:
void onLanguageSelected(QByteArray language)
{
this->m_selected_language=language;
this->m_future_view=2;
emit sFutureViewChanged();
}
Edit: If you were trying prevent the properties from beeing written directly from QML, using MEMBER will not work! Use a getter instead and only register the getter with the property. Use the same code as above to write and change the properties:
Q_PROPERTY(quint8 future_view READ futureView NOTIFY sFutureViewChanged)
As you can see in this article:
New keyword in Q_PROPERTY: MEMBER let you bind a property to a class member without requiring to have a getter or a setter.
So you should remove your getters, and your result code will look like this
class InterfaceBackend : public QObject
{
Q_OBJECT
Q_PROPERTY(quint8 current_view MEMBER m_current_view NOTIFY sCurrentViewChanged)
Q_PROPERTY(quint8 future_view MEMBER m_future_view NOTIFY sFutureViewChanged)
public:
explicit InterfaceBackend(QObject *parent = 0)
: QObject(parent)
{
this->setObjectName("backend");
QObject::connect(this, &InterfaceBackend::sFutureViewChanged, []() { qDebug() << "sFutureViewChanged";});
this->m_current_view=1;
emit sCurrentViewChanged();
this->m_future_view=1;
emit sFutureViewChanged();
}
~InterfaceBackend() = default;
private:
quint8 m_current_view;
quint8 m_future_view;
QByteArray m_selected_language;
public slots:
void onLanguageSelected(QByteArray language) {
this->m_selected_language=language;
this->m_future_view=2;
emit sFutureViewChanged();
}
signals:
void sCurrentViewChanged();
void sFutureViewChanged();
};
Every Q_PROPERTY must have READ public method and WRITE public slot, also the signal will never automatically emited, you should emit it whenever MEMBER change.
class InterfaceBackend : public QObject
{
Q_OBJECT
Q_PROPERTY(quint8 current_view MEMBER m_current_view READ currentView WRITE setCurrentView NOTIFY sCurrentViewChanged)
Q_PROPERTY(quint8 future_view MEMBER m_future_view READ futureView WRITE setFutureView NOTIFY sFutureViewChanged)
public:
explicit InterfaceBackend(QObject *parent = 0);
~InterfaceBackend();
quint8 currentView() const
{
return m_current_view;
}
quint8 futureView() const
{
return m_future_view;
}
private:
quint8 m_current_view;
quint8 m_future_view;
QByteArray m_selected_language;
public slots:
void onLanguageSelected(QByteArray language);
void setCurrentView(quint8 current_view)
{
if (m_current_view == current_view)
return;
m_current_view = current_view;
emit sCurrentViewChanged(m_current_view);
}
void setFutureView(quint8 future_view)
{
if (m_future_view == future_view)
return;
m_future_view = future_view;
emit sFutureViewChanged(m_future_view);
}
private slots:
signals:
void sCurrentViewChanged(quint8 current_view);
void sFutureViewChanged(quint8 future_view);
};
InterfaceBackend::InterfaceBackend(QObject *parent) : QObject(parent)
{
this->setObjectName("backend");
QObject::connect(this, &InterfaceBackend::sFutureViewChanged, []() {qDebug() << "sFutureViewChanged";});
this->m_current_view=1;
this->m_future_view=1;
}
InterfaceBackend::~InterfaceBackend()
{
}
void InterfaceBackend::onLanguageSelected(QByteArray language)
{
this->m_selected_language=language;
this->m_future_view=2;
}
Spending some time, I got it working as I need, althought this is EXTREMELY UGLY and BY NO MEANS DECLARATIVE I think it is the only way to achive double binding, and maybe it will be helpful for other people approaching the problem. Note this is not an accepted answer, I still hope someone somewhere, perhaps in the future if and when Qt will be updated, could come up with a far more elegant solution.
Main disadvantages: longer and verbose code, one has to use setters everywhere and (try not forget)
The property system doesn't do anything automatically in the end, just states wich properties are exposed and wich are their getters/setters and change signals
c++
class InterfaceController : public QObject
{
Q_OBJECT
Q_PROPERTY(quint8 current_view READ getCurrentView WRITE setCurrentView NOTIFY sCurrentViewChanged)
Q_PROPERTY(quint8 future_view READ getFutureView WRITE setFutureView NOTIFY sFutureViewChanged)
public:
explicit InterfaceController(QObject *parent = 0);
//getters
quint8 getCurrentView() { return this->m_current_view; }
quint8 getFutureView() { return this->m_future_view; }
//setters
Q_INVOKABLE void setCurrentView(quint8 current_view) { if(this->m_current_view!=current_view) {this->m_current_view=current_view; emit sCurrentViewChanged(this->m_current_view);} }
Q_INVOKABLE void setFutureView(quint8 future_view) { if(this->m_future_view!=future_view) {this->m_future_view=future_view; emit sFutureViewChanged(this->m_future_view);} }
private:
quint8 m_current_view;
quint8 m_future_view;
QByteArray m_selected_language;
public slots:
void onLanguageSelected(QByteArray language);
private slots:
signals:
void sCurrentViewChanged(quint8 current_view);
void sFutureViewChanged(quint8 future_view);
};
InterfaceController::InterfaceController(QObject *parent) : QObject(parent)
{
this->m_current_view=1;
this->m_future_view=1;
}
void InterfaceController::onLanguageSelected(QByteArray language)
{
this->m_selected_language=language;
this->setFutureView(2);
}
QML
id: root
property int current_view: 1
property int future_view: 1
Connections {
target: root
onCurrent_viewChanged: { backend.setCurrentView(current_view); }
onFuture_viewChanged: { backend.setFutureView(future_view); }
}
Connections {
target: backend
onSCurrentViewChanged: { if(root.current_view!=current_view) {root.current_view=current_view;} }
onSFutureViewChanged: { if(root.future_view!=future_view) {root.future_view=future_view;} }
}

How to manually send a QSignal from multiple std::threads?

Essentially, I'm trying to add a Qt GUI onto an already existing GLUT application I made, and I'm not sure how to pass signals from the existing code into Qt, without introducing the overhead of making dozens of existing classes QObjects. Currently I'm using a QTimer and an event loop, but that isn't scaling very well, seeming to involve a lot of waiting for mutexes to then decide there's no work, with attempts to resolve this more increasing coupling than fixing it.
The best alternate I could come up with is this:
static std::mutex relay_mutex;
static std::condition_variable relay_condition;
static std::queue<std::pair<int, void*> > relay_queue;
extern "C"
void passMessage(int a, void * b)
{
std::lock_guard<std::mutex> lock(relay_mutex);
relay_queue.emplace_back(a, b);
relay_condition.notify_all();
}
class RelayThread : public QThread
{
Q_OBJECT
public:
RelayThread() {}
signals:
void passMessage(int, void *);
protected:
void run() override
{
std::unique_lock<std::mutex> lock(relay_mutex);
for(;;)
{
while(relay_queue.empty())
relay_condition.wait();
do {
emit passMessage(relay_queue.front().first, relay_queue.front().second);
if(relay_queue.front().first == 0)
return;
relay_queue.pop()
} while(relay_queue.size());
}
}
};
But this will obviously incur the overhead of an extra context switch, which is no good either. Is there a way to manually queue up a signal without going through a QThread like this?
This should be sufficient. You can let Qt do the queueing, if needed, by setting the connection type to Qt::QueuedConnection, the value of the third argument of the connect() call.
#include <QObject>
void passMessage(int a, MessageType b) {
Relay r(a,b);
}
class Relay: public QObject {
Q_OBJECT
public:
Relay(int a, MessageType b) {
emit passMessage(a, b);
}
signals:
void passMessage(int, MessageType);
};
You will also need to register a metatype for MessageType. See here.

How would I connect this signal to the slot

I would like to create a signal in my main class foo so that a static method in a different class could emit it.I just started of with QT so I am a bit confused. I currently have the following code
class Foo : public QMainWindow
{
Q_OBJECT
public:
Foo(QWidget *parent = 0, Qt::WFlags flags = 0);
~Foo();
signals:
void UpdateSignal(int val);
private slots:
void MySlot(int val);
};
Foo::Foo(QWidget *parent, Qt::WFlags flags): QMainWindow(parent, flags)
{
//How do I connect Bfoo::somemethod() here. I know its suppose to be like
connect(xx,SIGNAL(UpdateSignal(int)),this, SLOT(MySlot(int)));
ui.setupUi(this);
}
void Foo::MySlot(int val)
{
//Do something..
}
Now I have this class
Class Bfoo
{
static void somemethod()
{
emit UpdateSignal(12);
}
}
Any suggestions on how the static somemethod() could emit the UpdateSignal
When you emit signal it is necessary to know which object is emitting it. This is because signals are not implemented to be messages between different classes but messages between instances of (possibly different) classes.
Secondly, signals are protected methods. They are not accessible for external users. What you can do is define public method in Foo which will do the emission:
void Foo:EmitUpdateSignal(int x) {
emit UpdateSignal(x);
}
And then in your Bfoo::somemethod() you need to pass object which will emit signal:
void BFoo::somemethod(Foo &f) {
f.EmitUpdateSignal(12);
}
However, notice what you are doing. You emit signal which is connected to the slot in the same instance. This suggests design flaw but I cannot give any hints without more details about what are you going to achieve.

QT Widget Slot Call Not Activated by Separate Thread Signal

I have 5 classes that interact (maintaining professionally, not author). My problem is that the code that is emitting the signal (there is just one, code below) is never activating the slot (MyWidget::HandleMeasurementChanged). This system has a large degree of complexity. I have tried to reduce that, but think the complexity likely contributes to the problem. There is also a high rate of calls to Observer::notify, but most of these will get filtered out by code that I have not posted here and the Emit calls are fairly rare. If anyone could help point me to why the slot is not getting activated, I'd really appreciate it. It is almost acting like the MyWidget class instance is not processing its event loop. I have had a little success setting the connect type to Direct Connection, but since the emit is in a separate thread and the production code for the slot will update the UI I have ruled that out as a final solution.
class IObserver { public: virtual void notify()=0; };
class ExternalMeasurement { ... };
class Measurement { public: Measurement(ExternalMeasurement source); };
class Observer : public QThread, public IObserver
{
signals:
void MeasurementChanged(boost::shared_ptr<Measurement> measurement);
public:
//called by 3rd party in separate thread
virtual void notify(ExternalMeasurement measurement)
{
_measurement_ =
boost::shared_ptr<Measurement>(new Measurement(measurement));
emit MeasurementChanged(_measurement);
}
private:
boost::shared_ptr<Measurement> _measurement_;
};
class MyWidget : public QWidget
{
private:
Component _component_;
public slots:
void HandleMeasurementChanged(boost::shared_ptr<Measurement> measurement);
public:
MyWidget(Component * component_)
};
MyWidget::MyWidget(Component * component_)
{
_component_ = component_;
connect(
_component_->_observer_,
MeasurementChanged(boost::shared_ptr<Measurement> measurement),
this,
HandleMeasurementChanged(boost::shared_ptr<Measurement> measurement));
}
class Component
{
private:
QApplication * _application_;
MyWidget * _widget_;
Observer * _observer_;
public:
void MainFunc();
}
void Component::MainFunc()
{
_observer_ = new Observer();
...
_application_ = new QApplication(...);
...
_widget_ = new MyWidget(...);
...
_widget_->show();
_application_->exec();
}
This was referenced in the link that Jeremy added in a comment to my question, but just for clarity:
The solution was to add:
qRegisterMetaType<shared_ptr<Measurement> >("shared_ptr<Measurement>");
immediately before the call to connect.