码上通QT实战27--系统设置02-加载用户列表

1、前言

SQLite 简介

SQLite 是一款轻量级、嵌入式、零配置、开源免费关系型数据库 (和 MySQL、Oracle、SQL Server 同属关系型数据库,支持完整 SQL 语法),由 D. Richard Hipp 在 2000 年发布,官网:sqlite.org。它广泛应用于移动设备、嵌入式系统和小型应用程序中,以简洁高效著称。SQLite 以其简单性和便携性成为轻量级数据存储的首选方案,尤其适合资源受限或需要快速集成的场景。这是 SQLite 最核心的优势,也是它在 Qt 开发中绝对主流的原因。其特点有:

嵌入式数据库,无独立进程 / 服务:SQLite 是嵌入式 的,它不是一个独立的数据库服务 / 进程,而是一个数据库引擎库(.dll/.so) 。程序调用时,直接链接这个库即可,不需要安装、启动、配置任何数据库服务,没有服务端,只有客户端

单文件存储整个数据库:SQLite 的整个数据库(所有表、数据、索引、视图)都存储在一个独立的文件中 (后缀一般是 .db / .sqlite / .db3),这个文件可以:

  • 直接复制、移动、备份,跨平台通用(Windows/Linux/macOS/ 嵌入式设备);
  • 文件体积极小,空数据库文件只有几 KB,存储大量数据时体积也可控。

跨平台完美兼容:支持 Windows、Linux、macOS、Android、iOS、各种嵌入式系统,Qt+SQLite 开发的程序,数据库文件可以无缝在不同系统间迁移。几乎所有主流语言(如 Python、Java、C/C++)均提供 SQLite 接口。

✅ 适用场景(Qt 开发 100% 匹配):Qt 桌面软件(本地数据存储、配置存储、业务数据存储)、嵌入式程序、移动端 App、小型工具类软件;

Navicat Premium 是由 PremiumSoft 开发的跨平台一体化数据库管理与开发工具,可在单一界面同时连接管理关系型、非关系型及云数据库,对 Qt+SQLite 开发而言,是高效管理本地 / 云数据库、数据迁移与可视化建模的核心工具

2、开始干

1、用户表users

这个数据库文件要放在项目编译目录下,注意这一点,当然也可以将编译输出目录放在别的地方,总之要跟着改变。

SQLite 官方推荐的 5 种原生类型(常用)

定义表字段时,优先用这 5 种,兼容性最好,Qt 中读取数据也最方便:

  1. NULL:空值

  2. INTEGER:整型(可存储 1/2/4/8 字节整数,自动适配,支持正负整数)

  3. REAL:浮点型(存储浮点数、小数,等价于 C++ 的 double)

  4. TEXT:文本字符串(支持 UTF-8 编码,Qt 中最常用,存储中文无乱码)

  5. BLOB:二进制数据(存储图片、文件、序列化数据等,原样存入,原样取出,无编码转换)

2、连接数据库

QApplication::applicationDirPath() 是 Qt 框架中的一个静态方法,用于获取当前应用程序的可执行文件所在的目录路径。该方法返回一个 QString 类型的路径字符串。

典型应用场景

  • 加载同目录下的配置文件(如 appDir + "/config.ini")。

  • 访问同级目录的子文件夹(如 appDir + "/data/resources")。

  • 动态库(DLL/SO)的加载路径设置。

3、绑定列值

QTableWidgetItem 是 Qt 框架中用于在 QTableWidget 中存储和显示数据的类。它代表表格中的一个单元格项,可以存储文本、图标、复选框等内容,并支持自定义样式和编辑行为。

cpp 复制代码
QTableWidgetItem *item = new QTableWidgetItem("Sample Text");
tableWidget->setItem(row, column, item);

通过以上方法,可以灵活使用 QTableWidgetItem 实现丰富的表格功能。

cpp 复制代码
#include "settingsview.h"
#include "ui_settingsview.h"

SettingsView::SettingsView(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::SettingsView)
{
    ui->setupUi(this);
    //设置"描述"这一列内容的模式为自动平铺,即第11列
    ui->tw_users->horizontalHeader()->setSectionResizeMode(11,QHeaderView::Stretch);
    //数据初始化
    DB=QSqlDatabase::addDatabase("QSQLITE");
    DB.setDatabaseName(QApplication::applicationDirPath()+"/data.db3");
    if(!DB.open()){
        //ui->lbl_error->setText("数据库对象初始化失败");
    }

    this->refresh();
}

