Qt所有的对于GUI的操作只能在一个GUI线程中执行,也就是return QApp::exec的线程。
一般main里面这样写。那么所有的GUI的操作只能在main主线程中执行。
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
WidgetUi w;
w.show();
return a.exec();
}
执行a.exec()程序进入消息循环。开始处理消息
如果打算CreateThread,把ui的指针传过去 在thread里面操作UI。是不行的
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
WidgetUi w;
w.show();
CreateThread(thread_proc,&w);
return a.exec();
}
{
ui=(WidgetUi *)arg;
ui->textedit.settext(xxx);
}
在thread_proc中的ui的确是有效的指针。但是这么操作ui。容易崩溃并且不一定达到想要的效果。
1可能崩溃的原因是 没有加锁同步操作某个数据。比如主GUI线程正在读取一个list。此时另一个非gui线程删除了同一个list当中的某项。就崩溃了。
所有GUI操作只在一个线程内进行。这样就不用加考虑锁,思路简单清晰。一旦加锁。各种难以理解的问题就来了。
老师在批改作业。数了一下一共20本。老师说小明你的作业有问题,先拿下去修改。小明拿走一本了作业。
老师知道还剩19本。这是同一线程操作
如果小明自己回忆作业有问题,没告诉老师。而是直接拿走了。老师数来数去少了一本,老师就迷糊了。
2达不到预期的效果。
比如非gui线程用settext设置了 text。但是界面上可能不会显示。
因为你的线程修改了text的内容,但是没有通知GUI线程,所以GUI线程不会画出来,直到他下次处理某些事件的时候才会一并刷新。
老师还在改作业,别的同学告诉老师作业做好了。老师说你放到。我一个个改。通知一个就改一个。小明没告诉老师,
而是直接把作业放到那一堆本子里。老师不知道,所以没改。直到另一个同学说交作业了。老师才批改。才发现小明的也在这里。然后才一起批改了
其他线程和GUI线程通信。
如果想在createThread的线程里面通知UI进行操作怎么办。通过信号和槽实现。
如果要用信号和槽。那么必须继承QObject 必须在类里面
所以可以定义一个信号发射类。
class Msg:public QObject
{
Q_OBJECT
public:
void sendsig(int arg)
{
emit sig(arg);
};
signals:
void sig(int arg);
}
connect(sig(),someslot());
Qt5以后signals变成public了。可以直接调用emit
Qt4是protected。不能直接调用。要写个public函数调用。
在外部定义一个 Msg msgsender
这样在 thread_proc里面
{
msgsender.sendsig(arg);
}
这样是可以的。
特别注意。这样的信号发射类一般只能做发射。不能接受信号。
因为如果要能发送和接受的都可以。这个QObject必须存在于一个生存的线程中。并且这个线程拥有事件循环。
也就是qt说的线程亲和性
http://doc.qt.io/qt-5/qobject.html#thread-affinity
如果QObject的-thread返回0或者生存在一个没有事件循环的线程当中。那么这个Qobject无法接受队列信号。和post过来的event
这个理解起来很简单。如果没有事件循环机制。线程去哪里取得投递到他队列里面的消息呢?
例如
thread_proc
{
p=new QObject
while(1)
{
sleep(100);
}
}
只要外部投递的是队列形式的信号。p的相关联的槽都不会被执行。因为线程一直在死循环。根本没机会从队列中取消息
但是通过指定DirectionConnection到p的槽是会执行的。只是并不运行在这个thread_proc的上下文当中。
如果想要这个线程可以接受槽。可以继承使用Qthread类。
在类的run方法里面调用this->exec。执行messageloop 。所以上面的写法可以改成
QSonThread::run()
{
p=new QObject
this->exec()
}