qt GUI執行緒和其他執行緒的訊號槽以及不同執行緒通訊
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說的執行緒親和性
如果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()
}