2014-01-15 34 views
1

我會盡量明確。我創建了一個Qt應用程序,它有一些按鈕和一個QTextEdit。接下來我創建一個pthread。並提供指向MainWindow的指針作爲參數。類似這樣的:從pthread發送信號到QObject

MainWindow w; 
pthread_create(&rThread,NULL,treat,&w); 

treat是創建線程時執行的函數。現在,如果我有一個名爲myButton的按鈕,和我做財產以後這樣的對待函數內部:

void *treat(void *arg) 
{ 
    MainWindow *win = (MainWindow*)arg; 
    win->ui->myButton->setEnabled(false); 
    close(pthread_self()); 
} 

它會正常工作,併爲myButton在我的應用程序將禁用。但是,如果我做這樣的事情:

void *treat(void *arg) 
{ 
    MainWindow *win = (MainWindow*arg; 
    win->ui->editText->setText("random string"); 
    close(pthread_self()); 
} 

我的應用程序將出現以下錯誤崩潰:

QObject: Cannot create children for a parent that is in a different thread. (Parent is QTextDocument(0x23af2e0), parent's thread is QThread(0x209a290), current thread is QThread(0x7f7eec000af0) The program has unexpectedly finished.

據我瞭解用戶界面是住在主線程,可能不是在入店我創建的線程,儘管我提供了主窗口的指針給這個線程。但爲什麼禁用按鈕的工作?我很困擾。我之所以使用QThread是因爲我們的老師告訴我不這樣做。我必須使用pthreads。我怎麼能從pthread應用到editText這樣的變化? 我怎麼能發送一個信號從一個pthread到另一個線程在這個Ui是「生活」。先謝謝你們。

回答

3

一般來說,這是一個錯誤調用QObject(或派生類)方法的任何從比object->thread()其他線程 - 除非它們被設計和記錄是線程安全的。在Qt中有幾個方法明確記錄爲線程安全的,例如QCoreApplication::postEvent

您面臨的行爲是由於從非gui線程訪問QWidget方法。這是未定義的行爲,所以有些方法可能會崩潰,有些不會,但即使它們不是仍然是未定義的行爲,您不能指望。對於我們所知道的,它可能取決於月球的相位。

從另一個線程執行的唯一安全的事情是將事件發佈到對象。當您在另一個線程的對象上使用QMetaMethod::invokeQMetaObject::invokeMethod時,Qt將在內部發布QMetaCallEvent到對象。由於發佈事件是線程安全的(可以從其他線程完成),因此可以使用其他線程中的任一調用方法。 QObject::event()通過執行正確的方法調用來響應此類事件。

所以,你可以從其他線程唯一要做的事情就是:

QMetaObject::invokeMethod(win->ui->editText, "setText", Q_ARG(QString, "random string")); 

唉,這是不好的設計,因爲你暴露主窗口的內部細節(如ui指針)到外部。你應該做的反而是把窗戶上的setEditText插槽:

MainWindow : public QWidget { 
    ... 
public: 
    Q_SLOT void setEditText(const QString & str) { 
    ui->editText->setText(str); 
    } 
    ... 
}; 

然後,從其他線程,你這樣做:

QMetaObject::invokeMethod(win, "setEditText", Q_ARG(QString, "random string")); 

我與馬立克的r建議不要使用pthreads時完全同意你有QThread可用。

+0

謝謝你。那就是訣竅。我無法在兩天內找到解決此問題的解決方案。我恨我的老師讓我們使用pthreads而不是QThreads。最後它的工作原理。先生,我向你致以最良好的問候! :) – Zan

+0

@Zan:'QThread'讓生活變得簡單一些,但是你會遇到和pthread一樣的限制。也就是說,你不能對非線程安全的方法直接進行跨線程調用(除非另有說明,否則請閱讀:*所有*方法)。 –

1

首先混合庫時,它是不必要的是壞習慣。 Qt提供QThread和非常方便的QtConcurrent

其次這是糟糕的設計。創建一些QObject它將處理你的線程計算,並將發送信號,當它應該將結果傳遞給UI(主線程)。然後創建連接,Qt將處理其餘的東西使其線程安全(默認情況下,它將排隊連接,如果信號在線程之間傳遞)。

你用Qt代碼併發:

void *treat(SomeClass *arg) { 
    arg->doStuff(); 
} 

QtConcurrent::run(treat, someObject); 
+0

所以我必須創建一個新的類繼承QObject,並將有信號發送到屬於MainWindow的插槽。但是,我什麼時候調用連接方法?線程啓動之前?或者我可以在治療功能內做到嗎? – Zan

+0

如果你懶惰,你可以添加信號到'MainWindow'連接到按鈕,並使用此信號,而不是直接在按鈕上操作。執行這樣的鏈'win-> ui-> editText->'是非常糟糕的代碼(很難維護)。 –

+0

謝謝你的建議。我希望我不會再有這個跨框架的事情了。這是一個痛苦。 – Zan