QT学生管理系统 开发文档
目录
第一章 UI界面设计与开发
登录界面
主界面
UI美化
第二章 数据库设计与开发
数据库设计
连接数据库
数据库功能设计
sql语句设计
查询所有学生数量
查询第几页学生数据
删除学生
修改学生信息
清空学生表
添加单个用户
删除单个用户
修改用户权限
查询所有用户
查询用户是否存在
数据库函数设计
sql函数设计
学生表操作
Init()
getStuCnt()
getPageStu()
addStu()
delStu()
updateInfo()
clearStuInfo()
用户表操作
addUser()
delUser()
UpdateUserAut()
getAllUser()
isExists()
模拟数据
创建单例
增
删
查询
修改
清空学生表
优化
打包
样式打包
数据库打包
第一章 UI界面设计与开发
登录界面
第一步先做登录界面
新建一个文件,命名为Page_login.
在MainWindows.h头文件里声明一个Page_login类型的变量m_dlgloin:
先放置控件:
设置控件属性
然后我们将用户名输入和密码输入框设置成为默认显示:
将密码输入框的模式改为密码模式:
将用户名和密码输入长度限制为8位:
在MainWindows.cpp文件里调用我们我们m_dlogin窗口,并且把主窗口阻塞不显示:
初步登录界面如下:
右击按钮选择转到槽:会自动生成框架:
我们接下来登录和退出的逻辑
退出
exit(0);
登录
登录一般是在数据库查找用户名和密码,查找到了就发送一个signal显示成功了:
如果失败就提示登录失败.
在Mainwindows.cpp里写下面这段代码,表示如果主界面捕获到来自登录界面登录成功的信号,那么就跳到主界面:
主界面
主界面放置如下控件:效果图如下:
改变一下背景色
往treewidget里添加信息
需要用到的函数:
所以我们需要new一个 QTreeWidgetItem 类型对象.QTreeWidgetItem 的构造函数如下:第一个参数要为自身,第二个参数想用字符串的话只能构造一个qstringlist类型的参数
效果图:
让文字居左显示(把indentation设为小一点):
构造两个子集目录:学生管理,管理员管理
效果图如下:
数据模拟
把我们之前的stackwidget设置一个背景色,然后在stackwidget里加一个tablewidget控件,给tablewidget加几列数据,效果图如下:
UI美化
在Mainwindows.cpp里面的Widget类里面有一个keyPressEvent()键盘响应事件函数,它的声明如下:
virtual void keyPressEvent(QKeyEvent *event);
我们在Mainwindows.h文件里进行声明,然后在Mainwindows.cpp文件进行定义.定义如下:
我们通过f6键进行换肤,首先提前新建一个stucss.css文件,在里面我们要换的样式写好,然后在我们按完F6之后将其加载进我们的UI界面
void MainWindow::keyPressEvent(QKeyEvent *event)
{if(event->key()==Qt::Key_F6){QFile f;auto str=QCoreApplication::applicationDirPath(); //获取application的路径f.setFileName(str+"//"+"stucss.css"); //将路径后加上我们皮肤的路径f.open(QIODevice::ReadOnly); //以只读方式打开QString str2=f.readAll(); //读取this->setStyleSheet(str2); //设置样式m_dlgloion.setStyleSheet(str2);//采用样式}
}
stucss.css代码如下:
/* 登录界面 */
QPushButton{color:white;background-color:#34495e;border-radius:6;} /*按钮*/
QLabel{color:#34495e;} /*标签*/
QLabel#lb1{color:#34495e;font-size: 22;} /*用户名标签*/
QLabel#lb2{color:#34495e;font-size: 22;} /*密码标签*/
QLineEdit{border: 2px solid #bdc3c7; min-height:22; border-radius: 6px;}/*搜索框 *//* 主界面 */
/* Widget#background-color:#34495e; */
QLabel[lab="main"]{color:white;}
QWidget#widget[bg_title="main"]{background-color:#34495e;}
QWidget#widget_3{background-color:#34495e;}
QPushButton[btn="main"]{font-family:"微软雅黑";min-height:22px;min-width:80px;background-color: #16a085;border-radius:6; } /*主界面按钮*/
QPushButton[btn="main"]:pressed{font-family:"微软雅黑";min-height:22px;min-width:80px;background-color: #2a5d53;border-radius:6; } /*按钮按压变色*//*左侧界面样式*/
QTreeWidget{font-family:"宋体";font-size:15px;background-color: rgba(52, 73, 94, 150); color:white;} QStackedWidget{border:1px solid gray;} 边框/*右侧界面样式*/
QHeaderView::section
{ font-size:16px;font-family: "宋体";text-align:center; height:30px; color:#909399;font-weight: bold;border:1px solid gray;border-left:none;/* border-bottom-color: rgb(168, 171, 180); */
}QTableWidget::item
{font:14px "微软雅黑";color:#667483;border:none;
}
优化完之后的效果如下所示:
第二章 数据库设计与开发
数据库设计
新建两张表,sql语句如下:
学生表
CREATE TABLE "student" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,"name" TEXT,"age" integer,"grade" integer,"class" integer,"studentid " integer,"phone" TEXT,"wechat" TEXT
);
用户表
CREATE TABLE "username" ("username" TEXT,"password" TEXT,"auth" TEXT
);
此时数据库里就有两张表了
创建一个新的基于OBject类的头文件,命名为stusql.cpp
在这个文件进行数据库操作
连接数据库
连接数据库的模板如下:
#include "stusql.h"
#include <QMessageBox>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>stusql::stusql(QObject *parent ): QObject {parent}
{// 检查是否有可用的数据库驱动if (QSqlDatabase::drivers().isEmpty()) {// 确保 stusql 类是 QObject 的子类,否则需要传递一个有效的 QWidget 指针或 nullptrQMessageBox::information(nullptr, tr("No database drivers found"),tr("This demo requires at least one Qt database driver. ""Please check the documentation how to build the ""Qt SQL plugins."));return;}QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); // 创建一个数据库连接db.setDatabaseName("./data.db");if (!db.open()) {qDebug() << "数据库文件加载失败: " << db.lastError().text();// 这里可能需要处理错误,比如显示一个消息框或者记录日志return; // 数据库打开失败,构造函数不应该继续执行}qDebug() << "打开数据库文件成功";
此时构建完之后控制台显示
然后我们可以向数据库里插一条数据看一下是否可以插入成功:
语句如下:
QSqlQuery q("", db);if (!q.exec("INSERT INTO student VALUES (NULL ,'张三',12,3,2,1,15518978071,'aasDASD')")) {qDebug() << "插入数据失败: " << q.lastError().text();// 这里应该处理SQL执行错误}
此时构建完之后控制台显示:
我们现在可以让插入失败的原因可视化的显示出来:
if (!q.exec("INSERT INTO student VALUES (NULL ,'张三',12,3,2,1,15518978071,'aasDASD')")) {QMessageBox::critical(nullptr, tr("Database Error"),tr("Failed to insert data: %1").arg(q.lastError().text()));// 显示具体的错误信息}
这样的话就会有一个断言:显示表不存在
我把路径换成绝对路径就可以了:
db.setDatabaseName("D:\\QTproject\\stumanger\\data.db");
现在数据库里student表就被插入一条数据了:
数据库功能设计
sql语句设计
查询所有学生数量
select count(id) from student;
查询第几页学生数据
offset代表从第几条记录之后"开始"查询,limit表示查询多少条结果
select * from student order by id limit 2 offset 1;
删除学生
delete from student where id = 1;
修改学生信息
update student set name = 'asd' where id=25;
清空学生表
delete from student
添加单个用户
INSERT INTO "username" VALUES ('admin', 111, 'admin');
删除单个用户
delete from username where username='admin';
修改用户权限
update username set auth='user';
查询所有用户
select * from username
查询用户是否存在
select *from username where username='admin';
数据库函数设计
实现基础的数据库增删查改功能,在stusql.h文件里列出以下声明:
首先创建两个结构体
在stusql.cpp文件里实现定义:
sql函数设计
学生表操作
Init()
在初始化的时候进行数据库连接
void stusql::Init()
{m_db = QSqlDatabase::addDatabase("QSQLITE"); // 创建一个数据库连接m_db.setDatabaseName("D:\\QTproject\\stumanger\\data.db");#if 0auto str=QCoreApplication::applicationDirPath(); //获取一下数据库的路径,目的是为了发布时找到数据库将其放于当前目录下qDebug()<<str;
#endifif (!m_db.open()) {QMessageBox::critical(nullptr, tr("Database Error"),tr("Failed to open database: %1").arg(m_db.lastError().text()));return; // 数据库打开失败,构造函数不应该继续执行}qDebug() << "打开数据库文件成功";
}
getStuCnt()
quint32 stusql::getStuCnt()
{QSqlQuery sql(m_db);sql.exec("select count(id) from student;");quint32 Cnt=0;while(sql.next()){Cnt=sql.value(0).toUInt();}qDebug() << "学生数量: " << Cnt;return Cnt;
}
getPageStu()
QList<StuInfo> stusql::getPageStu(quint32 page, quint32 Cnt)
{QList<StuInfo> l;QSqlQuery sql(m_db);QString strsql=QString("select * from student order by id limit %1 offset %2;").arg(Cnt).arg(page*Cnt);sql.exec(strsql);StuInfo info;while(sql.next()){info.id=sql.value(0).toUInt();info.stu_name=sql.value(1).toString();info.stu_age=sql.value(2).toUInt();info.stu_grade=sql.value(3).toUInt();info.stu_class=sql.value(4).toUInt();info.stu_id=sql.value(5).toUInt();info.stu_phone=sql.value(6).toString();info.stu_wechat=sql.value(7).toString();}l.push_back(info);// qDebug()<<l.begin()->id<<l.begin()->stu_name<<l.begin()->stu_age;return l;
}
addStu()
添加记录
bool stusql::addstu(StuInfo info)
{QSqlQuery sql(m_db);QString strsql=QString("insert into stdent values(null,'%1',%2,%3,%4,%5,'%6','%7')").arg(info.stu_name).arg(info.stu_age).arg(info.stu_grade).arg(info.stu_class).arg(info.stu_id).arg(info.stu_phone).arg(info.stu_wechat);if(!sql.exec(strsql)){QMessageBox::critical(nullptr, tr("Database Error"),tr("Failed to insert data: %1").arg(sql.lastError().text()));// 显示具体的错误信息return false;}return true;
}
delStu()
bool stusql::delStu(int id)
{QSqlQuery sql(m_db);QString strsql=QString("delete from student where id= %1;").arg(id);if(sql.exec(strsql)){return true;}else qDebug()<<"删除失败!";return false;
}
updateInfo()
修改学生信息
bool stusql::updateInfo(StuInfo info)
{QSqlQuery sql(m_db);QString strsql=QString("update student set name = '%1' where id=%2;").arg(info.stu_name).arg(info.stu_id);bool res=sql.exec(strsql);QSqlError e=sql.lastError();if(sql.isValid()){qDebug()<<e.text();}return res;
}
发现怎么修改都无法修改,后来发现是建表的问题(字段名后面有空格)
clearStuInfo()
清空学生表
void stusql::clearStuInfo()
{QSqlQuery sql(m_db);QString strsql=QString("delete from student");sql.exec(strsql);
}
在表设计视图里面把这些空格删掉就可以了
用户表操作
addUser()
//添加单个用户
bool stusql::addUser(UserInfo info)
{QSqlQuery sql(m_db);QString strsql = QString("INSERT INTO username VALUES ('%1', '%2', '%3');").arg(info.username).arg(info.passwd).arg(info.auth);if(!sql.exec(strsql)){qDebug()<<"ERROR:"<<sql.lastError();return false;}return true;
}
delUser()
bool stusql::delUser(QString userName)
{QSqlQuery sql(m_db);QString strsql =QString("delete from username where username= '%1'").arg(userName);if(!sql.exec(strsql)){qDebug()<<"ERROR:"<<sql.lastError();return false;}return true;
}
UpdateUserAut()
bool stusql::UpdateUserAut(QString str1, QString str2)
{QSqlQuery sql(m_db);// 使用参数化查询来避免SQL注入QString strsql = "UPDATE username SET auth = :auth WHERE username = :username;";// 绑定参数sql.prepare(strsql);sql.bindValue(":auth", str1);sql.bindValue(":username", str2);if (!sql.exec()) {qDebug() << "ERROR:" << sql.lastError();return false;}// 检查是否有行被更新if (sql.numRowsAffected() > 0) {return true;} else {// 没有行被更新,可能是没有找到匹配的用户名qDebug() << "No rows updated. User may not exist.";return false;}
}
getAllUser()
QList<UserInfo> stusql::getAllUser()
{QList<UserInfo> l; // 使用正确的类型QSqlQuery sql(m_db);QString strsql = "select * from username"; // 假设表名正确// 执行SQL查询if (!sql.exec(strsql)) {// 处理错误,例如打印错误信息qDebug() << "Error executing query:" << sql.lastError();return l; // 返回空列表或根据需要进行错误处理}UserInfo info; // 在循环内部初始化while (sql.next()) {info.username = sql.value("username").toString(); // 假设列名为 "username"info.passwd = sql.value("passwd").toString(); // 假设列名为 "passwd"info.auth = sql.value("auth").toString(); // 假设列名为 "auth"l.push_back(info); // 在每次迭代中将info添加到列表info = UserInfo(); // 重置info对象(如果UserInfo有默认构造函数)}auto a=l.front();qDebug()<<a.username<<a.passwd<<a.auth;return l;
}
isExists()
bool stusql::isExists(QString Name)
{QSqlQuery sql(m_db);// 使用参数化查询来避免SQL注入,并且只查询需要的列QString strsql = "SELECT COUNT(*) FROM username WHERE username = :name;";// 绑定参数sql.prepare(strsql);sql.bindValue(":name", Name);if (!sql.exec()) {qDebug() << "ERROR:" << sql.lastError();return false;}// 检查是否有结果返回if (sql.next()) {// 检查COUNT(*)是否大于0if(sql.value(0).toLongLong() > 0)qDebug()<<"用户存在";else qDebug()<<"用户不存在";}return false;
}
造一行数据,我们发现左边会有一个自增id:我们可以通过取消一个属性将其去掉:
模拟数据
创建单例
创建单例的目的:
单例模式是qt开发的一种模式.在发布的时候有两种模式,一种是怎么把资源载入exe文件当中,还有一种就是在发布的时候引入外部资源,这种模式占用内容特别少,就是单例模式.
我们在stusql.h文件里添加单利模式的声明
static stusql *ptrstuSql;static stusql *getinstance(){if(ptrstuSql==nullptr){ptrstuSql=new stusql;}return ptrstuSql;}
在stusql.cpp文件里面添加对单例的定义:
stusql *stusql::ptrstuSql=nullptr;
我们创建一个QStringList类型对象mlNames,然后将1000个名字读入到mlNames里面.
然后我们在主界面添加一个按钮命名为btn_moni,然后添加定义,定义如下:
void MainWindow::on_btn_moni_clicked()
{QString l;//制作1000条学生数据QRandomGenerator g,c;g.seed(0);c.seed(0);for(int i=0;i<mlNames.size();i++){auto rand_grade=g.bounded(7,10);auto rand_class=g.bounded(1,10);StuInfo info;info.stu_name=mlNames[i];if(i%3){info.stu_age=14;}if(i%7){info.stu_age=13;}if(i%9){info.stu_age=18;}info.stu_grade=rand_grade;info.stu_class=rand_class;info.id=i;info.stu_phone="13463464347";info.stu_wechat="sfwegewg";m_ptrStusql->addstu(info);}}
此时我们的数据库里就有了我们模拟的数据了:
接下来我们需要将这些数据展现在我们的Qtablewidget上.
我们写一个Update()函数,内容如下:
//刷新数据
void MainWindow::updateTable()
{//把自带的表头删了,我们自己来写表头ui->tableWidget_2->clear();//必须要告诉编译器有多少列才能显示表头ui->tableWidget_2->setColumnCount(8);QStringList l;//自定义表头l<<"序号"<<"姓名"<<"年龄"<<"年纪"<<"班级"<<"学号"<<"电话"<<"微信";ui->tableWidget_2->setHorizontalHeaderLabels(l); //设置显示表头ui->tableWidget_2->setSelectionBehavior(QAbstractItemView::SelectRows); //当用户点击任何一个单元格时,整个行都会被选中//显示数据内容auto cnt=m_ptrStusql->getStuCnt(); //获取记录行数ui->lb_cnt->setText(QString("数量:%1").arg(cnt)); //显示学生数量QList<StuInfo> lStudents=m_ptrStusql->getPageStu(0,cnt); //获取一共有多少页数据ui->tableWidget_2->setRowCount(cnt); //设置行数//将数据刷新到tablewidget上for(int i=0;i<lStudents.size();i++){ui->tableWidget_2->setItem(i,0,new QTableWidgetItem(QString::number(i)));ui->tableWidget_2->setItem(i,1,new QTableWidgetItem(lStudents[i].stu_name));ui->tableWidget_2->setItem(i,2,new QTableWidgetItem(QString::number(lStudents[i].stu_age)));ui->tableWidget_2->setItem(i,3,new QTableWidgetItem(QString::number(lStudents[i].stu_grade)));ui->tableWidget_2->setItem(i,4,new QTableWidgetItem(QString::number(lStudents[i].stu_class)));ui->tableWidget_2->setItem(i,5,new QTableWidgetItem(QString::number(lStudents[i].stu_id)));ui->tableWidget_2->setItem(i,6,new QTableWidgetItem(lStudents[i].stu_phone));ui->tableWidget_2->setItem(i,7,new QTableWidgetItem(lStudents[i].stu_wechat));}
}
此时我们的数据就刷新出来了:
增
接下来我们实现这几个按钮的功能:
先来实现增加按钮的功能.创建一个dlg_addstu文件,dlg_addstu.ui如下:
然后我们还有个修改操作,就不单独设计UI界面,直接用dlg_addstu.ui的界面.
我们用一个全局变量m_isAdd,如果m_isAdd为True表示当前是添加操作,为false表示当前是修改操作.
dlg_addstu.ui界面的保存按钮和取消按钮定义如下:
增加操作是我们获取一下每个输入框输入的内容,把这些信息全部传给变量info,然后再把Info作为参数传给addStu()函数执行insert SQL语句.
//保存
void Dlg_AddStu::on_btn_save_clicked()
{StuInfo info; //把所有的字段读取到info里auto ptr=stusql::getinstance();info.id=m_info.id; //传idinfo.stu_name=ui->le_name->text();info.stu_age=ui->sb_age->text().toInt();info.stu_grade=ui->le_grade->text().toInt();info.stu_class=ui->le_class->text().toInt();info.stu_id=ui->le_id->text().toUInt();info.stu_phone=ui->le_phone->text();info.stu_wechat=ui->le_wechat->text();if(m_isAdd) //为true表示当前是添加操作{ptr->addstu(info);}else{ //为false表示当前是修改操作ptr->updateInfo(info);}QMessageBox::information(nullptr,"消息","添加成功");this->hide(); //点击保存之后窗口消失
}
void Dlg_AddStu::setType(bool isAdd,StuInfo info)
{m_isAdd=isAdd;m_info=info; //把info的所有信息都给中间变量m_info,包括idui->le_name->setText(info.stu_name);ui->sb_age->setValue(info.stu_age);ui->le_grade->setText(QString::number(info.stu_grade));ui->le_class->setText(QString::number(info.stu_class));ui->le_phone->setText(QString(info.stu_phone));ui->le_wechat->setText(QString(info.stu_wechat));
}
取消按钮
//取消
void Dlg_AddStu::on_btn_cancel_clicked()
{this->hide();
}
添加按钮(调出添加输入框)
//点击添加按钮弹出添加界面
void MainWindow::on_btn_add_clicked()
{m_dlgAddstu.setType(true); //表示true代表当前是添加操作,false代表当前是修改操作m_dlgAddstu.exec(); //模态对话框 当前界面不操作完不能操作其他界面// m_dlgAddstu.show(); //非模态对话框updateTable(); //添加完成之后刷新一下界面
}
删
//删除按钮的定义如下
void MainWindow::on_btn_del_clicked()
{int i=ui->tableWidget_2->currentRow(); //获取一下鼠标点击的记录(即需要删除的记录)if(i>=0) //如果要删除的记录是合法记录 (即不是不存在的记录),那么就进行删除{int id=ui->tableWidget_2->item(i,1)->text().toUInt(); //删除需要通过id删除,这里获取idm_ptrStusql->delStu(id); //传给我们定义的delStu()函数执行删除sql语句 updateTable(); //删除完之后刷新一下界面QMessageBox::information(nullptr,"信息","删除成功");}
}
查询
//搜索
void MainWindow::on_btn_seacher_clicked()
{QString SearchFile=ui->lb_search->text(); //获取搜索输入框输入的内容//如果输入框的内容为空就退出查询 if(SearchFile.isEmpty()){QMessageBox::information(nullptr,"信息","查询为空!");updateTable();return;}//必须要告诉编译器有多少列才能显示表头ui->tableWidget_2->clear();ui->tableWidget_2->setColumnCount(9);QStringList l;//自定义表头l<<"序号"<<"id"<<"姓名"<<"年龄"<<"年纪"<<"班级"<<"学号"<<"电话"<<"微信";ui->tableWidget_2->setHorizontalHeaderLabels(l); //设置显示表头ui->tableWidget_2->setSelectionBehavior(QAbstractItemView::SelectRows); //当用户点击任何一个单元格时,整个行都会被选中//显示数据内容auto cnt=m_ptrStusql->getStuCnt(); //获取记录行数ui->lb_cnt->setText(QString("数量:%1").arg(cnt)); //显示学生数量ui->tableWidget_2->setSelectionBehavior(QAbstractItemView::SelectRows);//单击选中一行ui->tableWidget_2->setEditTriggers(QAbstractItemView::NoEditTriggers);//使记录不可被直接编辑QList<StuInfo> lStudents=m_ptrStusql->getPageStu(0,cnt); //获取一共有多少页数据int index=0;//将数据刷新到tablewidget上for(int i=0;i<lStudents.size();i++){//如果查询不到这个人就不执行下面的操作if(!lStudents[i].stu_name.contains(SearchFile)){continue;}index++;ui->tableWidget_2->setItem(i,0,new QTableWidgetItem(QString::number(index)));ui->tableWidget_2->setItem(i,1,new QTableWidgetItem(QString::number(lStudents[i].id)));ui->tableWidget_2->setItem(i,2,new QTableWidgetItem(lStudents[i].stu_name));ui->tableWidget_2->setItem(i,3,new QTableWidgetItem(QString::number(lStudents[i].stu_age)));ui->tableWidget_2->setItem(i,4,new QTableWidgetItem(QString::number(lStudents[i].stu_grade)));ui->tableWidget_2->setItem(i,5,new QTableWidgetItem(QString::number(lStudents[i].stu_class)));ui->tableWidget_2->setItem(i,6,new QTableWidgetItem(QString::number(lStudents[i].stu_id)));ui->tableWidget_2->setItem(i,7,new QTableWidgetItem(lStudents[i].stu_phone));ui->tableWidget_2->setItem(i,8,new QTableWidgetItem(lStudents[i].stu_wechat));}//最后统计一下查询出来的人数ui->tableWidget_2->setRowCount(index); //设置行数
}
修改
//修改按钮
void MainWindow::on_btn_modify_clicked()
{StuInfo info; //把当前鼠标点击的记录的每个字段添加到info里int i=ui->tableWidget_2->currentRow(); //获取鼠标点击的行(即要修改的记录)if(i>=0){info.id=ui->tableWidget_2->item(i,1)->text().toUInt(); //获取id的值并保存在Info里info.stu_name=ui->tableWidget_2->item(i,2)->text();info.stu_age=ui->tableWidget_2->item(i,3)->text().toUInt();info.stu_grade=ui->tableWidget_2->item(i,4)->text().toUInt();info.stu_class=ui->tableWidget_2->item(i,5)->text().toUInt();info.stu_id=ui->tableWidget_2->item(i,6)->text().toUInt();info.stu_phone=ui->tableWidget_2->item(i,7)->text();info.stu_wechat=ui->tableWidget_2->item(i,8)->text();m_dlgAddstu.setType(false,info);m_dlgAddstu.exec();}updateTable(); //更新完之后把界面刷新一下
}
清空学生表
//清除学生表
void MainWindow::on_btn_StuClear_clicked()
{m_ptrStusql->clearStuInfo();updateTable();
}
优化
优化一下数据模拟的加载时间
也就是优化一下add()函数
我们用QList<StuInfo> l来接收所有的添加信息,再通过循环从l里读取每条记录进行,执行添加sql语句,再把事务打开.
//添加学生
bool stusql::addstu(QList<StuInfo> l)
{QSqlQuery sql(m_db);m_db.transaction(); //开启事务for(auto info:l){QString strsql = QString("INSERT INTO student VALUES (NULL, '%1', %2, %3, %4, %5, '%6', '%7')").arg(info.stu_name).arg(info.stu_age).arg(info.stu_grade).arg(info.stu_class).arg(info.stu_id).arg(info.stu_phone).arg(info.stu_wechat);if (!sql.exec(strsql)) {QMessageBox::critical(nullptr, tr("Database Error"),tr("Failed to insert data: %1").arg(sql.lastError().text()));return false; // 返回 false 表示插入失败}}m_db.commit();return true; // 返回 true 表示插入成功
}
打包
样式打包
把我们的皮肤样式复制两份到当前目录,分别重名为dlg.css,main.css:
在dlg.css里只保留登录界面的样式,在main.css里只保留主界面的样式
在资源文件里把我们的所有资源都载入:
然后在主界面初始化的时候把我们的皮肤加载上:
数据库打包
数据库路径问题.我们把1端口打开,用当前目录下的路径,不要用绝对路径:
然后我们以debug方式打包我们的exe文件,打开QT安装目录下bin目录,在路径栏里输入"cmd",然后在dos窗户输入:"windeployqt.exe":
然后把我们的exe文件拖上去就开始打包了:
打包完成之后双击我们的exe文件发现运行不了:
这是QT的一个bug,有几个文件是不会打包的,需要我们手动打包.我们去bin目录下找到三个lib开头的文件,将其复制到打包好的exe文件同级目录下:
相关文章:

QT学生管理系统 开发文档
目录 第一章 UI界面设计与开发 登录界面 主界面 UI美化 第二章 数据库设计与开发 数据库设计 连接数据库 数据库功能设计 sql语句设计 查询所有学生数量 查询第几页学生数据 删除学生 修改学生信息 清空学生表 添加单个用户 删除单个用户 修改用户权限 查询所有用户…...

【五大海内外高校支持】2024年数字经济与计算机科学国际学术会议(DECS2024)
大会官网:www.icdecs.net 大会时间:2024年9月20-22日 大会地点:中国-厦门 截稿日期:详情见官网 支持单位 马来西亚理工大学 北京科技大学经济管理学院 南京信息工程大学 马来西亚敦胡先翁大学 大会嘉宾 大会主席 罗航…...

VS项目打包成lib库并使用
一、新建一个静态库项目 一般要把项目设为Release模式 二、添加文件 将所需要打包的头文件、源文件添加到该静态库项目中 三、生成项目 生成成功后即可在Release文件夹出现找到相应的.lib文件 四、使用静态库 将静态库文件复制到项目文件夹中,然后在项目属性设…...

社交巨头与去中心化:解析Facebook在区块链的角色
区块链技术的兴起为多个行业带来了变革,而社交平台也正在经历这一波技术浪潮。作为全球最大的社交网络之一,Facebook(现名Meta Platforms)在区块链领域的探索展示了其如何应对去中心化趋势的挑战,并利用这一技术推动自…...

MyBatis(学习记录)
一、ORM ORM是“对象关系映射”(Object-Relational Mapping)的缩写。这是一种编程技术,用于将对象模型与关系型数据库系统之间的数据进行转换。在面向对象的编程语言中,数据通常以对象的形式表示;而在关系型数据库中&a…...

QSpinbox
加载图片资源 在项目中,选择新建,QT->Qt Resource File -choose ,命名为 image ,创建完成了,会在目录下生成一个image.qrc。我们资源文件,要放在image.qrc 的同级目录或子目录中。 常用的小功能 在左下方&#x…...

分布式服务架构[原理、设计与实践]学习笔记
地震是由不可抗力导致的,而事故与之不同,任何大的生产事故在发生之前都有迹可循,而且事故的发生并不是偶然的,我们应该善于从现象中总结规律,找到发现、止损和避免的方法 海恩法则 每一起严重事故的背后,必…...

PDF发票解析并将信息回填到前端(1)后端解析PDF
文章目录 参考文章技术栈需求解析发票类型 1. 最终项目结构1.1 说明 2. 相关代码2.1 导入相应的maven依赖2.2 实体类2.3 工具类2.4 三层架构controllerservicemapper 参考文章 参考文章 技术栈 SpringBootVue 需求 本文主要是实现提取发票中的部分内容,并实现自…...

C++练习之插入删除
#include <iostream> #include <ctime> #include <cstring> #include<cstdlib> using namespace std;typedef struct bookInfo {char name[10];float price;int num; }BOOKINFO;// p: 结构体数组首元素地址 // n: 有效图书数量 void Print(const BOOKI…...

【LeetCode】238. 除自身以外数组的乘积
除自身以外数组的乘积 题目描述: 给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请…...

Excel公式与函数(运算符,计算限制,错误检查)(一)
公式 公式概念 公式 是以“”号为引导,用过运算符按照一定的顺序组合进行数据运算处理的等式,函数 则是按特定算法执行计算的产生一个或一组结果的预定义的特殊公式。 公式组成要素 公式的组成要素为“”,运算符,单元格引用&a…...

用AI助手写程序
用AI帮助写程序究竟靠不靠谱,下面来测试一下: 在文心一言中输入:写一个C Windows API串口通信程序。结果如下: #include <windows.h> #include <iostream> // 串口配置 void ConfigureCommPort(HANDLE hComm) {…...

动手学深度学习7.2 使用块的网络(VGG)-笔记练习(PyTorch)
以下内容为结合李沐老师的课程和教材补充的学习笔记,以及对课后练习的一些思考,自留回顾,也供同学之人交流参考。 本节课程地址:25 使用块的网络 VGG【动手学深度学习v2】_哔哩哔哩_bilibili 本节教材地址:7.2. 使用…...

SolidityFoundry ERC4626
ERC4626简介 ERC4626 协议是一种用于代币化保险库的标准。 我们经常说 DeFi 是货币乐高,可以通过组合多个协议来创造新的协议; ERC4626 扩展了 ERC20 代币标准,旨在推动收益金库的标准化,它是 DeFi 乐高中的基础,它允…...

大模型时代的操作系统:融合 Rust 和大模型,打造 AI 操作系统
每次技术革命,无论是个人电脑、互联网还是移动设备,总是从硬件开始,然后演化到软件层。而操作系统是计算机系统的核心,没有它,计算机就只是一堆硬件,无法运行任何程序。 微软 CEO 萨蒂亚纳德拉曾将生成式 …...

【ML】为什么要做batch normlization,怎么做batch normlization
为什么要做batch normlization,怎么做batch normlization 1. batch normlization1.1 批量归一化是什么:1.2 为什么要做批量归一化: 2. feature normalization2.1 特征归一化是什么:2.2 为什么要做特征归一化: 3. batc…...

【C++指南】命名空间
💓 博客主页:倔强的石头的CSDN主页 📝Gitee主页:倔强的石头的gitee主页 ⏩ 文章专栏:《C指南》 期待您的关注 目录 一、命名空间的重要性 1. C语言中没有命名空间而存在的问题 2. C引入了命名空间解决的问题 3.…...

RocketMQ Dashboard安装
RocketMQ Dashboard 是一个基于 Web 的管理工具,用于监控和管理 RocketMQ 集群。它提供了一个用户友好的界面,使管理员能够轻松地查看和操作 RocketMQ 系统中的各种组件和状态。 主要功能包括: 集群管理: 监控和管理 NameServer 和 Broker …...

前端web开发HTML+CSS3+移动web(0基础,超详细)——第3天
目录 一,列表-无序和有序的定义列表 二,表格-基本使用与表格结构标签 三,合并单元格 四,表单-input标签 五,表单-下拉菜单 六,表单-文本域 七,表单-label标签 八,表单-按钮 …...

认识MySQL
目录 数据库是什么呢?MySQL 数据库是什么呢? 在我们开始学习MySQL之前,先来了解一下,什么是数据库呢?我相信此时很多人会说是管理数据的,完全正确!用数据库我们可以去存储大量的数据。我来给你…...

尚品汇-创建ES索引库(二十七)
目录: (1)商品检索功能介绍 (2)根据业务搭建数据结构 (3)nested 介绍 (4)搭建service-list服务 (5)构建实体与es mapping建立映射关系 &…...

设计模式-六大原则
概述 设计模式的六大原则是设计模式的基础,了解设计模式的原则,有利于设计模式实际应用的理解,在真实使用的时候,一般不太可能一个程序满足所有的设计模式六大原则,或多或少都会有违背设计模的地方,所以不…...

MyBatis搭建和增删改查
MyBatis是一个开源的持久层框架,用于处理数据库的增删改查操作。它能够将Java对象与数据库中的数据进行映射关系的配置,并自动生成对应的SQL语句,从而简化了数据库操作的编码工作。 MyBatis的核心思想是将SQL语句与Java代码分离,…...

【一图学技术】6.反向代理 vs API网关 vs 负载均衡的原理和使用场景
反向代理 vs API网关 vs 负载均衡 一、概念 🌏反向代理(Reverse Proxy)是一种位于服务器和客户端之间的代理服务器。 它接收来自客户端的请求,并将其转发给后端服务器,然后将后端服务器的响应返回给客户端。客…...

python爬虫番外篇 | Reuqests库高级用法(1)
文章目录 1.会话对象(Session Objects)2.请求和响应对象(Request and Response Objects)3.准备好的请求(Prepared Requests)4.SSL证书验证5.客户端证书6.CA 证书7.正文内容工作流程(Body Conten…...

【链表OJ】常见面试题 3
文章目录 1.[环形链表II](https://leetcode.cn/problems/linked-list-cycle-ii/description/)1.1 题目要求1.2 快慢指针1.3 哈希法 2.[随机链表的复制](https://leetcode.cn/problems/copy-list-with-random-pointer/description/)2.1 题目要求2.2 迭代法 1.环形链表II 1.1 题目…...

Linux学习笔记9(Linux包管理)
目录 归档包管理 归档 查看归档包 解归档包 压缩包管理 Zip/unzip gzip/gunzip bzip2/bunzip2 源码包安装软件 三大步: 预备步骤:安装依赖的编译库 一、./configure --prefix/usr/local/nginx 二、make 三、make install 软件包安装 配置…...

论文阅读《Geometric deep learning of RNA structure》
引入了机器学习方法,通过少量的数据学习。只使用原子坐标作为输入。 预测RNA三维结构比预测蛋白质结构更困难。 设计了一个原子旋转等变评分器ARES,由每个原子的三维坐标和化学元素类型(输入)指定,ARES预测模型与未知真…...

宏集方案 | 传统建筑智能化改造,迈向物联新时代
前言 智能建筑涉及多个系统的集成,如照明、空调、安防等,这些系统的兼容性和协调运作是一大挑战。然而,传统的工业建筑和商业楼宇受早期设计的局限,多个控制系统间互不兼容,并且难以重新部署通信线缆。 针对传统建筑…...

如果服务器更改Web端口会减少被攻击的风险吗?
通过更改服务器的Web端口,是否能够显著降低被攻击的风险?首先,理解Web服务默认使用的端口是关键。HTTP协议通常使用80端口,而HTTPS则默认使用443端口。这些端口因其广泛认知而成为黑客攻击的首要目标。理论上,将Web服务迁移到非标…...