SettingsView::~SettingsView()
{
    delete ui;
}

//刷新
void SettingsView::refresh()
{
    if(!DB.isOpen()&&!DB.open()){
        return;
    }

    QSqlQuery query_user("select * from users");

    int row=0;
    //遍历查询结果,将每一行数据赋给表格控件
    while(query_user.next()){
        ui->tw_users->setRowCount(row+1);//产生一个新行
        //编号id
        QString rid = query_user.value(0).toString();
        QTableWidgetItem *item = new QTableWidgetItem(rid);
        item->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
        ui->tw_users->setItem(row, 0, item);//给当前行设置对象
        //用户id
        QString uid = query_user.value(1).toString();
        QTableWidgetItem *item2 = new QTableWidgetItem(uid);
        item->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
        ui->tw_users->setItem(row, 1, item2);//给当前行设置对象
        //用户名称
        QString name = query_user.value(2).toString();
        item = new QTableWidgetItem(name);
        item->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
        ui->tw_users->setItem(row, 2, item);
        //用户名
        QString real = query_user.value(3).toString();
        item = new QTableWidgetItem(real);
        item->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
        ui->tw_users->setItem(row, 3, item);
        //密码
        QString pwd = query_user.value(4).toString();
        item = new QTableWidgetItem(pwd);
        item->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
        ui->tw_users->setItem(row, 4, item);
        //班组
        QString group = query_user.value(5).toString();
        item = new QTableWidgetItem(group);
        item->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
        ui->tw_users->setItem(row, 5, item);
        //职务
        QString post = query_user.value(6).toString();
        item = new QTableWidgetItem(post);
        item->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
        ui->tw_users->setItem(row, 6, item);
        //部门
        QString dept = query_user.value(7).toString();
        item = new QTableWidgetItem(dept);
        item->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
        ui->tw_users->setItem(row, 7, item);
        //用户性别
        QString gender = query_user.value(8).toString();
        gender=gender=="1"?"男":"女";
        item = new QTableWidgetItem(gender);
        item->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
        ui->tw_users->setItem(row, 8, item);
        //年龄
        QString age = query_user.value(9).toString();
        item = new QTableWidgetItem(age);
        item->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
        ui->tw_users->setItem(row, 9, item);
        //手机
        QString phone = query_user.value(10).toString();
        item = new QTableWidgetItem(phone);
        item->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
        ui->tw_users->setItem(row, 10, item);
        //描述
        QString desc = query_user.value(11).toString();
        item = new QTableWidgetItem(desc);
        item->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
        ui->tw_users->setItem(row, 11, item);

        ++row;
    }
}

4、运行效果

