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();
    
...

数据库删除和遍历

留坑

可视化操作数据库

留坑

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