I'm looking at using QScriptEngine in a project as a simple way to interact with a console application. With a few lines I was able to get everything wired up and it works great (I'm able to call slots on my objects, access properties, etc) except it seems that whenever I invoke a slot through a script command, it is called on the thread that QScriptEngine is on instead of the thread that the QObject is on. I have 3 threads in my application and the slots on the QObjects must be called on their respective threads. Is there a simple way to tell the script engine to fire the slot into the QObjects' threads' event loop?
Related
I am creating an modular application. It has a Core, and a few modules, amongs which is a gui module. These modules can be started via the command line:
myApp gui=qml=qmlFile.qml connection=serial=/dev/ttyS1
will start the app with one (multiple is also possible) gui and one serial connection. There is always one Router object, which handles the connection. Guis are loaded by a class that looks like this:
class Gui :QObject{
Core* core;
public:
QQmlApplicationEngine engine;
public slots:
void start(){
engine.load("qrc:/gui/from/command/line.qml");
}
In the gui i have a nice bit of qml called ConnectionController{} which is an easy way of adding/removing connections. But this is only the first of it's kind: in the end every module should have a qml component for controlling it.
This all works fine, but now i am trying to add multiple threads. I want to have a RouterThread and a GuiThread. This in itself was simply a matter of changing the creation of the Router (by the Core) to this:
m_router = new Router(this /*not as parent, just as pointer*/);
m_router->moveToThread(&routerThread);
(gui's still live in the main thread for now)
After this everything still works fine, except for qml property bindings.
QQmlEngine: Illegal attempt to connect to SerialConnection(0x7f58d00013a0) that is in a different thread than the QML engine
Because Gui and it's child QQmlEngine engine live in the guiThread while the SerialConnection lives in the routerThread.
Signals and slots between Router and Gui (without qml) do work, because they are handled by default as asynchronous by qt. (as long as the appropriate types are defined.)
Well, no, you cant access threaded objects from your gui and should not try to either.
You need to build a c++ model that lives in the main thread that is exposed to qml. Inside this model you can connect to signals emitted by your threads via a queuedconnection and link them to the models' own signals.
Also you should make sure that when you read data from an object owned by another thread, everything is properly mutexed or you will be reading garbage.
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 using qt5 and opencv to create an application that give a user inteface using qt and does the image processing part in opencv.....
so far my design is:
i am displaying a video and some standard controls like buttons and checkboxes using qt main gui thread
for capturing image and processing it, i have created a worker class derived from QObject and moved it to a thread....
the function that is executed in the worker class(Worker::process) has a blocking while loop.....that constantly:
captures a frame from a video or a camera
does some processing on it
converts from cv::Mat to QImage
emit a signal to the main thread to display the QImage
also in order to recieve user input i was using emitting signal from the main thread to the worker slots
the problem i faced was that the signal from the main thread never got picked off by the worker because of the event loop blocking while loop.
after much searching i came up with the solution to use Qt::DirectConnection argument while connecting the signal from the main thread to the worker slots. that solved the problem at that time.
now i need to add a qtimer or qbasictimer inside the blocking while loop....and guess what, the timer slot(in the case of qtimer) and the protected timerEvent handler (in the case of the qbasictimer) never get called. my hunch is that again the blocking while loop is the culprit
after much searching and reading on forums i have come to the conclusion that somehow my over all design maybe incorrect.....and as i keep adding more functionality to my application these problems will keep showing up.
I have two options now:
somehow call the threads exec() function inside the blocking while loop. so the question to the gurus out there is:
"how do i call the thread::exec() method inside a worker QObject class, i need the reference to the thread running the worker to call exec()" (short term solution)
change the whole implementation.....and here the questions is:
"what are my options....." (long term)
please feel free to ask for details in case my wording or english has made the problem unclear in any way.....thanks...
Inside your worker's blocking loop, call qApp->processEvents(); periodically.
http://qt-project.org/doc/qt-5.1/qtcore/qcoreapplication.html#processEvents
#include <QApplication>
// ...
void Worker::doWork()
{
while(true)
{
someLongImageProcessingFunction();
qApp->processEvents();
}
}
This should allow for the slots to get processed, and for the timers to update.
Using a direct connection may let you access and change values local to your worker, but you should be careful about thread safety. If you put a QMutexLocker right before your values that get modified by your GUI and right before your worker thread uses them, you should be good.
http://qt-project.org/doc/qt-5.1/qtcore/qmutexlocker.html
Also if you wanted any of those slots to run on the worker thread, you need to use the QueuedConnection.
http://qt-project.org/doc/qt-5.1/qtcore/threads.html
http://qt-project.org/doc/qt-5.1/qtcore/qthread.html#details
In some parts of the Qt docs it describes subclassing the QThread class. Other parts recommend the Worker/Producer model, and just use connections to setup how you use your threads. Usually there are fewer gotchas with the Worker/Producer model.
Hope that helps.
I created a multithreaded application in Qt (4.7.2). Only the main thread has an event loop.
The issue is that sometimes I get the following warning in the console:
QObject::startTimer: timers cannot be started from another thread
After this happens, the app consumes 100% of CPU (I have a single core CPU). It seems, that the main thread consumes all of the CPU's resources. The program does not freeze, and everything still works.
When I stop the program in the debugger, I do not see my code in the call stack.
The problem is that I'm not using (explicitly, anyway) timers at all.
What could it be connected with? I know, that question is very common, but I can't even understand, what piece of code to show.
Thanks, to #vrince I've fixed the problem. I used signals/slots mechanism + Qt::QueuedConnection to communicate with GUI
For example, if I need to set text of QLabel from worker thread, I can make in my worker thread signal
void textChanged(QString);
then I connect this signal to the slot of QLabel using Qt::QueuedConnection
connect(worker, SIGNAL(textChanged(QString)), label, SLOT(setText(QString), Qt::QueuedConnection);
If I want to execute setText synchronously, I can use Qt::BlockingQueuedConnection
now in my worker thread I just emit signal:
emit textChanged(newText);
Also, it is possible to use QMetaObject functions to avoid signals and slots:
metaObject->invokeMethod(label, "setText", Qt::QueuedConnection, Q_ARG(QString, text));
One of several initially baffling PyQt "warnings" (with consequences) symptomatic of a single, classic cause: trying to manipulate GUI elements using a non-"application thread", without using signals and slots as you should.
See my answer here.
I've checked a satisfying explanation but could not find. Usually docs mention that in order to use signals/slots between threads, we need to use event loops and start them by calling exec.
However I can see that w/o using exec(), I can still send signals and handle them across threads.
What's the exact use of it?
Use QThread::exec() when you want to run the event loop Qt provides for you in the QThread class. If you don't call exec(), you need to create your own event loop that processes Qt events (that is, if you want signals / slots to work). This is almost certainly more work than it's worth, unless you have very specific needs.
You say you can still send signals / slots? My guess is that you're not actually running anything on a different thread. This is a very common issue when using QThread. Put a breakpoint inside the code you think is running on a different thread and have a look at the stack trace - you may be in for a shock!
A rough example.
Suppose you have a text box. On each letter user types on the text box you want to perform some background task. You can setup a QThread for that. Emit something whenever the contents of text box changes. Assign a slot from your QThread that handles the background task. Emit something from QThread when the task finished. Handle this signal from main thread. Connect them. Start the thread when the text box is created (or any appropriate time). If you call exec() from your QThread::run() then you don't need to start() the thread multiple times.
If you don't use this mechanism, you may need to create (and/or start()) a QThread each time the content of text box changes, perform the background task and get result. This time you can still use signal/slot between main thread and this thread, but you need to start() the thread multiple times.