Qt: сигнал/слот не работает после перемещения QObject в другой поток

Я использую Qt5, где реализую поток, передавая рабочий процесс QObject экземпляру QThread с помощью moveToThread(). Моя реализация выглядит так..

Worker.h

class worker : public QObject
{
    Q_OBJECT
public:
    explicit worker(QObject *parent = 0);
    bool IsWorkRunning();
    void MoveObjectToThread();

signal:
    void SignalToObj_mainThreadGUI();

public slots:
    void do_Work();
    void StopWork();
    void StartWork();

private:
    void Sleep();
    QThread *workerthread;    
    volatile bool running,stopped;
};

Worker.cpp

 worker::worker(QObject *parent) :
    QObject(parent),stopped(false),running(false)
{
}

void worker::do_Work()
{
    running = true;
    while(!stopped)
    {
       if(running)
       {
        emit SignalToObj_mainThreadGUI();
        workerthread->msleep(20);
       }
    }
}

void worker::StopWork()
{
    running = false;
}

void worker::StartWork()
{
    running = true;
}

bool worker::IsWorkRunning()
{
    return running;
}

void MoveObjectToThread()
{
  workerthread = new QThread;
  QObject::connect(workerthread,SIGNAL(started()),this,SLOT(do_Work()));

  this->moveToThread(workerthread);

  workerthread->start();
}

MainWindow.h

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

signals:
    void Startwork_mainwindow();
    void Stopwork_mainwindow();

public slots:

private slots:
    void on_pushButton_push_to_start_clicked();

    void on_pushButton_push_to_stop_clicked();

private:

    Ui::MainWindow *ui;
    worker myWorker;
    bool work_started;

};

MainWindow.cpp

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),work_started(false),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QObject::connect(this,SIGNAL(Startwork_mainwindow()),&myWorker,SLOT(StartWork()));
    QObject::connect(this,SIGNAL(Stopwork_mainwindow()),&myWorker,SLOT(StopWork()));
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_push_to_start_clicked()
{
    if(!work_started)
    {
      myWorker.MoveObjectToThread();
      work_started = true;
    }

    if(!myWorker.IsWorkRunning())
       emit this->Startwork_mainwindow();
}

void MainWindow::on_pushButton_push_to_stop_clicked()
{
  if(myWorker.IsWorkRunning())
       emit this->Stopwork_mainwindow();
}

Не знаю, почему следующие две пары сигнал/слот не работают

QObject::connect(this,SIGNAL(Startwork_mainwindow()),&myWorker,SLOT(StartWork()));
QObject::connect(this,SIGNAL(Stopwork_mainwindow()),&myWorker,SLOT(StopWork()));

В результате я не могу запустить или остановить поток, как только слот do_Work() запускается сигналом started() объекта QThread. Просто для справки, этот мой пост является продолжением моего предыдущего поста здесь описано. Любое понимание будет полезно... спасибо


person rotating_image    schedule 29.12.2012    source источник
comment
Вы пытались перейти к потоку, прежде чем выполнять какие-либо сигнальные соединения. Например, сразу после того, как вы это сделаете? Кроме того, разве ваш внешний цикл while не крутится в вашем потоке, когда он не работает? Возможно, ему также нужен сон, чтобы помочь планировщику.   -  person jdi    schedule 29.12.2012
comment
Вместо использования moveToThread я бы переопределил QThread. За ним легче следовать, и я думаю, что он менее подвержен ошибкам. А при использовании QObject::connect из основного потока в другой поток не подключайтесь к другому потоку с помощью AutoConnect, используйте QueuedConnection вместо этого.   -  person phyatt    schedule 29.12.2012
comment
что если я использую Qt::Directconnection ? и где-то на форумах я читал, что подклассы QThread не являются хорошей практикой... и Qt5 сделал многие статические частные функции общедоступными из-за этой парадигмы использования   -  person rotating_image    schedule 29.12.2012
comment
вы не хотите переопределять QThread. Посмотрите на это и связанные обсуждения, чтобы понять, почему. stackoverflow.com/questions/4093159/   -  person g19fanatic    schedule 10.01.2013
comment
Вы пытались сделать это, не запуская частный экземпляр QThread ВНУТРИ объекта, который вы пытаетесь переместить в поток? В основном я имею в виду, что вы должны сначала попытаться создать QThread вне вашего объекта (либо в главном окне, либо даже в main.cpp) и переместить экземпляр вашего объекта в поток и запустить его там.   -  person g19fanatic    schedule 10.01.2013


Ответы (1)


В определении класса MainWindow попробуйте изменить worker myWorker на worker * myWorker. Кроме того, я бы сделал, как предложили другие, и переместил поток за пределы класса worker. Конструктор MainWindow становится примерно таким:

MainWindow::MainWindow(QWidget *parent)
  : QMainWindow(parent)
  , work_started(false)
  , ui(new Ui::MainWindow)
  , myWorker( new worker() )
{
  // NOTE:  myWorker is created without a parent on purpose.
  // Qt won't change the thread affinity of an obj with a parent

  ui->setupUi(this);

  connect( this, SIGNAL(Startwork_mainwindow()), myWorker, SLOT(StartWork()) );
  connect( this, SIGNAL(Stopwork_mainwindow()), myWorker, SLOT(StopWork()) );

  QThread * thread = new QThread();
  // delete the worker obj whenever this obj is destroyed
  connect( this, SIGNAL(destroyed()), myWorker, SLOT(deleteLater()) );
  // stop the thread whenever the worker is destroyed
  connect( myWorker, SIGNAL(destroyed()), thread, SLOT(quit()) );
  // clean up the thread
  connect( thread, SIGNAL(finished()), thread, SLOT(deleteLater()) );
  myWorker->moveToThread( thread );
  thread->start();
}

Конечно, вам больше не нужен метод worker::MoveObjectToThread(). Кроме того, метод worker::IsWorkRunning() не совсем безопасно вызывать из MainWindow. Вы, вероятно, не столкнетесь с какими-либо проблемами с этим конкретным примером, но он определенно причинит вам боль, когда все станет более сложным. Вместо этого добавьте сигнал workFinished() или что-то подобное и слушайте его в классе MainWindow.

Сигнал Startwork_mainwindow() запустит работу. Поскольку вы не указываете тип соединения в своих вызовах connect, Qt будет использовать QueuedConnection при изменении привязки потока (moveToThread). По сути, myWorker находится в потоке с собственным циклом событий. Вызов слота с использованием Qt::QueuedConnection отправляет событие в этот цикл событий, который затем ставит в очередь метод слота. Он будет запускаться всякий раз, когда цикл обработки событий доходит до него.

person Jacob Robbins    schedule 24.01.2013