1. 線程
爲什麼需要使用線程
- 當界面中處理很複雜的數據時,可能會造成界面未響應,這時可以把數據處理放在線程中來處理
- 多任務處理
線程使用
主線程:UI
Qt 4中比較簡單
自定義一個類,繼承於QThread
class MyThread:public QThread
{
public:
void run();//只有這一個纔是線程處理函數(和主線程不在同一線程)
}
void MyThread::run()
{
QThread::sleep(5);//模擬複雜數據處理
emit isDone();
}
主線程中:
//啓動線程
//不能直接調用run()
//start()間接調用run()
MyThread thread;
thread.start();
注意,線程號是有限的,要養成良好的習慣,用完以後關閉線程!
線程關閉
對於線程的關閉,不推薦使用terminal()
,該函數不管是否處理完當前數據,都會直接關閉。
推薦使用quit()
Demo如下:
mywidget.h
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include <QWidget>
#include <QTimer> // 定時器頭文件
#include "mythread.h" //線程頭文件
namespace Ui {
class MyWidget;
}
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = nullptr);
~MyWidget();
void dealTimeout(); //定時器槽函數
void dealDone(); //線程結束槽函數
void stopThread(); //停止線程槽函數
private slots:
void on_pushButton_clicked();
private:
Ui::MyWidget *ui;
QTimer *myTimer; //聲明變量
MyThread *thread; //線程對象
};
#endif // MYWIDGET_H
mywidget.cpp
#include "mywidget.h"
#include "ui_mywidget.h"
#include <QThread>
#include <QDebug>
MyWidget::MyWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyWidget)
{
ui->setupUi(this);
myTimer = new QTimer(this);
//只要定時器啓動,自動觸發timeout信號
connect(myTimer,&QTimer::timeout,this,&MyWidget::dealTimeout);
//分配空間
thread = new MyThread(this);
connect(thread,&MyThread::isDone,this,&MyWidget::dealDone);
//當按窗口右上角x時,窗口觸發destroyed()信號
connect(this,&MyWidget::destroyed,this,&MyWidget::stopThread);
}
void MyWidget::dealTimeout()
{
static int i = 0;
i++;
//設置lcd的值
ui->lcdNumber->display(i);
}
MyWidget::~MyWidget()
{
delete ui;
}
void MyWidget::on_pushButton_clicked()
{
//如果定時器沒有工作,才啓動
if(myTimer->isActive() == false)
{
myTimer->start(500);
}
/***************************************
* 未使用線程前的測試代碼
***************************************/
/*
//非常複雜的數據處理,耗時較長
//此處模擬
QThread::sleep(3);
//處理完數據後,關閉定時器
//myTimer->stop();
qDebug() <<"over";
//什麼時候用線程?
//處理數據很複雜的時候,數據處理就應該放在線程而不是界面
*/
//啓動線程,處理數據
thread->start();
}
void MyWidget::dealDone()
{
qDebug() << "it is over";
myTimer->stop();//關閉定時器
}
void MyWidget::stopThread()
{
//停止線程
thread->quit();
//等待線程處理完手頭工作
thread->wait();//阻塞線程,直到線程完成
qDebug() << "退出線程";
/* 註釋thread->wait()時,調試記錄如下(線程正在執行,關閉窗口):
* 退出線程
* QThread: Destroyed while thread is still running
* 14:19:36: The program has unexpectedly finished.
*/
}
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = nullptr);
protected:
//QThread的虛函數
//線程處理函數
//不能直接調用,通過start()間接調用
void run();
signals:
void isDone();
public slots:
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
MyThread::MyThread(QObject *parent) : QThread(parent)
{
}
void MyThread::run()
{
//很複雜的數據處理
//需要耗時5s
sleep(5); //處理完了需要告訴一聲:已經處理完了
emit isDone();
}
Qt 5中的線程
- 設定一個類,繼承於QObject
- 類中設置一個線程函數(只有一個是線程函數)
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QObject>
class MyThread : public QObject
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = nullptr);
//線程處理函數
void myTimeout();
void setFlag(bool flag = true);
signals:
void mySignal();
public slots:
private:
bool isStop;
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
#include <QThread>
#include <QDebug>
MyThread::MyThread(QObject *parent) : QObject(parent)
{
isStop = false;
}
void MyThread::myTimeout()
{
while (isStop == false)
{
//每隔1s發一個信號
QThread::sleep(1);
emit mySignal();
qDebug() << "子線程號:" <<QThread::currentThread();
if(true == isStop)
{
break;
}
}
}
void MyThread::setFlag(bool flag)
{
isStop = flag;
}
mywidget.h
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include <QWidget>
#include "mythread.h"
#include <QThread>
namespace Ui {
class MyWidget;
}
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = nullptr);
~MyWidget();
void dealSignal();
void dealClose();
signals:
void startThread(); //啓動子線程的信號
private slots:
void on_butttonStart_clicked();
void on_buttonStop_clicked();
private:
Ui::MyWidget *ui;
MyThread *myT;
QThread *thread;
};
#endif // MYWIDGET_H
mywidget.cpp
#include "mywidget.h"
#include "ui_mywidget.h"
#include <QDebug>
MyWidget::MyWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyWidget)
{
ui->setupUi(this);
//動態分配空間,不能指定父對象
myT = new MyThread;
//創建子線程
thread = new QThread(this);
//把自定義的線程加入子線程中
myT->moveToThread(thread);
connect(myT,&MyThread::mySignal,this,&MyWidget::dealSignal);
qDebug() << "主線程號:" <<QThread::currentThread();
connect(this,&MyWidget::startThread,myT,&MyThread::myTimeout);
connect(this,&MyWidget::destroyed,this,&MyWidget::dealClose);
}
MyWidget::~MyWidget()
{
delete ui;
}
void MyWidget::on_butttonStart_clicked()
{
if(thread->isRunning() == true)
{
return;
}
//啓動線程,但是沒有線程處理函數
thread->start();
myT->setFlag(false);
// 不能直接調用線程處理函數
// 直接調用會導致:線程處理函數和主線程是在同一個線程
//myT->myTimeout();//反例
//只能通過signal - slot 方式調用
emit startThread();
}
void MyWidget::dealSignal()
{
static int i = 0;
i++;
ui->lcdNumber->display(i);
}
void MyWidget::on_buttonStop_clicked()
{
if(thread->isRunning() == false)
{
return;
}
// 由於quit()退出線程的方式很溫柔,
// 而且線程的工作是個死循環,退不出來,因此這種方法不可行
// 解決方案:在while(1)中加入一個標誌位
myT->setFlag(true);
thread->quit();
thread->wait();
}
void MyWidget::dealClose()
{
//槽函數就是函數,可以直接調
on_buttonStop_clicked();
delete myT;//防止內存泄露
}
注意:
線程處理函數內部,不允許操作圖形界面
純數據處理,在後臺運行!!
connect()第五個參數的作用,只有在多線程時纔有意義
連接方式:默認,隊列,直接
默認的時候:如果是多線程,默認使用隊列;如果單線程,默認使用直接方式
隊列:槽函數所在的線程和接收者一樣
直接:槽函數所在線程和發送者一樣
線程畫圖示例
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QObject>
#include <QImage>
class MyThread : public QObject
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = nullptr);
//線程處理函數
void drawImage();
signals:
void updateImage(QImage temp);
public slots:
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
#include <QPainter>
#include <QPen>
#include <QBrush>
MyThread::MyThread(QObject *parent) : QObject(parent)
{
}
void MyThread::drawImage()
{
//定義QImage繪圖設備
QImage image(500,500,QImage::Format_ARGB32);
//定義畫家,指定繪圖設備
QPainter p(&image);
//定義畫筆對象
QPen pen;
pen.setWidth(5);//設置畫筆寬度
//把畫筆交給畫家
p.setPen(pen);
//定義畫刷
QBrush brush;
brush.setStyle(Qt::SolidPattern);//設置樣式
brush.setColor(Qt::red);//設置顏色
//把畫刷交給畫家
p.setBrush(brush);
//定義5個點
QPoint a[]
{
QPoint(qrand()%500,qrand()%500),
QPoint(qrand()%500,qrand()%500),
QPoint(qrand()%500,qrand()%500),
QPoint(qrand()%500,qrand()%500),
QPoint(qrand()%500,qrand()%500)
};
p.drawPolygon(a,5);
//通過信號發送圖片
emit updateImage(image);
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "mythread.h"
#include <QThread>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
//重寫繪圖事件
void paintEvent(QPaintEvent *);
void getImage(QImage);//槽函數
void dealClose();//退出窗口時關閉線程
private:
Ui::Widget *ui;
QImage image;
MyThread *myT;//自定義線程對象
QThread *thread;//子線程
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//自定義類對象,分配空間,不可指定父對象
myT = new MyThread;
//創建子線程
thread = new QThread(this);
//把自定義模塊添加到子線程
myT->moveToThread(thread);
//啓動子線程,但是並沒有啓動線程處理函數
thread->start();
//線程處理函數,必須通過signal - slot 調用
connect(ui->pushButton,&QPushButton::pressed,myT,&MyThread::drawImage);
connect(myT,&MyThread::updateImage,this,&Widget::getImage);
connect(this,&Widget::destroyed,this,&Widget::dealClose);
}
Widget::~Widget()
{
delete ui;
}
void Widget::dealClose()
{
//退出子線程
thread->quit();
//回收資源
thread->wait();
delete myT;
}
void Widget::getImage(QImage temp)
{
image = temp;
update();//更新窗口,間接調用paintEvent()
}
void Widget::paintEvent(QPaintEvent *)
{
QPainter p(this);//創建畫家,指定繪圖設備爲窗口
p.drawImage(50,50,image);
}
2. 數據庫
以MySQL爲例,使用前需要先添加sql
模塊,並且將libmysql.dll
放到D:\Qt\Qt5.12.1\5.12.1\mingw73_64\bin
下(以實際安裝目錄爲準)
數據庫連接
#include <QSqlDatabase>
...
//添加MySQL數據庫
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
//連接數據庫
db.setHostName("127.0.0.1");//數據庫服務器IP
db.setUserName("root");//數據庫用戶名
db.setPassword("123456");//數據庫用戶密碼
db.setDatabaseName("info");//使用哪個數據庫
...
數據庫插入
#include <QSqlQuery>
#include <QVariantList>
...
QSqlQuery query;
//直接通過sql語句創建表
//query.exec("create table student(id int primary key auto_increment,name varchar(255),age int,score int);");
//插入
//query.exec("insert into student(id,name,age,score) values(1,'mike',18,59)");
//批量插入
//obdc風格
/*
//預處理語句
// ? 相當於佔位符
query.prepare("iinsert into student(name,age,score) values(?,?,?);");
//給字段設置內容 list
QVariantList nameList;
nameList << "xiaoming" << "xiaolong" << "xiangliang";
QVariantList ageList;
ageList << 11 << 22 << 33;
QVariantList scoreList;
scoreList << 59 << 69 << 79;
//給字段綁定相應的值,按順序綁定,否則會出問題
query.addBindValue(nameList);
query.addBindValue(ageList);
query.addBindValue(scoreList);
//執行預處理命令
query.execBatch();
*/
//oracle風格
//佔位符 : + 自定義名字
query.prepare("iinsert into student(name,age,score) values(:name,:age,:score);");
QVariantList nameList;
nameList << "xiaoA" << "xiaoB" << "xiangC";
QVariantList ageList;
ageList << 21 << 12 << 43;
QVariantList scoreList;
scoreList << 49 << 29 << 59;
//給字段綁定
query.bindValue(":name",nameList);
query.bindValue(":age",ageList);
query.bindValue(":score",scoreList);
//執行預處理命令
query.execBatch();
...
數據庫刪除和遍歷
留坑
可視化操作數據庫
留坑