[转]QT中线程调用GUI主线程控件的问题

blog.csdn.net/dongfangyu/article/details/5972764

之前写过一篇文章,是传界面指针到线程中去,从而在线程中操作主界面中控件。
今天发现,这种方法是极其错误的,文章我已经删掉,希望没有误人子弟。
前面转的两篇文章中对于为什么不能在线程中操纵界面控件指针有了很好的解释。下面在做下解释:
尽管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.cpp:


#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线程里的控件。

Signals and Slots Across Threads

Qt supports these signal-slot connection types:

  • Auto Connection (default) If the signal is emitted in the thread which the receiving object has affinity then the behavior is the same as the Direct Connection. Otherwise, the behavior is the same as the Queued Connection."
  • Direct Connection The slot is invoked immediately, when the signal is emitted. The slot is executed in the emitter's thread, which is not necessarily the receiver's thread.
  • Queued Connection The slot is invoked when control returns to the event loop of the receiver's thread. The slot is executed in the receiver's thread.
  • Blocking Queued Connection The slot is invoked as for the Queued Connection, except the current thread blocks until the slot returns. Note: Using this type to connect objects in the same thread will cause deadlock.
  • Unique Connection The behavior is the same as the Auto Connection, but the connection is made only if it does not duplicate an existing connection. i.e., if the same signal is already connected to the same slot for the same pair of objects, then the connection is not made and connect() returns false.

The connection type can be specified by passing an additional argument to connect(). Be aware that using direct connections when the sender and receiver live in different threads is unsafe if an event loop is running in the receiver's thread, for the same reason that calling any function on an object living in another thread is unsafe.

QObject::connect() itself is thread-safe.

The Mandelbrot example uses a queued connection to communicate between a worker thread and the main thread. To avoid freezing the main thread's event loop (and, as a consequence, the application's user interface), all the Mandelbrot fractal computation is done in a separate worker thread. The thread emits a signal when it is done rendering the fractal.

Similarly, the Blocking Fortune Client example uses a separate thread for communicating with a TCP server asynchronously.

原文地址:https://www.cnblogs.com/pulas/p/2777665.html