使用Qt5开发桌面程序
Qt开发信息管理客户端一.Qt的简介二.Qt中的重要概念1.信号与槽机制信号(signal)槽(slot)2.Lambda表达式三.使用Qt设计师快速开发1. 环境搭建(1)连接Mysql数据库2.设计你的窗口(1)添加4个选项卡来切换信息的显示(2)为图书选项卡添加TableView控件和一些功能按钮(3)添加布局(4)将数据库的表与QSqlTableModel绑定,再将QSqlTableMod
Qt开发信息管理客户端
一.Qt的简介
Qt 是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架。它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器。基本上,Qt 同 Windows 平台上的 MFC,OWL,VCL,ATL 是同类型的东西。
使用Qt有哪些优点:
- 优良的跨平台性 一次编写,处处运行
- 面向对象 模块化的设计
- 丰富的 API 良好的开发文档
使用Qt开发的著名程序有:
- Google Earth
- Opera(浏览器)
- VirtualBox(创建虚拟机)
- wps Office(著名办公软件)
二.Qt中的重要概念
1.信号与槽机制
用Java做过桌面程序的伙伴都知道,我们需要为按钮、文本输入框、窗体等各种组件添加事件监听器(鼠标监听器、键盘监听器)来与用户产生交互。在Qt中,信号与槽函数就负责交互任务。
作用:发出一个信号,可被所有对象监听,该信号是没有目的的,就像校园里的广播一样,所有人都会听到。在Qt中,这也称为信号的广播机制,如果有对象对这个信号感兴趣,那么它可以使用connect函数来连接此信号,将这个信号(signal)与自己的一个槽(slot)绑定来进行处理。
作用:接收到信号后要如何处理(进行什么动作),其本质是类的一个成员函数,和C++成员函数几乎无区别。可以为任意类型:public、protected、private,唯一区别在于:槽函数可以绑定信号,每当与槽函数绑定的信号被发射时,该槽函数将被触发调用。
connect()函数最常用的一般形式:
connect(sender, &Send::signal, receiver, &Receiver::slot)
可以解释为:
sender:发送者
signal:信号
receiver:接收者
slot:动作
举个例子,在Qt4中:
connect(button, SIGNAL(clicked()), &a, SLOT(quit()));
现在有一个名为button的按钮,一个名为a的窗口,connect函数将按钮的点击信号绑定在a的一个槽函数,那么当按钮button被点击后,a执行quit操作。
2.Lambda表达式
在Qt5中,可以使用Lambda表达式来定义并创建匿名的函数对象,不过这是C++11引入的新特性,如果要使用Lambda表达式,必须在.pro文件中写入
CONFIG += c++11
connect(button, &QPushButton::clicked,
// = :把外部所有局部变量、类中所有成员以值传递方式
// this: 类中所有成员以值传递方式
// & : 把外部所有局部变量, 引用符号
[=]()
{
qDebug() <<" button is clicked";
}
);
三.使用Qt设计师快速开发
1. 环境搭建
我使用Qt官方提供的Qt 5.12.3 安装时会安装Qt Creator 4.9.0 这个IDE,数据库使用Mysql x64 5.6版本.
(1)连接Mysql数据库
- 使用ODBC driver连接数据库,你需要先将驱动复制到Qt 安装目录下。
驱动位置一般在你的数据库安装目录下(例如):
C:\Program Files\MySQL\MySQL Server 5.6\lib
复制上面目录下的 libmysql.dll文件到Qt安装目录的bin目录下:
C:\Qt\Qt5.12.3\5.12.3\mingw73_64\bin
- 测试连接数据库是否成功
打开Qt->New Project->Qt Widget Application->项目名称->mainWindow->勾选创建界面
在demo1.pro 后添加sql ,并点击小锤子图标,作用是编译但不运行,编译添加的sql模块
打开mainWindow.cpp,添加以下头文件代码:
#include<QSqlDatabase>
#include<QDebug>
#include<QMessageBox>
#include<QSqlError>
#include<QSqlTableModel>
#include<QSqlRecord>
编写连接数据库的代码
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QSqlDatabase>
#include<QDebug>
#include<QMessageBox>
#include<QSqlError>
#include<QSqlTableModel>
#include<QSqlRecord>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//连接数据库
QSqlDatabase db= QSqlDatabase::addDatabase("QMYSQL");
//设置数据库
db.setHostName("127.0.0.1");
db.setPort(3306);
db.setDatabaseName("book");
db.setUserName("root");
db.setPassword("123456");
//打开时数据库
if(!db.open() )
{
qDebug()<<"连接失败";
}
else
{
qDebug()<<"连接成功";
}
}
MainWindow::~MainWindow()
{
delete ui;
}
点击“运行”按钮,查看控制台信息
如果连接不成功,可能会出现以下问题:
- Qt连接MySQL的时候提示“QSqlDatabase: QMYSQL driver not loaded”,
原因一:缺少文件
解决办法:
从 C:\Program Files\MySQL\MySQL Server 5.6\lib中将 libmysql.dll 文件复制C:\Qt\Qt5.12.3\5.12.3\mingw73_64\bin中。运行程序
原因二:Qt Creator与MySQL位数不统一
如果上面的方法试过了还是不行那么应该是你安装的MySQL和QT的位数不同,可以打开Mysql控制台 输入show variables like ‘%version_%’;
即可查看Mysql位数。
解决办法:
查看Qt Creator的位数,若为32位,则去下载32位的 libmysql.dll(自行搜索),重新运行程序。
2.设计你的窗口
我在book数据库中建立4张信息表,book(书籍表)、customer(客户表)、worker(职工表)、sale(销售信息表)
Qt遵循MVC的设计模式:
- M (model)模型层,一般存放持久数据,Headers文件夹下的 .h文件
- V(view)视图层,将模型层中的数据显示出来,Forms文件夹下的 .ui 文件
- C(controller)控制层,负责从视图读取数据,并向模型发送数据,Sources文件夹下的 .cpp 文件
(1)添加4个选项卡来切换信息的显示
打开demo1->Forms->mainwindow.ui
(2)为图书选项卡添加TableView控件和一些功能按钮
(3)添加布局
(4)将数据库的表与QSqlTableModel绑定,再将QSqlTableModel放在TableView(控件)中显示
先在mainwindow.h中添加头文件,并在private下声明一个私有QSqlTableModel 的对象
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include<QSqlTableModel>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
QSqlTableModel *bookModel;
};
#endif // MAINWINDOW_H
在mainwindow.cpp中将表book与bookModel绑定,再将bookModel放在图书选项卡的tableView中显示
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QSqlDatabase>
#include<QDebug>
#include<QMessageBox>
#include<QSqlError>
#include<QSqlTableModel>
#include<QSqlRecord>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//连接数据库
QSqlDatabase db= QSqlDatabase::addDatabase("QMYSQL");
//设置数据库
db.setHostName("127.0.0.1");
db.setPort(3306);
db.setDatabaseName("book");
db.setUserName("root");
db.setPassword("123456");
//打开时数据库
if(!db.open() )
{
qDebug()<<"连接失败";
}
else
{
qDebug()<<"连接成功";
}
bookModel=new QSqlTableModel();//为bookModel分配空间
bookModel->setTable("book");//指定表 ,此处将book表与bookModel绑定
bookModel->select();//显示表里所有数据
ui->tableView_book->setModel(bookModel);// 将bookModel放在tableView_book中
}
MainWindow::~MainWindow()
{
delete ui;
}
点击运行,查看效果
但是发现,表头没有改过来,并且表里的数据可以直接修改,并且修改后数据库里的数据也同时被修改了,所以还需要设置表头和编辑策略
ui->tableView_book->setModel(bookModel);// 将bookModel放在tableView_book中
//设置表头
bookModel->setHeaderData(0,Qt::Horizontal,"图书编号");
bookModel->setHeaderData(1,Qt::Horizontal,"书名");
bookModel->setHeaderData(2,Qt::Horizontal,"作者");
bookModel->setHeaderData(3,Qt::Horizontal,"版次");
bookModel->setHeaderData(4,Qt::Horizontal,"单价(元)");
bookModel->setHeaderData(5,Qt::Horizontal,"出版社");
bookModel->setHeaderData(6,Qt::Horizontal,"所属类别");
//设置model的编辑策略 为 手动提交修改
bookModel->setEditStrategy(QSqlTableModel::OnManualSubmit);
(5)为按钮添加事件监听器
1.为添加按钮添加槽
右击添加按钮->转到槽->clicked(),鼠标单击时,按钮发出signal,由槽函数接受处理
void MainWindow::on_pushButton_clicked()
{
//添加空记录
QSqlRecord record=bookModel->record();
int row=bookModel->rowCount();
bookModel->insertRecord(row,record);
}
无论是添加还是删除,点击确定后才会去执行sql语句,即提交事务
2.为删除按钮添加槽
右击删除按钮->转到槽->clicked(),处理代码如下
void MainWindow::on_pushButton_2_clicked()
{
//获取选中的模型
QItemSelectionModel *smodel=ui->tableView_book->selectionModel();
//获取模型中的索引
QModelIndexList list=smodel->selectedRows();
//删除所有选中的行
for(int i=0;i<list.size();i++)
{
bookModel->removeRow(list.at(i).row());
}
}
这样,当你在选中某行后,点击删除按钮,行的左侧会出现 ! 号,这是因为我设置了编辑策略为手动提交,即向数据库添加了事务,所以为确定添加槽
3.为确定按钮添加槽
右键 确定->转到槽->clicked()
void MainWindow::on_pushButton_3_clicked()
{
bookModel->submitAll();
}
5.为取消按钮添加槽
void MainWindow::on_pushButton_4_clicked()
{
//取消完再提交一次
bookModel->revertAll();
bookModel->submitAll();
}
6.为查找按钮添加槽
此处设置了两个查询条件
void MainWindow::on_pushButton_6_clicked()
{
QString name=ui->lineEdit_name->text();//获取用户输入
QString id=ui->lineEdit_id->text();
//组包 %1为占位符,第一个参数,%2为第二个参数
QString str=QString("name='%1' or id='%2' ").arg(name,id);
//添加过滤器
bookModel->setFilter(str);
bookModel->select();
}
6.为显示所有按钮添加槽
void MainWindow::on_pushButton_5_clicked()
{
bookModel->setTable("book");//重新关联表
bookModel->select();//再次显示所有数据
}
3.美化窗口控件
- 为选项卡设置图标
修改字体大小,选择font->14
去互联网找需要的图标,此处使用阿里巴巴矢量图标库的图标资源
下载好图标资源后,将它们放在一个image文件夹下,并将此image文件夹拷贝至项目根目录下
右键项目名 demo1->Add New->Qt->Qt Resource File
填上文件名,先选择“添加前缀” ,只留下 / 即可,再选择添加文件
为选项卡添加图标:添加时需要图片的路径
右击img.qrc->open in editor->右击图片->复制资源路径到剪切板
//添加图标
ui->tabWidget->setTabIcon(0,QIcon("://image/book.png"));
ui->tabWidget->setTabIcon(1,QIcon("://image/职工.png"));
ui->tabWidget->setTabIcon(2,QIcon("://image/客户.png"));
ui->tabWidget->setTabIcon(3,QIcon("://image/销售订单.png"));
ui->tabWidget->setIconSize(QSize(25,25));
接着为按钮添加图标
//按钮添加图标
ui->pushButton->setIcon(QIcon("://image/新增.png"));
// ui->pushButton->setIconSize(QSize(25,25));//图标大小
ui->pushButton_2->setIcon(QIcon("://image/删 除 .png"));
ui->pushButton_3->setIcon(QIcon("://image/确定.png"));
ui->pushButton_4->setIcon(QIcon("://image/取消.png"));
ui->pushButton_6->setIcon(QIcon("://image/查找.png"));
ui->pushButton_5->setIcon(QIcon("://image/所有.png"));
效果
4.使用QCharts绘制图表实现数据可视化
官方推出QCharts后,Qt画图再也不用需要配置第三方的qcustomplot和qwt插件了,QCharts功能比两者都好,且易用安装和使用。
使用QCharts绘图,你必须在安装Qt时勾选QCharts模块(默认不勾选),否则不能使用QCharts.
此处以柱状图为例演示官方示例的使用:
Qt欢迎页->示例->输入barmodelmapper
打开运行:
下面将它用到自己的项目中,可以看到他有两个头文件和.cpp文件,分别是:CustomTableModel和TableWidget
从头文件可以看到它继承自QAbstractTableModel,所以我在项目中建立两个Qt Item Model文件,名字和示例一致
1.在demo1.pro中添加charts,并点击小锤子编译此模块
2.建立文件
(1)创建CustomTableModel:
右击项目名demo1->Add New->Qt ->Qt Item Model
class Name:输入CustomTableModel(和示例保持一致)
Base Class:选择QAbstractTableModel
(2)创建TableWidget:
右击项目名demo1->Add New->Qt ->Qt Item Model
class Name:TableWidget
Base Class:选择QAbstractTableModel
(3)将官方示例中customTableModel和TableWidget的头文件和.cpp文件内容直接复制到对应的建立好的2个头文件和cpp文件中
3.修改main函数,看是否成功运行
向main函数中添加头文件“tablewidget.h”,并生成一个实例,运行该实例:
#include "mainwindow.h"
#include <QApplication>
#include"tablewidget.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// MainWindow w;
// w.show();
TableWidget t;
t.show();
return a.exec();
}
效果如下:
在我们的项目中成功运行了该官方实例,下面让这个图显示我们数据库中的数据
以显示数据库表sale中的销售信息为例,统计sale表中2018年和2019年每个月书籍的总销售量。
因此,我修改了CustomTableModel.cpp和TableWidget.cpp的部分代码:
CustomTableModel.cpp:
#include "customtablemodel.h"
#include <QtCore/QVector>
#include <QtCore/QTime>
#include <QtCore/QRect>
#include <QtCore/QRandomGenerator>
#include <QtGui/QColor>
#include<QDebug>
#include<QMessageBox>
#include<QSqlError>
#include<QSqlTableModel>
#include<QSqlRecord>
#include<QSqlDatabase>
#include<QSqlQuery>
CustomTableModel::CustomTableModel(QObject *parent) :
QAbstractTableModel(parent)
{
//连接数据库
QSqlDatabase db= QSqlDatabase::addDatabase("QMYSQL");
//设置数据库
db.setHostName("127.0.0.1");
db.setPort(3306);
db.setDatabaseName("book");
db.setUserName("root");
db.setPassword("123456");
//打开时数据库
if(!db.open() )
{
qDebug()<<"连接失败";
return;
}
else
{
qDebug()<<"连接成功";
}
QSqlQuery query;
//
m_columnCount = 2;
m_rowCount = 12;
QString a[]={"2018","2019"};
// m_data
for (int i = 0; i < m_rowCount; i++)
{
//类型为qreal的一维向量
QVector<qreal>* dataVec = new QVector<qreal>(m_columnCount);
for (int k = 0; k < dataVec->size(); k++)
{
// if (k % 2 == 0)
// dataVec->replace(k, i * 50 + QRandomGenerator::global()->bounded(20));
// else
// dataVec->replace(k, QRandomGenerator::global()->bounded(100));
//用YEAR、Month这两个数据库中内置函数,分别统计每月的书籍销售量
QString sql=QString("select SUM(count) from sale where YEAR(saledate)='%1' and"
" MONTH(saledate)='%2' ").arg(a[k],QString::number(i+1));
// qDebug()<<sql<<"\n";
query.exec(sql);
while(query.next())
{
int count=query.value(0).toInt();
//销售量插入到dataVec
dataVec->replace(k,count);
}
}
//向m_data追加dataVec
m_data.append(dataVec);
}
}
CustomTableModel::~CustomTableModel()
{
qDeleteAll(m_data);
}
int CustomTableModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_data.count();
}
int CustomTableModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_columnCount;
}
QVariant CustomTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
if (orientation == Qt::Horizontal)
return QString("201%1").arg(section+8);
else
return QString("%1").arg(section + 1);
}
QVariant CustomTableModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::DisplayRole) {
return m_data[index.row()]->at(index.column());
} else if (role == Qt::EditRole) {
return m_data[index.row()]->at(index.column());
} else if (role == Qt::BackgroundRole) {
for (const QRect &rect : m_mapping) {
if (rect.contains(index.column(), index.row()))
return QColor(m_mapping.key(rect));
}
// cell not mapped return white color
return QColor(Qt::white);
}
return QVariant();
}
bool CustomTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.isValid() && role == Qt::EditRole) {
m_data[index.row()]->replace(index.column(), value.toDouble());
emit dataChanged(index, index);
return true;
}
return false;
}
Qt::ItemFlags CustomTableModel::flags(const QModelIndex &index) const
{
return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
}
void CustomTableModel::addMapping(QString color, QRect area)
{
m_mapping.insertMulti(color, area);
}
TableWidget.cpp代码修改如下:
#include "tablewidget.h"
#include <QtWidgets/QGridLayout>
#include <QtWidgets/QTableView>
#include <QtCharts/QChart>
#include <QtCharts/QChartView>
#include <QtCharts/QLineSeries>
#include <QtCharts/QVXYModelMapper>
#include <QtCharts/QBarSeries>
#include <QtCharts/QBarSet>
#include <QtCharts/QVBarModelMapper>
#include <QtWidgets/QHeaderView>
#include <QtCharts/QBarCategoryAxis>
#include <QtCharts/QValueAxis>
#include<QDebug>
QT_CHARTS_USE_NAMESPACE
TableWidget::TableWidget(QWidget *parent)
: QWidget(parent)
{
qDebug()<<"窗口已生成";
// create simple model for storing data
// user's table data model
//! [1]
m_model = new CustomTableModel;
//! [1]
//! [2]
// create table view and add model to it
QTableView *tableView = new QTableView;
tableView->setModel(m_model);
tableView->setMinimumWidth(300);
tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
m_model->setParent(tableView);
//! [2]
//! [3]
QChart *chart = new QChart;
chart->setAnimationOptions(QChart::AllAnimations);
//! [3]
// series 1
//! [4]
QBarSeries *series = new QBarSeries;
int first = 0;
int count = 12;
QVBarModelMapper *mapper = new QVBarModelMapper(this);
mapper->setFirstBarSetColumn(0);
mapper->setLastBarSetColumn(1);
mapper->setFirstRow(first);
mapper->setRowCount(count);
mapper->setSeries(series);
mapper->setModel(m_model);
chart->addSeries(series);
//! [4]
//! [5]
// for storing color hex from the series
QString seriesColorHex = "#000000";
// get the color of the series and use it for showing the mapped area
QList<QBarSet *> barsets = series->barSets();
for (int i = 0; i < barsets.count(); i++) {
seriesColorHex = "#" + QString::number(barsets.at(i)->brush().color().rgb(), 16).right(6).toUpper();
m_model->addMapping(seriesColorHex, QRect(1 + i, first, 1, barsets.at(i)->count()));
}
//! [5]
//! [6]
QStringList categories;
//修改坐标字段
categories << "1月" << "2月" << "3月" << "4月" <<"5月"<<"6月"<<"7月"
<<"8月"<<"9月"<<"10月"<<"11月"<<"12月";
QBarCategoryAxis *axisX = new QBarCategoryAxis();
axisX->append(categories);
chart->addAxis(axisX, Qt::AlignBottom);
series->attachAxis(axisX);
QValueAxis *axisY = new QValueAxis();
chart->addAxis(axisY, Qt::AlignLeft);
series->attachAxis(axisY);
//! [6]
//! [7]
QChartView *chartView = new QChartView(chart);
chartView->setRenderHint(QPainter::Antialiasing);
chartView->setMinimumSize(800, 600);
//! [7]
//! [8]
// create main layout
QGridLayout *mainLayout = new QGridLayout;
mainLayout->addWidget(tableView, 1, 0);
mainLayout->addWidget(chartView, 1, 1);
mainLayout->setColumnStretch(1, 1);
mainLayout->setColumnStretch(0, 0);
setLayout(mainLayout);
//! [8]
}
运行后的柱状图变成了下面这样,可以看到,数据是从数据库读出(在sale表中插入测试数据即可测试)
demo源码已上传至github:点我去下载https://github.com/HelloZzy23/bookManageClient
更多推荐
所有评论(0)