一、引言
QT完整源代码贴在文末。入门文章,大佬可直接略过本篇。
本文仅介绍QT的使用心得以及快速上手的方法,需要读者有一点c++\c的基础,以及会SQL语言和SQL server数据库的使用。
题目为学校的数据库系统概论的期末专题训练。总用时两天半左右,第一天是QT的安装及了解,第二天继续了解+功能实现,最后半天给成品完善了一下功能和布局。
在整个过程中产生了许多疑问也走了一些弯路,因此写下此文,旨在帮助像我一样完全没接触过QT或交互界面的朋友快速上手,以制作出最后成品为导向。懂的并不多,如有错误欢迎指出。
以下是题目要求,不需要的朋友可以跳过:
系统实现:用QT/JAVA SWING/Tkinter等GUI框架开发数据库系统的交互界面。用SQL语言实现数据定义、数据查询和数据更新等操作并实现数据的持久化存储。
**题目三 工资管理系统的设计 **
系统概述:通过对人事管理管理部门中的职称、工资、员工、部门、工资类别等相关内容进行分析,完成具有人员管理、工资管理、部门管理等相关功能的小型数据库管理应用系统,系统需要具备增减工资中应发、应扣类别的灵活性以适应将来需求的变化。
基本要求
1.完成人员、部门、工资类别、职称表的维护
2.根据需要对工资类别进行添加 。
3.完成工资表的生成,并计算相关数据,进行查询。
4.按部门计算平均工资 。
5.按人、年统计平均工资 。
6.完成权限控制功能,如果一个同学独立完成,仅要求简单的用户登录即可。 本题目所需的知识点:E-R关系图、数据库表设计、数据库表维护等。
该题可2个同学完成,各同学题目为:
工资管理系统——基础数据
工资管理系统——工资管理
其中基础数据包括职称、工资、部门、工资类别、员工基本信息的录入、修改、删除、查询、打印。
工资管理包括工资的录入、删除、修改、查询、打印以及相关统计查询等。
二、QT各功能的介绍和解释
下载安装的方法csdn有很多,不再赘述。链接数据库前需要下载配置ODBC,也有很多文章。
1、QT的创建
整个项目只用得到创建项目和打开项目这两个按钮。
点击创建项目,选择第一个QT Widgets Applications,取个名选个路径,选择qmake,然后再base class这一栏选择QWidget(QMainwindow制作起来差不多,我选的是QWidget),然后有一个选择编译工具的,如果有多个就挑着选,都选也行,以创建后不报错为标准。最后一直点下一步直到完成创建。
此步也有很多教程,所以就简单几句话略过。
2、QT特点介绍:信号与槽
QT中有一种特殊的概念,叫信号与槽。举个例子,当你在一个窗口上点击一个按钮时,这个按钮相当于向自己所在的界面发出了一个“我被点击了,我要进行相应的动作了”的信号,而槽就是接收这个信号的专门的接收器。可以理解为生物上的激素与受体。如果有读者用过MFC,那这个槽就是MFC中的点击事件。一个按钮(实际上是界面上所以可供交互的组件)相应的执行函数叫槽函数。
3、各文件的意义和交互的设计界面
点开源文件,双击widget.cpp开始我们的项目,主要的代码都写在这里。main.cpp暂时不用动。
此处通俗易懂地解释一下,main起的作用跟c++一样,不过在此项目里基本就起一个调出主交互页面(主页面的代码就在widget.cpp里)的作用,起这个作用的就一句话,点开来可以看到:w.show();
我们继续回到widget.cpp里。第一个类似函数体的大括号,为了简单易懂,我在此不严谨地称之为构造函数(确实有点像),我们的数据库的链接、控制界面的显示、对象和变量的定义新建、信号与槽函数(后面专门介绍)等都在这里进行定义,只有先在此定义了才能在后面使用。在这个文件里还需要写我们实现各种功能的函数,这些函数的定义写在相应的.h文件中,具体实现代码写在.cpp,跟c++一样。(具体形式我会在后面给出)
** 紧接着,我们进入到好玩的、可视化的部分!**
这也是和交互界面和平常编程的区别和乐趣所在。
左侧的目录里还有一个“界面文件”,点开,双击widget.ui,我们就会来到如下图所示的ui设计界面。
左边一列是各种组件,点击拖动到中间的界面上即可自由放置,拉拽四周的点可以放大缩小。右上角这一栏会显示你已经放置上的组件及其名字、属性。双击即可更改相应组件的名字,建议大家取一个自己记得住的名字便于区分。接下来我来简单介绍一下各个组件,以我的界面为例:
左右两侧的按钮,叫pushButton,在Buttons栏里,作用是点击后完成设定的动作,比如跳转新页面、显示数据等,具体需要自己设定。添加完按钮后系统会自动在.cpp文件中新建一个空的槽函数,我们右键添加的组件,选择“转到槽...”即可跳转到相应的槽函数进行编写。
中间的空白框是tableview,在Item Views栏,顾名思义是用来显示表格内容的,后续我们会链接数据库然后让数据库中的表在这个组件上显示出来。
接下来是界面上面:

