What are alternatives to using Qt-like signals and slots for communication between two objects (class instances) in both directions?
I know this can be realized by saving a reference of the other in each object. However, this gets sort of confusing when there are many different objects and all objects are supposed to interact with (and thus to know) one another.
The other question I've had was how Qt signals and slots implement this inter-object communication internally (a simple overview of the internal course of events would be helpful)?
A Qt example of what I'm talking about would be the following. Both objects, c and d, can emit a signal that is connected to a slot of the other.
class Chicken : public QObject
{
Q_OBJECT
public:
Chicken() = default;
public slots:
void helloBackDuck() {
std::cout << "Hello Back Duck" << std::endl;
}
signals:
void helloDuck();
};
class Duck : public QObject
{
Q_OBJECT
public:
Duck() = default;
public slots:
void helloBackChicken() {
std::cout << "Hello Back Chicken" << std::endl;
}
signals:
void helloChicken();
};
int main() {
Chicken c;
Duck d;
connect(&c, &Chicken::helloDuck, &d, &Duck::helloBackChicken);
connect(&d, &Duck::helloChicken, &c, &Chicken::helloBackDuck);
}
Qt Signal and slot mechanism is best choose in Qt programs, but if you want to know about other options, you have these:
You can develop your own Observer structure that would be like Qt signal and slot but you should invent the wheel from the beginning by yourself.
You should create an Observe class and a Subject class. In the beginning connect your Observer by passing a reference of it to Subject and as soon as the condition meet Subject will call the update function of all of the instances (emit).
For more explanation check this link.
Another option is using Boost signal2 that is also similar to Qt signal slot but from third party libraries. Usage is similar to Qt signal.
For more explanation check this.
Related
I have difficulty connecting to SLOTs defined in a different class. I have 2 classes - Computations and MainWindow. MainWindow is supposed to handle the GUI part of the program only and Computations handles the calculations. I am creating a calculator.
Basically, I want to understand how I can connect to SLOTs in the Computations Class from the MainWindow Class.
I guess you already checked the Qt Signals & Slots page. To implement what you want you need to have an instance of your class in the other one.
So for example in your MainWindow class, you can include the Computations class and create a member variable of it:
#include "computations.h"
class MainWindow : public QMainWindow
{
Q_ObBJECT
public:
//..
private:
Computations *_computation;
};
and then in the constructor of MainWindow after initializing the _computation object (_computation = new Computations();) you do the connections like this (works for Qt5):
QObject::connect(_computation, &Computations::somethingHappened, this, &MainWindow::doSomeGuiStuff);
QObject::connect(this, &MainWindow::guiThingHappened, _computation, &Computations::doSomeComputation);
depending on which way it should go.
I hope this helps.
This is another version how to use, I think can be easier to understand for beginners
You need define signal and slots in your classes.
Add to header of your class, for example signals to MainWindow, slots to Computations
public slots:
void something();
signals:
void something_happend();
Then, in anywhere, where you want use it, in your example in mainwindow.cpp, you need to connect signal and slot. Do this by QObject::connect :
QObject::connect(who_emit,SIGNAL(what_signal),who_receive,SLOT(what_slot))
Example:
mainwindow.h
signals:
void something_happend();
computations.h
public slots:
void something_happend();
mainwindow.cpp
Computations *c = new Computations(this);
QObject::connect(this,SIGNAL(something_happend()),c,SLOT(something()));
If you want to pass some arguments, SIGNAL and SLOT that you want to connect need have same types of arguments:
public slots:
void something(int c);
signals:
void something_happend(int c);
QObject::connect(this,SIGNAL(something_happend(int)),c,SLOT(something(int)));
Such connections belong at a level where both the UI and the controller (computation object) are available. Thus, either in the body of main, or in a class that composes that various elements of the application at a high level (such a class usually shouldn't derive from QApplication, though).
It is almost always too tight of a coupling if the UI class knows of the existence of the computation object, or is somehow tied to its details. I usually design the UI to have an interface composed of signals and slots of as generic a nature as practicable, and then tie it to one or more controller objects via signal/slot connections. I also leverage the property system to expose UI-relevant properties in a generic manner, often using viewmodel objects to interface a UI-agnostic controller to a concrete kind of a UI.
In your case, I'd suggest that neither MainWindow know of Computations, nor vice versa:
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
Computations comp;
MainWindow ui;
QObject::connect(&comp, ..., &ui, ...);
/* more connections here */
ui.show();
return app.exec();
}
For more concrete examples, see answer #1 or answer #2.
you need slots and signals because those work together, like this:
your file.h
public slots:
void start();
signals:
void levelChanged(int level);
implementing:
void MainBoard::start()
{
isStarted = true;
clearBoard();
emit levelChanged(1);
}
now you need to link a button
startButton = new QPushButton(tr("&Start"));
startButton->setFocusPolicy(Qt::NoFocus);
connect(startButton, &QPushButton::clicked, board, &MainBoard::start);
My problem is: I'm using an example made by Qt as educational (it's a terminal to send and receive serial data). I need to create a QTimer starting in a function built in a class and stop it in another function built in a different class.
Qtimer obviously works with no problem in function declared in the same class where Timer is; but it remains unknown in the class built to the other class.
How can I solve?
You may want to use the Qt SIGNAL / SLOT system. Then you may have something like that:
class A : public QObject
{
Q_OBJECT
//...
public slots:
stopTimerHandler() {timer.stop();}
private:
QTimer timer;
};
class B : public QObject
{
Q_OBJECT
//...
private:
stopTimer() {emit stopTimerSig();}
signals:
stopTimerSig();
};
// somewhere
A a;
B b;
connect(&b, SIGNAL(stopTimerSig()), &a, SLOT(stopTimerHandler())); // old connect version
// or ( ty #eyllanesc for the new syntax )
QObject::connect(&b, &B::stopTimerSig, &a, &A::stopTimerHandler); // qt5
B emit a signal listened by A, when A receive the signal, A stops his timer.
Let's say I have a class
class B : public class QObject {
Q_OBJECT
public:
B(QObject* parent=Q_NULLPTR);
signals:
void signalData(int data);
public slots:
void slotGetData();
private:
}
The slotGetData() is triggered externally and basically retrieves some data from somewhere and sends it back using signalData(int data). On the other hand I also have another class
class A : public class QObject {
Q_OBJECT
public:
A(QObject* parent=Q_NULLPTR) {
// Init B, move to thread, setup timer, connect timer's timeout to B's slotGetData()
// Connect B to A
connect(this->B, SIGNAL(signalData(int)), this, SLOT(slotGetData(int)));
}
signals:
// Emit signal containing data to another Qt component
void signalData(int x);
private slots:
// Connect B::signalData(int x) to this slot and re-emit the data using A::signalData(int x). Don't do anything with the data!
void slotGetData(int x);
private:
B* workerObj;
QThread worker;
QTimer workerTimer;
}
which basically is responsible for instantiating workerObj, moving it to worker thread and connecting the workerTimer's timeout() signal to B::slotGetData().
The purpose of this class is to enable proper integration (multi-threaded) of B inside a third-party class that wants to use it for example:
class ThirdParty : public class QWidget {
Q_OBJECT
public:
ThirdParty(QObject* parent=Q_NULLPTR) {
// Init A
// Connect to B through A
connect(this->integrationObj, SIGNAL(signalData(int)), this, SLOT(slotGetData(int)));
}
private slots:
// Connect A::signalData(int x) to this slot and do something with the x (change UI, whatever)
void slotGetData(int x);
private:
A* integrationObj;
}
The class ThirdParty gains indirect access to specific features of B through A.
Now the dilemma I'm facing is the following:
Should I just relay the data signal which comes from B through A and expose it as a signal of A or
Should I just return a const reference to B to allow an class which has A in it (so that it can use B) to directly connect to B's signalData(int x)?
In the first case (which I have) I basically have to mirror every single signal B wants to offer to the outside inside A (by providing the respective private slots in A and also signals that are basically the same as B's). Needless to say this leads to having too much of the same stuff and also has some (even if just slight) performance impact since I get 2 signal emissions (from B to A and then from A to whichever other object has A) and 2 slot calls (one in A to get signal from B and one in whichever other object has A to get signal from A).
The second case seems nice but I fear that I will expose features of B which the class containing A may not be allowed to have access to
If the second case is implemented I would have something like this (B doesn't change):
class A : public class QObject {
Q_OBJECT
public:
A(QObject* parent=Q_NULLPTR);
const B* getB() const; // Return a reference to B that cannot be changed but can be used to expose B's slots and signals
signals:
private slots:
private:
B* workerObj;
QThread worker;
QTimer workerTimer;
}
class ThirdParty : public class QWidget {
Q_OBJECT
public:
ThirdParty(QObject* parent=Q_NULLPTR) {
// Init A
// Connect to B directly!
connect(this->A->getB(), SIGNAL(signalData(int)), this, SLOT(slotGetData(int)));
}
private slots:
// Connect B::signalData(int x) to this slot and do something with the x (change UI, whatever)
void slotGetData(int x);
private:
A* integrationObj;
}
What should I do here? Sadly there are no private signals hence all B signals but also all of its public slots will be exposed. If all these are to be used, there is no issue with this degree of exposure but otherwise...not so much.
facepalm I already had the solution in another project of mine. :D I just need to provide a connectTo(QObject* thirdparty) inside A, which internally connects the thirdparty slot only to specific signal of B thus not exposing things from B that are not meant to be exposed!
Another better solution is to use a signal-to-signal connection (connect(const QObject *sender, const char *signal, const char *method, Qt::ConnectionType type)) or use the child-parent relation though in the latter I have to make sure that the parent actually is set. This is also known as signal forwarding and requires 2 signals but no extra slots. Since A is visible to ThirdParty I can do (this here represents ThirdParty) connect(this, SIGNAL(signalFromThirdPartyToB()), this->a, SIGNAL(signalFromAToB())) to set the forwarding of ThirdParty signals to B through A and then do the actual signal-slot connection between the forwarding signal in A to a slot in B by calling (this here represents A) connect(this, SIGNAL(signalFromAToB()), this->b, SLOT(slotB())).
I am developing a C++ class library for some computing-intensive tasks (machine vision).
// I am a part of a Qt-agnostic library
class Cruncher
{
/* ... */
public:
void doStuff();
};
Then there's a Qt GUI using that library. I'm creating a worker thread to call the heavy-lifting routines from the library:
// I am a part of a Qt-based GUI which utilizes the library
class Worker : public QThread
{
/* ... */
protected:
virtual void run()
{
/* ... */
Cruncher c;
for (int i = 0; i < count; ++i)
c.doStuff(); // takes some time, and while it's working
// it should communicate status changes which should
// become visible in the GUI
}
};
Now inside doStuff() a lot happens and I want to provide some feedback to the user on what is going on without waiting for doStuff() to return. For one, maybe some finer progress reporting than just increasing the meter by one step after a each call to doStuff(). Also, doStuff() may encounter non-critical failures which let it continue a part of the work, but I'd like a message to appear in the GUI when this happens as Cruncher is working (and Worker is currently busy with a call to doStuff()).
I want the library to remain Qt-independent so I'm not willing to add signals and slots to Cruncher. Any other way to enable it to provide feedback to the GUI to report on its work when it's not a Qt class?
I was considering creating a QTimer which would poll some "status" and "errorMsg" members of Cruncher at fixed intervals while Worker is running, but this seems highly sub-optimal.
I am posting my own answer because though I took #Nim's advice, I'd like the answer to be a little more verbose and hence more useful if someone should have the same problem.
I created the skeleton of a message dispatcher in the library:
// doesn't need to know about Qt
class MessagePort
{
public:
virtual void message(std::string msg) = 0;
};
Next, I added a handle to this object to Cruncher and spiced doStuff() with occasional calls to message():
// now with Super Cow powers!
class Cruncher
{
protected:
MessagePort *msgPort_;
public:
Cruncher(MessagePort *msgPort) : msgPort_(msgPort) {}
void doStuff()
{
while(...)
{
/*...*/
msgPort_->message("Foo caused an overload in Bar!");
}
}
};
Finally, I crafted an implementation of MessagePort inside the GUI using all necessary Qt goodness:
class CruncherMsgCallback : public QObject, public MessagePort
{
Q_OBJECT
public:
CruncherMsgCallback() : QObject(), MessagePort()
{
connect(this, SIGNAL(messageSignal(const QString &)),
GUI, SLOT(messageShow(const QString &)),
Qt::QueuedConnection);
}
virtual void message(std::string msg)
{
emit messageSignal(QString::fromStdString(msg));
}
signals:
void messageSignal(const QString &msg);
};
Finally when the Worker creates an instance of Cruncher, it also gives it a pointer to a working MessagePort:
class Worker
{
protected:
virtual void run()
{
CruncherMsgCallback msgC;
Cruncher c(&msgC); // &msgC works as a pointer to a
// generic MessagePort by upcasting
c.doStuff(); // Cruncher can send messages to the GUI
// from inside doStuff()
}
};
Use a callback function (class) etc, and pass that in during construction. Things you need to report, report via that callback.
You can safely emit signals from the run() method, I think that's the best way to pass information from worker thread to the main thread. Just add the signals to your QThread subclass (avoid adding slots, if you're at all unsure how QThread threading works).
Better make the connections from these signals explicitly queued, to avoid problems. Though the default, automatic connection type should also work and do Queued signal emit, but I think it's better to be explicit in cases like this. Actually also direct signals should work as such, but then you have to take care of thread safety yourself instead of letting Qt handle it for you, and you can't connect to slots which use any of the QtGui classes which only work in the main thread, so it's better to stick to queued connections.
To pass simple information to the run() method, and if immediate reaction is not needed, maybe use a few shared QAtomicInt variables or something like that as flags, which the worker thread checks when convenient. Slightly more complex method, still requiring polling, is to have shared data structure which you protect with mutex. More complex way of communicating to that direction would involve some kind of message queue (just like Qt uses in the event loop of the main thread, when you emit signal to that direction).
Boost.Signals allows various strategies of using the return values of slots to form the return value of the signal. E.g. adding them, forming a vector out of them, or returning the last one.
The common wisdom (expressed in the Qt documentation [EDIT: as well as some answers to this question ]) is that no such thing is possible with Qt signals.
However, when I run the moc on the following class definition:
class Object : public QObject {
Q_OBJECT
public:
explicit Object( QObject * parent=0 )
: QObject( parent ) {}
public Q_SLOTS:
void voidSlot();
int intSlot();
Q_SIGNALS:
void voidSignal();
int intSignal();
};
Not only doesn't moc complain about the signal with the non-void return type, it seems to actively implement it in such a way as to allow a return value to pass:
// SIGNAL 1
int Object::intSignal()
{
int _t0;
void *_a[] = { const_cast<void*>(reinterpret_cast<const void*>(&_t0)) };
QMetaObject::activate(this, &staticMetaObject, 1, _a);
return _t0;
}
So: according to the docs, this thing isn't possible. Then what is moc doing here?
Slots can have return values, so can we connect a slot with a return value to a signal with a return value now? May that be possible, after all? If so, is it useful?
EDIT: I'm not asking for workarounds, so please don't provide any.
EDIT: It obviously isn't useful in Qt::QueuedConnection mode (neither is the QPrintPreviewWidget API, though, and still it exists and is useful). But what about Qt::DirectConnection and Qt::BlockingQueuedConnection (or Qt::AutoConnection, when it resolves to Qt::DirectConnection).
OK. So, I did a little more investigating. Seems this is possible. I was able to emit a signal, and receive value from the slot the signal was connected to. But, the problem was that it only returned the last return value from the multiple connected slots:
Here's a simple class definition (main.cpp):
#include <QObject>
#include <QDebug>
class TestClass : public QObject
{
Q_OBJECT
public:
TestClass();
Q_SIGNALS:
QString testSignal();
public Q_SLOTS:
QString testSlot1() {
return QLatin1String("testSlot1");
}
QString testSlot2() {
return QLatin1String("testSlot2");
}
};
TestClass::TestClass() {
connect(this, SIGNAL(testSignal()), this, SLOT(testSlot1()));
connect(this, SIGNAL(testSignal()), this, SLOT(testSlot2()));
QString a = emit testSignal();
qDebug() << a;
}
int main() {
TestClass a;
}
#include "main.moc"
When main runs, it constructs one of the test classes. The constructor wires up two slots to the testSignal signal, and then emits the signal. It captures the return value from the slot(s) invoked.
Unfortunately, you only get the last return value. If you evaluate the code above, you'll get: "testSlot2", the last return value from the connected slots of the signal.
Here's why. Qt Signals are a syntax sugared interface to the signaling pattern. Slots are the recipients of a signal. In a direct connected signal-slot relationship, you could think of it similar to (pseudo-code):
foreach slot in connectedSlotsForSignal(signal):
value = invoke slot with parameters from signal
return value
Obviously the moc does a little more to help in this process (rudimentary type checking, etc), but this helps paint the picture.
No, they can't.
Boost::signals are quite different from those in Qt. The former provide an advanced callback mechanism, whereas the latter implement the signaling idiom. In the context of multithreading, Qt's (cross-threaded) signals depend on message queues, so they are called asynchronously at some (unknown to the emitter's thread) point in time.
Qt's qt_metacall function returns an integer status code. Because of this, I believe this makes an actual return value impossible (unless you fudge around with the meta object system and moc files after precompilation).
You do, however, have normal function parameters at your disposal. It should be possible to modify your code in such a way to use "out" parameters that act as your "return".
void ClassObj::method(return_type * return_)
{
...
if(return_) *return_ = ...;
}
// somewhere else in the code...
return_type ret;
emit this->method(&ret);
You may get a return value from Qt signal with the following code:
My example shows how to use a Qt signal to read the text of a QLineEdit.
I'm just extending what #jordan has proposed:
It should be possible to modify your code in such a way to use "out" parameters that act as your "return".
#include <QtCore>
#include <QtGui>
class SignalsRet : public QObject
{
Q_OBJECT
public:
SignalsRet()
{
connect(this, SIGNAL(Get(QString*)), SLOT(GetCurrentThread(QString*)), Qt::DirectConnection);
connect(this, SIGNAL(GetFromAnotherThread(QString*)), SLOT(ReadObject(QString*)), Qt::BlockingQueuedConnection);
edit.setText("This is a test");
}
public slots:
QString call()
{
QString text;
emit Get(&text);
return text;
}
signals:
void Get(QString *value);
void GetFromAnotherThread(QString *value);
private slots:
void GetCurrentThread(QString *value)
{
QThread *thread = QThread::currentThread();
QThread *mainthread = this->thread();
if(thread == mainthread) //Signal called from the same thread that SignalsRet class was living
ReadObject(value);
else //Signal called from another thread
emit GetFromAnotherThread(value);
}
void ReadObject(QString *value)
{
QString text = edit.text();
*value = text;
}
private:
QLineEdit edit;
};
To use this, just request call();.
You can try to workaround this with following:
All your connected slots must save their results in some place (container) accessible from signaling object
The last connected slot should somehow (select max or last value) process collected values and expose the only one
The emitting object can try to access this result
Just as an idea.