1. 说明
Qt不允许子线程直接操作主线程UI,推荐的做法是,子线程发送信号,主线程响应该信号。其最终的效果,依旧不是子线程直接操作主线程UI。
2. 场景
假设现在有一个任务队列,队列里的每一个任务,都会在结束时,往主窗口的控制台打印一条消息。
代码如下:
class Console {
public:
void printMessage(const QString& msg) const {
m_lineEdit->setText(msg);
}
protected:
QLineEdit* m_lineEdit;
};
void threadFunc(Console * console) {
// some other codes
console->printMessage("message");
}
int main() {
Console console;
// some other codes
std::thread th(threadFunc, &console);
}
在上述代码中,子线程更新主线程ui界面,这种方法在Qt中是不允许
的。Qt禁止子线程更新主线程UI。
3. 子线程更新UI的方法
3.1 一般做法
一般的做法是,子线程发送信号,主线程接收信号,并执行对应槽函数。上述代码按照这种方式进行改造,如下:
class Console {
public slots:
void printMessage(const QString& msg) const {
m_lineEdit->setText(msg);
}
protected:
QLineEdit* m_lineEdit;
};
class Singal{
signals:
void print(const QString&) const;
public:
void printMessage() const {
emit print("message");
}
};
int main() {
Console console;
Singal signal;
connect(&signal, SIGNAL(print(const QString&)), &console, SLOT(printMessage(const QString&)));
std::thread th(&Singal::printMessage, &signal);
}
3.2 新玩法
从上面代码来看,一般做法里,代码量增加了不少。而且对现有代码的破坏性较大,这种破坏性更改,在接口类中,是难以容忍的。
为了保护接口,同时兼容信号槽方式,这里介绍一种新的方式:
class Console {
public:
void printMessage(const QString& msg) const {
emit print(msg);
}
signal:
void print(const QString& msg);
protected slots:
void slotOnPrint(const QString& msg) {
m_lineEdit->setText(msg);
}
protected:
QLineEdit* m_lineEdit;
};
void threadFunc(Console * console) {
// some other codes
console->printMessage("message");
}
int main() {
Console console;
// some other codes
std::thread th(threadFunc, &console);
}
上面介绍的这种新玩法,调用方没做任何更改,接口类(Console
)的公开接口也没改,增加了一个信号和一个本身的槽函数,同时它还是线程安全的。