左边的文字点击后没有任何反应,它只是起到标识作用,是display widgets栏中的label组件。
右边则是一个可供输入的文本框,在input widget是中的line edit。它用来获取用户写在文本框里的内容,然后进行处理或者其他操作。
以上就是基础的组件及其用途,会使用他们已经可以完成很多窗口的设计了,也足够完成本系统。读者可自行探索更多不同功能的组件。
4、QT的代码应该如何写?写在哪?以什么方式写?
widget.h部分和widget.cpp中构造函数部分的代码
以下是我项目的widget.cpp中构造函数的源代码:
#include "widget.h"
#include "ui_widget.h"
#include "QMessageBox"
#include "QSqlError"
#include "QSqlQueryModel"
#include <QtDebug>
#include <QStandardItemModel>
#include <QTableView>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
userMode = new QSqlQueryModel(ui->tableView);//绑定
QSqlDatabase db = QSqlDatabase::addDatabase("QODBC");
db.setHostName("**********");//替换成自己的
db.setDatabaseName("hhhh");
// 使用 Windows 身份验证连接数据库
db.setUserName(""); // 留空以使用 Windows 身份验证
db.setPassword(""); // 留空以使用 Windows 身份验证
if(db.open())
{
QMessageBox::information(this, "连接提示" ,"连接成功");
}
else
{
QMessageBox::warning(this, "连接提示" ,"连接失败");
qDebug() << db.lastError().text(); // 输出错误信息
}
// 通过指针创建登录界面类的对象
m_log = new login;
// 调用登录窗口的show()函数显示登录界面
m_log->show();
m_register = new Lregister;
// 建立信号槽,当接收到登录界面发来的login()信号后,调用主窗口的show()函数。
connect(m_log,SIGNAL(log()),this,SLOT(show()));
// 建立信号槽,当接收到登录界面发来的re()信号后,调用注册窗口的show()函数。
connect(m_log,SIGNAL(re()),m_register,SLOT(show()));
// 建立信号槽,当接收到注册界面发来的log2()信号后,调用主窗口的show()函数。
connect(m_register,SIGNAL(log2()),this,SLOT(show()));
// 建立信号槽,当接收到注册界面发来的close_window2()信号后,调用登录窗口的close()函数。
connect(m_register,SIGNAL(close_window2()),m_log,SLOT(close()));
m0 = new QSqlTableModel;
m_statistics = new statistics;
}
Widget::~Widget()
{
delete ui;
}
//显示表employee的内容
void Widget::on_pushButton_displayE_clicked()
{
m0->setTable("employee");
ui->tableView->setModel(m0);
m0->select();
}
//显示表department的内容
void Widget::on_pushButton_displayD_clicked()
{
m0->setTable("department");
ui->tableView->setModel(m0);
m0->select();
}
//显示表profession的内容
void Widget::on_pushButton_displayP_clicked()
{
m0->setTable("profession");
ui->tableView->setModel(m0);
m0->select();
}
//显示表Wcategory的内容
void Widget::on_pushButton_displayW_clicked()
{
m0->setTable("Wcategory");
ui->tableView->setModel(m0);
m0->select();
}
//显示表wage的内容
void Widget::on_pushButton_wage_clicked()
{
userMode->setQuery("UPDATE wage SET Rwage = Swage - Dwage");
m0->setTable("wage");
ui->tableView->setModel(m0);
m0->select();
}
//查找查询的功能
void Widget::on_pushButton_query_clicked()
{
QString item = ui->lineEdit_item->text() ;
QString content = ui->lineEdit_content->text() ;
QString str = QString('%1' + " = '%2'").arg(item).arg(content) ;
userMode->setQuery("SELECT employee.name, employee.id, employee.gender, employee.birthday, employee.department, employee.profession, Wcategory.Wcategory, wage.year, wage.Rwage, wage.Swage, wage.Dwage from employee INNER JOIN wage ON wage.id = employee.id INNER JOIN Wcategory ON Wcategory.Wno = wage.Wno INNER JOIN department ON department.department = employee.department INNER JOIN profession ON profession.profession = employee.profession WHERE employee." + item + " = '" + content + "'");
ui->tableView->setModel(userMode);
}
//删除所选中行的功能
void Widget::on_pushButton_delete_clicked()
{
if(rights2 == "普通用户")
{
QMessageBox::information(this, "通知", "您无此权限!");
}
else if(rights2 == "管理员")
{
// 创建确认对话框
QMessageBox::StandardButton reply;
reply = QMessageBox::question(this, "确认", "您确定要对选中的行执行删除操作吗?", QMessageBox::Yes | QMessageBox::No);
// 判断用户的选择
if (reply == QMessageBox::Yes) {
QItemSelectionModel *selectionModel = ui->tableView->selectionModel();
QModelIndexList selectedIndexes = selectionModel->selectedRows();
QAbstractItemModel *model = ui->tableView->model();
for (const QModelIndex &selectedIndex : selectedIndexes) {
model->removeRow(selectedIndex.row());
}
ui->tableView->update();
QMessageBox::information(this, "通知" ,"删除操作成功!");
} else {
QMessageBox::information(this, "通知" ,"删除操作已取消!");
}
}
else if (rights2 == "")
{
QMessageBox::information(this, "通知" ,"rights2未赋值!");
}
}
//退出的按钮
void Widget::on_pushButton_exit_clicked()
{
exit(0);
}
//实现添加的按钮(增加空行)
void Widget::on_pushButton_addrow_clicked()
{
if(rights2 == "管理员")
{
// 获取当前显示的表名
QString tableName = m0->tableName();
m0->setTable(tableName);
ui->tableView->setModel(m0);
m0->select();
//加入空行
m0->insertRow(m0->rowCount());
m0->submitAll();
}
else
{
QMessageBox::information(this, "通知", "您无此权限!");
}
}
//跳转到statistics统计界面的按钮
void Widget::on_pushButton_statistics_clicked()
{
m_statistics->show();
}
//退出登录的按钮
void Widget::on_pushButton_logout_clicked()
{
m_log->show();
this->close();
}
还有widget.h的完整代码(除了两条虚线之间以及#include部分,其他部分都是创建工程即有的,不需要改动):
#ifndef WIDGET_H
#define WIDGET_H
#include "login.h"
#include "statistics.h"
#include "Lregister.h"
#include <QWidget>
#include <QtSql/QSqlDatabase>
#include <QMessageBox>
#include <QSqlTableModel>
#include <QSqlQuery>
#include <QSqlQueryModel>
#include <QSqlError>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
//--------此处添加函数声明!!!!----------
private slots:
void on_pushButton_displayP_clicked();
void on_pushButton_displayD_clicked();
void on_pushButton_displayE_clicked();
void on_pushButton_displayW_clicked();
void on_pushButton_query_clicked();
void on_pushButton_delete_clicked();
void on_pushButton_add_clicked();
void on_pushButton_wage_clicked();
void on_pushButton_kindofwage_clicked();
void on_pushButton_exit_clicked();
void on_pushButton_addrow_clicked();
void on_pushButton_statistics_clicked();
void on_pushButton_logout_clicked();
//--------此处添加函数声明!!!!----------
//--------此处添加属性定义!!!!----------
private:
Ui::Widget *ui;
QSqlQueryModel *userMode;
QSqlTableModel *m0;
QSqlTableModel *m1;
QSqlTableModel *m2;
QSqlTableModel *m3;
QSqlTableModel *m4;
QSqlTableModel *n0;
QSqlTableModel *model;
QSqlTableModel *tableView;
QAbstractItemModel* tn;
// 登录界面类的对象作为指针
login * m_log;
// 统计界面类的对象作为指针
statistics * m_statistics;
Lregister * m_register;
//--------此处添加属性定义!!!!----------
};
#endif // WIDGET_H
以上涉及到的东西有:基本设定、定义与声明(包括信号与槽)、数据库的链接等。我们一个一个来介绍。
基本设定
.cpp构造函数的代码中,ui->setupUi(this);这句就是对ui的操作,意思是建立ui。这句话一建立项目就有,不用改。
userMode = new QSqlQueryModel(ui->tableView);//绑定
上面这句与我们的组件table view的设置有关,不加上这句没法用这个组件。但是需要在.h中添加相应声明(下面会说)。
可以理解为对使用组件所需要提前写下的基本设定。不需要懂为什么,只需要知道要用table view组件,就必须要这句话。推广开来,很多其他组件也都需要这样的设定。
定义与声明(包括信号与槽)
与C++一样,属性与成员函数声明写在.h文件,代码具体实现写在.cpp文件(构造函数后)。
例如,在.cpp里的一个函数,即一个按钮的槽函数on_pushButton_displayE_clicked(),它在.cpp里的代码如下:
//显示表employee的内容
void Widget::on_pushButton_displayE_clicked()
{
m0->setTable("employee");
ui->tableView->setModel(m0);
m0->select();
}
以上函数中有一个对象m0,它是需要定义才能使用的。通常定义在**构造函数**的最后,也就是上面完整代码的倒数第二句话:m0 = new QSqlTableModel;
还有我们的信号与槽:
// 建立信号槽,当接收到登录界面发来的login()信号后,调用主窗口的show()函数。
connect(m_log,SIGNAL(log()),this,SLOT(show()));
这样形式的就是信号槽,用来接收其他部分发出的信号,然后做出反应。
格式是:**connect(发出信号的对象名,发出的信号名,接收信号的对象名,接收对象后做出反应的相应函数);**
上式中,m_log是我制作的登陆界面类的对象名字,发出的信号名叫log(),接收对象的就是现在这个widget窗口,所以是this,然后相应的事件函数名则是show(),即widget自带的一个函数:show(),作用是显示widget窗口。
整个语句的意思是:当对象m_log(也就是登陆界面)发出一个名叫log()的信号时,会被此窗口(widget)接收到,然后启动widget自带的函数show(),显示此窗口,实现一个登陆后展示主界面的效果。
当然,m_log也是需要在.h文件声明的,即上面.h完整代码中的 login * m_log; 句。我解释一下,这个login是我制作的登陆界面的文件名,就像widget。这句话的意思是,我定义了一个名字是m_log的、用来指代登录界面login的对象。可以把login看作一个类型,就像是string,char,int。
而log()函数则是需要在login.cpp和.h中实现的。各个界面文件的实现方法都一样,不再赘述。这里在一个窗口的代码里出现另一个窗口的原因,则是我需要由一个窗口通过满足特定条件来引出另一个窗口。如果窗口数量多且每个窗口都可以引出另一个窗口,那么就可以像串糖葫芦串烧烤一样把他们串起来,甚至还可以变成一个“环”。网上,一些电脑中病毒后会源源不断地跳出新窗口来让电脑崩溃,或许就是这个原理,跟上面不同的是它的条件不需要点击按钮,可能是等待特定时间,比如0.5秒。但是想来应该没有这么简单,我对网安和黑客一窍不通。
数据库的链接
这部分没什么好说的,写在widget.cpp的构造函数位置。csdn上也有许多贴,照着来就行了,如果出错就多找经验贴。注意windows身份验证和用户名密码这两种方式的区分。
三、项目的整体实现思路
如图所示:
四、项目各功能思路详细说明
1、主界面

