QtSerialPort in a separate thread?
-
@code_fodder
So in this case you would use a signal to be handled on the worker thread's slot (in the context of that worker thread), and then emit a signal back to notify of success/error? -
Almost...
calling functions directly on the serialPort object object is now "bad/unsafe" (as mentioned). You can pass data/messages between the worker OBJECT and serialPort OBJECT (note: I am not shouting, just highlighting, the word object because you connect objects, not threads :) )
Without more of your code to work with its hard to generate an example, so I produce a theoretical one here:
Lets say we have object workerObj which lives in the worker thread and serialPort which lives in its own thread (as setup in your code) then:
@
// Connect I/O from serial port to worker object
connect(serialPort, SIGNAL(dataReceived(QByteArray)), workerObj, SLOT(displaySerialData(QByteArray)));// Send data to the serial Port
connect(workerObj, SIGNAL(userDataToSend(QByteArray)), serialPort, SLOT(sendSerialData(QByteArray)));
@Now you can send data from the serial port to the worker object by emitting dataReceived(<data>) within the serialPort object. This will use a queued connecion by default which will mean your data will arrive in order.
Also you can send data to the serial port from worker object by "emit userDataToSend(<data>);"You have to implement the slot functions displaySerialData(QByteArray) and sendSerialData(QByteArray) in their respective objects...
-
Thanks code_fodder. And thanks for clarifying that you're not shouting : D
I'm asking about handling opening / closing the port. So would that be the same concept?
For example, would it be something like this :
This connection in the worker object code:
@
// Open port
connect(workerObj, SIGNAL(openPort(QString)), serialPort, SLOT(openPortRequested(QString)));
@This one in the serial port object:
@
// Result of trying to open the port
connect(serialPort, SIGNAL(openPortFinished(QString)), workerObj, SLOT(openPortFinished(QString)));
@ -
Perfect, you have got it :)
I think you should do these connections just after you move serialPort into the new thread. I am not sure if there is any issue connecting it before that, but I have just never bothered to find out. Conceptually (in my head only) it seems wrong to do that because you connect them and then move one of the objects, I am sure Qt's "black magic" can handle it, but I just not 100% sure :o
-
Also my earlier comment was not 100% accurate.
You CAN connect to the thread as well, but I meant you connect directly to the objects in question.
But, for example, if you wanted your serialPort object to do something (like start processing) as soon as the thread starts then you can do this:
@connect(thread, SIGNAL(started()), serialPort, SLOT(startProcessing()));@
Implement startProcessing() in serialPort, and then as soon as the thread starts (after you call thread->start()) it will emit a signal to your serialPort and it can safely begin its operations... :) -
I have another question (I am really kidnapping this thread I see).
The thread where I'm doing all of the actual thread operations - let's call it SerialPortThread, it would now work based on signals and slots. But this means that the QSerialPort cannot just be a data member of that class since it would be created on the stack in the Worker thread, and not under SerialPortThread.
Which is, I guess, why I get this error:
@
QObject: Cannot create children for a parent that is in a different thread.
@How should the actual QSerialPort object be defined then, considering I don't have a "while" code to create it within a run() method, but really only need it in my slots?
I could create a (QSerialPort *) member and to initialize it within the relevant slot but I'm not sure that's the correct way.
-
Ohh... hangon, I thought you where the original poster :) .. oh, well, they are not as picky here as they are on stackoverflow.... we would definatley get told off there! :o
Yes you are right, the objects should not have an owner already assigned. So you can make them member pointers as you mentioned:
@
QSerialPort *mp_serialPort; //(for example)
@Then create it WITHOUT a parent
@
mp_serialPort = new QSerialPort(0); // 0 = no parent pointer
@
Then you can do the rest:
@
QThread *aThreadToStickSerialPortIn = new QThread(this); // ok to use this a parent thread.
mp_serialPort.moveToThread(aThreadToStickSerialPortIn);
etc...
@ -
But I don't understand that last part.
(bah writing this in words is tough)
Let's reintroduce it:
I have a Worker object which wants to use a QSerialPort on another thread. So I introduce a SerialPortWrapper class, and in my Worker I create a QThread, move the SerialPortWrapper to that thread and start() it. Implicitly it runs exec(), SerialPortWrapper doesn't subclass QThread and doesn't do any run() logic.
So now the question. In the context of that thread that I started, SerialPortWrapper has only the slots. So where would I initialize the actual QSerialPort? I could store a QPointer<QSerialPort> as a datamember and initialize it in the beginOpenPort() slot but I'd still be talking with a pointer that is declared as a data member of the class (Even if the object it points to lives in the newly running thread).
Is this clear? Or must I throw in code parts?
-
Ok, so I am not 100% clear, lets get it sorted :)
Start with the workerObject:
@
workerObj::workerObj(...blah...) :
mp_serialPortWrapper(0) // null pointer to your serialPortWrapper class
{
// Create thread and serialWrapper
QThread *p_thread = new QThread(this); // make a new thread
mp_serialPortWrapper = new serialPortWrapper(0, params...); // 0 = pointer to parent, set this to 0 (null)// Now move the serialWrapper into the thread: mp_serialPortWrapper.moveToThread(p_thread); // Now connect a slot in the wrapper to the the started() signal of the thread so it can do initialisations once the thread starts: connect(p_thread, SIGNAL(started()), mp_serialPortWrapper, SLOT(doStartupStuff()); // Now just start the thread, and the doStartupStuff() will be called in the serialWrapper p_thread->start(); // Done (omitted other connections for this example)
}
@Now in the serialWrapper doStartupStuff slot:
@
serialPortWrapper::doStartupStuff()
{
// initiate your serial port. It will be created in this thread
mp_serialPort = new QSerialPort(this, params...);// Safe to call function directly on the serialPort here, because it is a member of this class and is in the same thread: mp_serialPort->someFunc();
}
@Now all you need is to pass data between serialWrapper and worker via slots and signals. The serialPort itself is created within the serialWrapper.
Note: It's probably easier if you just inherit QSerialPort to your object:
@
class franklefranksSerialPort : public QSerialPort
{
Q_OBJECT
//etc...
}
@And then you can do what you want with it and it also has full QSerialPort capabilities. You just call the serial port function calls as a result of recieving certain signals in certain slots...
-
Thank you very much for your help!
Believe it or not, I now encounter some issues which are more about signals and slots across and less about the QSerialPort part. But I think I took advantage of this post enough, so now it's all about here: http://qt-project.org/forums/viewthread/39345/
-
haha, no problem. Yes, they can be a little fiddly at first, but they suddenly become easy to use once you know what to do!