Управление QObject, работающим в другом потоке, в обход очереди событий, замораживает графический интерфейс - PullRequest
2 голосов
/ 03 февраля 2012

Я работаю с QThread и механизмом слотов / сигналов;Я знаю, что в Интернете в целом и в частности в SO, много обсуждений, но я все еще не смог найти решение.В любом случае, вот контекст.

Кусок кода, который я пытаюсь придумать, нацелен на управление хотя бы GUI в конечном итоге долгим процессом, поэтому используется QThread.

У меня есть окно с двумя кнопками, запуск и остановка.Мои Window также имеют QThread и Task, где последний наследуется от QObject.Я хочу иметь возможность остановить свою задачу, когда она выполняется, и предотвратить запуск ее снова, если нажать кнопку «Пуск», когда она уже запущена.

Вот выдержка из Task (которая подделывает длиннуюпроцесс):

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_;
};

Я сделал два соединения между кнопками и задачей в конструкторе моего 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_;
};

Я использовал Qt::DirectConnection во втором connect() чтобы «форсировать» обработку моего сигнала, запрашивающего остановку задачи, поскольку (как я понимаю) task_ необходимо вернуться из своей работы перед дальнейшей обработкой событий (если я использую соединение по умолчанию, все мои клики обрабатываются послемоя задача "выполнена").

Здесь Qt::DirectConnection "обходит" очередь событий, и таким образом я могу остановить свою задачу.Но, честно говоря, я не знаю, является ли это правильным способом, или это обходной путь (таким образом, возможно, корень моей проблемы).

В любом случае, это работает нормально таким образом, нокогда я начинаю играть с моими кнопками, графический интерфейс в конечном итоге зависает, и это моя проблема!

Любая помощь приветствуется;спасибо за ваше время!

Ответы [ 2 ]

4 голосов
/ 03 февраля 2012

Использование DirectConnection означает, что слоты будут выполняться в потоке пользовательского интерфейса (пока ваш поток задач все еще работает). Это не то, что вы хотите. Способ справиться с этим - использовать цикл обработки событий в вашем потоке, запустив команду QThread::exec().

Чтобы ваш поток мог отвечать так, как вы хотите, вам необходимо убедиться, что поток может обрабатывать входящие события. Есть несколько способов справиться с этим. Можно было бы иногда вызывать QCoreApplication::processEvents() во время выполнения вашей задачи. Другой способ - использовать QTimer, подключенный к слоту, который выполняет некоторую обработку. Важно убедиться, что цикл обработки событий в вашем потоке может выполняться.

2 голосов
/ 03 февраля 2012

Первое, что вам нужно понять, - в каком потоке выполняются соединения вашего сигнала / слота.

Поведение по умолчанию в данном конкретном случае - использование Qt::QueuedConnection, которое будет помещать ваше событие сигналав очередь событий принимающего потока.В случае Qt::DirectConnection слот выполняется в потоке объекта, который испустил сигнал .

Использование Qt :: DirectConnection будет безопасным только в этом случае, еслиВы помещаете мьютекс вокруг своей переменной stop_, чтобы предотвратить одновременный доступ к ней обоих потоков.

...