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. QThread and QTimer
Forum Updated to NodeBB v4.3 + New Features

QThread and QTimer

Scheduled Pinned Locked Moved General and Desktop
13 Posts 4 Posters 20.2k 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.
  • S Offline
    S Offline
    saam_6066
    wrote on last edited by
    #1

    Hi,

    I have a slot that is connected to QTimer's signal. I like to move this function to another thread since it's a very time consuming process but I also need to start or stop the function from running in runtime so I have to call timer->start() or timer->stop(). so far I've done this:

    @m_frameTimer = new QTimer(0);
    m_frameTimer->setInterval(0);
    connect(m_frameTimer, SIGNAL(timeout()), this, SLOT(timerFired()));

    m_thread = new QThread(this);
    m_frameTimer->moveToThread(m_thread);@

    and I call the m_thread->start() when a specific button is clicked and then I call timer->start() if the process is ready otherwise timer->stop. but it doesn't work and it gives me the following error:
    "QObject::startTimer: QTimer can only be used with threads started with QThread".

    1 Reply Last reply
    0
    • U Offline
      U Offline
      utcenter
      wrote on last edited by
      #2

      When you connect objects that live in the same thread, you get a direct connection by default, when you connect objects that live in different threads, you get a queued connection by default. In your case you implicitly create a direct connection and then move one of the objects to another thread.

      1 Reply Last reply
      0
      • K Offline
        K Offline
        KA51O
        wrote on last edited by
        #3

        also the timer (which you move to another thread) doesn't really do the heavy work. I guess that job is done in the timerFired slot which is still executed in the context of the main thread, because the object that is implementing this slot was not moved to another thread.

        1 Reply Last reply
        0
        • U Offline
          U Offline
          utcenter
          wrote on last edited by
          #4

          BTW the actual error message is due to the fact QTimer needs a thread with a running event loop to work. And the event loop of QThread does not start by default unless you explicitly start it.

          As KA51O said, the timer does not need to be in a separate thread, you are not offloading anything this way, but if you still want to do it, you have to have the thread your QTimer is into running its event loop.

          @class MyThread : public QThread
          {
          Q_OBJECT
          public:
          explicit MyThread(QObject *parent = 0) : QThread(parent) {
          t = new QTimer();
          t->moveToThread(this);
          t->setInterval(1000);
          connect(t, SIGNAL(timeout()), this, SLOT(timeout()));
          }

          ~MyThread() {
              if (t) t->deleteLater();
          }
          

          protected:
          void run() {
          t->start();
          exec();
          }

          signals:
          void timerElapsed();

          private slots:
          void timeout() {
          emit timerElapsed();
          }

          private:
          QTimer *t;
          };@

          Note that the QTimer is created without a parent because otherwise it cannot be moved, so remember to delete it manually.

          1 Reply Last reply
          0
          • A Offline
            A Offline
            andre
            wrote on last edited by
            #5

            [quote author="utcenter" date="1364392954"]When you connect objects that live in the same thread, you get a direct connection by default, when you connect objects that live in different threads, you get a queued connection by default. In your case you implicitly create a direct connection and then move one of the objects to another thread.[/quote]

            No, you create an auto connection by default. And an auto connection is evaluated at runtime, every time the signal is send.

            [quote author="utcenter" date="1364396202"]BTW the actual error message is due to the fact QTimer needs a thread with a running event loop to work. And the event loop of QThread does not start by default unless you explicitly start it.[/quote]
            Also not true. A vanilla QThread that is started has an eventloop by default. See the implementation of QThread::run():
            @
            /*!
            The starting point for the thread. After calling start(), the
            newly created thread calls this function. The default
            implementation simply calls exec().

            You can reimplement this function to facilitate advanced thread
            management. Returning from this method will end the execution of
            the thread.
            
            \sa start() wait()
            

            */
            void QThread::run()
            {
            (void) exec();
            }
            @

            1 Reply Last reply
            0
            • K Offline
              K Offline
              KA51O
              wrote on last edited by
              #6

              [quote author="utcenter" date="1364396202"]BTW the actual error message is due to the fact QTimer needs a thread with a running event loop to work. And the event loop of QThread does not start by default unless you explicitly start it.

              As KA51O said, the timer does not need to be in a separate thread, you are not offloading anything this way, but if you still want to do it, you have to have the thread your QTimer is into running its event loop.
              [/quote]

              Acctually a standard QThread started by calling its start() function does have its own eventloop, because the default implemetation of QThread::run() calls exec(). Thats why you can simply do this:
              @
              QThread* thread = new QThread(this);
              MyWorker* worker = new MyWorker();
              worker->moveToThread(thread);

              connect(thread, SIGNAL(started()), worker, SLOT(doWork()));
              //further connect statements for ending and deleting everything properly

              thread->start();
              @

              BTW I would not recommend subclassing QThread unless you really have to.

              1 Reply Last reply
              0
              • U Offline
                U Offline
                utcenter
                wrote on last edited by
                #7

                @Andre - if a QThread does run an event loop by default, why does QTimer refuse to start once moved to it?

                1 Reply Last reply
                0
                • K Offline
                  K Offline
                  KA51O
                  wrote on last edited by
                  #8

                  You mean this does not work ?
                  @
                  QThread* thread = new QThread(this);
                  QTimer* timer = new QTimer();
                  timer->setInterval(1000);
                  timer->setSingleShot(true);
                  timer->moveToThread(thread);

                  connect(thread, SIGNAL(started()), timer, SLOT(start()));
                  connect(timer, SIGNAL(timeout()), thread, SLOT(quit()));
                  connect(thread, SIGNAL(finished()), timer, SLOT(deleteLater()));
                  connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));

                  thread->start();
                  @

                  I'm not sure if you can call timer->start() from another thread but you should be able to do this:
                  @
                  connect(objectInMainThread, SIGNAL(startTimer()), timer, SLOT(start()));
                  emit startTimer();
                  @

                  1 Reply Last reply
                  0
                  • U Offline
                    U Offline
                    utcenter
                    wrote on last edited by
                    #9

                    I mean it will not work if you move the timer to the thread.

                    1 Reply Last reply
                    0
                    • K Offline
                      K Offline
                      KA51O
                      wrote on last edited by
                      #10

                      Sorry forgot to add the moveToThread() call in my example above. Corrected it.

                      1 Reply Last reply
                      0
                      • A Offline
                        A Offline
                        andre
                        wrote on last edited by
                        #11

                        [quote author="utcenter" date="1364397701"]@Andre - if a QThread does run an event loop by default, why does QTimer refuse to start once moved to it?[/quote]

                        It doesn't. I just tried the code posted by KA51O above. It works without complaint.

                        1 Reply Last reply
                        0
                        • U Offline
                          U Offline
                          utcenter
                          wrote on last edited by
                          #12

                          But only because it is called from the same thread. In the code above it is still impossible to start the timer instead of using the connection from the thread.

                          1 Reply Last reply
                          0
                          • A Offline
                            A Offline
                            andre
                            wrote on last edited by
                            #13

                            Sure, the timer must be started from the thread. But if you don't want to use a connection to the thread's started signal (or any other suitable signal: your choice), you can also do something like this:
                            @
                            QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection);
                            @

                            That also works. Note that for many objects belonging to a thread B, it would be unsafe to call methods on them directly from thread A. It's just QTimer that's nice enough to tell you that you're doing something dangerous. I don't see why that is a problem.

                            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