主界面要实现的功能有以下:在tableview上显示各种表的内容、增、删、改、查、工资情况的自定义统计以及退出界面和退出登录(登出)。
显示各种表的内容的函数非常简单,以下是显示employee表按钮的槽函数:
//显示表employee的内容
void Widget::on_pushButton_displayE_clicked()
{
m0->setTable("employee");
ui->tableView->setModel(m0);
m0->select();
}
显示其他表只需把“employee”换成其他的表名就可以了。
接下来是增、删、改、查。改最简单。我发现tableview本身是自带修改功能的,用法就是显示出表的内容后双击,然后直接修改就行。这个功能不需要写任何代码。如下图所示:
增加的功能我取了个巧。点击一个按钮实现添加一行空行的功能,然后利用上述修改的功能直接在空行里修改,这样就相当于添加了一行了。如果不想取巧,那么思路是将要添加的内容写在一个个lineEdit中,然后在类似“确定添加”的按钮槽函数中使用SQL语句将获取的lineEdit内容添加进数据库中。就结果而言这二者是一样的。
接下来是删除。点击表上一行的最左边,可以选中一整行,然后点击删除按钮进行删除。按钮的代码如下(添加了一个弹出对话框进行确认)。
// 创建确认对话框
QMessageBox::StandardButton reply;
reply = QMessageBox::question(this, "确认", "您确定要对选中的行执行删除操作吗?", QMessageBox::Yes | QMessageBox::No);
// 判断用户的选择
if (reply == QMessageBox::Yes) {
QItemSelectionModel *selectionModel = ui->tableView->selectionModel();
QModelIndexList selectedIndexes = selectionModel->selectedRows();
QAbstractItemModel *model = ui->tableView->model();
for (const QModelIndex &selectedIndex : selectedIndexes) {
model->removeRow(selectedIndex.row());
}
ui->tableView->update();
QMessageBox::information(this, "通知" ,"删除操作成功!");
} else {
QMessageBox::information(this, "通知" ,"删除操作已取消!");
}
最后是查找。原理和添加的常规方法思路一样,不再赘述。源码我会贴在最后。
接下来是退出。退出界面的按键最简单,槽函数里就一句话:exit(0);
退出登录即登出,一登出就有登录,这里涉及到登录界面和注册界面的制作。还有工资情况的自定义统计,也涉及到一个新的界面。接下来介绍这两个界面。
2、登陆界面/注册界面
登录界面我命名为login,注册界面则为lregister。
主要思路与添加功能相似。注册功能是在数据库中事先创建一个表,用来记录注册的用户名和密码。
将在lineEdit获取的用户名用SQL语句首先在库中查询是否注册过,若有,弹出提示,若无,就用户名和密码存储进去。
登录更为简单,只需将已输入的用户名和密码用SQL语句查找是否符合库中的记录。一致则登录。
这里还涉及到一个权限问题。我只将最初的第一个注册的用户(即管理员)的权限设为管理员,其他后来注册的都默认是普通用户。权限作为一个列名一起放入用户的数据库中。我对权限的设定是:管理员能使用所有功能,但是普通用户不能使用修改、添加和删除功能,只能查看与查找表的内容和工资情况统计。
对普通用户所能使用的功能的限制如何实现呢?我在普通用户不能使用的三个功能的函数里多加了一个if判断,判断此时用户是否是管理员。为此我设置了一个全局变量int rights = -1,放置在login.cpp的incldue后一行。当我登录时,程序会去寻找此时登录的用户的权限是什么,如果是管理员,则将rights赋值为1,普通用户则为0。同时,在相应限制功能的按钮槽函数中添加if判断,如果rights为0,即普通用户,则弹出弹窗显示无权限(注意,此处跳出的弹窗是小弹窗,或者可以认为是系统环境内置的封装好的窗口,我们不需要自己写,如果想跳出自己写的窗口,则不能使用QMessageBox)。包含权限相关的登录按钮(在login.cpp中)代码如下:
void login::on_pushButton_login_clicked()
{
// 从输入框获取账号
QString username = ui->lineEdit_username->text();
// 从输入框获取密码
QString password = ui->lineEdit_password->text();
QSqlQuery query;
QSqlQuery query2;
QString sql = "SELECT password FROM user_table WHERE username = '" + username + "'";
if (!query.exec(sql)) {
qDebug() << "无此账号!";
return;
}
// 接下来,获取指定位置的数值并保存为QString类型
if (query.next()) {
QString value = query.value(0).toString();
qDebug() << "密码数据找到了: " << value;
//账号和密码匹配正确
if (value == password)
{
QString sql2 = "SELECT rights FROM user_table WHERE username = '" + username + "'";
if (!query2.exec(sql2)) {
qDebug() << "查询语句错误!";
return;
}
if (query2.next())
{
QString value2 = query2.value(0).toString();
qDebug() << "用户权限找到了: " << value2;
if(value2 == "管理员")
{
QMessageBox::information(this, "通知", "登录成功!您的权限是" + value2 + "权限!");
rights = 1;
rights2 = value2;
}
else if(value2 == "普通用户")
{
QMessageBox::information(this, "通知", "登录成功!您的权限是" + value2 + "权限!");
rights = 0;
rights2 = value2;
}
}
else
{
qDebug() << "寻找权限时出错!";
}
emit(log());
qDebug() << "已发送log信号";
emit(close_window());
}
else // 账号或密码错误
QMessageBox::information(this, "错误" ,"账号或密码错误!");
} else {
qDebug() << "寻找数据时出错!";
}
}
如何限制权限的代码以删除为例:
//删除所选中行的功能
void Widget::on_pushButton_delete_clicked()
{
if(rights2 == "普通用户")
{
QMessageBox::information(this, "通知", "您无此权限!");
}
else if(rights2 == "管理员")
{
// 创建确认对话框
QMessageBox::StandardButton reply;
reply = QMessageBox::question(this, "确认", "您确定要对选中的行执行删除操作吗?", QMessageBox::Yes | QMessageBox::No);
// 判断用户的选择
if (reply == QMessageBox::Yes) {
QItemSelectionModel *selectionModel = ui->tableView->selectionModel();
QModelIndexList selectedIndexes = selectionModel->selectedRows();
QAbstractItemModel *model = ui->tableView->model();
for (const QModelIndex &selectedIndex : selectedIndexes) {
model->removeRow(selectedIndex.row());
}
ui->tableView->update();
QMessageBox::information(this, "通知" ,"删除操作成功!");
} else {
QMessageBox::information(this, "通知" ,"删除操作已取消!");
}
}
else if (rights2 == "")
{
QMessageBox::information(this, "通知" ,"rights2未赋值!");
}
}
接下来还有2点很重要。1、如何在主界面前先显示登录界面;2、如何登录成功后跳出主界面(也就是widget界面),即如何使一个窗口拥有弹出另一个窗口的能力。
我们先将第二点。想要使一个窗口拥有弹出另一个窗口的能力很简单,即让被呼出的窗口在发起呼出动作的窗口里包含就行了。然后需要在发起呼出的界面的.h文件中声明一下,具体参见上文**定义与声明**的部分。
然后我们只需要一个发起动作的信号,来让新窗口跳出。具体使用的就是信号与槽,参见上文的定义与声明部分。请记得connect开头的那个语句要写在被调出的界面的.cpp里。
在发起呼出的界面.cpp中,我们需要一个语句来发出信号:
emit(re());
re()是一个函数名,可以自己定义。这个函数就是connect后面跟着的四个参数的第二个。
第一点,我需要先解释一下。我们点开man.cpp文件,我们可以看到有一句:
w.show();
这句话的意思是,整个项目一启动,第一个跳出来的界面就是主界面widget,而不是我们想要的登陆界面。相应的解决办法是
3、统计界面
即工资情况的自定义统计界面,我命名为statistics。
这有四个按钮,分别是四个模块,每个模块对应一个按钮,每个按钮点击后会弹出一个新的窗口。我把4个新窗口展示在下面。
年度平均工资界面,我命名为qyear:
个人工资界面,我命名为qname:
部门平均工资界面,我命名为qdepartment:
职位平均工资界面,我命名为qprofession:
这几个没什么好说的,跟查找功能一样,获取lineEdit内容然后在槽函数中进行查找然后处理运算,如何运算根据具体要求来。
五、补充
有一些针对用户体验的小细节我就没讲,读者要是有相应需求可以自行研究,比如进行重要操作时的确认弹窗、撤销功能、管理员可以对赋权限给普通用户的权限管理功能,还有记住密码功能。
还有一些很小的地方,但是对于用户体验来说不算小。比如在输入完成用户名和密码后,多数人习惯直接按下Enter回车键来代替鼠标对登录按钮的点击。
这些都是完全可以实现的,我因为时间不够,没有完全优化这些功能,但是可以给其中的一些功能提供简单的思路。
确认弹窗可以使用内置的QMessageBox。
记住密码功能,可以创建一个txt文件,在登录时储存这次登录的用户名和密码,下次登录时可供读取。每次往里储存时记得比较一下是否和已经记载的数据一直,不一致则清空txt再进行储存。
权限管理功能本质上就是对数据库指定内容的修改。
Enter键代替鼠标点击的实现csdn有帖子,不敢班门弄斧。
六、完整源码
main.cpp:
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
//w.show();
return a.exec();
}
widget.h:
#ifndef WIDGET_H
#define WIDGET_H
#include "login.h"
#include "statistics.h"
#include "Lregister.h"
#include <QWidget>
#include <QtSql/QSqlDatabase>
#include <QMessageBox>
#include <QSqlTableModel>
#include <QSqlQuery>
#include <QSqlQueryModel>
#include <QSqlError>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_pushButton_displayP_clicked();
void on_pushButton_displayD_clicked();
void on_pushButton_displayE_clicked();
void on_pushButton_displayW_clicked();
void on_pushButton_query_clicked();
void on_pushButton_delete_clicked();
void on_pushButton_add_clicked();
void on_pushButton_wage_clicked();
void on_pushButton_kindofwage_clicked();
void on_pushButton_exit_clicked();
void on_pushButton_addrow_clicked();
void on_pushButton_statistics_clicked();
void on_pushButton_logout_clicked();
private:
Ui::Widget *ui;
QSqlQueryModel *userMode;
QSqlTableModel *m0;
QSqlTableModel *m1;
QSqlTableModel *m2;
QSqlTableModel *m3;
QSqlTableModel *m4;
QSqlTableModel *n0;
QSqlTableModel *model;
QSqlTableModel *tableView;
QAbstractItemModel* tn;
// 登录界面类的对象作为指针
login * m_log;
// 统计界面类的对象作为指针
statistics * m_statistics;
Lregister * m_register;
};
#endif // WIDGET_H
widget.cpp:
#include "widget.h"
#include "ui_widget.h"
#include "QMessageBox"
#include "QSqlError"
#include "QSqlQueryModel"
#include <QtDebug>
#include <QStandardItemModel>
#include <QTableView>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
userMode = new QSqlQueryModel(ui->tableView);//绑定
QSqlDatabase db = QSqlDatabase::addDatabase("QODBC");
db.setHostName("LAPTOP-PFEJR2BB");
db.setDatabaseName("hhhh");
// 使用 Windows 身份验证连接数据库
db.setUserName(""); // 留空以使用 Windows 身份验证
db.setPassword(""); // 留空以使用 Windows 身份验证
if(db.open())
{
QMessageBox::information(this, "连接提示" ,"连接成功");
}
else
{
QMessageBox::warning(this, "连接提示" ,"连接失败");
qDebug() << db.lastError().text(); // 输出错误信息
}
// 通过指针创建登录界面类的对象
m_log = new login;
// 调用登录窗口的show()函数显示登录界面
m_log->show();
m_register = new Lregister;
// 建立信号槽,当接收到登录界面发来的login()信号后,调用主窗口的show()函数。
connect(m_log,SIGNAL(log()),this,SLOT(show()));
// 建立信号槽,当接收到登录界面发来的re()信号后,调用注册窗口的show()函数。
connect(m_log,SIGNAL(re()),m_register,SLOT(show()));
// 建立信号槽,当接收到注册界面发来的log2()信号后,调用主窗口的show()函数。
connect(m_register,SIGNAL(log2()),this,SLOT(show()));
// 建立信号槽,当接收到注册界面发来的close_window2()信号后,调用登录窗口的close()函数。
connect(m_register,SIGNAL(close_window2()),m_log,SLOT(close()));
m0 = new QSqlTableModel;
m_statistics = new statistics;
}
Widget::~Widget()
{
delete ui;
}
//显示表employee的内容
void Widget::on_pushButton_displayE_clicked()
{
m0->setTable("employee");
ui->tableView->setModel(m0);
m0->select();
}
//显示表department的内容
void Widget::on_pushButton_displayD_clicked()
{
m0->setTable("department");
ui->tableView->setModel(m0);
m0->select();
}
//显示表profession的内容
void Widget::on_pushButton_displayP_clicked()
{
m0->setTable("profession");
ui->tableView->setModel(m0);
m0->select();
}
//显示表Wcategory的内容
void Widget::on_pushButton_displayW_clicked()
{
m0->setTable("Wcategory");
ui->tableView->setModel(m0);
m0->select();
}
//显示表wage的内容
void Widget::on_pushButton_wage_clicked()
{
userMode->setQuery("UPDATE wage SET Rwage = Swage - Dwage");
m0->setTable("wage");
ui->tableView->setModel(m0);
m0->select();
}
//查找查询的功能
void Widget::on_pushButton_query_clicked()
{
QString item = ui->lineEdit_item->text() ;
QString content = ui->lineEdit_content->text() ;
QString str = QString('%1' + " = '%2'").arg(item).arg(content) ;
userMode->setQuery("SELECT employee.name, employee.id, employee.gender, employee.birthday, employee.department, employee.profession, Wcategory.Wcategory, wage.year, wage.Rwage, wage.Swage, wage.Dwage from employee INNER JOIN wage ON wage.id = employee.id INNER JOIN Wcategory ON Wcategory.Wno = wage.Wno INNER JOIN department ON department.department = employee.department INNER JOIN profession ON profession.profession = employee.profession WHERE employee." + item + " = '" + content + "'");
ui->tableView->setModel(userMode);
}
//删除所选中行的功能
void Widget::on_pushButton_delete_clicked()
{
if(rights2 == "普通用户")
{
QMessageBox::information(this, "通知", "您无此权限!");
}
else if(rights2 == "管理员")
{
// 创建确认对话框
QMessageBox::StandardButton reply;
reply = QMessageBox::question(this, "确认", "您确定要对选中的行执行删除操作吗?", QMessageBox::Yes | QMessageBox::No);
// 判断用户的选择
if (reply == QMessageBox::Yes) {
QItemSelectionModel *selectionModel = ui->tableView->selectionModel();
QModelIndexList selectedIndexes = selectionModel->selectedRows();
QAbstractItemModel *model = ui->tableView->model();
for (const QModelIndex &selectedIndex : selectedIndexes) {
model->removeRow(selectedIndex.row());
}
ui->tableView->update();
QMessageBox::information(this, "通知" ,"删除操作成功!");
} else {
QMessageBox::information(this, "通知" ,"删除操作已取消!");
}
}
else if (rights2 == "")
{
QMessageBox::information(this, "通知" ,"rights2未赋值!");
}
}
//退出的按钮
void Widget::on_pushButton_exit_clicked()
{
exit(0);
}
//实现添加的按钮(增加空行)
void Widget::on_pushButton_addrow_clicked()
{
if(rights2 == "管理员")
{
// 获取当前显示的表名
QString tableName = m0->tableName();
m0->setTable(tableName);
ui->tableView->setModel(m0);
m0->select();
//加入空行
m0->insertRow(m0->rowCount());
m0->submitAll();
}
else
{
QMessageBox::information(this, "通知", "您无此权限!");
}
}
//跳转到statistics统计界面的按钮
void Widget::on_pushButton_statistics_clicked()
{
m_statistics->show();
}
//退出登录的按钮
void Widget::on_pushButton_logout_clicked()
{
m_log->show();
this->close();
}
login.h:
#ifndef LOGIN_H
#define LOGIN_H
#include <QWidget>
#include <QSqlQuery>
#include <QMessageBox>
extern int rights;
extern QString rights2;
namespace Ui {
class login;
}
class login : public QWidget
{
Q_OBJECT
public:
explicit login(QWidget *parent = nullptr);
~login();
signals:
void log(); //登录主界面信号
void close_window(); //关闭登录界面信号
void re();
private slots:
void on_pushButton_login_clicked();
void on_pushButton_exit_clicked();
void on_pushButton_register_clicked();
void on_pushButton_Lregister_clicked();
private:
Ui::login *ui;
};
#endif // LOGIN_H
login,cpp:
#include "login.h"
#include "ui_login.h"
#include <QMessageBox>
int rights = -1;
QString rights2 = "";
login::login(QWidget *parent)
: QWidget(parent)
, ui(new Ui::login)
{
ui->setupUi(this);
// 发出信号后关闭登录窗口的信号槽连接
connect(this,SIGNAL(close_window()),this,SLOT(close()));
ui->lineEdit_password->setEchoMode(QLineEdit::Password);//输入时显示圆点
}
login::~login()
{
delete ui;
}
void login::on_pushButton_login_clicked()
{
// 从输入框获取账号
QString username = ui->lineEdit_username->text();
// 从输入框获取密码
QString password = ui->lineEdit_password->text();
QSqlQuery query;
QSqlQuery query2;
QString sql = "SELECT password FROM user_table WHERE username = '" + username + "'";
if (!query.exec(sql)) {
qDebug() << "无此账号!";
return;
}
// 接下来,获取指定位置的数值并保存为QString类型
if (query.next()) {
QString value = query.value(0).toString();
qDebug() << "密码数据找到了: " << value;
//账号和密码匹配正确
if (value == password)
{
QString sql2 = "SELECT rights FROM user_table WHERE username = '" + username + "'";
if (!query2.exec(sql2)) {
qDebug() << "查询语句错误!";
return;
}
if (query2.next())
{
QString value2 = query2.value(0).toString();
qDebug() << "用户权限找到了: " << value2;
if(value2 == "管理员")
{
QMessageBox::information(this, "通知", "登录成功!您的权限是" + value2 + "权限!");
rights = 1;
rights2 = value2;
}
else if(value2 == "普通用户")
{
QMessageBox::information(this, "通知", "登录成功!您的权限是" + value2 + "权限!");
rights = 0;
rights2 = value2;
}
}
else
{
qDebug() << "寻找权限时出错!";
}
emit(log());
qDebug() << "已发送log信号";
emit(close_window());
}
else // 账号或密码错误
QMessageBox::information(this, "错误" ,"账号或密码错误!");
} else {
qDebug() << "寻找数据时出错!";
}
}
void login::on_pushButton_exit_clicked()
{
exit(0);
}
void login::on_pushButton_Lregister_clicked()
{
emit(re());
qDebug() << "已发送re信号";
}
lregister.h:
#ifndef LREGISTER_H
#define LREGISTER_H
#include <QWidget>
#include <QString>
#include <QSqlQuery>
#include <QMessageBox>
namespace Ui {
class Lregister;
}
class Lregister : public QWidget
{
Q_OBJECT
public:
explicit Lregister(QWidget *parent = nullptr);
~Lregister();
signals:
void log2(); //登录主界面信号
void close_window2(); //关闭注册界面和登录的信号
void close_window3(); //只关闭注册界面的信号
private slots:
void on_pushButton_register_clicked();
void on_pushButton_cancel_clicked();
private:
Ui::Lregister *ui;
};
#endif // LREGISTER_H
lregister.cpp:
#include "lregister.h"
#include "ui_lregister.h"
Lregister::Lregister(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Lregister)
{
ui->setupUi(this);
// 发出信号后关闭注册窗口的信号槽连接
connect(this,SIGNAL(close_window2()),this,SLOT(close()));
// 发出信号后关闭注册窗口的信号槽连接
connect(this,SIGNAL(close_window3()),this,SLOT(close()));
ui->lineEdit_password->setEchoMode(QLineEdit::Password);//输入时显示圆点
ui->lineEdit_Apassword->setEchoMode(QLineEdit::Password);//输入时显示圆点
}
Lregister::~Lregister()
{
delete ui;
}
void Lregister::on_pushButton_register_clicked()
{
// 从输入框获取账号
QString username = ui->lineEdit_username->text();
// 从输入框获取密码
QString password = ui->lineEdit_password->text();
// 从输入框获取确认密码
QString Apassword = ui->lineEdit_Apassword->text();
QSqlQuery query;
QSqlQuery query2;
QString sql = "SELECT password FROM user_table WHERE username = '" + username + "'";
QString sql2 = "INSERT INTO user_table (username, password, rights) VALUES ('" + username + "', '" + password +"', '普通用户');";
if (query.exec(sql))
{
if (!query.next())
{
qDebug() << "此用户名不重复,可以注册! ";
if(password == Apassword)
{
if (query2.exec(sql2))
{
QMessageBox::information(this, "通知", "注册成功!权限为普通用户权限!");
// 创建确认对话框
QMessageBox::StandardButton reply;
reply = QMessageBox::question(this, "选择", "您想现在登录此账号吗?", QMessageBox::Yes | QMessageBox::No);
// 判断用户的选择
if (reply == QMessageBox::Yes)
{
emit(log2());
qDebug() << "已发送log2信号";
emit(close_window2());
}
else
{
emit(close_window2());
}
}
else
{
QMessageBox::information(this, "通知", "注册失败!");
}
}
else
{
QMessageBox::information(this, "通知", "确认密码与密码不一致!请重新输入!");
}
}
else
{
QMessageBox::information(this, "通知", "sql2语句错误!");
}
}
else
{
QMessageBox::information(this, "通知", "sql语句错误!");
}
}
void Lregister::on_pushButton_cancel_clicked()
{
emit(close_window3());
}
staistics.h:
#ifndef STATISTICS_H
#define STATISTICS_H
#include <QWidget>
#include "qyear.h"
#include "qname.h"
#include "qdepartment.h"
#include "qprofession.h"
namespace Ui {
class statistics;
}
class statistics : public QWidget
{
Q_OBJECT
public:
explicit statistics(QWidget *parent = nullptr);
~statistics();
private slots:
void on_pushButton_Qyear_clicked();
void on_pushButton_Qname_clicked();
void on_pushButton_Qdepartment_clicked();
void on_pushButton_Qprofession_clicked();
private:
Ui::statistics *ui;
// 按年份统计界面类的对象作为指针
Qyear * m_Qyear;
// 按人统计界面类的对象作为指针
Qname * m_Qname;
// 按部门统计界面类的对象作为指针
Qdepartment * m_Qdepartment;
// 按职位统计界面类的对象作为指针
Qprofession * m_Qprofession;
};
#endif // STATISTICS_H
statistics.cpp:
#include "statistics.h"
#include "ui_statistics.h"
statistics::statistics(QWidget *parent)
: QWidget(parent)
, ui(new Ui::statistics)
{
ui->setupUi(this);
m_Qyear = new Qyear;
m_Qname = new Qname;
m_Qdepartment = new Qdepartment;
m_Qprofession = new Qprofession;
}
statistics::~statistics()
{
delete ui;
}
void statistics::on_pushButton_Qyear_clicked()
{
m_Qyear->show();
}
void statistics::on_pushButton_Qname_clicked()
{
m_Qname->show();
}
void statistics::on_pushButton_Qdepartment_clicked()
{
m_Qdepartment->show();
}
void statistics::on_pushButton_Qprofession_clicked()
{
m_Qprofession->show();
}
qyear.h:
#ifndef QYEAR_H
#define QYEAR_H
#include <QWidget>
namespace Ui {
class Qyear;
}
class Qyear : public QWidget
{
Q_OBJECT
public:
explicit Qyear(QWidget *parent = nullptr);
~Qyear();
private slots:
void on_pushButton_clicked();
private:
Ui::Qyear *ui;
};
#endif // QYEAR_H
qyear.cpp:
#include "qyear.h"
#include "ui_qyear.h"
#include <QMessageBox>
#include <QSqlQuery>
#include <QSqlQueryModel>
Qyear::Qyear(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Qyear)
{
ui->setupUi(this);
}
Qyear::~Qyear()
{
delete ui;
}
void Qyear::on_pushButton_clicked()
{
QString year = ui->lineEdit_year->text();
QSqlQuery query;
if (!year.isEmpty()) {
query.prepare("SELECT 4*AVG(Rwage) AS avg_wage "
"FROM wage "
"JOIN Wcategory ON wage.Wno = Wcategory.Wno "
"JOIN employee ON wage.id = employee.id "
"WHERE wage.year = '" + year + "' ");
}
else
{
QMessageBox::warning(this, "错误" ,"请输入年份!");
}
if (!query.exec()) {
QMessageBox::critical(this, "错误", "查询失败!");
return;
}
// 获取结果并显示到lineEdit_result中
if (query.next()) {
QString averageWage = query.value("avg_wage").toString();
ui->lineEdit_result->setText(averageWage);
QMessageBox::information(this, year + "年的人均总工资",
QString( year + "年的人均总工资为:%1")
.arg(averageWage));
}
else
{
QMessageBox::information(this, "错误", "找不到结果!错误!");
}
}
qname.h:
#ifndef QNAME_H
#define QNAME_H
#include <QWidget>
namespace Ui {
class Qname;
}
class Qname : public QWidget
{
Q_OBJECT
public:
explicit Qname(QWidget *parent = nullptr);
~Qname();
private slots:
void on_pushButton_clicked();
private:
Ui::Qname *ui;
};
#endif // QNAME_H
qname.cpp:
#include "qname.h"
#include "ui_qname.h"
#include <QMessageBox>
#include <QSqlQuery>
#include <QSqlQueryModel>
Qname::Qname(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Qname)
{
ui->setupUi(this);
}
Qname::~Qname()
{
delete ui;
}
void Qname::on_pushButton_clicked()
{
QString name = ui->lineEdit_name->text();
QSqlQuery query;
if (!name.isEmpty()) {
query.prepare("SELECT sum(Rwage) AS avg_wage "
"FROM wage "
"JOIN Wcategory ON wage.Wno = Wcategory.Wno "
"JOIN employee ON wage.id = employee.id "
"WHERE employee.name = '" + name + "' ");
}
else
{
QMessageBox::warning(this, "错误" ,"请输入员工姓名!");
}
if (!query.exec()) {
QMessageBox::critical(this, "错误", "查询失败!");
return;
}
// 获取结果并显示到lineEdit_result中
if (query.next()) {
QString averageWage = query.value("avg_wage").toString();
ui->lineEdit_result->setText(averageWage);
QMessageBox::information(this, "员工" + name + "的年均总工资",
QString("员工" + name + "的年均总工资为: %1")
.arg(averageWage));
}
else
{
QMessageBox::information(this, "错误", "找不到结果!错误!");
}
}
qdepartment.h:
#ifndef QDEPARTMENT_H
#define QDEPARTMENT_H
#include <QWidget>
namespace Ui {
class Qdepartment;
}
class Qdepartment : public QWidget
{
Q_OBJECT
public:
explicit Qdepartment(QWidget *parent = nullptr);
~Qdepartment();
private slots:
void on_pushButton_clicked();
private:
Ui::Qdepartment *ui;
};
#endif // QDEPARTMENT_H
qdepartment.cpp:
#include "qdepartment.h"
#include "ui_qdepartment.h"
#include <QMessageBox>
#include <QSqlQuery>
#include <QSqlQueryModel>
Qdepartment::Qdepartment(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Qdepartment)
{
ui->setupUi(this);
}
Qdepartment::~Qdepartment()
{
delete ui;
}
void Qdepartment::on_pushButton_clicked()
{
QString department = ui->lineEdit_department->text();
QSqlQuery query;
if (!department.isEmpty()) {
query.prepare("SELECT 4*AVG(Rwage) AS avg_wage "
"FROM wage "
"JOIN Wcategory ON wage.Wno = Wcategory.Wno "
"JOIN employee ON wage.id = employee.id "
"WHERE employee.department = '" + department + "' ");
}
else
{
QMessageBox::warning(this, "错误" ,"请输入部门!");
}
if (!query.exec()) {
QMessageBox::critical(this, "错误", "查询失败!");
return;
}
// 获取结果并显示到lineEdit_result中
if (query.next()) {
QString averageWage = query.value("avg_wage").toString();
ui->lineEdit_result->setText(averageWage);
QMessageBox::information(this, department + " 部门的人均年均总工资",
QString( department + " 部门的人均年均总工资为: %1")
.arg(averageWage));
}
else
{
QMessageBox::information(this, "错误", "找不到结果!错误!");
}
}
qprofession.h:
#ifndef QPROFESSION_H
#define QPROFESSION_H
#include <QWidget>
namespace Ui {
class Qprofession;
}
class Qprofession : public QWidget
{
Q_OBJECT
public:
explicit Qprofession(QWidget *parent = nullptr);
~Qprofession();
private slots:
void on_pushButton_clicked();
private:
Ui::Qprofession *ui;
};
#endif // QPROFESSION_H
qprofession.cpp:
#include "qprofession.h"
#include "ui_qprofession.h"
#include <QMessageBox>
#include <QSqlQuery>
#include <QSqlQueryModel>
Qprofession::Qprofession(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Qprofession)
{
ui->setupUi(this);
}
Qprofession::~Qprofession()
{
delete ui;
}
void Qprofession::on_pushButton_clicked()
{
QString profession = ui->lineEdit_profession->text();
QSqlQuery query;
if (!profession.isEmpty()) {
query.prepare("SELECT 4*AVG(Rwage) AS avg_wage "
"FROM wage "
"JOIN Wcategory ON wage.Wno = Wcategory.Wno "
"JOIN employee ON wage.id = employee.id "
"WHERE employee.profession = '" + profession + "' ");
}
else
{
QMessageBox::warning(this, "错误" ,"请输入职位!");
}
if (!query.exec()) {
QMessageBox::critical(this, "错误", "查询失败!");
return;
}
// 获取结果并显示到lineEdit_result中
if (query.next()) {
QString averageWage = query.value("avg_wage").toString();
ui->lineEdit_result->setText(averageWage);
QMessageBox::information(this, profession + " 职位的人均年均总工资",
QString( profession + " 职位的人均年均总工资为: %1")
.arg(averageWage));
}
else
{
QMessageBox::information(this, "错误", "找不到结果!错误!");
}
}
各个窗口我都是直接拖组件然后调大小,没有使用代码进行修改,因此只放图片。
登录界面login:
注册界面lregister:
主界面widget:
工资统计界面statistics:
年度平均工资界面qyear:
个人工资qname:
部门平均工资qdepartment:
职位平均工资qprofession:
七、结语
再次声明,本篇仅是为了初学QT的朋友快速上手而作,大佬可直接忽略此文。写下此文也是想记录一下自己制作的心路历程,毕竟是自己第一次接触类似ui的东西,也对这种窗口的制作产生了兴趣。
谨以此文,纪念自己大学前两年半的计算机所学。希望未来我能在自己感兴趣的领域越走越远,越走越深,也希望未来的自己回望一生时,不会因虚度年华而悔恨。愿常有书籍电影相伴,常有知己相知,常有爱人相爱。
版权归原作者 飞檐流丹 所有, 如有侵权,请联系我们删除。