Qt學習第5天:線程與數據庫【筆記】

1. 線程

爲什麼需要使用線程

  1. 當界面中處理很複雜的數據時,可能會造成界面未響應,這時可以把數據處理放在線程中來處理
  2. 多任務處理

線程使用

主線程: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中的線程
  1. 設定一個類,繼承於QObject
  2. 類中設置一個線程函數(只有一個是線程函數)
    在這裏插入圖片描述
    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();
    
...

數據庫刪除和遍歷

留坑

可視化操作數據庫

留坑

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章