Search code examples
qtsignals-slotsqthread

Controlling a QObject working in another thread, bypassing event queue, freezes the GUI


I'm working with QThread and slots/signals mechanism; I know there's a lot of threads about this on the Web in general and here at SO in particular, but I still could not find a solution. Anyway, here is the context.

The piece of code I am trying to come up with is aimed at controlling though the GUI an eventually long process, hence the use of a QThread.

I have a window with two buttons, start and stop. My Window also has a QThread and Task, where the latter inherits from a QObject. I do want to be able to stop my task while it is running, and to prevent starting it again if start is clicked while it is already running.

Here is an excerpt of Task (which fakes the long process):

class Task: public QObject
{
public:

  Task(): QObject(), stop_(true) {}

private slots:

  void startTask()
  {
    stop_ = false;
    run();
  }

  void stopTask()
  {
    stop_ = true;
  }

  void run() const
  {
    while ( ! stop_)
    {
      sleep(1);
    }
  }

  bool stop_;
};

I made two connections between the buttons and the task in the constructor of my Window:

class Window: public QWidget
{
public:

  Window()
  {
    // Instantiate buttons and put them in a layout.
    // ...

    connect(buttonStart_, SIGNAL(clicked()), &task_, SLOT(startTask()));
    connect(buttonStop_, SIGNAL(clicked()), &task_, SLOT(stopTask()),
            Qt::DirectConnection);

    task_.moveToThread(&thread);
    thread_.start();
  }

private:
  QPushButton buttonStart_;
  QPushButton buttonStop_;

  QThread thread_;
  Task task_;
};

I used Qt::DirectConnection in the second connect() in order to "force" processing of my signal requesting to stop the task, as (as I understand) task_ needs to return from its work before processing events further (if I use the default connection, all my clicks are processed after my task is "done").

Here, the Qt::DirectConnection "bypasses" the event queue, and that way I can stop my task. But to be honest I don't know if that is the proper way to do it, or if it is a workaround (thus possibly the root of my problem).

Anyway, this works OK that way, but when I start playing with my buttons, the GUI gets eventually frozen, and that's my problem!

Any help is welcome; thanks for your time!


Solution

  • Using a DirectConnection means that the slots will be executed in the UI thread (while your task thread is still running). This is not what you want. The way to handle this is to use an event loop in your thread, by running with QThread::exec().

    In order for your thread to be able to respond as you want, you will need to ensure that the thread is able to process incoming events. There are a few ways to deal with this. One would be to call QCoreApplication::processEvents() occasionally while your task is running. Another is to use a QTimer connected to a slot which performs some processing. The important thing is to make sure that the event loop in your thread can run.