窗口本身就是一个死循环,在这样一个死循环中执行任何耗时的操作,都会导致程序崩溃。所以多线程对于窗口编程而言是必要的。
例如,在窗口中拖入一个 pushButton 和 lineEdit ,将 pushButton 的名字改为 pbStart ,然后为其绑定一个函数,函数中创建一个死循环,并让死循环中的内容实时输出到 lineEdit 。
(相关资料图)
接下来设计一个逻辑,当点击 pbStart 之后,开始执行死循环,同时按钮内容变为 Stop ;当按钮内容为 Stop 时,点击按钮,停止执行死循环。
所以,需要设计一个用于控制循环是否继续执行的成员变量,在 MainWindow 的头文件中添加私有成员 bool flag 。在 cpp 文件中添加 flag = true 。
然后转到设计界面 ,右键 pbStart 按钮->转到槽 ->clicked() 。从而 MainWindow 中添加了一个名为 on_pbStart_clicked 的函数,填充其内容为
void MainWindow::on_pbStart_clicked(){ int i= 0; bool flag = QString::compare(ui->pbStart->text(),\"start\"); if(flag) ui->pbStart->setText(\"stop\"); while(flag){ i++; ui->lineEdit->setText(QString::number(i)); }}
复制
Jetbrains全家桶1年46,售后保障稳定
结果运行之后,果然陷入了死循环,程序也无法响应了,最过分的是 lineEdit 并没有变化,这个时候就要考虑多线程。
QThread 是Qt中最基础的线程类,每个实例都可以控制一个线程。其传统的调用方式是,新建一个继承 QThread 的类,然后将耗时任务写入 run 函数。而自QT4.4之后,则建议通过 moveToThread() 函数来调用多线程。
首先新建一个类,在项目中 Ctrl + N ,在弹出对话框中选择 Files and Classes ->C/C++ ->C++ Class ,定义类的名称为 ThTest ,并选中 QObject 。
令该类继承 QObject ,将头文件中的 ThTest 改为
class ThTest : public QObject{ Q_OBJECTpublic: ThTest(); ~ThTest(); void Func(void);};
复制
相应地, cpp 文件的内容为
#include \"thtest.h\"#include #include ThTest::ThTest(){ }ThTest::~ThTest(){ }void ThTest::Func(){ int NowNum = 0; while(true){ NowNum++; qDebug()<
复制
然后更改 mainwindow 的代码,在其头文件内容如下
#ifndef MAINWINDOW_H#define MAINWINDOW_H#include #include #include #include #include \"thtest.h\"QT_BEGIN_NAMESPACEnamespace Ui { class MainWindow; }QT_END_NAMESPACEclass MainWindow : public QMainWindow{ Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();signals:void ToThread(); // 信号private slots:void on_pbStart_clicked();private:Ui::MainWindow *ui;QThread *qThTest;ThTest *th1;};#endif
复制
然后将 cpp 文件中的 on_pbStart_clicked 函数改为
void MainWindow::on_pbStart_clicked(){ qThTest = new QThread;th1 = new ThTest;connect(this,&MainWindow::ToThread,th1,&ThTest::Func);th1->moveToThread(qThTest);qThTest->start();emit ToThread();}
复制
其中, connect 将 ToThread 函数和 ThTest 中的 Func 函数绑定在了一起。也就是说,当这边发射 ToThread 的信号的时候, Th1 会执行 Func 这个函数。
所以,当线程启动后,通过 emit 发射 ToThread 信号,果然命令行中会持续输出数字了,同时窗口并不会死掉。
到了这一步,其实已经可以处理一些多线程任务了,但还不能把递增的 NowNum 显示到主窗口上,从而让 lineEdit 看上去有些鸡肋。为了实现在窗口上显示递增的数字,接下来需要做的就是线程间的通信。
得益于Qt的信号槽机制,多线程之间的通信并不复杂。乃至于,可以广义地认为 emit ToThread() 本身也是一个线程间通信的过程。
所以只需在 on_pbStart_clicked 中添加一条
connect(th1,SIGNAL(sendInt(int)),this,SLOT(getInt(int)));
复制
意思就是 th1 发射一个 sendInt(int) , this 接收一个 getInt(int) ,这两个函数的名字无所谓,但一定不包含形参,而只有形参的数据类型。
接下来,在 thtest.h 中添加 sendInt ,
signals:void sendInt(int);
复制
并更改其 Func 函数
void ThTest::Func(){ int NowNum = 0;while(true){ QThread::sleep(1);emit sendInt(NowNum++);}}
复制
最后,在 mainwindow.h 中添加
private slots:void getInt(int);
复制
以及 cpp 文件中的
void MainWindow::getInt(int num){ ui->lineEdit->setText(QString::number(num));}
复制
这样,在点击 start 之后,就可以看到 lineEdit 上数字的变化了。
最后,回到一开始的需求,是点击 start 开始,然后按钮变为 stop ,点击 stop 后再停止。
考虑到 terminate 并不安全,故而采用终结 while 的方式来退出死循环。方法是在 ThTest 中添加一个 bool 类型的成员 running ,即在头文件中添加
public:bool running=true;
复制
并更改源文件中的 while 循环
void ThTest::Func(){ int NowNum = 0;while(running){ emit sendInt(NowNum++);QThread::sleep(1);}}
复制
最后修改 on_pbStart_clicked
void MainWindow::on_pbStart_clicked(){ bool flag = QString::compare(ui->pbStart->text(),\"stop\");qDebug()<pbStart->text();th1->running = flag;ui->pbStart->setText(flag?\"stop\":\"start\");if(flag){ th1->moveToThread(qThTest);qThTest->start();emit ToThread();}elseui->lineEdit->setText(\"0\");}
复制
并把 th1 等初始化过程移除去
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow){ ui->setupUi(this);qThTest = new QThread;th1 = new ThTest;connect(th1,SIGNAL(sendInt(int)),this,SLOT(getInt(int)));connect(this,&MainWindow::ToThread,th1,&ThTest::Func);}
复制
结果线程果然终止了
【领 QT开发教程 学习资料, 点击下方链接莬费领取↓↓ ,先码住不迷路~】
点击这里:
发布者:全栈程序员栈长
出处:https://javaforall.cn/234553.html原文链接:https://javaforall.cn
X 关闭
X 关闭