Qt - 在C++线程中发射信号

19

我想从C++线��(std::thread)中在Qt中发出一个信号。

我该怎么做?


1
你不想使用Qt自己的线程有什么原因吗?Qt是一个相当“包容”的库,如果你使用Qt,几乎所有的东西都必须使用Qt。 - Some programmer dude
1
我不想创建另一个类等等。我需要线程来完成一个小任务。 - Mohammad Reza Ramezani
“我不想创建另一个类”真的是一个很糟糕的理由。替代方案会更加复杂和脆弱! - Basile Starynkevitch
我必须将UI组件传递给新类。这更加复杂! - Mohammad Reza Ramezani
只是为了澄清:使用QThread并不意味着“创建另一个类”。事实上,有些人认为子类化QThread是一种反模式。 - kkoehne
4个回答

15

您绝对可以从线程(QThreadstd::thread或甚至是boost::thread)发出信号。只需注意连接函数的第五个参数(Qt::ConnectionType):

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

有关更多选项,请参见ConnectionType-enum

问题并非真正由哪个线程发出信号,而是由哪个线程调用槽。例如,我认为QLabel :: setText必须在QLabel的所有者线程(最可能是主线程)中执行。因此,如果您从线程发出连接到QLabelsetText的信号,则必须使用Qt::AutoConnectionQt::QueuedConnectionQt::BlockingQueuedConnection进行连接。


我认为QLabel :: setText必须从QLabel的所有者线程(很可能是主线程)执行 - 更多的是,所有小部件都必须存在于主(QApplication)线程上。 P.S. 无需在SO上签名! - HostileFork says dont trust SE
并不是所有的GUI控件都不能在线程中更新。我认为QCheckBox :: setChecked可以在线程中使用(或者可能是QWidget :: setVisible或QWidget :: setEnabled....但我记得其中一个没有问题地从线程中调用)。但你是对的,总体而言,所有的GUI控件都必须从主线程中更新... - jpo38
有些东西可能会 似乎 起作用,因为你所在的平台允许它,或者它还没有崩溃。你可能会收到警告信息,但你没有看到。但是,如果你正在非 GUI 线程上运行,与 Qt 的协议是使用信号/槽与小部件进行通信。“...GUI 类,特别是 QWidget 及其所有子类,不可重入。它们只能从主线程中使用。” - HostileFork says dont trust SE

4
一般情况下,您不应该在std :: thread创建的线程中发出Qt信号而不加注意。请参见Jpo38的回答:连接类型很重要等等...

如果线程正在运行某些Qt事件循环,则可能可以。请参见线程和QObject

有一个(可能是Unix特定的)解决方法,与使用Qt处理Unix信号相同:从std :: thread 到主线程使用管道。

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

请注意,据我所知,只有主线程应该进行Qt GUI操作。您不应在主线程之外使用任何QWidget(等...)! (顺便说一句,GTK有相同的限制,至少在Linux上:只有主线程才应该使用X Windows系统协议


谢谢您的回复。那Windows呢? - Mohammad Reza Ramezani
我对Windows一无所知(从未为其编写过代码,我的第一个程序是在1974年的穿孔卡上)。如果你能获得某种等效的管道(或者创建一个套接字),你可以尝试一下。但正如Joachim Pileburg所评论的那样,你应该采用Qt的方式并使用QThread。 - Basile Starynkevitch
5
即使不是由Qt创建/管理的线程,您仍然可以从任何线程发出信号,但为了处理排队的调用,该线程必须是QThread /具有正在运行的事件循环。 - peppe

1
如果您正在保留指向QObject的指针,则可以使用其中一个QMetaObject :: invokeMethod成员http://qt-project.org/doc/qt-5/qmetaobject.html#invokeMethod
可能您将不得不使用Qt :: QueuedConnection,以便您的信号将在正确的线程(而不是您的std :: thread)中调用。请记住,您的信号不会立即被调用。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接