cpp 复制代码
#include "mainwindow.h"
#include "systemutils.h"
#include "ui_mainwindow.h"
#include <QMessageBox>
#include <QMouseEvent>
#include <QThread>
#include <QtConcurrent/QtConcurrentRun>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 1.设置窗口无边框和窗口透明
    this->setWindowFlags(Qt::FramelessWindowHint);
    this->setAttribute(Qt::WA_TranslucentBackground);


    // 设置相关阴影效果
    SystemUtils * utils=new SystemUtils();
    utils->SetDropShadowEffect(ui->wdg_root,Qt::gray,10);

    //设置关闭,最小,最大的图标
    // 设置相关字体图标
    QFont font=QFont(QString("zx_icons"),9);
    // 关闭
    ui->pb_close->setFont(font);
    ui->pb_close->setText(QChar(0xe653));
    // 最大化
    ui->pb_max->setFont(font);
    ui->pb_max->setText(QChar(0xe694));
    // 最小化
    ui->pb_min->setFont(font);
    ui->pb_min->setText(QChar(0xe7e6));
    //设置第二行消息的图标
    ui->lbl_message_icon->setFont(font);
    ui->lbl_message_icon->setText(QChar(0xe7ff));

    //设置标题字体
    QFont font2=QFont(QString("钉钉进步体"),14);
    ui->lbl_title->setFont(font2);

    //设置"实时监控"按钮的信号和槽函数
    connect(ui->nb_monitor,SIGNAL(onClicked(int)),SLOT(onNavClicked(int)));
    //设置"趋势图表"导航按钮的信号和槽函数
    connect(ui->nb_trend,SIGNAL(onClicked(int)),SLOT(onNavClicked(int)));
    //设置"异常报警"导航按钮的信号和槽函数
    connect(ui->nb_alarm,SIGNAL(onClicked(int)),SLOT(onNavClicked(int)));
    //设置"系统设置"导航按钮的信号和槽函数
    connect(ui->nb_settings,SIGNAL(onClicked(int)),SLOT(onNavClicked(int)));

    //绑定监控页面Monitor的信号和槽函数关联,即monitor页面中的信号函数onConnect由本页面的槽函数onConnect响应处理
    connect(ui->Monitor,SIGNAL(onConnect(QString,QString,QString,QString,QString)),SLOT(onConnect(QString,QString,QString,QString,QString)));
    //绑定监控页面Monitor的信号和槽函数关联,即monitor页面中的信号函数onTextSend由本页面的槽函数onTextCompleted响应处理
    connect(ui->Monitor,SIGNAL(onTextSend(QString)),this,SLOT(onTextCompleted(QString)));
    //绑定监控页面Monitor的信号和槽函数关联,即monitor页面中的信号函数onStatusSend由本页面的槽函数onStatusCompleted响应处理
    connect(ui->Monitor,SIGNAL(onStatusSend(bool,int)),this,SLOT(onStatusCompleted(bool,int)));




    //创建线程及对象,处理串口
    thread=new QThread();
    worker=new QWorker();
    //1、本页面的信号函数onOpen由worker对象的槽函数openPort响应处理
    connect(this,SIGNAL(onOpen(QString,QString,QString,QString,QString)),worker,SLOT(openPort(QString,QString,QString,QString,QString)));
    //2、worker对象中的信号函数openCompleted由本页面的onSetopenState槽函数响应处理
    connect(worker,SIGNAL(openCompleted(bool)),this,SLOT(onSetopenState(bool)));
    //3、本页面的信号函数onSend由worker对象的槽函数sendData响应处理
    connect(this,SIGNAL(onSend(QByteArray,int)),worker,SLOT(sendData(QByteArray,int)));
    //4、worker对象中的信号函数sendCompleted由本页面的槽函数onSendCompleted响应处理
    connect(worker,SIGNAL(sendCompleted(QByteArray,int)),this,SLOT(onSendCompleted(QByteArray,int)));



    worker->moveToThread(thread);//把对象worker放在多线程中
    thread->start();//启动线程

    //f1=QtConcurrent::run(this,&MainWindow::onMonitor);//这是qt5.X的写法,这个写法在6.x中不支持
    //实例化异步任务
    f1 = QtConcurrent::run([this]() {
        this->onMonitor(); // 替换为你的成员函数名
    });
}

MainWindow::~MainWindow()
{
    delete ui;
}

//监控事件
void MainWindow::onMonitor()
{
    while(this->isMonitor){
        if(m_isConnected){
            // 从设备中获取到相关数据
            QByteArray bytes;
            bytes.resize(6);
            bytes[0]=0x01;//设备地址
            bytes[1]=0x03;//功能码
            bytes[2]=0x00;//开始地址
            bytes[3]=0x00;
            bytes[4]=0x00;//请求数量
            bytes[5]=0x03;

            // CRC16算法
            auto crc=this->CRC16(bytes);
            uint8_t hi=uint8_t(crc>>8);
            uint8_t lo=uint8_t(crc);
            bytes.resize(8);
            bytes[6]=lo;//crc校验码
            bytes[7]=hi;

            // 需要给QWorker对象发送一个信号
            emit onSend(bytes,1);//向传感器发送信号

            QByteArray bytes2;
            bytes2.resize(6);
            bytes2[0]=0x01;//设备地址
            bytes2[1]=0x01;//功能码
            bytes2[2]=0x00;//开始地址
            bytes2[3]=0x00;
            bytes2[4]=0x00;//请求数量
            bytes2[5]=0x05;

            // CRC16算法
            auto crc2=this->CRC16(bytes2);
            uint8_t hi2=uint8_t(crc2>>8);
            uint8_t lo2=uint8_t(crc2);
            bytes2.resize(8);
            bytes2[6]=lo2;//crc校验码
            bytes2[7]=hi2;

            emit onSend(bytes2,2);//向灯珠发送信号
        }
        QThread::msleep(500);
    }
}

//鼠标按下事件
void MainWindow::mousePressEvent(QMouseEvent *event){
    if (event->button() == Qt::LeftButton) {  // 如果按下左边按钮
        m_drag = true;//表示要移动
        // 获取当前光标的位置
        m_dragPos = event->pos();
        // 当前鼠标点相对于桌面屏幕左上角的坐标(0,0),全局坐标;
        m_resizeDownPos = event->globalPosition().toPoint();
        // 获取当前窗口的相关参数,包括位置,大小等等各种参数
        m_mouseDownRect = this->rect();
    }
}

