15

我是 QT 的新手,我正在学习。

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

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

这是一些代码:

(MAIN + Thread 类)

   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 部分。

感谢您的帮助

4

5 回答 5

19

stribika 几乎是正确的:

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

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

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

不再需要来自 cjhuitt 的回答(在 Qt <= 4.1 中是这样),因为connect()默认情况下Qt::AutoConnectionnow (Qt >= 4.2) 会根据QThread::currentThread()和线程亲和性在队列和直接连接模式之间切换是正确的发射QObject时的接收者(而不是连接时的发送者和接收者亲和性)。

于 2009-07-20T05:33:47.390 回答
9

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

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();
}
于 2009-07-17T20:51:35.860 回答
7

您需要使用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 )

于 2009-07-17T18:28:43.903 回答
2

我认为不允许您直接调用导致来自除主线程之外的任何其他线程的绘制事件的事物。这将导致崩溃。

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

于 2009-07-18T00:15:43.710 回答
2

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

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

于 2010-03-18T14:15:57.780 回答