Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QtSerialPort in a separate thread?
Forum Updated to NodeBB v4.3 + New Features

QtSerialPort in a separate thread?

Scheduled Pinned Locked Moved General and Desktop
17 Posts 5 Posters 16.3k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • C Offline
    C Offline
    cidadao
    wrote on last edited by
    #1

    Hello,

    Im using QtSerialPort along with QCustomPlot to plot in real time a stream of values I receive from an Arduino.
    Im using a QElapsedTimer to control the time axis of each plot, so for example if I have a time window of 10sec the timer will count up to that value and then it is reset.

    The problem is: when I move or resize the main window the data I receive from Arduino stops being processed (proper processing slot() is not called). Data isn't lost since after releasing the mouse button the processing slot is called and data is processed but this means all that buffered data will have (almost) the same "time" value.

    How can I avoid this? By using the QtSerialPort in a separate thread or is there another (easier/less complex) approach?

    1 Reply Last reply
    0
    • K Offline
      K Offline
      kuzulis
      Qt Champions 2020
      wrote on last edited by
      #2

      At present, the single decision (workaround) - to use a separate thread.

      In principle, the solution of this problem is known. Maybe it will fixed in Qt5.2.1, I hope.

      Please watch for: https://bugreports.qt-project.org/browse/QTBUG-34946

      1 Reply Last reply
      0
      • C Offline
        C Offline
        cidadao
        wrote on last edited by
        #3

        Thanks!
        Can you elaborate a little bit on how to solve this with a separate thread?
        Im trying to use .moveToThread() as follows:

        @ QSerialPort serialPort = new QSerialPort();
        m_device = (QIODevice
        )m_sp;

        QThread *thread = new QThread();
        serialPort->moveToThread(thread);
        thread->start();@
        

        But when I try to establish the serial connection I get the following error:

        bq. QObject: Cannot create children for a parent that is in a different thread.
        (Parent is QSerialPort(0x4366a58), parent's thread is QThread(0x435a910), current thread is QThread(0x42a6728)
        QObject: Cannot create children for a parent that is in a different thread.

        1 Reply Last reply
        0
        • F Offline
          F Offline
          frankiefrank
          wrote on last edited by
          #4

          Sorry if this is a bit late, maybe you resolved this already.

          This is also referenced here, so maybe this may help.
          http://qt-project.org/forums/viewthread/21369

          In short, the open() call needs to happen on the worker-thread where you moved the serial port object.

          "Roads? Where we're going, we don't need roads."

          1 Reply Last reply
          0
          • martin_kyM Offline
            martin_kyM Offline
            martin_ky
            wrote on last edited by
            #5

            I had no issues running QSerialPort in worker threads. Just create the thread first and then, already within the context of the new thread, create a QSerialPort object.

            1 Reply Last reply
            0
            • C Offline
              C Offline
              code_fodder
              wrote on last edited by
              #6

              Your thread code is fine, but there is one important thing to note here:

              @
              QSerialPort serialPort = new QSerialPort();
              m_device = (QIODevice
              )m_sp;

              QThread *thread = new QThread();
              serialPort->moveToThread(thread);
              thread->start();

              /*
              Any further direct function calls to serialPort should be done via slots/signals, a direct function call will result in objects created within serialPort being attached to the main thread and not the new thread.
              */
              @

              Also, as a good design principal, I believe it is always a good idea to separate your I/O and other non-user tasks away from the GUI where possible, this is a good example of why :) (event though it may be fixed in future versions).

              1 Reply Last reply
              0
              • F Offline
                F Offline
                frankiefrank
                wrote on last edited by
                #7

                @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?

                "Roads? Where we're going, we don't need roads."

                1 Reply Last reply
                0
                • C Offline
                  C Offline
                  code_fodder
                  wrote on last edited by
                  #8

                  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...

                  1 Reply Last reply
                  0
                  • F Offline
                    F Offline
                    frankiefrank
                    wrote on last edited by
                    #9

                    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)));
                    @

                    "Roads? Where we're going, we don't need roads."

                    1 Reply Last reply
                    0
                    • C Offline
                      C Offline
                      code_fodder
                      wrote on last edited by
                      #10

                      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

                      1 Reply Last reply
                      0
                      • C Offline
                        C Offline
                        code_fodder
                        wrote on last edited by
                        #11

                        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... :)

                        1 Reply Last reply
                        0
                        • F Offline
                          F Offline
                          frankiefrank
                          wrote on last edited by
                          #12

                          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.

                          "Roads? Where we're going, we don't need roads."

                          1 Reply Last reply
                          0
                          • C Offline
                            C Offline
                            code_fodder
                            wrote on last edited by
                            #13

                            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...
                            @

                            1 Reply Last reply
                            0
                            • F Offline
                              F Offline
                              frankiefrank
                              wrote on last edited by
                              #14

                              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?

                              "Roads? Where we're going, we don't need roads."

                              1 Reply Last reply
                              0
                              • C Offline
                                C Offline
                                code_fodder
                                wrote on last edited by
                                #15

                                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...

                                1 Reply Last reply
                                0
                                • F Offline
                                  F Offline
                                  frankiefrank
                                  wrote on last edited by
                                  #16

                                  @code_fodder

                                  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/

                                  "Roads? Where we're going, we don't need roads."

                                  1 Reply Last reply
                                  0
                                  • C Offline
                                    C Offline
                                    code_fodder
                                    wrote on last edited by
                                    #17

                                    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!

                                    1 Reply Last reply
                                    0

                                    • Login

                                    • Login or register to search.
                                    • First post
                                      Last post
                                    0
                                    • Categories
                                    • Recent
                                    • Tags
                                    • Popular
                                    • Users
                                    • Groups
                                    • Search
                                    • Get Qt Extensions
                                    • Unsolved