Jadi mekanismenya adalah Anda tidak dapat memodifikasi widget dari dalam utas jika tidak, aplikasi akan mogok dengan kesalahan seperti:
QObject::connect: Cannot queue arguments of type 'QTextBlock'
(Make sure 'QTextBlock' is registered using qRegisterMetaType().)
QObject::connect: Cannot queue arguments of type 'QTextCursor'
(Make sure 'QTextCursor' is registered using qRegisterMetaType().)
Segmentation fault
Untuk menyiasatinya, Anda perlu mengenkapsulasi pekerjaan berulir di kelas, seperti:
class RunThread:public QThread{
Q_OBJECT
public:
void run();
signals:
void resultReady(QString Input);
};
Di mana run() berisi semua pekerjaan yang ingin Anda lakukan.
Di kelas induk Anda, Anda akan memiliki fungsi panggilan yang menghasilkan data dan fungsi pembaruan widget QT:
class DevTab:public QWidget{
public:
void ThreadedRunCommand();
void DisplayData(QString Input);
...
}
Kemudian untuk memanggil utas, Anda akan menghubungkan beberapa slot, ini
void DevTab::ThreadedRunCommand(){
RunThread *workerThread = new RunThread();
connect(workerThread, &RunThread::resultReady, this, &DevTab::UpdateScreen);
connect(workerThread, &RunThread::finished, workerThread, &QObject::deleteLater);
workerThread->start();
}
Fungsi koneksi membutuhkan 4 parameter, parameter 1 adalah kelas penyebab, parameter 2 adalah sinyal di dalam kelas tersebut. Parameter 3 adalah kelas fungsi panggilan balik, parameter 4 adalah fungsi panggilan balik di dalam kelas.
Maka Anda akan memiliki fungsi di utas anak Anda untuk menghasilkan data:
void RunThread::run(){
QString Output="Hello world";
while(1){
emit resultReady(Output);
sleep(5);
}
}
Maka Anda akan memiliki panggilan balik di fungsi induk Anda untuk memperbarui widget:
void DevTab::UpdateScreen(QString Input){
DevTab::OutputLogs->append(Input);
}
Kemudian saat Anda menjalankannya, widget di induk akan diperbarui setiap kali makro emit dipanggil di utas. Jika fungsi koneksi dikonfigurasikan dengan benar, parameter yang dipancarkan akan secara otomatis diambil, dan disimpan ke dalam parameter input fungsi callback Anda.
Cara kerjanya:
- Kami menginisialisasi kelas
- Kami menyiapkan slot untuk menangani apa yang terjadi dengan utas selesai dan apa yang harus dilakukan dengan "dikembalikan" alias
emit
ted data karena kami tidak dapat mengembalikan data dari utas dengan cara biasa - kami kemudian menjalankan utas dengan
->start()
panggilan (yang dikodekan keras ke dalam QThread), dan QT mencari nama kode keras.run()
fungsi anggota di kelas - Setiap kali
emit
makro resultReady dipanggil di utas anak, ia menyimpan data QString ke beberapa area data bersama yang tertahan di antara utas - QT mendeteksi bahwa resultReady telah terpicu dan memberi sinyal pada fungsi Anda, UpdateScreen(QString ) untuk menerima QString yang dipancarkan dari run() sebagai parameter fungsi sebenarnya di utas induk.
- Ini berulang setiap kali kata kunci emit dipicu.
Pada dasarnya connect()
fungsi adalah antarmuka antara utas anak dan induk sehingga data dapat melakukan perjalanan bolak-balik.
Catatan: resultReady() tidak perlu didefinisikan. Anggap saja seperti makro yang ada di internal QT.
Hal penting tentang Qt adalah Anda harus bekerja dengan Qt GUI hanya dari utas GUI, yaitu utas utama.
Itulah mengapa cara yang tepat untuk melakukannya adalah dengan memberi tahu utas utama dari pekerja, dan kode di utas utama benar-benar akan memperbarui kotak teks, bilah kemajuan, atau yang lainnya.
Cara terbaik untuk melakukan ini, menurut saya, adalah menggunakan QThread daripada posix thread, dan menggunakan sinyal Qt untuk komunikasi antar thread. Ini akan menjadi pekerja Anda, pengganti thread_func
:
class WorkerThread : public QThread {
void run() {
while(1) {
// ... hard work
// Now want to notify main thread:
emit progressChanged("Some info");
}
}
// Define signal:
signals:
void progressChanged(QString info);
};
Di widget Anda, tentukan slot dengan prototipe yang sama dengan sinyal di .h:
class MyWidget : public QWidget {
// Your gui code
// Define slot:
public slots:
void onProgressChanged(QString info);
};
Di .cpp implementasikan fungsi ini:
void MyWidget::onProgressChanged(QString info) {
// Processing code
textBox->setText("Latest info: " + info);
}
Sekarang di tempat di mana Anda ingin menelurkan utas (saat mengklik tombol):
void MyWidget::startWorkInAThread() {
// Create an instance of your woker
WorkerThread *workerThread = new WorkerThread;
// Connect our signal and slot
connect(workerThread, SIGNAL(progressChanged(QString)),
SLOT(onProgressChanged(QString)));
// Setup callback for cleanup when it finishes
connect(workerThread, SIGNAL(finished()),
workerThread, SLOT(deleteLater()));
// Run, Forest, run!
workerThread->start(); // This invokes WorkerThread::run in a new thread
}
Setelah Anda menghubungkan sinyal dan slot, keluarkan slot dengan emit progressChanged(...)
di utas pekerja akan mengirim pesan ke utas utama dan utas utama akan memanggil slot yang terhubung ke sinyal itu, onProgressChanged
di sini.
Nb. Saya belum menguji kodenya, jadi jangan ragu untuk menyarankan edit jika saya salah di suatu tempat