不能发出 QThread 的信号

Posted

技术标签:

【中文标题】不能发出 QThread 的信号【英文标题】:Can not emit QThread's signals 【发布时间】:2013-09-06 08:33:45 【问题描述】:

QT 5.1.0rc2 , msvc 2010 , Microsoft Visual Studio 2010

它在 Qt 4.8.4 , msvc 2008 上运行代码

我在

处出现编译错误
#if defined( HANDLE_PROCESSING_IN_MAIN_THREAD )
    if(QThread::currentThread() != this)
        emit started();
#endif
    inherited::run();

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

错误 C2660:“QThread::started”:函数不接受 0 个参数 错误 C2660:“QThread::finished”:函数不接受 0 个参数

在 QThread 我见过

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

当我定义 Q_QDOC 时,我在 QT 源代码中遇到了很多错误。

QPrivateSignal 是在宏 Q_OBJECT 中定义的空结构

需要一个不影响应用程序架构的解决方案,以向后兼容Qt4.8.4

一些想法?

【问题讨论】:

你自己从线程发出这些信号吗?根据文档,它们是由 QThread 实现自动发出的。 错误说明一切,你有信号的参数,但你发出的信号没有参数。 @vahancho #if defined( HANDLE_PROCESSING_IN_MAIN_THREAD ) if(QThread::currentThread() != this) emit started(); #endif inherited::run(); @MM。我明白那个。需要的参数是在 Q_Object 宏中定义的 QObject 的私有空结构 @VladMikitich:问题是你为什么要发出这些信号。它们是“私有的”并由 QThread 处理。我不确定自己发出它们是否有意义。 【参考方案1】:

线程的信号是自动发出的。你不应该手动发出它们。

您正在尝试使用预处理器来处理代码的两种变体:在 gui 线程或专用线程中处理。 Qt 提供了一种非常简单的处理方式。

    在派生自 QObject 的类中的插槽中实现您的处理功能。如果通过发布事件而不是调用槽更容易开始处理,您也可以在重新实现的 event() 方法中执行此操作。

    如果您希望对象的槽在不同的线程中运行,请使用moveToThread() 方法。

    您不需要从QThread 派生。它的默认实现 run() 会旋转一个本地事件循环。

    1234563

下面是一个完整的示例,演示了如何在 GUI 或单独的线程中启动工作对象,以及如何在线程之间安全地移动它。

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

/// See http://***.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_layoutthis;
   QPushButton m_startGUI"Start in GUI Thread";
   QPushButton m_startWorker"Start in Worker Thread";
   QLabel m_label;
   QThread m_threadthis;
   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"

【讨论】:

以上是关于不能发出 QThread 的信号的主要内容,如果未能解决你的问题,请参考以下文章

如何向 Qt 中 QThread 类的特定对象发出信号?

Qthread在Golang中只发出一个信号

Qt信号发射和QThread执行流程

QThread 不会停止/不处理信号

QThread如何从其自己的线程发送一个带有枚举作为QML消耗参数的信号?

QThread 没有启动