1

QT 5.1.0rc2 , msvc 2010 , Microsoft Visual Studio 2010

It is working code on Qt 4.8.4 , msvc 2008

I have compile error at

#if defined( HANDLE_PROCESSING_IN_MAIN_THREAD )
    if(QThread::currentThread() != this)
        emit started();
#endif
    inherited::run();

and

#if defined( HANDLE_PROCESSING_IN_MAIN_THREAD )
    if(QThread::currentThread() != this)
      emit finished();
#endif

error C2660: 'QThread::started' : function does not take 0 arguments
error C2660: 'QThread::finished' : function does not take 0 arguments

In QThread i have seen

Q_SIGNALS:
    void started(
#if !defined(Q_QDOC)
      QPrivateSignal
#endif
    );
    void finished(
#if !defined(Q_QDOC)
      QPrivateSignal
#endif
    );

when I defined Q_QDOC I got many errors in QT sources.

QPrivateSignal is empty structure that defined in macro Q_OBJECT

Need a solution that does not affect the architecture of the application, as to be backward compatible with Qt4.8.4

Some ideas?

Vlad Mikitich
  • 11
  • 1
  • 3
  • 1
    Do you emit these signals yourself from the thread? According to the docs, they are emiting by QThread implementation automatically. – vahancho Sep 06 '13 at 08:42
  • Error says everything, you have arguments for signals, but you are emitting them without arguments. – masoud Sep 06 '13 at 08:44
  • @vahancho `#if defined( HANDLE_PROCESSING_IN_MAIN_THREAD ) if(QThread::currentThread() != this) emit started(); #endif inherited::run();` – Vlad Mikitich Sep 06 '13 at 08:46
  • @MM. I understand that. Needed argument is QObject's private empty struct that defined in Q_Object macro – Vlad Mikitich Sep 06 '13 at 08:49
  • 1
    @VladMikitich: the question is why are you trying to emit those signals. They're "private" and handled by QThread. I'm not sure emiting them yourself makes sense. – Mat Sep 06 '13 at 09:31
  • @Mat How I figured out the code of the project. they have created a separate thread to generate a preview image. Then they decided to give up a separate thread and perform all in the main thread. In order not to change the architecture of the application, they have introduced a macro and emulate work of class as a separate thread. In this case, all connections remained the same. And now the problem started when upgrading to qt5.1.0 – Vlad Mikitich Sep 06 '13 at 12:12
  • see https://woboq.com/blog/how-qt-signals-slots-work-part2-qt5.html , "Protected, Public, or Private Signals." for rationale – mlvljr Jul 03 '16 at 17:55

1 Answers1

3

The thread's signals are emitted automatically. You should never emit them manually.

You're trying to use preprocessor to handle two variants of the code: processing in the gui thread or a dedicated thread. Qt provides a very easy way of dealing with it.

  1. Implement your processing functionality in a slot in a class deriving from QObject. You could also do it in the reimplemented event() method if it's easier for you to start processing by posting an event rather than invoking a slot.

  2. If you want your object's slots to run in a different thread, use the moveToThread() method.

  3. You don't need to derive from QThread. Its default implementation of run() spins a local event loop.

  4. If you want your object to be compatible with living in the GUI thread, it must do its processing in small chunks and relinquish control so that the GUI doesn't stall.

Below is a complete example that demonstrates how you can start the worker object in either GUI or a separate thread, and how you can safely move it between threads.

// https://github.com/KubaO/stackoverflown/tree/master/questions/qobject-thread-18653347
#include <QtGui>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif

/// See http://stackoverflow.com/a/40382821/1329652
bool isSafe(QObject * obj) {
   Q_ASSERT(obj->thread() || qApp && qApp->thread() == QThread::currentThread());
   auto thread = obj->thread() ? obj->thread() : qApp->thread();
   return thread == QThread::currentThread();
}

class Helper : private QThread {
public:
   using QThread::usleep;
};

class Worker : public QObject {
   Q_OBJECT
   int m_counter;
   QBasicTimer m_timer;
   void timerEvent(QTimerEvent * ev) override;
public:
   Worker(QObject *parent = nullptr) : QObject(parent) {}
   /// This method is thread-safe.
   Q_SLOT void start() {
      if (!isSafe(this)) return (void)QMetaObject::invokeMethod(this, "start");
      if (m_timer.isActive()) return;
      m_counter = 0;
      m_timer.start(0, this);
   }
   /// This method is thread-safe.
   Q_INVOKABLE void startInThread(QObject *targetThread) {
      if (!isSafe(this)) return (void)QMetaObject::invokeMethod(this, "startInThread", Q_ARG(QObject*, targetThread));
      QObject::moveToThread(qobject_cast<QThread*>(targetThread));
      start();
   }
   Q_SIGNAL void done();
   Q_SIGNAL void progress(int percent, bool inGuiThread);
};

void Worker::timerEvent(QTimerEvent * ev)
{
   const int busyTime = 50; // [ms] - longest amount of time to stay busy
   const int testFactor = 128; // number of iterations between time tests
   const int maxCounter = 30000;
   if (ev->timerId() != m_timer.timerId()) return;

   const auto inGuiThread = []{ return QThread::currentThread() == qApp->thread(); };
   QElapsedTimer t;
   t.start();
   while (1) {
      // do some "work"
      Helper::usleep(100);
      m_counter ++;
      // exit when the work is done
      if (m_counter > maxCounter) {
         emit progress(100, inGuiThread());
         emit done();
         m_timer.stop();
         break;
      }
      // exit when we're done with a timed "chunk" of work
      // Note: QElapsedTimer::elapsed() may be expensive, so we call it once every testFactor iterations
      if ((m_counter % testFactor) == 0 && t.elapsed() > busyTime) {
         emit progress(m_counter*100/maxCounter, inGuiThread());
         break;
      }
   }
}

class Window : public QWidget {
   Q_OBJECT
   QVBoxLayout m_layout{this};
   QPushButton m_startGUI{"Start in GUI Thread"};
   QPushButton m_startWorker{"Start in Worker Thread"};
   QLabel m_label;
   QThread m_thread{this};
   Worker m_worker;

   Q_SLOT void showProgress(int p, bool inGuiThread) {
      m_label.setText(QString("%1 % in %2 thread")
                      .arg(p).arg(inGuiThread ? "gui" : "worker"));
   }
   Q_SLOT void on_startGUI_clicked() {
      m_worker.startInThread(qApp->thread());
   }
   Q_SLOT void on_startWorker_clicked() {
      m_worker.startInThread(&m_thread);
   }
public:
   Window(QWidget *parent = {}, Qt::WindowFlags f = {}) : QWidget(parent, f) {
      m_layout.addWidget(&m_startGUI);
      m_layout.addWidget(&m_startWorker);
      m_layout.addWidget(&m_label);
      m_thread.start();
      connect(&m_worker, SIGNAL(progress(int,bool)), SLOT(showProgress(int,bool)));
      connect(&m_startGUI, SIGNAL(clicked(bool)), SLOT(on_startGUI_clicked()));
      connect(&m_startWorker, SIGNAL(clicked(bool)), SLOT(on_startWorker_clicked()));
   }
   ~Window() {
      m_thread.quit();
      m_thread.wait();
   }
};

int main(int argc, char *argv[])
{
   QApplication a(argc, argv);
   Window w;
   w.show();
   return a.exec();
}

#include "main.moc"
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313