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.
Related
So, I have this simplified code as an example for my problem:
#include <QDebug>
#include <functional>
void third()
{
qInfo() << "finished everything!";
}
// will use stuff downloaded by `first()`, which will be used by `third`
void second(const std::function<void()>& cb = [] {})
{
// this has to be called after `first()` has been called and finished
// finished, calling callback
cb();
}
// Will download some stuff, which will be used by `second()`
void first(const std::function<void()>& cb = [] {})
{
// finished, calling callback
cb();
}
int main(int argc, char* argv[])
{
first([=] {
second(third);
});
// or, even worse
first([=] {
second([=] {
third();
});
});
}
Is there any way, that I can prevent this callback hell from happening?
I thought about making another thread, where I would call those functions, block that thread and wait for them to finish.
I am not super sure on how I would do that though. So, first of all, is there a better way to write this (maybe even without creating another thread?) and secondly, if I had to make this happen on another thread, how would I do that?
Thanks for any help in advance!
Signals and slots quite naturally clean up this kind of code. For example:
class Downloader : public QObject {
Q_OBJECT
public:
void beginDownload() // this is your first
{
emit downloadFinished({});
}
signals:
void downloadFinished(const QByteArray& data);
};
class DataProcessorA: public QObject {
Q_OBJECT
public:
void processData(const QByteArray& data) // this is your second
{
emit processingFinished({});
}
signals:
void processingFinished(const QDateArray& processedData);
};
class DataProcessorB: public QObject {
Q_OBJECT
public:
void processData2(const QByteArray& data) // this is your third
{
emit processingFinished({});
}
signals:
void processingFinished(const QDateArray& processedData);
};
void myFunction()
{
auto* downloader = new Downloader(parent);
auto* processorA = new DataProcessorA(parent);
auto* processorB = new DataProcessorB(parent);
// make first call second...
QObject::connect(downloader, &Downloader::downloadFinished, processorA, &DataProcessorA::processData);
// make second call third...
QObject::connect(processorA , &DataProcessorA::processingFinished, processorB, &DataProcessorB::processData);
}
This can work even if the download is executed on a separate thread because Qt's signals/slots can work between threads. The data in the signal will be copied to the receiving thread. Ref: https://doc.qt.io/qt-6/qt.html#ConnectionType-enum
There is a bit of boilerplate with all the QObject stuff, but this should allow you to write each piece in isolation and then be able to flexibly connect the individual pieces using QObject::connect.
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.
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.
I need to fire different signal from SomeClass::fireSignal member function depending on it parameters type. The solution I see is to use Qt meta system. Is there more correct solution for such task? Please help. Thanks in advance.
class SomeClass : public QObject
{
Q_OBJECT
public:
void fireSignal(BaseClass *param) {
if(param->metaObject->className() == "DerivedClass1") {
emit derivedClass1Signal(param)
}
if(param->metaObject->className() == "SecondDerivedClass") {
emit secondDerivedClassSignal(param)
}
if(param->metaObject->className() == "OtherDerivedClass") {
emit otherDerivedClassSignal(param)
}
}
signals:
void derivedClass1Signal(DerivedClass1 *param);
void secondDerivedClassSignal(SecondDerivedClass *param);
void otherDerivedClassSignal(OtherDerivedClass *param)
};
Maybe try:
class SomeClass : public QObject
{
Q_OBJECT
public:
void fireSignal(BaseClass *param) {
if(param->metaObject->className() == DerivedCass::staticMetaObject.className()) {
emit sig(qobject_cast<DerivedClass*>(param);
}
if(param->metaObject->className() == SecondDerivedClass::staticMetaObject.className()) {
emit sig(qobject_cast<SecondDerivedClass*>(param);
}
if(param->metaObject->className() == OtherDerivedClass::staticMetaObject.className()) {
emit sig(qobject_cast<OtherDerivedClass*>(param);
}
}
signals:
void sig(DerivedClass1 *param);
void sig(SecondDerivedClass *param);
void sig(OtherDerivedClass *param)
};
Generally speaking, this is Ok. You can make some improvements, but that will heavilly depend on how you are using these signals. For example, if all three are connected to the same slot(s), you can reduce your code here, leaving one signal that has one argument which is a pointer of a base class:
class SomeClass : public QObject
{
Q_OBJECT
public:
void fireSignal(BaseClass *param) {
emit derivedClassSignal(param);
}
signals:
void derivedClassSignal(DerivedClass1 *param);
};
However, removing the check from the sender's code will force you to include it into the receiver's code (i.e. the check for which exact derived type was passed). If, however, each signal is connected to a different slot, and those slots require only a specific type of argument, this will not work, and your approach will be better.
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.