//鼠标移动事件
void MainWindow::mouseMoveEvent(QMouseEvent *event){
    // 如果是鼠标在拖动时,当前窗口是全屏,不做任何处理
    if (isFullScreen()) {
        return;
    }
    if (m_move) {
        move(event->globalPosition().toPoint() - m_dragPos);
        return;
    }

    setCursor(Qt::ArrowCursor);
    if (m_drag && (event->buttons() & Qt::LeftButton)) {
        m_move = true;
        move(event->globalPosition().toPoint() - m_dragPos);
    }
}

//鼠标释放事件
void MainWindow::mouseReleaseEvent(QMouseEvent *event){
    Q_UNUSED(event)
    m_drag = false;
    if (m_move) {
        m_move = false;
    }
    setCursor(Qt::ArrowCursor);
}

//关闭事件
void MainWindow::on_pb_close_clicked()
{
    QMessageBox::StandardButton ret=QMessageBox::question(this,tr("关闭系统运行"),tr("您确定要退出系统吗?"),QMessageBox::Yes | QMessageBox::No,QMessageBox::Yes );
    if(ret== QMessageBox::Yes){
        this->isMonitor=false;
        this->close();
    }
}

//最大化
void MainWindow::on_pb_max_clicked()
{
    this->showFullScreen();  // 全屏展示
}

//最小化
void MainWindow::on_pb_min_clicked()
{
    this->showMinimized();
}

//窗体双击事件
void MainWindow::mouseDoubleClickEvent(QMouseEvent *event)
{
    Q_UNUSED(event); // 标记参数未使用,消除警告
    if (isFullScreen()) {
        showNormal();  // 如果是全屏,就恢复到非全屏
    } else {
        showFullScreen();  // 否则就变成全屏的
    }
}

//导航按钮的点击响应事件
void MainWindow::onNavClicked(int index)
{
    ui->sw_pages->setCurrentIndex(index);
}

//连接串口设备
void MainWindow::onConnect(QString port, QString baud, QString parity, QString data, QString stop)
{
    //执行串口对象的连接动作,动作必须在后台线程中处理
    //触发信号,让qworker这个对象来接收信号
    emit onOpen(port,baud,parity,data,stop);
}

//串口连接状态
void MainWindow::onSetopenState(bool state)
{
    m_isConnected=true;
    //接收到worker对象中的打开状态信号
    ui->Monitor->setOpenState(state);
}

//计算CRC校验码
uint16_t MainWindow::CRC16(QByteArray bytes)
{
    int len=bytes.size();
    uint16_t wcrc=0XFFFF;//预置16位crc寄存器,初值全部为1
    uint8_t temp;//定义中间变量
    int i=0,j=0;//定义计数

    for(i=0;i<len;i++)//循环计算每个数据
    {
        temp=bytes.at(i);
        wcrc^=temp;
        for(j=0;j<8;j++){
            //判断右移出的是不是1,如果是1则与多项式进行异或。
            if(wcrc&0X0001){
                wcrc>>=1;//先将数据右移一位
                wcrc^=0XA001;//与上面的多项式进行异或
            }
            else//如果不是1,则直接移出
                wcrc>>=1;//直接移出
        }
    }
    temp=wcrc;//crc的值
    return wcrc;
}


//发送完成后
void MainWindow::onSendCompleted(QByteArray bytes, int flag)
{
    if(flag==1){
        if(bytes.length()>3&&(bytes[1]&0x80)==0){//这个条件说明返回的数据没有问题,这里只是一个简单的判断
            bool ok;
            //截取指定位置指定长度的数据,转换成16进制,再转换成10进制
            uint16_t temp=bytes.mid(3,2).toHex().toUInt(&ok,16);
            uint16_t humi=bytes.mid(5,2).toHex().toUInt(&ok,16);
            uint16_t bright=bytes.mid(7,2).toHex().toUInt(&ok,16);
            // qDebug()<<"温度:"<<temp;
            // qDebug()<<"湿度:"<<humi;
            // qDebug()<<"亮度:"<<bright;
            ui->Monitor->setValue(temp,humi,bright);//设置监控页面数据值
            ui->Trend->onDataReceive(temp,humi,bright);//设置趋势页面数据值
        }
    }else if(flag==2){
        //处理灯珠状态,如果不是写的命令,即是读的命令时才执行读取灯珠状态
        if(!m_isWriting&&bytes.length()>3&&(bytes[1]&0x80)==0){
            bool ok;
            Q_UNUSED(ok); // 标记参数未使用,消除警告
            //解析状态数据
            bool s1=bytes[3]&1;
            bool s2=bytes[3]&2;
            bool s3=bytes[3]&4;
            bool s4=bytes[3]&8;
            bool s5=bytes[3]&16;
            ui->Monitor->setStatus(s1,s2,s3,s4,s5);//将数据交给界面显示
        }
        if(this->m_isWriting){
            m_isWriting=false;//修改写的状态为假,即读
        }
    }
}

