【问题标题】:QT + How to call slot from custom C++ code running in a different threadQT + 如何从在不同线程中运行的自定义 C++ 代码调用插槽
【发布时间】:2023-04-05 08:28:02
【问题描述】:

我是 QT 新手,正在学习。

我想触发一个从 C++ 线程(当前是 Qthread)修改 GUI 小部件的插槽。

不幸的是,我得到一个:断言失败:Q_ASSERT(qApp && qApp->thread() == QThread::currentThread());

这里有一些代码:

(MAIN + 线程类)

   class mythread : public QThread
    {
    public:
        mythread(mywindow* win){this->w = win;};
        mywindow* w;
        void run()
        {
            w->ui.textEdit->append("Hello");        //<--ASSERT FAIL
            //I have also try to call a slots within mywindow which also fail.
        };
    };

    int main(int argc, char *argv[])
    {
        QApplication* a = new QApplication(argc, argv);
        mywindow* w = new mywindow();

        w->show();
        mythread* thr = new mythread(w);
        thr->start();

        return a->exec();
    }

窗口:

class mywindow : public QMainWindow
{
    Q_OBJECT

public:
    mywindow (QWidget *parent = 0, Qt::WFlags flags = 0);
    ~mywindow ();
    Ui::mywindow ui;

private:



public slots:
    void newLog(QString &log);
};

所以我很好奇如何通过不同线程中的代码更新 gui 部分。

感谢您的帮助

【问题讨论】:

    标签: qt qt4 signals-slots


    【解决方案1】:

    stribika 几乎是对的:

    QMetaObject::invokeMethod( textEdit, "append", Qt::QueuedConnection,
                               Q_ARG( QString, myString ) );
    

    cjhuitt 是对的:您通常希望在线程上声明一个信号并将其连接到append() 插槽,以免费获得对象生命周期管理(好吧,以较小的接口更改为代价)。在旁注中,附加论点:

                   Qt::QueuedConnection ); // <-- This option is important!
    

    不再需要来自 cjhuitt 的回答(在 Qt connect() 默认为 Qt::AutoConnection,现在(Qt >= 4.2)做正确的事情并在排队和直接连接之间切换模式基于QThread::currentThread() 和接收者QObjectemit 时的线程亲和性(而不是连接时的发送者和接收者亲和性)。

    【讨论】:

    • 我不知道连接现在是在发出时间...这是否仍然适用于线程对象,它在创建它的线程中“活动”,而不是在何时产生的线程QThread::run 被调用? (几周前我们刚刚在工作中对此进行了辩论,并决定在这些情况下指定 QueuedConnection 选项更安全。)
    • 我用它来从根本不是 Qthread 的线程发出信号。非常适合发送事件 Qt 的主循环。此外,在 Qt 4.3 及更高版本中不必指定 QueuedConnection。
    【解决方案2】:

    除了stribika's answer,我经常发现使用信号/插槽连接更容易。您可以在连接时指定它应该是排队连接,以避免线程信号在其所属对象的上下文中出现问题。

    class mythread : public QThread
    {
    signals:
        void appendText( QString );
    public:
    
        mythread(mywindow* win){this->w = win;};
        mywindow* w;
        void run()
        {
            emit ( appendText( "Hello" ) );
        };
    };
    
    int main(int argc, char *argv[])
    {
        QApplication* a = new QApplication(argc, argv);
        mywindow* w = new mywindow();
    
        w->show();
        mythread* thr = new mythread(w);
        (void)connect( thr, SIGNAL( appendText( QString ) ),
                       w->ui.textEdit, SLOT( append( QString ) ),
                       Qt::QueuedConnection ); // <-- This option is important!
        thr->start();
    
        return a->exec();
    }
    

    【讨论】:

    • 如果来自非 Qt 非 UI 线程,这将如何实现? (即没有任何 Qt 依赖的代码;不是 QThread 子类)
    • @DavidJ 您可以查看其他答案,但在这种情况下,我的感觉是您远远超出了 Qt 的信号槽用例,如果您一直将其视为投币口。但是,所有槽也是可以正常调用的函数(等待对该函数的访问),因此其他指示应该调用它的跨线程技术应该可以工作。
    【解决方案3】:

    您需要使用QMetaObject::invokeMethod。例如:

    void MyThread::run() {
        QMetaObject::invokeMethod(label, SLOT(setText(const QString &)), Q_ARG(QString, "Hello"));
    }
    

    (以上代码来自这里:http://www.qtforum.org/article/26801/qt4-threads-and-widgets.html

    【讨论】:

    • @MarcMutz-mmutz 你能解释一下这段代码有什么问题吗?我怀疑它是 SLOT 宏,但可能是你有其他观点 ;-)
    • invokeMethod 只接受函数名 ("setText"),而不是 SLOT 的结果。
    【解决方案4】:

    我不认为您可以直接调用任何会导致绘制事件的事物 主线程以外的其他线程。这将导致崩溃。

    我认为您可以使用事件循环来异步调用事物,以便主 gui 线程启动,然后从主线程进行更新,这是 cjhuit 建议的。

    【讨论】:

      【解决方案5】:

      如果我们的线程关联显示 GUI,但我们不在 GUI 线程中,也不在 QThread 中?

      我的意思是,一个非 Qt(通知)线程调用一个 QObject 的接口方法,我们在其中发出一个 AutoConnected 信号。 QObject 的线程亲和性是主线程,但过程实际上是从另一个线程调用的。 Qt 在这里做什么?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-05-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多