汽车销售管理系统
一、主界面设计
新建项目manager
基类
进入项目后,要进行以下设置
设置宽750,高500,windowTitle为:汽车销售管理系统
拖拽一个Stacked Widget,并设置宽高700*400,修改currentPageName属性为managePage
向Stacked Widget内拖入以下部件
label
- 显示文本:品牌车管理
- 字号:12
- frameShape:StylePanel
- alignment:水平的设置为AlignHCenter
ToolBox:
- frameShape:WinPanel
- currentItemText:出售车辆
添加组件如下
部件 | objectName属性 |
---|---|
厂商右侧的QComboBox | sellFactoryComboBox |
品牌右侧的QComboBox | sellBrandComboBox |
报价右侧的QLineEdit | sellPriceLineEdit |
数量右侧的QSpinBox | sellNumSpinBox |
金额右侧的QLineEdit | sellNumLineEdit |
剩余数量QLabel | sellLastNumLabel |
确定QPushButton | sellOkBtn |
取消QPushButton | sellCancelBtn |
显示日销量清单(右侧) | dailyList |
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Widget</class>
<widget class="QWidget" name="Widget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>750</width>
<height>500</height>
</rect>
</property>
<property name="windowTitle">
<string>汽车销售管理系统</string>
</property>
<widget class="QStackedWidget" name="stackedWidget">
<property name="geometry">
<rect>
<x>20</x>
<y>40</y>
<width>700</width>
<height>400</height>
</rect>
</property>
<widget class="QWidget" name="managePage">
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>0</x>
<y>20</y>
<width>691</width>
<height>31</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>13</pointsize>
<underline>false</underline>
</font>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="text">
<string>品牌车管理</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="QListWidget" name="dailyList">
<property name="geometry">
<rect>
<x>370</x>
<y>70</y>
<width>321</width>
<height>321</height>
</rect>
</property>
</widget>
<widget class="QToolBox" name="toolBox">
<property name="geometry">
<rect>
<x>0</x>
<y>70</y>
<width>341</width>
<height>321</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::WinPanel</enum>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>337</width>
<height>249</height>
</rect>
</property>
<attribute name="label">
<string>出售车辆</string>
</attribute>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>30</x>
<y>0</y>
<width>67</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>厂商</string>
</property>
</widget>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>30</x>
<y>40</y>
<width>67</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>品牌</string>
</property>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>30</x>
<y>80</y>
<width>67</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>报价</string>
</property>
</widget>
<widget class="QLabel" name="label_5">
<property name="geometry">
<rect>
<x>30</x>
<y>120</y>
<width>67</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>数量</string>
</property>
</widget>
<widget class="QLabel" name="label_6">
<property name="geometry">
<rect>
<x>30</x>
<y>160</y>
<width>67</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>金额</string>
</property>
</widget>
<widget class="QPushButton" name="sellOkBtn">
<property name="geometry">
<rect>
<x>30</x>
<y>200</y>
<width>99</width>
<height>27</height>
</rect>
</property>
<property name="text">
<string>确定</string>
</property>
</widget>
<widget class="QPushButton" name="sellCancelBtn">
<property name="geometry">
<rect>
<x>170</x>
<y>200</y>
<width>99</width>
<height>27</height>
</rect>
</property>
<property name="text">
<string>取消</string>
</property>
</widget>
<widget class="QComboBox" name="sellFactoryComboBox">
<property name="geometry">
<rect>
<x>130</x>
<y>0</y>
<width>111</width>
<height>27</height>
</rect>
</property>
</widget>
<widget class="QComboBox" name="sellBrandComboBox">
<property name="geometry">
<rect>
<x>130</x>
<y>40</y>
<width>111</width>
<height>27</height>
</rect>
</property>
</widget>
<widget class="QLineEdit" name="sellPriceLineEdit">
<property name="geometry">
<rect>
<x>130</x>
<y>80</y>
<width>113</width>
<height>27</height>
</rect>
</property>
</widget>
<widget class="QSpinBox" name="sellNumSpinBox">
<property name="geometry">
<rect>
<x>130</x>
<y>120</y>
<width>48</width>
<height>27</height>
</rect>
</property>
</widget>
<widget class="QLineEdit" name="sellNumLineEdit">
<property name="geometry">
<rect>
<x>130</x>
<y>160</y>
<width>113</width>
<height>27</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="sellLastNumLabel">
<property name="geometry">
<rect>
<x>180</x>
<y>124</y>
<width>111</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>剩余数量:000</string>
</property>
</widget>
</widget>
<widget class="QWidget" name="page_3">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>337</width>
<height>249</height>
</rect>
</property>
<attribute name="label">
<string>Page 2</string>
</attribute>
</widget>
</widget>
</widget>
<widget class="QWidget" name="page_2"/>
</widget>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>
二、菜单设置
设置头文件
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
//添加头文件
#include <QMainWindow>
#include <QMenuBar>
namespace Ui {
class Widget;
}
//修改基类为QMainWindow
class Widget : public QMainWindow
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
//添加主菜单
QMenu *manageMenu;//销售管理主菜单
QMenu *passwordMenu;//修改密码主菜单
//动作Action定义
QAction *manageAction;
QAction *chartAction;
QAction *quitAction;
private slots:
void manageMenu_clicked();//实现品牌车管理子菜单功能函数
void chartMenu_clicked();//实现销售统计子菜单功能函数
void quitMenu_clicked();//实现退出子菜单功能函数
private:
Ui::Widget *ui;
void createMenuBar(); //生成菜单栏函数
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
//修改基类
QMainWindow(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
createMenuBar();//生成菜单栏
}
Widget::~Widget()
{
delete ui;
}
void Widget::createMenuBar()
{
/*
* tr()用来定义一个本地化字符串
* this表示当前对象所在窗口
*/
manageAction = new QAction(tr("品牌车管理"), this);
chartAction = new QAction(tr("销售统计"), this);
quitAction = new QAction(tr("退出"), this);
//设置快捷键
manageAction->setShortcut(tr("Ctrl+M"));
chartAction->setShortcut(tr("Ctrl+C"));
quitAction->setShortcut(tr("Ctrl+Q"));
//添加菜单到菜单栏中
manageMenu = menuBar()->addMenu(tr("销售管理"));
//在菜单等控件中添加动作
manageMenu->addAction(manageAction);
manageMenu->addAction(chartAction);
manageMenu->addSeparator();//添加工具栏中的分隔线
manageMenu->addAction(quitAction);
passwordMenu = menuBar()->addMenu(tr("修改密码"));
connect(manageAction,SIGNAL(triggered()),
this,SLOT(on_manageMenu_clicked()));
connect(chartAction,SIGNAL(triggered()),this,SLOT(on_chartMenu_clicked()));
connect(quitAction,SIGNAL(triggered()),this,SLOT(on_quitMenu_clicked()));
}
void Widget::on_manageMenu_clicked()
{
//stackedWidget通常用于在不同的子页面中进行切换
//setCurrentIndex()设置当前子页面的索引
ui->stackedWidget->setCurrentIndex(0);
}
void Widget::on_chartMenu_clicked()
{
ui->stackedWidget->setCurrentIndex(1);
}
void Widget::on_quitMenu_clicked()
{
this->close();
}
三、出售车辆
创建两个表
- factory:厂家表,存3个汽车生产商。
- brand:品牌表,保存品牌所属的厂家、总量、销售量和库存信息。
集成QtSQL模块和QtXML模块
在manage.pro文件中添加
QT += core gui sql xml
新建头文件connection.h
#ifndef CONNECTION_H
#define CONNECTION_H
#include <QtSql>
#include <QtDebug>
//处理xml文件
#include "qdom.h"
//连接数据库
static bool createConnection()
{
//连接sqlite数据库
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
//设置数据库名
db.setDatabaseName("data.db");
//通过open()方法打开到数据库的连接
if(!db.open())
{
qDebug() << "ERROR:Failed to connect datbase." << db.lastError();
return false;
}
//创建Query对象,执行sql语句并在数据库中对数据进行操作
QSqlQuery query;
//创建工厂表
query.exec("create table factory(id varchar primary key,name varchar)");
query.exec(QString::fromLocal8Bit("insert into factory values('0','请选择厂家')"));
query.exec(QString::fromLocal8Bit("insert into factory values('01','一汽大众')"));
query.exec(QString::fromLocal8Bit("insert into factory values('02','二汽神龙')"));
query.exec(QString::fromLocal8Bit("insert into factory values('03','上海大众')"));
//创建品牌表
query.exec("create table brand(id varchar primary key,name varchar,factory varchar,price int,sum int,sell int,last int)");
query.exec(QString::fromLocal8Bit("insert into brand values ('01','奥迪A6','一汽大众',36,50,10,40)"));
query.exec(QString::fromLocal8Bit("insert into brand values('02','捷达','一汽大众',41,80,20,60)"));
query.exec(QString::fromLocal8Bit("insert into brand values('03','宝来','一汽大众',83,40,15,25)"));
query.exec(QString::fromLocal8Bit("insert into brand values('04','奔驰','一汽大众',39,50,15,35)"));
query.exec(QString::fromLocal8Bit("insert into brand values('05','毕加索','二汽神龙',28,60,20,40)"));
query.exec(QString::fromLocal8Bit("insert into brand values('06','富士康','二汽神龙',27,70,20,50)"));
query.exec(QString::fromLocal8Bit("insert into brand values('07','帕萨特','上海大众',25,70,10,60)"));
query.exec(QString::fromLocal8Bit("insert into brand values('08','辉腾','上海大众',26,55,5,50)"));
query.exec(QString::fromLocal8Bit("insert into brand values('09','桑塔纳','上海大众',36,65,25,40)"));
qDebug() << "数据导入成功";
return true;
}
#endif // CONNECTION_H
在main.cpp引入头文件
#include "widget.h"
#include <QApplication>
//引入自定义头文件
#include <connection.h>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//一个简单的错误处理机制,如果连接数据库失败,返回0,如果成功,继续执行
if(!createConnection()) return 0;
Widget w;
w.show();
return a.exec();
}
加载厂家对应的品牌名称
首先要设置信号对应的槽
#include "widget.h"
#include "ui_widget.h"
#include <QtSql>
Widget::Widget(QWidget *parent) :
//修改基类
QMainWindow(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
createMenuBar();//生成菜单栏
QSqlQueryModel *factoryModel = new QSqlQueryModel(this);
//执行查询语句
factoryModel->setQuery("select name from factory");
//把查到的结果设置到sellFactoryComboBox组件上
ui->sellFactoryComboBox->setModel(factoryModel);
}
.....
void Widget::on_sellFactoryComboBox_currentIndexChanged(const QString &arg1)
{
if(arg1 == "请选择厂家")
{
//对其他部件的状态设置
}
else
{
//设置品牌控件为启用
ui->sellBrandComboBox->setEnabled(true);
QSqlQueryModel *model = new QSqlQueryModel(this);
model->setQuery(QString("select name from brand where factory='%1'").arg(arg1));
ui->sellBrandComboBox->setModel(model);
}
}
加载某品牌的报价和数量
当选择了品牌后需要自动在下方显示报价和数量的对应信息。
在设计模式中,鼠标右键点击控件并设置对应的信号槽
void Widget::on_sellBrandComboBox_currentIndexChanged(const QString &arg1)
{
//创建查询对象
QSqlQuery query;
query.exec(QString("select price from brand where name='%1' and factory='%2'").arg(arg1).arg(ui->sellFactoryComboBox->currentText()));
//使用next()函数来获取当前游标所指向的结果集的下一行数据
query.next();
//启用该控件,使该控件可以与用户进行交互,我们目前需要他输入销售价格
ui->sellPriceLineEdit->setEnabled(true);
//将查询结果集中的第一列值(销售价格)自动填充到指定的控件中
ui->sellPriceLineEdit->setText(query.value(0).toString());
query.exec(QString("select last from brand where name='%1' and factory='%2'").arg(arg1).arg(ui->sellFactoryComboBox->currentText()));
query.next();
int num = query.value(0).toInt();
ui->sellNumSpinBox->setEnabled(true);
//数量控件中的数值,不能超过库存最大值
ui->sellNumSpinBox->setMaximum(num);
ui->sellLastNumLabel->setText(
QString::fromLocal8Bit("剩余数量:%1").arg(num));
ui->sellLastNumLabel->setVisible(true);
}
显示购车总价
更改购买数量之后,自动显示总价格
void Widget::on_sellNumSpinBox_valueChanged(int arg1)
{
if (arg1 == 0)
{
//确保每次操作之前,控件不会显示上一次的销售数量
ui->sellNumLineEdit->clear();
//禁用控件
ui->sellNumLineEdit->setEnabled(false);
ui->sellOkBtn->setEnabled(false);
}
else
{
ui->sellNumLineEdit->setEnabled(true);
//计算销售总额
int num = arg1 * ui->sellPriceLineEdit->text().toInt();
ui->sellNumLineEdit->setText(QString::number(num));
ui->sellOkBtn->setEnabled(true);
}
}
实现购买成功
取消
void Widget::on_sellCancelBtn_clicked()
{
//清空控件内容
ui->sellFactoryComboBox->setCurrentIndex(0);
ui->sellBrandComboBox->clear();
}
然后在这里调用
void Widget::on_sellFactoryComboBox_currentIndexChanged(const QString &arg1)
{
if(arg1 == "请选择厂家")
{
//对其他部件的状态设置
on_sellCancelBtn_clicked();
}
确定
void Widget::on_sellOkBtn_clicked()
{
//计算剩余库存
int last = ui->sellNumSpinBox->maximum()
- ui->sellNumSpinBox->value();
//销售数量
QSqlQuery query;
query.exec(QString("select sell from brand where name='%1' and factory='%2'").arg(ui->sellBrandComboBox->currentText()).arg(ui->sellFactoryComboBox->currentText()));
query.next();
int sell = query.value(0).toInt()
+ ui->sellNumSpinBox->value();
//事务操作
QSqlDatabase::database().transaction();
//更新brand表的销售和库存字段,更新完成rtn设置为true,表示成功
bool rtn = query.exec(QString("update brand set sell=%1,last=%2 where name='%3' and factory='%4'").arg(sell).arg(last).arg(ui->sellBrandComboBox->currentText()).arg(ui->sellFactoryComboBox->currentText()));
if(rtn)
{
QSqlDatabase::database().commit();//提交
//需要引入头文件QMessageBox
QMessageBox::information(
this,
QString::fromLocal8Bit("提示"),
QString::fromLocal8Bit("购买成功"),
QMessageBox::Ok);
on_sellCancelBtn_clicked();
}
else
{
QSqlDatabase::database().rollback();//回滚
}
ui->sellOkBtn->setEnabled(false);
}
日销售清单
汽车的销售记录使用XML文档保存
在XML文件中,按照时间存储交易的记录,每完成一次交易就将交易信息写入XML文件中,然后在dailyList中更新显示。
创建XML文件
conneciont.h
#include "qxml.h"
......
//创建XML文件
static bool createXML()
{
//创建文件,如果存在则会退出
QFile file("data.xml");
if (file.exists())
return true;
//以只读打开文件并清除文件内容
//如果文件不存在,则创建文件,如果文件存在则打开
if(!file.open(QIODevice::WriteOnly|QIODevice::Truncate))
return false;
//创建和操作XML文档,也可以解析XML文档,并将其表示成一个DOM树的形式
//引入#include <QDomDocument>
QDomDocument dom;
//使用writer写入文件
QXmlStreamWriter writer(&file);
//开始自动格式化,可以在写入XML文档的时候,自动进行缩进和换行
writer.setAutoFormatting(true);
//写入XML文档的开头
writer.writeStartDocument();
//写入XML文档的结尾
writer.writeEndDocument();//意味着XML文档已经写入完成
//根元素
QDomElement root = dom.createElement(
QString::fromLocal8Bit("日销售清单"));
//添加到文档中,成成XML文档的根节点
dom.appendChild(root);
QTextStream out(&file);
//out:输出设备(输出到文件中),表示将XML数据写入目标设备(如内存,文件等)
//4:int类型,指定自动缩进的空格数
dom.save(out, 4);
file.close();
return true;
}
main.cpp
#include "widget.h"
#include <QApplication>
//引入自定义头文件
#include <connection.h>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//一个简单的错误处理机制,如果连接数据库失败,返回0,如果成功,继续执行
if(!createConnection()||!createXML()) return 0;
Widget w;
w.show();
return a.exec();
}
widget.h
添加枚举变量
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
//添加头文件
#include <QMainWindow>
#include <QMenuBar>
#include "qdom.h"
namespace Ui {
class Widget;
}
//修改基类为QMainWindow
class Widget : public QMainWindow
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
//添加主菜单
QMenu *manageMenu;//销售管理主菜单
QMenu *passwordMenu;//修改密码主菜单
/*
* 新增枚举变量用于获取当前的时间和日期
* 定义了一个枚举类型,分别包含三个枚举值Time,Date,DataTime
* 分别表示时间,日期,日期时间三种情况
* 枚举值是常量,不可更改,可以在程序中更安全的使用,减少了出错的可能
*/
enum DateTimeType{Time, Date, DateTime};
QString getDateTime(DateTimeType type);
//动作Action定义
QAction *manageAction;
QAction *chartAction;
QAction *quitAction;
添加几个私有对象和私有函数的定义声明
private:
Ui::Widget *ui;
void createMenuBar(); //生成菜单栏函数
QDomDocument doc;
bool docRead(); //读取XML文件
bool docWrite(); //写入XML文件
bool writeXML();
void createNodes(QDomElement &date); //创建销售汽车信息节点
void showDailyList(); //按照指定格式显示销售记录
};
widget.cpp
//添加获取时间和日期的函数的代码
QString Widget::getDateTime(DateTimeType type)
{
QDateTime datetime = QDateTime::currentDateTime();
QString date = datetime.toString("yyyy-MM-dd");
QString time = datetime.toString("hh:mm");
QString dateAndTime =
datetime.toString("yyyy-MM-dd dddd:hh:mm");
if(type == Time)
{
return time;
}
else if(type == Date)
{
return date;
}
else
{
return dateAndTime;
}
}
//读取XML文件
bool Widget::docRead()
{
QFile file("data.xml");
if(!file.open(QIODevice::ReadOnly))
{
return false;
}
/*
* setContent()是QDomDocument的一个成员函数
* 用于从一个QIODevice对象中读取XML数据并解析
* 成一个QDomDocument文档树
*/
if(!doc.setContent(&file))
{
file.close();
return false;
}
file.close();
return true;
}
bool Widget::docWrite()
{
QFile file("data.xml");
if(!file.open(QIODevice::WriteOnly|QIODevice::Truncate))
{
return false;
}
QTextStream out(&file);
doc.save(out, 4);
file.close();
return true;
}
//修改XML文档函数,该函数在售车成功的时候被调用
//将销售的汽车信息保存到XML文件中
bool Widget::writeXML()
{
if(docRead())
{
QString currentDate = getDateTime(Date);
//返回根节点元素
QDomElement root = doc.documentElement();
//判断在根节点下是否有子节点
if(!root.hasChildNodes())//没有子节点
{
QDomElement date = doc.createElement(
QString::fromLocal8Bit("日期")
);
//创建属性
QDomAttr curDate = doc.createAttribute("date");
//设置属性
curDate.setValue(currentDate);
//设置节点属性
date.setAttributeNode(curDate);
//在根节点上添加指定子节点
root.appendChild(date);
createNodes(date);
}
else//有子节点
{
//获取根节点的最后一个元素
QDomElement date = root.lastChild().toElement();
//判断是否有今天的日期的节点
if(date.attribute("date") == currentDate)
{
createNodes(date);
}
else
{
//如果没有今天的节点,那么创建今日的节点
//可以在今天的节点下直接添加销售信息
QDomElement date = doc.createElement(
QString::fromLocal8Bit("日期")
);
//创建属性
QDomAttr curDate = doc.createAttribute("date");
//设置属性
curDate.setValue(currentDate);
//设置节点属性
date.setAttributeNode(curDate);
//在根节点上添加指定子节点
root.appendChild(date);
}
}
return docWrite();
}
else
{
return false;
}
}
//创建销售汽车信息节点
void Widget::createNodes(QDomElement &date)
{
QDomElement time = doc.createElement(
QString::fromLocal8Bit("时间")
);
QDomAttr curtime = doc.createAttribute("time");
curtime.setValue(getDateTime(Time));
time.setAttributeNode(curtime);
date.appendChild(time);
QDomElement factory = doc.createElement(
QString::fromLocal8Bit("厂家"));
QDomElement brand = doc.createElement(
QString::fromLocal8Bit("品牌"));
QDomElement price = doc.createElement(
QString::fromLocal8Bit("价格"));
QDomElement num = doc.createElement(
QString::fromLocal8Bit("数量"));
QDomElement sum = doc.createElement(
QString::fromLocal8Bit("金额"));
QDomText text;
text = doc.createTextNode(
ui->sellFactoryComboBox->currentText());
factory.appendChild(text);
text = doc.createTextNode(
ui->sellBrandComboBox->currentText());
brand.appendChild(text);
text = doc.createTextNode(
ui->sellPriceLineEdit->text());
price.appendChild(text);
text = doc.createTextNode(
QString("%1").arg(ui->sellNumSpinBox->value()));
num.appendChild(text);
text = doc.createTextNode(
ui->sellNumLineEdit->text());
sum.appendChild(text);
time.appendChild(factory);
time.appendChild(brand);
time.appendChild(price);
time.appendChild(num);
time.appendChild(sum);
}
//显示日销售清单
//显示日销售清单
void Widget::showDailyList()
{
//在此函数中,我们需要读取XML文档中的今天的销售数据
//并以指定的格式显示出来
ui->dailyList->clear();
if(docRead())
{
QDomElement root = doc.documentElement();
//获取元素标签名
QString title = root.tagName();
QListWidgetItem *titleItem = new QListWidgetItem();
titleItem->setText(QString("------%1------").arg(title));
titleItem->setTextAlignment(Qt::AlignCenter);//居中
ui->dailyList->addItem(titleItem);
if(root.hasChildNodes())
{
QString currentDate = getDateTime(Date);
QDomElement dateElement = root.lastChild().toElement();
QString date = dateElement.attribute("date");
if(date == currentDate)
{
ui->dailyList->addItem("");
ui->dailyList->addItem(
QString::fromLocal8Bit("日期%1").arg(date));
ui->dailyList->addItem("");
//遍历出当前所有的销售的汽车信息
QDomNodeList children = dateElement.childNodes();
for(size_t i=0; i< children.count();i++)
{
QDomNode node = children.at(i);
QString time = node.toElement().attribute("time");
QDomNodeList list = node.childNodes();
QString factory = list.at(0).toElement().text();
QString brand = list.at(1).toElement().text();
QString price = list.at(2).toElement().text();
QString num = list.at(3).toElement().text();
QString sum = list.at(4).toElement().text();
QString str = time + QString::fromLocal8Bit("出售")
+ brand + factory + " " + num
+ QString::fromLocal8Bit("辆,成交价:") + price
+ QString::fromLocal8Bit("万,共计") + sum
+ QString::fromLocal8Bit("万元");
QListWidgetItem *temp = new QListWidgetItem;
temp->setText("**************************");
temp->setTextAlignment(Qt::AlignCenter);
ui->dailyList->addItem(temp);
ui->dailyList->addItem(str);
}
}
}
}
}
然后我们需要在构造函数中调用
Widget::Widget(QWidget *parent) :
//修改基类
QMainWindow(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
createMenuBar();//生成菜单栏
QSqlQueryModel *factoryModel = new QSqlQueryModel(this);
//执行查询语句
factoryModel->setQuery("select name from factory");
//把查到的结果设置到sellFactoryComboBox组件上
ui->sellFactoryComboBox->setModel(factoryModel);
//显示销售清单
showDailyList();
}
并且在clicked函数中调用
void Widget::on_sellOkBtn_clicked()
{
......
if(rtn)
{
QSqlDatabase::database().commit();//提交
//需要引入头文件QMessageBox
QMessageBox::information(
this,
QString::fromLocal8Bit("提示"),
QString::fromLocal8Bit("购买成功"),
QMessageBox::Ok);
//调用xml操作的函数
writeXML();
showDailyList();
on_sellCancelBtn_clicked();
}
......
}
销售统计功能表
表格、直方图显示汽车销售的数量信息。
视图设计
新建类
头文件
#ifndef PIEVIEW_H
#define PIEVIEW_H
//一个抽象基类,用于显示和编辑模型数据的视图类
#include <QAbstractItemView>
class PieView : public QAbstractItemView
{
Q_OBJECT
public:
explicit PieView(QWidget *parent);
~PieView();
//视图显示函数
void paintEvent(QPaintEvent*);
//为selections赋值
void setSelectionModel(QItemSelectionModel *selectionModel);
//QRegion用于表示二维控件中的几何区域,可以表示多个几何形状,包括矩形,圆形等
QRegion itemRegion(QModelIndex index);
//获取指定索引所在项在视图坐标中的矩形区域
QRect visualRect(const QModelIndex &index) const;
//用于滚动视图以确保索引所在项可见
void scrollTo(const QModelIndex &index,
ScrollHint hint=EnsureVisible);
//获取给定的点所在的模型索引
QModelIndex indexAt(const QPoint &point) const;
//根据指定的光标操作和键盘修饰符来计算返回一个新的模型索引
QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers);
//获取水平,垂直方向的偏移量
int horizontalOffset() const;
int verticalOffset() const;
//检查所在索引是否被隐藏
bool isIndexHidden(const QModelIndex &index) const;
//根据给定的矩形范围和选择命令设置视图中的选中项
void setSelection(const QRect &rect,
QItemSelectionModel::SelectionFlags command);
//根据给定的选项计算并返回一个表示视图中相对应区域的QRegion对象
QRegion visualRegionForSelection(const QItemSelection &selection) const;
private:
//用于管理项的选择模型的类,用于管理被选择的项
QItemSelectionModel *selections;
//用于存储Qregion对象的列表
QList<QRegion> RegionList;
};
#endif // PIEVIEW_H
pieview.cpp
#include "pieview.h"
#include "qpainter.h"
PieView::PieView(QWidget *parent)
:QAbstractItemView(parent)
{
}
PieView::~PieView()
{
}
void PieView::paintEvent(QPaintEvent *)
{
QPainter painter(viewport());
painter.setPen(Qt::black);
int x0 = 40;
int y0 = 250;
//绘制Y轴
painter.drawLine(x0,y0,40,30);
painter.drawLine(38,32,40,30);
painter.drawLine(40,30,42,32);
painter.drawText(20,30,tr("销售数量"));
for(int i=1;i<5;i++)
{
painter.drawLine(-1,-i*50,1,-i*50);
painter.drawText(-20,-i*50,tr("%1").arg(i*5));
}
//X轴
painter.drawLine(x0,y0,540,250);
painter.drawLine(538,248,540,250);
painter.drawLine(540,250,538,252);
painter.drawText(540,250,tr("品牌"));
int pos = x0+20;
int row;
for(row=0;row<model()->rowCount(rootIndex());row++)
{
QModelIndex index = model()->index(row,0,rootIndex());
QString dep = model()->data(index).toString();
painter.drawText(pos,y0+20,dep);
pos+=50;
}
int posN = x0+20;
for(row=0;row<model()->rowCount(rootIndex());row++)
{
//从指定字段获取销量
QModelIndex index = model()->index(row,1,rootIndex());
int sell = model()->data(index).toDouble();
//直方图的高度
int width = 10;
//从第一个字段获取颜色
QModelIndex colorIndex = model()->index(row,0,rootIndex());
QColor color = QColor(model()->data(colorIndex,
Qt::DecorationRole).toString());
if(selections->isSelected(index))
{
painter.setBrush(QBrush(color,Qt::Dense3Pattern));
}
else
{
painter.setBrush(QBrush(color));
}
painter.drawRect(QRect(posN,y0-sell*10,width,sell*10));
QRegion regionM(posN,y0-sell*10,width,sell*10);
RegionList<<regionM;
posN+=50;
}
}
//设置Pieview的选择模型对象
void PieView::setSelectionModel(QItemSelectionModel *selectionModel)
{
//将外部传入的选择模型对象与PieView关联起来,目的是实现对图形的选择和交互
selections = selectionModel;
}
//根据给定的index获取 相应项目的区域
QRegion PieView::itemRegion(QModelIndex index)
{
//首先先创建一个QRegion对象
QRegion region;
if(index.column() == 1)
//判断索引的列是否为1,如果是1,那么将列表中索引所对应的区域赋值给region
//这里索引的行数和列表的索引对应
//如果index.row返回的行数在有效范围内,那么可以通过索引访问到对应的区域对象
region = RegionList[index.row()];
return region;
}
QModelIndex PieView::indexAt(const QPoint &point) const
{
//创建一个新的点,使用传入参数point的x和y的坐标值
QPoint newPoint(point.x(), point.y());
QRegion region;
//遍历RegionList中的对象
foreach(region, RegionList)
{
if(region.contains(newPoint))
{
//对每一个对象进行检查,判断newPoint是否在该区域
//如果true,那么说明newPoint所在的位置对应该区域
//获取该区域对象在列表中的索引值
int row = RegionList.indexOf(region);
//获取对应索引位置的模型索引对象,赋值给index
QModelIndex index = model()->index(row, 1, rootIndex());
return index;
}
}
//如果遍历整个区域后列表仍未找到匹配的区域,那么返回一个无线的模型索引对象
return QModelIndex();
}
//以下是未实现的虚函数的定义声明
QRect PieView::visualRect(const QModelIndex &index) const{}
void PieView::scrollTo(const QModelIndex &index, ScrollHint hint){}
QModelIndex PieView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers){}
int PieView::horizontalOffset() const{}
int PieView::verticalOffset() const{}
bool PieView::isIndexHidden(const QModelIndex &index) const{}
void PieView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command){}
QRegion PieView::visualRegionForSelection(const QItemSelection &selection) const{}
界面设计
首先我们找到stackedWidget
然后选中右侧的三角箭头,进入第二个界面
更改page_2的objectName为chartPage,在此页面,我们显示销售数据的图形
拖入一个QLabel,QPushButton,一个QComboBox
- QLabel对应修改文字:销售统计
- QComboBox修改objectName为factoryComboBox
- QPushButton文字:更新显示,objectName:updateBtn
回到widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
//添加头文件
#include <QMainWindow>
#include <QMenuBar>
#include "qdom.h"
//用于存储和管理标准项目的Qt框架中的模型类
//提供了组织数据的方式,他使用一个二维表格来存储数据
#include <QStandardItemModel>
namespace Ui {
class Widget;
}
//添加类的前置声明
class QStandardItemModel;
//修改基类为QMainWindow
class Widget : public QMainWindow
{
...
private:
...
//添加私有对象
QStandardItemModel *chartModel;
//添加私有函数声明
void createChartModelView();
void showChart();
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QtSql>
#include <QMessageBox>
//新增
#include <QSplitter>
#include <QTableView>
#include "pieview.h"
Widget::Widget(QWidget *parent) :
//修改基类
QMainWindow(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
createMenuBar();//生成菜单栏
QSqlQueryModel *factoryModel = new QSqlQueryModel(this);
//执行查询语句
factoryModel->setQuery("select name from factory");
//把查到的结果设置到sellFactoryComboBox组件上
ui->sellFactoryComboBox->setModel(factoryModel);
//显示销售清单
showDailyList();
//将factoryModel设置位factoryComboBox类型
ui->factoryComboBox->setModel(factoryModel);
//创建销售数据的视图和模型
createChartModelView();
}
Widget::~Widget()
{
delete ui;
}
void Widget::createMenuBar()
{
/*
* tr()用来定义一个本地化字符串
* this表示当前对象所在窗口
*/
manageAction = new QAction(tr("品牌车管理"), this);
chartAction = new QAction(tr("销售统计"), this);
quitAction = new QAction(tr("退出"), this);
//设置快捷键
manageAction->setShortcut(tr("Ctrl+M"));
chartAction->setShortcut(tr("Ctrl+C"));
quitAction->setShortcut(tr("Ctrl+Q"));
//添加菜单到菜单栏中
manageMenu = menuBar()->addMenu(tr("销售管理"));
//在菜单等控件中添加动作
manageMenu->addAction(manageAction);
manageMenu->addAction(chartAction);
manageMenu->addSeparator();//添加工具栏中的分隔线
manageMenu->addAction(quitAction);
passwordMenu = menuBar()->addMenu(tr("修改密码"));
connect(manageAction,SIGNAL(triggered()),
this,SLOT(on_manageMenu_clicked()));
connect(chartAction,SIGNAL(triggered()),this,SLOT(on_chartMenu_clicked()));
connect(quitAction,SIGNAL(triggered()),this,SLOT(on_quitMenu_clicked()));
}
void Widget::on_manageMenu_clicked()
{
//stackedWidget通常用于在不同的子页面中进行切换
//setCurrentIndex()设置当前子页面的索引
ui->stackedWidget->setCurrentIndex(0);
}
void Widget::on_chartMenu_clicked()
{
ui->stackedWidget->setCurrentIndex(1);
}
void Widget::on_quitMenu_clicked()
{
this->close();
}
void Widget::on_sellFactoryComboBox_currentIndexChanged(const QString &arg1)
{
if(arg1 == "请选择厂家")
{
//对其他部件的状态设置
on_sellCancelBtn_clicked();
}
else
{
//设置品牌控件为启用
ui->sellBrandComboBox->setEnabled(true);
QSqlQueryModel *model = new QSqlQueryModel(this);
model->setQuery(QString("select name from brand where factory='%1'").arg(arg1));
ui->sellBrandComboBox->setModel(model);
}
}
void Widget::on_sellBrandComboBox_currentIndexChanged(const QString &arg1)
{
//创建查询对象
QSqlQuery query;
query.exec(QString("select price from brand where name='%1' and factory='%2'").arg(arg1).arg(ui->sellFactoryComboBox->currentText()));
//使用next()函数来获取当前游标所指向的结果集的下一行数据
query.next();
//启用该控件,使该控件可以与用户进行交互,我们目前需要他输入销售价格
ui->sellPriceLineEdit->setEnabled(true);
//将查询结果集中的第一列值(销售价格)自动填充到指定的控件中
ui->sellPriceLineEdit->setText(query.value(0).toString());
query.exec(QString("select last from brand where name='%1' and factory='%2'").arg(arg1).arg(ui->sellFactoryComboBox->currentText()));
query.next();
int num = query.value(0).toInt();
ui->sellNumSpinBox->setEnabled(true);
//数量控件中的数值,不能超过库存最大值
ui->sellNumSpinBox->setMaximum(num);
ui->sellLastNumLabel->setText(
QString::fromLocal8Bit("剩余数量:%1").arg(num));
ui->sellLastNumLabel->setVisible(true);
}
void Widget::on_sellNumSpinBox_valueChanged(int arg1)
{
if (arg1 == 0)
{
//确保每次操作之前,控件不会显示上一次的销售数量
ui->sellNumLineEdit->clear();
//禁用控件
ui->sellNumLineEdit->setEnabled(false);
ui->sellOkBtn->setEnabled(false);
}
else
{
ui->sellNumLineEdit->setEnabled(true);
//计算销售总额
int num = arg1 * ui->sellPriceLineEdit->text().toInt();
ui->sellNumLineEdit->setText(QString::number(num));
ui->sellOkBtn->setEnabled(true);
}
}
void Widget::on_sellCancelBtn_clicked()
{
//清空控件内容
ui->sellFactoryComboBox->setCurrentIndex(0);
ui->sellBrandComboBox->clear();
}
void Widget::on_sellOkBtn_clicked()
{
//计算剩余库存
int last = ui->sellNumSpinBox->maximum()
- ui->sellNumSpinBox->value();
//销售数量
QSqlQuery query;
query.exec(QString("select sell from brand where name='%1' and factory='%2'").arg(ui->sellBrandComboBox->currentText()).arg(ui->sellFactoryComboBox->currentText()));
query.next();
int sell = query.value(0).toInt()
+ ui->sellNumSpinBox->value();
//事务操作
QSqlDatabase::database().transaction();
//更新brand表的销售和库存字段,更新完成rtn设置为true,表示成功
bool rtn = query.exec(QString("update brand set sell=%1,last=%2 where name='%3' and factory='%4'").arg(sell).arg(last).arg(ui->sellBrandComboBox->currentText()).arg(ui->sellFactoryComboBox->currentText()));
if(rtn)
{
QSqlDatabase::database().commit();//提交
//需要引入头文件QMessageBox
QMessageBox::information(
this,
QString::fromLocal8Bit("提示"),
QString::fromLocal8Bit("购买成功"),
QMessageBox::Ok);
//调用xml操作的函数
writeXML();
showDailyList();
on_sellCancelBtn_clicked();
}
else
{
QSqlDatabase::database().rollback();//回滚
}
ui->sellOkBtn->setEnabled(false);
}
//添加获取时间和日期的函数的代码
QString Widget::getDateTime(DateTimeType type)
{
QDateTime datetime = QDateTime::currentDateTime();
QString date = datetime.toString("yyyy-MM-dd");
QString time = datetime.toString("hh:mm");
QString dateAndTime =
datetime.toString("yyyy-MM-dd dddd:hh:mm");
if(type == Time)
{
return time;
}
else if(type == Date)
{
return date;
}
else
{
return dateAndTime;
}
}
//读取XML文件
bool Widget::docRead()
{
QFile file("data.xml");
if(!file.open(QIODevice::ReadOnly))
{
return false;
}
/*
* setContent()是QDomDocument的一个成员函数
* 用于从一个QIODevice对象中读取XML数据并解析
* 成一个QDomDocument文档树
*/
if(!doc.setContent(&file))
{
file.close();
return false;
}
file.close();
return true;
}
bool Widget::docWrite()
{
QFile file("data.xml");
if(!file.open(QIODevice::WriteOnly|QIODevice::Truncate))
{
return false;
}
QTextStream out(&file);
doc.save(out, 4);
file.close();
return true;
}
//修改XML文档函数,该函数在售车成功的时候被调用
//将销售的汽车信息保存到XML文件中
bool Widget::writeXML()
{
if(docRead())
{
QString currentDate = getDateTime(Date);
//返回根节点元素
QDomElement root = doc.documentElement();
//判断在根节点下是否有子节点
if(!root.hasChildNodes())//没有子节点
{
QDomElement date = doc.createElement(
QString::fromLocal8Bit("日期")
);
//创建属性
QDomAttr curDate = doc.createAttribute("date");
//设置属性
curDate.setValue(currentDate);
//设置节点属性
date.setAttributeNode(curDate);
//在根节点上添加指定子节点
root.appendChild(date);
}
else//有子节点
{
//获取根节点的最后一个元素
QDomElement date = root.lastChild().toElement();
//判断是否有今天的日期的节点
if(date.attribute("date") == currentDate)
{
createNodes(date);
}
else
{
//如果没有今天的节点,那么创建今日的节点
//可以在今天的节点下直接添加销售信息
QDomElement date = doc.createElement(
QString::fromLocal8Bit("日期")
);
//创建属性
QDomAttr curDate = doc.createAttribute("date");
//设置属性
curDate.setValue(currentDate);
//设置节点属性
date.setAttributeNode(curDate);
//在根节点上添加指定子节点
root.appendChild(date);
}
}
return docWrite();
}
else
{
return false;
}
}
//创建销售汽车信息节点
void Widget::createNodes(QDomElement &date)
{
QDomElement time = doc.createElement(
QString::fromLocal8Bit("时间")
);
QDomAttr curtime = doc.createAttribute("time");
curtime.setValue(getDateTime(Time));
time.setAttributeNode(curtime);
date.appendChild(time);
QDomElement factory = doc.createElement(
QString::fromLocal8Bit("厂家"));
QDomElement brand = doc.createElement(
QString::fromLocal8Bit("品牌"));
QDomElement price = doc.createElement(
QString::fromLocal8Bit("价格"));
QDomElement num = doc.createElement(
QString::fromLocal8Bit("数量"));
QDomElement sum = doc.createElement(
QString::fromLocal8Bit("金额"));
QDomText text;
text = doc.createTextNode(
ui->sellFactoryComboBox->currentText());
factory.appendChild(text);
text = doc.createTextNode(
ui->sellBrandComboBox->currentText());
brand.appendChild(text);
text = doc.createTextNode(
ui->sellPriceLineEdit->text());
price.appendChild(text);
text = doc.createTextNode(
QString("%1").arg(ui->sellNumSpinBox->value()));
num.appendChild(text);
text = doc.createTextNode(
ui->sellNumLineEdit->text());
sum.appendChild(text);
time.appendChild(factory);
time.appendChild(brand);
time.appendChild(price);
time.appendChild(num);
time.appendChild(sum);
}
//显示日销售清单
void Widget::showDailyList()
{
//在此函数中,我们需要读取XML文档中的今天的销售数据
//并以指定的格式显示出来
ui->dailyList->clear();
if(docRead())
{
QDomElement root = doc.documentElement();
//获取元素标签名
QString title = root.tagName();
QListWidgetItem *titleItem = new QListWidgetItem();
titleItem->setText(QString("------%1------").arg(title));
titleItem->setTextAlignment(Qt::AlignCenter);//居中
ui->dailyList->addItem(titleItem);
if(root.hasChildNodes())
{
QString currentDate = getDateTime(Date);
QDomElement dateElement = root.lastChild().toElement();
QString date = dateElement.attribute("date");
if(date == currentDate)
{
ui->dailyList->addItem("");
ui->dailyList->addItem(
QString::fromLocal8Bit("日期%1").arg(date));
ui->dailyList->addItem("");
//遍历出当前所有的销售的汽车信息
QDomNodeList children = dateElement.childNodes();
for(size_t i=0; i<children.count();i++)
{
QDomNode node = children.at(i);
QString time = node.toElement().attribute("time");
QDomNodeList list = node.childNodes();
QString factory = list.at(0).toElement().text();
QString brand = list.at(1).toElement().text();
QString price = list.at(2).toElement().text();
QString num = list.at(3).toElement().text();
QString sum = list.at(4).toElement().text();
QString str = time + QString::fromLocal8Bit("出售")
+ brand + factory + "\n" + num
+ QString::fromLocal8Bit("辆,成交价:") + price
+ QString::fromLocal8Bit("万,共计") + sum
+ QString::fromLocal8Bit("万元");
QListWidgetItem *temp = new QListWidgetItem;
temp->setText("**************************");
temp->setTextAlignment(Qt::AlignCenter);
ui->dailyList->addItem(temp);
ui->dailyList->addItem(str);
}
}
}
}
}
void Widget::createChartModelView()
{
chartModel = new QStandardItemModel(this);
//设置模型的列数
chartModel->setColumnCount(2);
chartModel->setHeaderData(0, Qt::Horizontal, QString("品牌"));
chartModel->setHeaderData(1, Qt::Horizontal, QString("销售数量"));
//分割窗口的控件,可以实现分割和调整大小,可以通过拖动来分割改变窗口中的部分子控件大小
QSplitter *splitter = new QSplitter(ui->chartPage);
splitter->resize(700, 320);
splitter->move(0, 80);
//表格控件
QTableView *table = new QTableView;
//记得在上面引用pieview.h,主要要在头文件中初始化explicit PieView(QWidget *parent=0);
PieView *pieChart = new PieView;
splitter->addWidget(table);
splitter->addWidget(pieChart);
//设置伸缩因子,第二个参数越大,布局占比越大
splitter->setStretchFactor(0, 1);
splitter->setStretchFactor(1, 2);
table->setModel(chartModel);
pieChart->setModel(chartModel);
QItemSelectionModel *selectionModel = new QItemSelectionModel(chartModel);
//设置表格视图中显示模型
table->setSelectionModel(selectionModel);
pieChart->setSelectionModel(selectionModel);
}
void Widget::showChart()
{
QSqlQuery query;
query.exec(QString::fromLocal8Bit("select name,sell from brand where factory='%1'").arg(ui->factoryComboBox->currentText()));
//从模型中移除指定范围内的行
//0:要移除的起始行
//chartModel->rowCount(QModelIndex()):要移除的行数
chartModel->removeRows(0, chartModel->rowCount(QModelIndex()), QModelIndex());
int row = 0;
while (query.next()) {
int r = qrand()%256;
int g = qrand()%256;
int b = qrand()%256;
chartModel->insertRows(row, 1, QModelIndex());
chartModel->setData(chartModel->index(row, 0, QModelIndex()),
query.value(0).toString());
chartModel->setData(chartModel->index(row, 1, QModelIndex()),
query.value(1).toInt());
chartModel->setData(chartModel->index(row, 0, QModelIndex()),
QColor(r,g,b), Qt::DecorationRole);
row++;
}
}
void Widget::on_factoryComboBox_currentIndexChanged(const QString &arg1)
{
if(arg1 != "请选择厂家")
showChart();
}
void Widget::on_updateBtn_clicked()
{
if(ui->factoryComboBox->currentText() != "请选择厂家")
showChart();
}