//发送指令
void MainWindow::onTextCompleted(QString text)
{
    QByteArray data(text.toLatin1());
    uint16_t len=data.size();
    uint16_t count=len/2;
    if(len%2>0) count+=1;


    // 将前面文本数据拼接在一起发给设备
    QByteArray req;
    req.resize(7);
    req[0]=0x01;
    req[1]=0x10;
    req[2]=0x00;
    req[3]=0x08;
    req[4]=uint8_t(count>>8);
    req[5]=uint8_t(count);
    req[6]=len;
    req.append(data);

    auto crc=this->CRC16(req);
    uint8_t hi=uint8_t(crc>>8);
    uint8_t lo=uint8_t(crc);
    req.append(lo);
    req.append(hi);

    // 将指定数据指令发到QWorker里处理
    emit onSend(req,3);
}

//灯珠状态发送指令完成
void MainWindow::onStatusCompleted(bool state, int index)
{
    this->m_isWriting=true;

    QByteArray bytes;
    if(index==5){// 灯珠统一设置的命令
        char l1=state?0x01:0x00;
        char l2=state?(0x01<<1):0x00;
        char l3=state?(0x01<<2):0x00;
        char l4=state?(0x01<<3):0x00;
        char l5=state?(0x01<<4):0x00;

        bytes.resize(8);
        bytes[0]=0x01;
        bytes[1]=0x0F;
        bytes[2]=0x00;
        bytes[3]=0x00;
        bytes[4]=0x00;
        bytes[5]=0x05;
        bytes[6]=0x01;
        bytes[7]=0x00|l1|l2|l3|l4|l5;

        auto result=this->CRC16(bytes);
        uint8_t hi=uint8_t(result>>8);
        uint8_t lo=uint8_t(result);
        bytes.append(lo);
        bytes.append(hi);
    }
    else {// 单个灯珠设置的命令
        bytes.resize(6);
        bytes[0]=0x01;
        bytes[1]=0x05;
        bytes[2]=0x00;
        bytes[3]=uint8_t(index);
        bytes[4]=state?0xFF:0x00;
        bytes[5]=0x00;

        auto result=this->CRC16(bytes);
        uint8_t hi=uint8_t(result>>8);
        uint8_t lo=uint8_t(result);
        bytes.append(lo);
        bytes.append(hi);
    }
    emit onSend(bytes,3);
}

5、小结

SQLite,Navicat Premium,QTableWidget 与 QTabWidget ,QTableView ,QTableWidgetItem,QApplication::applicationDirPath()

复制代码
原创不易,打字不易,截图不易,撸码不易,整理不易,走过路过,不要错过,欢迎点赞,收藏,转载,复制,抄袭,留言,灌水,请动动你的金手指,祝您早日实现财务自由。

.

相关推荐
木木木一2 小时前
Rust学习记录--C0 总目录
开发语言·学习·rust
蕨蕨学AI2 小时前
【Wolfram语言】46 音频与视频
开发语言·wolfram
Microsoft Word2 小时前
HashMap面试题总结
java·开发语言
ekkcole2 小时前
java实现对excel文件合并单元格(只针对文件)
java·开发语言·excel
lihao lihao2 小时前
C++ set和map
开发语言·c++·算法
小陈phd2 小时前
langGraph从入门到精通(三)——基于LangGraph的智能问答系统开发:Python单代理架构实战
开发语言·python·架构
@zulnger2 小时前
Django 框架
数据库·django·sqlite
电子_咸鱼2 小时前
Linux IPC 实战:管道与共享内存的使用场景 + 底层原理全剖析
linux·运维·服务器·开发语言·网络·vscode·qt
smile_5me2 小时前
RK3588 csm400b调试记录
c语言·开发语言