使用Qt5开发桌面程序

一.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中,信号与槽函数就负责交互任务。

  • 信号(signal)

作用:发出一个信号,可被所有对象监听,该信号是没有目的的,就像校园里的广播一样,所有人都会听到。在Qt中,这也称为信号的广播机制,如果有对象对这个信号感兴趣,那么它可以使用connect函数来连接此信号,将这个信号(signal)与自己的一个槽(slot)绑定来进行处理。

  • 槽(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

  • 58
    点赞
  • 315
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值