【问题标题】:Qt - emit a signal from a c++ threadQt - 从 C++ 线程发出信号
【发布时间】:2014-09-18 21:04:20
【问题描述】:

我想从 Qt 中的 C++ 线程 (std::thread) 发出信号。

我该怎么做?

【问题讨论】:

  • 有什么理由不想使用 Qt 自己的线程? Qt 是一个非常“包容”的库,如果你使用 Qt,你几乎必须使用 Qt 来处理所有事情。
  • 我不想创建另一个类等等。我需要线程来完成一项小工作
  • “我不想创建另一个类”确实是一个糟糕的理由。替代解决方案会更加复杂和脆弱!
  • 我必须将 UI 的一个组件传递给新类。它更复杂!
  • 澄清一下:使用 QThread 并不意味着“创建另一个类”。事实上,继承 QThread 被一些人认为是一种反模式。

标签: c++ qt signals-slots stdthread emit


【解决方案1】:

一般来说,您可能不应该不小心从std::thread 创建的线程发出 Qt 信号。见Jpo38's answer:连接类型很重要,等等......

如果线程正在运行一些 Qt 事件循环,您可能可以。见threads and QObject

有一个(可能是特定于 Unix 的)变通方法,与 Unix signals with Qt 一样:使用从 std::thread 到主线程的管道。

但是,正如Joachim Pileborg 所评论的,您应该创建自己的QThread。它是最简单的,也可能是最短的(就源代码而言),您只需复制并粘贴一些现有示例并根据您的需要进行调整。

注意 AFAIK 只有 主线程 应该执行 Qt GUI 操作。你不应该在主线程之外使用任何 QWidget (等等)! (顺便说一句,GTK 有同样的限制,至少在 Linux 上:只有主线程应该使用X Windows system protocols

【讨论】:

  • 感谢您的回复。窗户呢?
  • 我对 Windows 一无所知(从未为它编写过代码,我的第一个程序是 1974 年的穿孔卡片)。如果你能得到一些等效的管道,(或者做一个套接字),你可以试试。但是正如Joachim Pileburg 评论的那样,您应该采用Qt 方式并拥有一个QThread。
  • 可以从任何线程发出信号,即使不是由 Qt 通过 QThread 创建/管理的。为了处理排队的调用,线程必须是 QThread / 有一个事件循环旋转。
【解决方案2】:

您绝对可以从线程(QThreadstd::thread 甚至boost::thread)发出信号。只有你必须小心你的连接函数的第五个参数(Qt::ConnectionType):

如果Qt::DirectConnection:在发出信号时立即(从当前线程)调用槽。 如果Qt::QueuedConnection:当控制返回到接收者线程的事件循环时调用该槽。该槽在接收者的线程中执行。

更多选项请参见ConnectionType-enum

问题不在于您从哪个线程发出信号,而更多的是从哪个线程调用了插槽。例如,我认为QLabel::setText 必须从QLabel 的所有者线程(很可能是主线程)执行。因此,如果您从线程发出连接到QLabelsetText 的信号,则必须使用Qt::AutoConnectionQt::QueuedConnectionQt::BlockingQueuedConnection 完成连接。

【讨论】:

  • “我认为 QLabel::setText 必须从 QLabel 的所有者线程(很可能是主线程)执行” - 很可能...所有小部件都必须在主(QApplication)线程。附言no need to sign posts on SO!
  • 不是全部...我认为 QCheckBox::setChecked 从线程工作(或者它可能是 QWidget::setVisible 或 QWidget::setEnabled ....但我记得调用那些没有问题的人从线程)。但你是对的,一般来说,所有的 GUI 都必须从主线程更新......
  • 有些事情可能会看起来起作用,因为您所在的平台允许它,或者它还没有崩溃。您可能会或可能不会在未看到的调试输出中收到警告。但是,如果您在非 GUI 线程上,您应该遵守的带有 Qt 的合同是使用信号/插槽与小部件进行通信。 "...the GUI classes, notably QWidget and all its subclasses, are not reentrant. They can only be used from the main thread."
【解决方案3】:

如果您保留指向 QObject 的指针,那么您可以使用 QMetaObject::invokeMethod 成员 http://qt-project.org/doc/qt-5/qmetaobject.html#invokeMethod 之一

您可能必须使用Qt::QueuedConnection,因此您的信号将在适当的线程(而不是您的 std::thread)上被调用。请记住,您的信号不会立即被调用。

【讨论】:

    【解决方案4】:
    class MainForm : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainForm(QWidget *parent = nullptr);
        virtual ~MainForm();
    
    private:
    signals:
        void signalSendButtonEnable(bool);
    
    private slots:
        void singalReceiveButtonEnable(bool);
    
    
    };
    
    
    MainForm::MainForm(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainForm), status_{false}, io_context_{}, timer_{io_context_}
    {
        ui->setupUi(this);
    
        // bind SIGNAL & SLOT
        connect(this, SIGNAL(signalSendButtonEnable(bool)), this, SLOT(singalReceiveButtonEnable(bool)));
    }
    
    MainForm::~MainForm()
    {
        delete ui;
    }
    
    void MainForm::singalReceiveButtonEnable(bool status){  //recv signal
        qDebug() << "singalReceiveButtonEnable";
        this->ui->btnConnect->setEnabled(status);
    }
    
    
    void MainForm::start(){
        std::thread t([](){
            sleep(20);
            emit signalSendButtonEnable(true);   //send signal
        });
        t.detach();
    }
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-09-27
      • 2018-01-15
      • 2010-10-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-13
      相关资源
      最近更新 更多