因为 qt 子线程中部分ui更新的接口不能用,就需要使用信号槽,让子线程通知进程更新UI。

1. 在线程类当中添加信号的定义

线程是信号的发出方,因此信号函数的定义在线程内。信号函数的声明在.h文件中,可以带参数。

  1. class led_thread : public QThread {
  2. Q_OBJECT
  3. public:
  4. int btn_cnt;
  5. protected:
  6. void run();
  7. signals:
  8. void update_btn(int num); // 声明信号
  9. };

void update_btn(int) 就是定义的信号函数,注意返回值是void


2. 在线程类当发送信号

以更新按钮为例子,要轮流发送信号,通知槽函数更新某个按钮。发送信号时可以带一个int值,对应更新这个int下标的按钮:

  1. void led_thread::run() {
  2. while(true) {
  3. for(int i = 0; i < btn_cnt; i++) {
  4. emit update_btn(i);// 发送信号,参数值是对应下标的标号
  5. sleep(1);
  6. }
  7. }
  8. }

3. 在界面进程里面添加槽函数

首先在mainwindows.h 定义槽函数,注意要和信号函数一样,定义参数值

  1. private slots:
  2. void updateBtnSlot(int num);

然后在.cpp文件中实现这个槽函数。在槽函数中,通过接收到的num,在mainwindows的btn数组内找到对应下标的按钮,设置颜色。并且把上一个按钮颜色清空。如果当前是0,就把最后一个按钮清空(上次循环的末尾)。

  1. void MainWindow::updateBtnSlot(int num) {
  2. btn_list[num]->setStyleSheet("background-color:rgb(255,255,255)");
  3. if(num == 0) {
  4. btn_list[btn_list.size() - 1]->setStyleSheet("background-color:rgb(255,255,255)");
  5. } else {
  6. btn_list[num - 1]->setStyleSheet("background-color:rgb(255,255,255)");
  7. }
  8. }

4. 初始化,并连接信号槽

在mainwindows.cpp的初始化函数里面,连接子线程的信号,和ui进程的槽

  1. led_th->btn_cnt = btn_list.size();
  2. connect(led_th, SIGNAL(update_btn(int)), this, SLOT(updateBtnSlot(int)));
  3. led_th->start();

5. 在ui进程类里面,保存按钮数组

因为界面更新在ui类里面,所以按钮数组定义在mainwindows.h 里面

  1. class MainWindow : public QMainWindow {
  2. Q_OBJECT
  3. public:
  4. explicit MainWindow(QWidget *parent = nullptr);
  5. ~MainWindow();
  6. QTimer *timer1;
  7. my_thread * blink_th;
  8. led_thread * led_th;
  9. QVector<QPushButton*> btn_list; // 按钮数组
  10. private:
  11. Ui::MainWindow *ui;
  12. private slots:
  13. void time_out();
  14. void updateBtnSlot(int num);
  15. };

然后在初始化函数里面,存入按钮。注意要先存入按钮,后再给线程类里面的btn_cnt赋值

  1. btn_list.append(ui->pushButton_1);
  2. btn_list.append(ui->pushButton_2);
  3. btn_list.append(ui->pushButton_3);
  4. btn_list.append(ui->pushButton_4);
  5. btn_list.append(ui->pushButton_5);
  6. btn_list.append(ui->pushButton_6);
  7. btn_list.append(ui->pushButton_7);
  8. btn_list.append(ui->pushButton_8);
  9. btn_list.append(ui->pushButton_9);
  10. btn_list.append(ui->pushButton_10);
  11. btn_list.append(ui->pushButton_11);
  12. btn_list.append(ui->pushButton_12);
  13. led_th->btn_cnt = btn_list.size();
  14. connect(led_th, SIGNAL(update_btn(int)), this, SLOT(updateBtnSlot(int)));
  15. led_th->start();

6. 在进程里面自定义延时函数

由于主进程没有sleep函数,可以用QTime和事件循环来实现不阻塞的延时:
先获取要定时的时间,然后在循环中不断判断是否到达那个时间。
.h中定义

  1. private:
  2. Ui::MainWindow *ui;
  3. void Sleep(int msec);

.cpp中实现延时

  1. #include <QTime>
  2. #include <QEventLoop>
  3. #include <QCoreApplication>
  4. void MainWindow::Sleep(int msec)
  5. {
  6. // 用QTime 设定结束的时间
  7. QTime dieTime = QTime::currentTime().addMSecs(msec);
  8. // while循环直到时间到
  9. while( QTime::currentTime() < dieTime )
  10. QCoreApplication::processEvents(QEventLoop::AllEvents, 100);// 执行事件循环,防止阻塞ui
  11. }

调用如下:

  1. btn_list[num]->setStyleSheet("background-color:rgb(255,0,0)");
  2. Sleep(400);
  3. btn_list[num]->setStyleSheet("background-color:rgb(255,255,255)");