QT中线程调用GUI主线程控件的问题
之前写过一篇文章,是传界面指针到线程中去,从而在线程中操作主界面中控件。 今天发现,这种方法是极其错误的,文章我已经删掉,希望没有误人子弟。 前面转的两篇文章中对于为什么不能在线程中操纵界面控件指针有了很好的解释。下面在做下解释: 尽管QObject是可重入的,但GUI类,特别是QWidget与它的所有子类都是不可重入的。它们仅用于主线程。正如前面提到过 的,QCoreApplication::exec() 也必须从那个线程中被调用。实践上,不会在别的线程中使用GUI类,它们工作在主线程上,把一些耗时的操作放入独立的工作线程中,当工作线程运行完成,把 结果在主线程所拥有的屏幕上显示。 下面的几个帖子是我在解决问题的过程中看到的几个很有帮助的帖子,一并贴下: http://www.qtcn.org/bbs/read.php?tid=12121&keyword=%CF%DF%B3%CC http://www.qtcn.org/bbs/read.php?tid=13508&keyword=%CF%DF%B3%CC http://www.qtcn.org/bbs/read.php?tid=18586&keyword=%CF%DF%B3%CC 下面截取里面比较有价值的回复: 线程里面直接操作界面元素是肯定不行的,即使不总是出现错误,偶尔也会出莫明其妙,在 文档里面已经有说了,你再找找看, 你需要使用比如postEvent之类的异步处理方式. 至于进程中的那个错误,你设断点调试下吧. 不可以在非界面的进程中直接操作界面的 Qt的文档有这么说 据说4之前版本可以,没有试过 signal/slot目前有三种调用方式 1.DirectConnection 和以前一样,在emit处直接invoke你的slot 函数,一般情况是sender,receiver在同一线程 2.QueuedConnection 将 发送Event给你的receiver所在的线程 postEvent(QEvent::MetaCall,...) slot 函数会在receiver所在的线程的event loop中进行处理,一般情况是sender,receiver不在同一线程 3.BlockingQueuedConnection 调 用sendEvent(QEvent::MetaCall,...),在receiver所在的线程处 理完成后才会返回;只能当sender,receiver不在同一线程时才可以 好了,上面的是说为什么不行的,那如果非要在非GUI线程里操作GUI线程里的控件,该怎么做呢? 答案是使用signal/slot。 在线程里的run()里定期emit signal,GUI线程里建立连接,写槽函数,注意connect的第五个参数应该使用Queued方式。 下面列个从QT论坛上找的例子,专门用来解释这个问题的 thread.h: #ifndef THREAD_H #define THREAD_H #include <QThread> class Thread : public QThread { Q_OBJECT public: Thread(); signals: void sendString(QString); protected: void run(); }; #endif // THREAD_H widget.h: #ifndef WIDGET_H #define WIDGET_H #include <QtGui/QWidget> #include <QtGui/QTextEdit> class Thread; class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = 0); ~Widget(); private: QTextEdit *m_textEdit; Thread *m_thread; }; #endif // WIDGET_H thread.cpp: #include "thread.h" #include <QtCore/QTime> Thread::Thread() { } void Thread::run() { while(1) { emit sendString(QTime::currentTime().toString("hh:mm:ss.zzz")); msleep(200); } } widget.h: #include <QtGui/QHBoxLayout> #include "widget.h" #include "thread.h" Widget::Widget(QWidget *parent) : QWidget(parent) { m_textEdit = new QTextEdit(this); QHBoxLayout * layout = new QHBoxLayout(this); layout->addWidget(m_textEdit); setLayout(layout); m_thread = new Thread(); connect(m_thread, SIGNAL(sendString(QString)), m_textEdit, SLOT(append(QString))); m_thread->start(); } Widget::~Widget() { } main.cpp: #include <QtGui/QApplication> #include "widget.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; w.show(); return a.exec(); } 这个例子就演示了如何在非GUI线程里调GUI线程里的控件。 |