1
votes

I am using Qt5 where I am implementing a thread by passing the the QObject worker to a instance of QThread by moveToThread(). My implementation looks like this..

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();
}

Dont know why the following two signal/slot pair doesnt seem to work

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

As a result i cant start or stop the thread once the do_Work() slot is triggered by started() signal of the QThread object. Just for reference this post of mine is a continuation of my previous post here described .Any insight will be helpful...thank you

1
Have you tried moving to thread before you do any of the signal connections. Like right after you make it? Also isn't your outer while loop spinning wildly in your thread when it is not running? Maybe it needs a sleep as well to help the scheduler.jdi
Instead of using moveToThread, I would reimplement QThread. It is easier to follow, and I think is less error prone. And when using QObject::connect from the main thread to another thread, don't connect to another thread using AutoConnect, use QueuedConnection instead.phyatt
what if I use Qt::Directconnection ? and some where in forums i read sub classing the QThread is not a good practice...and Qt5 has made many static private functions public due to this paradigm of userotating_image
you do not want to reimplement QThread. Look at this and the linked discussions for reasons why. stackoverflow.com/questions/4093159/…g19fanatic
Have you tried doing this without starting a private QThread instance INSIDE the object you're trying to movetothread? Basically I mean that you should first try creating the QThread outside your object (either in the main window or even in main.cpp) and moving the instance of your object to the thread and starting it there.g19fanatic

1 Answers

2
votes

In your MainWindow class definition, try changing worker myWorker to worker * myWorker. Also, I would do as others have suggested and move the thread outside the worker class. The MainWindow constructor becomes something like this:

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();
}

Of course, you don't need the worker::MoveObjectToThread() method anymore. Also, the method worker::IsWorkRunning() isn't really safe to call from MainWindow. You probably won't run into any trouble with this specific example, but it will definitely cause you pain when things get more complex. Instead, add a workFinished() signal or something similar and listen for it in the MainWindow class.

The Startwork_mainwindow() signal will start the work. Since you don't provide a connection type in your calls to connect, Qt will use QueuedConnection when you change the thread affinity (moveToThread). Basically, myWorker is in a thread with its own event loop. Invoking a slot using Qt::QueuedConnection posts an event to that event loop which then queues the slot method. It will run whenever the event loop gets around to it.