I'm trying to use signals and slots to pass information to the GUI thread from another thread, as I can't modify a pixmap from any other thread. I'm encountering a runtime error:
Object::connect: No such signal QThread::image_change(std::string) in visualiser.cpp:33
Judging from this, though I may be wrong, it looks like the signal is being searched for in the wrong namespace, as it is actually defined in Visualiser::image_change().
My code is as follows:
Visualiser.cpp:
QFutureWatcher<void> watcher;
connect(watcher.thread(), SIGNAL(image_change(std::string)), QCoreApplication::instance()->thread(), SLOT(update_image(std::string)), Qt::QueuedConnection);
QFuture<void> update_thread = QtConcurrent::run(this, &Visualiser::update_state);
watcher.setFuture(update_thread);
...
emit(image_change(imageSrc));
...
void Visualiser::update_image(std::string src)
{
QImage image;
image.load(src.c_str());
ui->visualContainer->setPixmap(QPixmap::fromImage(image));
}
visualiser.h:
signals:
void image_change(std::string src);
public slots:
void update_image(std::string src);
Don't pass thread pointers into connect - pass pointers to the sender and receiver of the event (like this). Because you're giving it QThread pointers instead, Qt is looking for those signals and slots in QThread, where they don't exist. If you give it Visualizer pointers instead, Qt will look for those functions in Visualizer, where they really are, and everything will work.
Hope that helps!
The source and the target of the connection are the same object, this, so the connect call should be:
connect(this, SIGNAL(image_change(std::string)), this, SLOT(update_image(std::string)));
Since the signal will be emitted from another thread than the one the Visualizer has an affinity with (see QObject::moveToThread()), the connection with the slot will automatically be queued, and the slot will be executed by the correct thread.
But for queued connection to work, Qt has to store temporarily the parameter until it can actually call the slot, which is done by converting it to QVariant, storing it somewhere, and then reconverting it to the actual type when the receiving thread is ready to execute the slot.
So you need to register std::string to Qt's metatype system with Q_DECLARE_METATYPE or change the signal and slot parameter type to one that is already registered to (like QString or QByteArray).
Related
I would like to understand a simple piece of code I came across. It is a connection between an object of the interface and two signals.
The code is:
connect( ui->checkbox_legEnabled,
SIGNAL( stateChanged( int ) ), SIGNAL( edited() ) );
What is the meaning of this line of code?
Thanks,
Sara
With Qt signals and slots, you can directly connect one signal to another signal (or non-signal member function), without having a slot in between. See connection function invoked here is this overload of QObject::connect.
This line of code hence means, whenever the object ui->checkbox_legEnabled (presumably some kind of QCheckbox) emits the stateChanged signal (that has an int parameter passed along), directly emit another signal (or ordinary member function) edited (without parameters).
Short answer is you can connect a signal to another signal and that means that the second signal will also emit whenever the first signal is emitted, read more about signals and slots in the documentation here, also check out the new way to call connect (with function pointers)
Now in your case what it does it's basically allows you to keep the ui private, but in the same time forward the signals you want to the outside of your object, by allowing other objects to connect to the signal(s) you provide in the interface.
Incomplete usage example (based on your code, i named the class that contains your code MyWidget): the main-window (or whoever) that has access to your widget can be notified whenever something changes inside, by connecting to the edited signal:
void MainWindow::createMyWidget()
{
m_myWidget = new MyWidget(this);
connect(m_myWidget, &MyWidget::edited, this, &MainWindow::myWidgetWasEdited));
}
This way whenever something changes inside MyWidget the MainWindow can be notified about the edit and it can take the necessary actions inside the myWidgetWasEdited slot.
This can be expanded, if needed, to provide an signal for each particular "edit" instead of a single generic edited signal (but this depends on your needs).
I know that normally you wouldn’t do what I’m asking. I understand that these two layers should be separate and connect via signal/slot mechanism, which maybe asynchronous if we deal with threads.
Understanding this, I still need to call qml signal handler synchronously from SG thread. Qml objects live in GUI thread, thus emitting a signal from SG thread (particularly from updatePaintNode() method) results in asynchronous event.
I have read docs and I have no problem calling qml function synchronously from cpp from another thread. For example:
QMetaObject::invokeMethod(this, "myNiceQmlFunction", Qt::DirectConnection);
But imagine this:
//some.cpp
signal void callQmlHandler();
//some.qml
MyObject {
onCallQmlHandler: {
// do something right now
}
}
I don’t know how to call onCallQmlHandler synchronously via QMetaObject::invokeMethod.
I don’t create qml object from code and at this point in cpp I don’t have access to qml component to look for its children, find MyObject there by name and call its handler (if it is possible). Anyways, this is not a beautiful way to do so.
I tried to find signal handler among QMetaObject methods, but it's not there. Nor it is in properties list (I checked just in case, because syntax of signal handlers is similar to property's one)
Does anyone know if I miss the right syntax to call signal handler via QMetaObject::invokeMethod or it is not possible at all? Any ideas?
You can pass a C++ object to QML using its context.
qmlviewer.rootContext()->setContextProperty("backend", backend);
QML side:
Connections {
target: backend
onCallQmlHandler: {
// do something right now
}
}
When you emit callQmlHandler from backend object, you get the handler executed.
But Connections object may create queued connections, so you can implement your own DirectConnections. At the end of this post you have an implementation.
However QML is excecuted by a QML engine, which I think is intended to be run by a single thread, so you may run into a bigger problem unless you really know what you are doing!
i am at the moment really confused about threading in QT. I read so much different opinions which i can realize this.
I have a class (Computations) which do heavy computations and generate a lot of solution. When this class find a solution i call with help of
boost::function<void (Solution)> f;
a custom function. Which can be bind with
f = boost::bind( &MainWindow::FoundSolution, this,_1);
No i have my mainwindow in QT. There i call my class Computations with
Computations comp(f);
QFuture<void> future = QtConcurrent::run(comp,&Computations::DoSomethink);
So it compute while i can use the GUI and i get the response of new solutions to the FoundSolution function in my mainwindow. In this function I use QGraphicview to draw my solution. The Qgraphicsview is a member of my Mainwindow.
This works sometimes.
But i often get the following error
Assert failure in QCOREApplication::SendEvent: " cannot send events to
objects owend by a different thread. Current thread 438bc40. Receiver
" (of type "Qgraphicsscene) was created in thread 15dcc00, file
kernel\qcoreapllication line 494
This mean i call my GUI application from a thread which is not the main and this is forbidden by QT. But with my boost bind i should be in my main thread or? And why it works sometimes ?
Do you know a better implementation for this? I am really new with QT and threads.
Thank you for your help.
If you emit a signal from you worker thread that is connected to a slot of an object that lives in the ui thread, the slot will be executed in the ui thread.
So basically if you want to draw a new point in the QGraphicsView, send a signal from your worker thread passing in the coordinates of the new point to draw. The signal need to be connected to an object that lives in the ui thread. The slot will then handle drawing the new point in the QGraphicsView which will work since the slot is executed in the ui thread.
For more information see Signals and Slots Across Threads
The normal way to do this is to use signals and slots. You can find some documentation, which is specific to your problem, here: Qt 4.8, Qt 5.
connect( &sender, SIGNAL(), &receiver, SLOT(), Qt::QueuedConnection)
The easier fix is to add Qt::QueuedConnection to the problematic slot connection call.
problem
i'm currently putting FUSE together with qt5. there is no bridge between Qt and FUSE yet, both the FUSE main thread (which is spawning the other working FUSE threads) and the QCoreApplication are simply running side by side.
but i want to be able to send and receive data between a QObject based object and the pthread's Read(..) function shown in [0] using Qt's SIGNALS and SLOTS.
question
now i want to alter the Read(..) function from [0] to retrieve data using Qt's SIGNALS and SLOTS from a QObject based class. sending a signal from a pthread works but without an explicit QEventLoop i can't receive the reply. therefore i was looking at the code from [1] which is excellent in design but i didn't get it working yet.
pseudo code (taken from [1]):
QNetworkAccessManager qnam;
QNetworkReply *reply = qnam.get(QNetworkRequest(QUrl(...)));
QEventLoop loop;
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
/* reply has finished, use it */
looks interesting, all i would need is a QObject deriving class similar to the QNetworkReply which would handle the request.
when i was playing with that code i had the problem that my implementation of QNetworkReply wouldn't wait for loop.exec() to be running and then the finished() SIGNAL wouldn't be received by the loop.
but isn't there something easier than to spawn a QEventLoop?
NOTE: the QNetworkReply and QNetworkAccessManager in the example from [1] is spawned inside the pthread, i however, need to be able to communicate with the QCoreApplication's even queue using SIGNALS and SLOTS since the object with the data in it comes from a different QThread (in this either the QCoreApplication or a special QThread).
using a Qt::QueuedConnection
i've also found [2] and maybe:
connect(src, SIGNAL(signal-signature), dest, SLOT(slot-signature), Qt::QueuedConnection);
is all i want but i doubt that.
links
[0] https://github.com/qknight/qt-fuse-example/blob/4d92a74fad22fd559588e58be67f766174c7efb8/qt-fuse/examplefs.cc#L74
[1] http://qt-project.org/wiki/ThreadsEventsQObjects#7494f2b4836907fc1c09311e3a0305e6
[2] emit Qt signal from non Qt Thread or ouside Qt main event loop with at 4.5
What you're likely facing is that QNetworkAccessManager internally uses threads to process http requests. That's why it "doesn't wait" for you. There's a rather simple modification needed to fix it:
QNetworkAccessManager qnam;
QEventLoop loop;
QNetworkReply *reply = qnam.get(QNetworkRequest(QUrl(...)));
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
if (!reply->isFinished()) loop.exec();
Things to note when using QObjects in Multiple Threads
When the object that is the source of a signal lives (is instantiated in) a thread different than the thread of the object with slots, the connection will be of the QueuedConnection type automatically.
The real issue is: Each QObject has a thread affinity. The default affinity is the thread where the object was instantiated. You're not supposed to use such objects directly from other threads.
What you're likely doing is instantiating the sender and receiver objects in the same thread, but then emitting the signal from another thread. This is a source of potential errors error and leads to undefined behavior if the user of such an object is forcing a non-automatic direct connection.
Whenever you do emit object->signal(...), the following invariant should hold:
Q_ASSERT(QThread::currentThread() == object->thread());
Feel free to add those invariant checks in front of every emit() that you explicitly perform.
If the assertion fails, you need to use QObject::moveToThread to move the object to the thread where you want to fire its signals. You can get a QThread for a given pthread by calling QThread::currentThread() from the code that runs in the pthread. An instance of a QThread will be created automagically for you :)
Yes, you want the Qt::QueuedConnection method. But also ensure that you are using the multithreading Qt library. IIRC it is a build-time option.
See also: Qt documentation
I have some problems with Qt. I have a class with a signal who's parameters are strings, and a slot. I'm connecting the signal to the slot in the class constructor. Also, I'm creating a thread in the class constructor. The thread reads data from a server and updates the UI(emits the UpdateMe signal). This is how I connect the signal to the slot:
QObject::connect(this, SIGNAL(UpdateMe(string, string)), this, SLOT(ModifyUI(string, string)));
I have a QTreeWidget with some file names. When I rename a file I notify the server and the server notifies the other clients. When I connect a single client there is no problem, but when I connect more than one client a problem appears: when I notify the server from the second client(when I write into the socket) the following error appears:
QObject::connect: Cannot queue arguments of type 'QVector<int>'
I tried to register QVector with qRegisterMetaType but I also have a signal that is emited when I modify an QTreeWidgetItem(when I rename the item, for example) and I need to dissconnect this signal when I want to change the item's text. If I register QVector I can't dissconnect this signal and the signal is emited.
When you register the QVector, does your call look like this?
qRegisterMetaType<QVector<int> >("QVector<int>");
Once you make this call, you should be able to emit the QVector type over queued connections.
If I register QVector I can't dissconnect this signal and the signal is emited.
Registering a metatype shouldn't prevent you from disconnecting a signal. It just allows you to queue types that aren't already registered with the meta system.
Most of the time, errors which look like this seem to be a result of mixing up threads, and specifically with this one, in my (limited) experience, a result of attempting to manipulate GUI elements "held" in the GUI thread using commands run a worker QThread.
I say "held" because quite often you get a complaint/error/crash saying something like "QObject: Cannot create children for a parent that is in a different thread." (i.e. the GUI thread).
The solution: from a non-GUI QThread ALWAYS communicate with GUI elements using signals and slots.