计算器界面及其模块介绍如下:
我们点击查看历史,可以看到效果:
计算器整体功能简单,就是基本的四则运算,加上存储历史记录的功能,可以用来学习QT和SQLite。
我们会使用三个软件,分别为VS+QT(提供界面UI)+SQLite(提供数据库)。
不会QT和SQLite没关系。这里不涉及很深入的知识,手把手教学,包教包会。
下面就从配置开发环境开始。
这里有VS的下载链接,自行取用:VS_Community下载
双击VS_Community.exe,傻瓜式安装(我们只需要C++开发环境),不再详述。
双击打开qt-opensource-windows-x86-5.12-8.exe,出现欢迎界面,点 next:
登录Qt账户:
直接下一步,然后选择文件夹,这个自由选择,只要知道自己装在哪就好了。
打开VS,找到标题栏的 扩展,点击子选项 管理扩展:
在管理扩展中安装Qt VS Tools:
下载安装完成后,关闭VS的所有窗口,会自动应用更改,你只需要最后点击 “modify” 按钮确认即可。
注意: 再次打开VS有可能会出现下图错误,这时两种尝试方法:
之后配置环境变量,WIN+Q组合键打开搜索,键入 “编辑系统环境变量” ,选中打开:
打开VS,找到依次点击:扩展 -> Qt VS Tools -> Qt Options,VS会自动为你选中QT的msvc,如果不报错,配置完成。
注意:如果出现报错,信息显示如:The following error occured:These Qt version are inaccessible
,那么需要以下操作:
重新打开 Qt Options,进行下面操作:
删除原先的Qt Version,只保留我们新添加的,然后将其设为默认:
不报错,就配置完成了。
这里提供SQLite的组件下载地址,大家自行取用:SQLite下载
将两个压缩包放到合适的位置,分别解压即可。
配置环境变量,仍旧是在Path里面,添加SQLite的路径:
按下 WIN+Q 组合键打开搜索,输出 “cmd” ,然后回车,打开命令行,输入sqlite3,回车:
打开VS,点击创建新项目,找到 Qt Empty Application 项目,点击之后进入下一步:
配置项目名称和位置大家自选就好了,如下:
现在编译程序会产生如下的报错,因为我们用的MSVC2017,版本落后,需要升级:
我们需要按照提示,重定向目标工具集,首先右击解决方案,找到重定向目标解决方案,单击即可:
出现重定向项目窗口,确定即可:
再次生成解决方案即可成功。
输入以下代码:
//引入Qt窗口应用程序
#include
int main(int argc, char* argv[]) {
// 打开Qt应用环境
QApplication a(argc, argv);
// 结束Qt应用环境
return a.exec();
}
这里只有Qt的基本应用环境的开启和关闭,没有生成任何界面。
我们需要为项目添加QT的模块,否则连接会出错,如下:
首先右键单击项目,找到 属性,单击,进入属性配置界面:
然后在属性配置界面进行如下操作,进入模块选择界面:
选择 Core GUI SQL Widget 四个模块,点击Finish即可:
右键单击项目,光标移到添加,点击Add Qt Class,如图:
添加一个名为CalculatorMine的类,如下:
配置类的属性:
为构造函数添加默认参数 Q_NULLPTR,否则编译会报错:
我们已经有了计算器类,那么就可以在main函数中添加它的对象。在main中添加以下代码:
// 创建一个计算器对象
CalculatorMine calculatorMine;
// 运行计算器对象的界面
calculatorMine.show();
添加之后的main函数如下:
运行程序,得到基本的窗口,效果如下:
在资源文件夹中添加UI界面,首先右击 Resource Files,光标移到 添加,单击 新建项,如下:
然后根据提示进行配置:
Qt设计师界面会自动打开,或者你双击Resource Files下的CalculatorMine.ui也可以打开,如下:
这个时候,你还查询不到ui界面的头文件,需要将ui文件编译之后得到,如下操作:
编译完成之后,在外部依赖项中有可能可以找到 ui_CalculatorMine.h 文件,找不到没事,不影响。
QT默认的窗口名称和标题为mianWindow,我们可以改为自己想要的:
重新编译CalculatorMine.ui文件。一般来说,UI界面的更改在VS中重新编译.ui文件即可生效,如果不行,重启VS即可。
有了UI类,我们就可以实例化计算器的UI了,在CalculatorMine.h中添加的代码如下:
下一步我们需要初始化对象,并展示其UI。更改CalculatorMine.cpp文件:
我们运行程序,可以看到效果:
面板功能介绍如下:
鼠标左键点击Vertical Layout不放,将其拖放到UI界面效果栏中,如下:
调整UI界面和水平布局,使其大小合适(调整方式就是拖动边缘),效果如下:
先添加一个Text Browser进入上一个水平布局中,可以看到其占据了所有的界面:
我们应该调整其大小,令其占据空间合适,这时候就要用到属性编辑器,操作如下:
以同样的方式编辑其显示字体的大小,把里面显示的字体调大一点,看着舒服:
拖一个水平布局进去,效果如下:
在第二个水平布局中拖入5个垂直布局,可以采用如下方式:
在每个垂直布局中分别拖入4个按钮(Push Button),效果如下:
然后我们修改按钮大小和按钮字体大小,ctrl+点击 逐个选中所有按钮,用属性编辑器:
双击按钮可以编辑按钮的显示文字,编辑成如下的效果:
VS编辑控件都需要其名称,为了方便使用,这里给所有的按钮都对应一个名字,双击其对象即可编辑,效果如下:
添加菜单,双击 “在这里输入” ,然后输入 “设置” ,回车即可,如下:
点击设置,双击 “在这里输入” ,然后输入 “查看历史” ,回车即可,如下:
重复添加 “关闭历史” ,“清除历史”。最终效果如下:
下一步仍旧是修改对象名称,便于使用,修改后的结果如下:
首先,使用Ctrl+S保存对界面的修改,整个界面就算设计完成了。
然后返回VS,编译CalculatorMine.ui。
最后编译运行项目,效果如下:
这里首先为计算器类添加我们用到的成员变量,在CalculatorMine.h的类中添加如下代码:
private:
// 添加一个计算器UI的实例,UI界面的命名空间为Ui
Ui::CalculatorMineWindow* ui;
// 定义数字按钮组(numButtonGroup)和操作符按钮组(operatorButtonGroup)。按钮组内的按钮都是相似的,可以共用一个槽函数
QButtonGroup* numButtonGroup;
QButtonGroup* operatorButtonGroup;
// 定义存储操作符(operatorChar)和操作数(storedNum)以及运算结果(resultNum)的变量
QChar operatorChar;
double storedNum;
double resultNum;
// 定义标志位,isOperatorClicked标记是否已经点击了操作符,isStoredNum标记是否有一个已经存储了一个数字
bool isOperatorClicked;
bool isStoredNum;
// 定义输入数字限制,输入数字的数量不能超过numLimit
const int numLimit = 16;
注释应该比较清楚,添加进去的效果如下:
在这里,我们用到了QButtonGroup这个类,所以需要将其头文件包括一下,输入以下代码:
#include
在文件中的效果如下:
之后我们需要在CalculatorMine.cpp中为成员变量作初始化,在CalculatorMine类的构造函数中添加以下代码:
// 初始化存储变量和标志位变量
isOperatorClicked = false;
isStoredNum = false;
storedNum = 0;
resultNum = 0;
// 初始化按钮组
numButtonGroup = new QButtonGroup;
operatorButtonGroup = new QButtonGroup;
// 为数字按钮组添加按钮: 0 1 2 3 4 5 6 7 8 9
numButtonGroup->addButton(ui->pushButton1, 1);
numButtonGroup->addButton(ui->pushButton2, 2);
numButtonGroup->addButton(ui->pushButton3, 3);
numButtonGroup->addButton(ui->pushButton4, 4);
numButtonGroup->addButton(ui->pushButton5, 5);
numButtonGroup->addButton(ui->pushButton6, 6);
numButtonGroup->addButton(ui->pushButton7, 7);
numButtonGroup->addButton(ui->pushButton8, 8);
numButtonGroup->addButton(ui->pushButton9, 9);
numButtonGroup->addButton(ui->pushButton0, 0);
// 为操作符按钮组添加按钮:+ - * / =
operatorButtonGroup->addButton(ui->pushButtonAdd, 1);
operatorButtonGroup->addButton(ui->pushButtonSub, 2);
operatorButtonGroup->addButton(ui->pushButtonMul, 3);
operatorButtonGroup->addButton(ui->pushButtonDiv, 4);
operatorButtonGroup->addButton(ui->pushButtonEqual, 5);
注释是比较清楚的,其在VS中的效果如下:
我们已经有了各种控件和所有需要的成员变量,下一步就是定义槽函数,执行按钮功能。
在CalculatorMine.h文件的CalculatorMine的类中添加以下代码:
// 定义一系列槽函数(与控件的信号连接),当点击计算器的某个键时,执行。
private slots:
// 当数字按钮组中的某个键被点击时调用,用来更新textBrowser等。
void NumButtonClicked(QAbstractButton*);
// 当操作符按钮组中的某个键被点击时调用,用来更新textBrower和执行运算等。
void OperatorButtonClicked(QAbstractButton*);
// 当C按钮被点击时调用,用来清空textBrowser和各种变量。
void PushButtonCClicked();
// 当Del按钮被点击时调用,用来删除一个输入数字。
void PushButtonDelClicked();
// 当%按钮被点击时调用,用来将操作数除100。
void PushButonPercentClicked();
// 当+/-按钮被点击时调用,用来将操作数取负。
void PushButtonReverseClicked();
// 当=按钮被点击时调用,用来计算和显示结果。
void PushButtonEqualClicked();
// 当Point按钮被点击时调用,用来增加小数点。
void PushButtonPointClicked();
// 当查看历史被点击时调用,用来和数据库通信,获得历史记录并显示。
void SearchHistory();
// 用来清空历史显示
void CloseHistory();
// 用来清空数据库和显示。
void ClearHistory();
private:
// 用来执行运算(在操作符被点击时调用),并与数据库通信,存储数据
void CalculateResult();
这里的注释也是比较清楚的,看一下在VS中的效果:
函数声明之后再CalculatorMine.cpp中实现,在文件末尾添加以下代码:
void CalculatorMine::NumButtonClicked(QAbstractButton* numButton) {
}
void CalculatorMine::OperatorButtonClicked(QAbstractButton* operatorButton) {
}
void CalculatorMine::CalculateResult() {
}
void CalculatorMine::PushButtonCClicked() {
}
void CalculatorMine::PushButtonDelClicked() {
}
void CalculatorMine::PushButonPercentClicked() {
}
void CalculatorMine::PushButtonReverseClicked() {
}
void CalculatorMine::PushButtonEqualClicked() {
}
void CalculatorMine::PushButtonPointClicked() {
}
void CalculatorMine::SearchHistory() {
}
void CalculatorMine::CloseHistory() {
}
void CalculatorMine::ClearHistory() {
}
这不需要多说,就是一个函数实现的框架,在VS中的效果如下:
最后,我们需要连接按钮信号和我们定义的槽函数,在Calculator类的构造函数中添加以下代码:
// 连接按钮组和按钮组的槽函数
numButtonGroup->connect(numButtonGroup, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(NumButtonClicked(QAbstractButton*)));
operatorButtonGroup->connect(operatorButtonGroup, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(OperatorButtonClicked(QAbstractButton*)));
// 连接普通按钮和他们对应的槽函数
connect(ui->pushButtonC, SIGNAL(clicked()), this, SLOT(PushButtonCClicked()));
connect(ui->pushButtonDel, SIGNAL(clicked()), this, SLOT(PushButtonDelClicked()));
connect(ui->pushButtonPercent, SIGNAL(clicked()), this, SLOT(PushButonPercentClicked()));
connect(ui->pushButtonReverse, SIGNAL(clicked()), this, SLOT(PushButtonReverseClicked()));
connect(ui->pushButtonEqual, SIGNAL(clicked()), this, SLOT(PushButtonEqualClicked()));
connect(ui->pushButtonPoint, SIGNAL(clicked()), this, SLOT(PushButtonPointClicked()));
// 连接菜单项和他们对应的槽函数
connect(ui->actionSearchHistory, SIGNAL(triggered()), this, SLOT(SearchHistory()));
connect(ui->actionCloseHistory, SIGNAL(triggered()), this, SLOT(CloseHistory()));
connect(ui->actionClearHistory, SIGNAL(triggered()), this, SLOT(ClearHistory()));
就是一个函数connect,用来连接信号和槽,VS中效果如下:
这里用到了数据库的操作,因此要引入数据库的库。在CalculatorMine.h中添加以下代码:
#include
在VS中的效果如下:
下面开始操作数据库了,在CalculatorMine的构造函数中(连接菜单项代码之前)添加以下代码:
// 设置与数据库的连接。 数据库的类型为“QSQLITE”,即SQLite; 连接的名称为“sqliteMine”(随意设置);
// 连接数据库的主机地址为:127.0.0.1,即回环地址(自己);要操作的数据库名称为 “HistoryData.db”(随意设置);
// 连接的用户为root用户,使用默认密码123456
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "sqliteMine");
db.setHostName("127,0,0,1");
db.setDatabaseName("HistoryData.db");
db.setUserName("root");
db.setPassword("123456");
if (!db.open()) {
ui->textBrowser->setText("can't open database");
}
注释挺清楚的,代码在VS中的效果如下:
所有的准备工作均已完成,下面我们就要实现按钮的功能了。
我们点击一个数字按钮时,调用NumGroupClicked函数,因此在改函数中添加代码:
// 用textShow接收显示器上的显示文本,用来执行判断等
QString textShow = ui->textBrowser->toPlainText();
// 如果上一个点击的是操作符按钮,那么就代表要输入第二个操作数了,此时清空显示,操作符标志置否。
if (isOperatorClicked) {
textShow.clear();
isOperatorClicked = false;
}
// 如果输入的数字数量过多,那么就不能输入了,直接返回
if (textShow.length() >= numLimit) {
return;
}
// 为textShow追加按钮上的文本(0/1/2/3/4/5/6/7/8/9)
textShow.append(numButton->text());
// 将textShow的文本显示到计算器显示器上
ui->textBrowser->setText(textShow);
代码注释比较清楚,在VS中显示其效果:
我们运行程序,在计算器的按钮面板上点击数字按钮,发现会出现相应的数字:
在OperatorButtonClicked函数中添加以下代码:
// 如果上一个点击的也是操作符,那么替换掉,将操作符存储在operatorChar中。
if (isOperatorClicked) {
operatorChar = operatorButton->text().at(0);
}
else {
// 如果已经存储的有数字,那么证明输入了两个数字,可以运算了,进行运算。
if (isStoredNum) {
CalculateResult();
}
else {
// 如果没存数字,证明是第一次点击操作符,那么把显示屏上的数字存起来,这是第一个操作数
QString textShow = ui->textBrowser->toPlainText();
storedNum = textShow.toDouble();
isStoredNum = true;
}
// 操作符标志位设置为真,存储操作符
isOperatorClicked = true;
operatorChar = operatorButton->text().at(0);
}
注释挺清楚的,就不多解释了。
CalculateResult不是槽函数,是点击操作符需要调用的函数,添加如下代码:
// 用textShow记录显示器界面的数字。
QString textShow = ui->textBrowser->toPlainText();
// 第一个操作数是存储的操作数,第二个操作时为显示屏上显示的数字。
double previousNum = storedNum;
double presentNum = textShow.toDouble();
// 根据操作符(+/-/*/÷)的不同,对两个操作数执行不同的运算,存储到resultNum中。
if (operatorChar == '+') {
resultNum = previousNum + presentNum;
}
else if (operatorChar == '-') {
resultNum = previousNum - presentNum;
}
else if (operatorChar == '*') {
resultNum = previousNum * presentNum;
}
else if (operatorChar == '/') {
resultNum = previousNum / presentNum;
}
// 计算的结果可以作为下一次计算的第一个操作数,便于连续运算。
storedNum = resultNum;
// 将数字转为QString类型的字符串,限制其串长度,赋值给texxtShow,显示在显示屏上。
textShow = QString::number(resultNum, 'g', numLimit);
ui->textBrowser->setText(textShow);
// 使用连接 "sqliteMine" 连入数据库,这样就可以访问我们创建的数据库HistoryData了
QSqlDatabase db = QSqlDatabase::database("sqliteMine");
// 使用db初始化一个数据库操作的对象,用来对数据库进行管理
QSqlQuery query(db);
// 定义一个字符串,字符串内容是SQLite的命令,用于创建表。
// CREATE TABLE IF NOT EXISTS :标准的SQL命令,意味着如果后面这个表名 history 不存在的话,那么创建一个新表。
// history是表的名字,previousNum是第一个列的列名,double是第一列的数据类型。很清楚,一共四列。
QString createTableString = "CREATE TABLE IF NOT EXISTS history(previousNum double, operator varchar, presentNum double, resultNum double)";
// 执行创建表的命令,如果创建不成功,在显示器中进行提示。
bool success = query.exec(createTableString);
if (!success) {
ui->textBrowser->setText("can't create table");
}
// 再次定义一个SQLite命令的字符串,用于插入一行数据。
// INSERT INTO :标准的SQL命令,向后面的表 history 中插入数据。
// (previousNum, operator, presentNum, resultNum) : 插入数据列的列名,可以用不一样的顺序,但是列名一定是表中的列。
// VALUES('%1', '%2', '%3', '%4') : VALUES里面就是插入数据的值,这里的‘%1’等是占位符,真正的值是arg()里面的数据。
QString insertDataString = QString("INSERT INTO history(previousNum, operator, presentNum, resultNum) VALUES('%1', '%2', '%3', '%4')").arg(previousNum).arg(operatorChar).arg(presentNum).arg(resultNum);
// 执行插入命令,如果插入不成功,那么在显示器中进行提示。
success = query.exec(insertDataString);
if (!success) {
ui->textBrowser->setText("can't insert data");
}
注释清楚,不多解释。
剩下的槽函数更加简单,所以直接放代码了(都有注释):
void CalculatorMine::PushButtonCClicked() {
// 清除显示器和俩标志位即可
ui->textBrowser->clear();
isOperatorClicked = false;
isStoredNum = false;
}
void CalculatorMine::PushButtonDelClicked() {
// 删除显示器上的最后一个数字后再显示
QString textShow = ui->textBrowser->toPlainText();
if (textShow.length() == 0) {
return;
}
textShow.chop(1);
ui->textBrowser->setText(textShow);
}
void CalculatorMine::PushButonPercentClicked() {
// 把显示器上的数字除以100之后再显示
QString textShow = ui->textBrowser->toPlainText();
double percentResult = 0.01 * textShow.toDouble();
textShow = QString::number(percentResult, 'g', numLimit);
ui->textBrowser->setText(textShow);
}
void CalculatorMine::PushButtonReverseClicked() {
// 把显示器上的数字取负之后再显示
QString textShow = ui->textBrowser->toPlainText();
double reverseResult = -1 * textShow.toDouble();
textShow = QString::number(reverseResult, 'g', numLimit);
ui->textBrowser->setText(textShow);
}
void CalculatorMine::PushButtonEqualClicked() {
// 如果符合运算标准,执行运算操作,操作数标志位置假
QString textShow = ui->textBrowser->toPlainText();
if (!isStoredNum || textShow.length() == 0 || isOperatorClicked) {
return;
}
CalculateResult();
isStoredNum = false;
}
void CalculatorMine::PushButtonPointClicked() {
// 如果显示器上已经有了小数点,直接返回
QString textShow = ui->textBrowser->toPlainText();
if (textShow.length() >= (numLimit - 1) || textShow.contains('.', Qt::CaseSensitive)) {
return;
}
// 如果显示器没有数字,那么那么就是0-1之间的小数,先加入一个0
if (textShow.length() == 0) {
textShow = "0";
}
// 给显示器的末尾添加一个小数点
textShow.append('.');
ui->textBrowser->setText(textShow);
}
void CalculatorMine::SearchHistory() {
// 我们获取的数据共有四个列,分别记录了第一个操作数、操作符、第二个操作数、计算结果。
// 这里我们设置最多取200条运算记录,定义4个数组存储
double previousNum[200];
QString operatorChar[200];
double presentNum[200];
double resultNum[200];
// 连接数据库,初始化数据库操作对象
QSqlDatabase db = QSqlDatabase::database("sqliteMine");
QSqlQuery query(db);
// 定义一个SQL语句的字符串,用于获取数据库中所有的记录
// SELECT * FROM: 标准的SQL命令,用于从后面的表 history 中查询所有的数据
QString selectDataString = "SELECT * FROM history";
// 执行查询语句,如果不成功,在显示屏上进行提示
bool success = query.exec(selectDataString);
if (!success) {
ui->textBrowser->setText("can't get history data");
}
// 将获取的查询结果放入我们事先准备好的数组中。
int index = 0;
while (query.next()) {
previousNum[index] = query.value(0).toDouble();
operatorChar[index] = query.value(1).toString();
presentNum[index] = query.value(2).toDouble();
resultNum[index] = query.value(3).toDouble();
index++;
}
// 将历史记录一条一条的插入到我们的显示器中,格式为 IDx:操作数1 操作符 操作数2 = 运算结果。
ui->textBrowser->clear();
for (int i = 0; i < index; i++) {
QString historyString = QString("ID%1: %2 %3 %4 = %5").arg(i + 1).arg(previousNum[i]).arg(operatorChar[i]).arg(presentNum[i]).arg(resultNum[i]);
ui->textBrowser->append(historyString);
}
}
void CalculatorMine::CloseHistory() {
// 直接清空显示器就好了
ui->textBrowser->clear();
}
void CalculatorMine::ClearHistory() {
// 连接数据库,建立数据库操作对象
QSqlDatabase db = QSqlDatabase::database("sqliteMine");
QSqlQuery query(db);
// 定义一个SQL语句的字符串,用来删除表。
// DROP TABLE : 标准SQL语句,用于删除后面的表 history 。
QString dropTableString = "DROP TABLE history";
// 执行删除表的语句,如果未成功,在显示器上提醒。
bool success = query.exec(dropTableString);
if (!success) {
ui->textBrowser->setText("can't clear history data");
}
else {
ui->textBrowser->clear();
}
}
不解释啦,注释好好看,肯定都看得懂,功能都是类似的。
main.cpp
#include
#include "CalculatorMine.h"
int main(int argc, char* argv[]) {
// 打开Qt应用环境
QApplication a(argc, argv);
// 创建一个计算器对象
CalculatorMine calculatorMine;
// 运行计算器对象的界面
calculatorMine.show();
// 结束Qt应用环境
return a.exec();
}
CalculatorMine.h
#pragma once
#include
#include
#include
#include "ui_CalculatorMine.h"
class CalculatorMine : public QMainWindow
{
Q_OBJECT
public:
CalculatorMine(QWidget *parent = Q_NULLPTR);
~CalculatorMine();
// 定义一系列槽函数(与控件的信号连接),当点击计算器的某个键时,执行。
private slots:
// 当数字按钮组中的某个键被点击时调用,用来更新textBrowser等。
void NumButtonClicked(QAbstractButton*);
// 当操作符按钮组中的某个键被点击时调用,用来更新textBrower和执行运算等。
void OperatorButtonClicked(QAbstractButton*);
// 当C按钮被点击时调用,用来清空textBrowser和各种变量。
void PushButtonCClicked();
// 当Del按钮被点击时调用,用来删除一个输入数字。
void PushButtonDelClicked();
// 当%按钮被点击时调用,用来将操作数除100。
void PushButonPercentClicked();
// 当+/-按钮被点击时调用,用来将操作数取负。
void PushButtonReverseClicked();
// 当=按钮被点击时调用,用来计算和显示结果。
void PushButtonEqualClicked();
// 当Point按钮被点击时调用,用来增加小数点。
void PushButtonPointClicked();
// 当查看历史被点击时调用,用来和数据库通信,获得历史记录并显示。
void SearchHistory();
// 用来清空历史显示
void CloseHistory();
// 用来清空数据库和显示。
void ClearHistory();
private:
// 用来执行运算(在操作符被点击时调用),并与数据库通信,存储数据
void CalculateResult();
private:
// 添加一个计算器UI的实例,UI界面的命名空间为Ui
Ui::CalculatorMineWindow* ui;
// 定义数字按钮组(numButtonGroup)和操作符按钮组(operatorButtonGroup)。按钮组内的按钮都是相似的,可以共用一个槽函数
QButtonGroup* numButtonGroup;
QButtonGroup* operatorButtonGroup;
// 定义存储操作符(operatorChar)和操作数(storedNum)以及运算结果(resultNum)的变量
QChar operatorChar;
double storedNum;
double resultNum;
// 定义标志位,isOperatorClicked标记是否已经点击了操作符,isStoredNum标记是否有一个已经存储了一个数字
bool isOperatorClicked;
bool isStoredNum;
// 定义输入数字限制,输入数字的数量不能超过numLimit
const int numLimit = 16;
};
CalculatorMine.cpp
#include "CalculatorMine.h"
CalculatorMine::CalculatorMine(QWidget *parent)
: QMainWindow(parent),
ui(new Ui::CalculatorMineWindow)
{
// 启动我们的ui界面
ui->setupUi(this);
// 初始化存储变量和标志位变量
isOperatorClicked = false;
isStoredNum = false;
storedNum = 0;
resultNum = 0;
// 初始化按钮组
numButtonGroup = new QButtonGroup;
operatorButtonGroup = new QButtonGroup;
// 为数字按钮组添加按钮: 0 1 2 3 4 5 6 7 8 9
numButtonGroup->addButton(ui->pushButton1, 1);
numButtonGroup->addButton(ui->pushButton2, 2);
numButtonGroup->addButton(ui->pushButton3, 3);
numButtonGroup->addButton(ui->pushButton4, 4);
numButtonGroup->addButton(ui->pushButton5, 5);
numButtonGroup->addButton(ui->pushButton6, 6);
numButtonGroup->addButton(ui->pushButton7, 7);
numButtonGroup->addButton(ui->pushButton8, 8);
numButtonGroup->addButton(ui->pushButton9, 9);
numButtonGroup->addButton(ui->pushButton0, 0);
// 为操作符按钮组添加按钮:+ - * / =
operatorButtonGroup->addButton(ui->pushButtonAdd, 1);
operatorButtonGroup->addButton(ui->pushButtonSub, 2);
operatorButtonGroup->addButton(ui->pushButtonMul, 3);
operatorButtonGroup->addButton(ui->pushButtonDiv, 4);
operatorButtonGroup->addButton(ui->pushButtonEqual, 5);
// 连接按钮组和按钮组的槽函数
numButtonGroup->connect(numButtonGroup, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(NumButtonClicked(QAbstractButton*)));
operatorButtonGroup->connect(operatorButtonGroup, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(OperatorButtonClicked(QAbstractButton*)));
// 连接普通按钮和他们对应的槽函数
connect(ui->pushButtonC, SIGNAL(clicked()), this, SLOT(PushButtonCClicked()));
connect(ui->pushButtonDel, SIGNAL(clicked()), this, SLOT(PushButtonDelClicked()));
connect(ui->pushButtonPercent, SIGNAL(clicked()), this, SLOT(PushButonPercentClicked()));
connect(ui->pushButtonReverse, SIGNAL(clicked()), this, SLOT(PushButtonReverseClicked()));
connect(ui->pushButtonEqual, SIGNAL(clicked()), this, SLOT(PushButtonEqualClicked()));
connect(ui->pushButtonPoint, SIGNAL(clicked()), this, SLOT(PushButtonPointClicked()));
// 设置与数据库的连接。 数据库的类型为“QSQLITE”,即SQLite; 连接的名称为“sqliteMine”(随意设置);
// 连接数据库的主机地址为:127.0.0.1,即回环地址(自己);要操作的数据库名称为 “HistoryData.db”(随意设置);
// 连接的用户为root用户,使用默认密码123456
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "sqliteMine");
db.setHostName("127,0,0,1");
db.setDatabaseName("HistoryData.db");
db.setUserName("root");
db.setPassword("123456");
if (!db.open()) {
ui->textBrowser->setText("can't open database");
}
// 连接菜单项和他们对应的槽函数
connect(ui->actionSearchHistory, SIGNAL(triggered()), this, SLOT(SearchHistory()));
connect(ui->actionCloseHistory, SIGNAL(triggered()), this, SLOT(CloseHistory()));
connect(ui->actionClearHistory, SIGNAL(triggered()), this, SLOT(ClearHistory()));
}
CalculatorMine::~CalculatorMine()
{
}
void CalculatorMine::NumButtonClicked(QAbstractButton* numButton) {
// 用textShow接收显示器上的显示文本,用来执行判断等
QString textShow = ui->textBrowser->toPlainText();
// 如果上一个点击的是操作符按钮,那么就代表要输入第二个操作数了,此时清空显示,操作符标志置否。
if (isOperatorClicked) {
textShow.clear();
isOperatorClicked = false;
}
// 如果输入的数字数量过多,那么就不能输入了,直接返回
if (textShow.length() >= numLimit) {
return;
}
// 为textShow追加按钮上的文本(0/1/2/3/4/5/6/7/8/9)
textShow.append(numButton->text());
// 将textShow的文本显示到计算器显示器上
ui->textBrowser->setText(textShow);
}
void CalculatorMine::OperatorButtonClicked(QAbstractButton* operatorButton) {
// 如果上一个点击的也是操作符,那么替换掉,将操作符存储在operatorChar中。
if (isOperatorClicked) {
operatorChar = operatorButton->text().at(0);
}
else {
// 如果已经存储的有数字,那么证明输入了两个数字,可以运算了,进行运算。
if (isStoredNum) {
CalculateResult();
}
else {
// 如果没存数字,证明是第一次点击操作符,那么把显示屏上的数字存起来,这是第一个操作数
QString textShow = ui->textBrowser->toPlainText();
storedNum = textShow.toDouble();
isStoredNum = true;
}
// 操作符标志位设置为真,存储操作符
isOperatorClicked = true;
operatorChar = operatorButton->text().at(0);
}
}
void CalculatorMine::CalculateResult() {
// 用textShow记录显示器界面的数字。
QString textShow = ui->textBrowser->toPlainText();
// 第一个操作数是存储的操作数,第二个操作时为显示屏上显示的数字。
double previousNum = storedNum;
double presentNum = textShow.toDouble();
// 根据操作符(+/-/*/÷)的不同,对两个操作数执行不同的运算,存储到resultNum中。
if (operatorChar == '+') {
resultNum = previousNum + presentNum;
}
else if (operatorChar == '-') {
resultNum = previousNum - presentNum;
}
else if (operatorChar == '*') {
resultNum = previousNum * presentNum;
}
else if (operatorChar == '/') {
resultNum = previousNum / presentNum;
}
// 计算的结果可以作为下一次计算的第一个操作数,便于连续运算。
storedNum = resultNum;
// 将数字转为QString类型的字符串,限制其串长度,赋值给texxtShow,显示在显示屏上。
textShow = QString::number(resultNum, 'g', numLimit);
ui->textBrowser->setText(textShow);
// 使用连接 "sqliteMine" 连入数据库,这样就可以访问我们创建的数据库HistoryData了
QSqlDatabase db = QSqlDatabase::database("sqliteMine");
// 使用db初始化一个数据库操作的对象,用来对数据库进行管理
QSqlQuery query(db);
// 定义一个字符串,字符串内容是SQLite的命令,用于创建表。
// CREATE TABLE IF NOT EXISTS :标准的SQL命令,意味着如果后面这个表名 history 不存在的话,那么创建一个新表。
// history是表的名字,previousNum是第一个列的列名,double是第一列的数据类型。很清楚,一共四列。
QString createTableString = "CREATE TABLE IF NOT EXISTS history(previousNum double, operator varchar, presentNum double, resultNum double)";
// 执行创建表的命令,如果创建不成功,在显示器中进行提示。
bool success = query.exec(createTableString);
if (!success) {
ui->textBrowser->setText("can't create table");
}
// 再次定义一个SQLite命令的字符串,用于插入一行数据。
// INSERT INTO :标准的SQL命令,向后面的表 history 中插入数据。
// (previousNum, operator, presentNum, resultNum) : 插入数据列的列名,可以用不一样的顺序,但是列名一定是表中的列。
// VALUES('%1', '%2', '%3', '%4') : VALUES里面就是插入数据的值,这里的‘%1’等是占位符,真正的值是arg()里面的数据。
QString insertDataString = QString("INSERT INTO history(previousNum, operator, presentNum, resultNum) VALUES('%1', '%2', '%3', '%4')").arg(previousNum).arg(operatorChar).arg(presentNum).arg(resultNum);
// 执行插入命令,如果插入不成功,那么在显示器中进行提示。
success = query.exec(insertDataString);
if (!success) {
ui->textBrowser->setText("can't insert data");
}
}
void CalculatorMine::PushButtonCClicked() {
// 清除显示器和俩标志位即可
ui->textBrowser->clear();
isOperatorClicked = false;
isStoredNum = false;
}
void CalculatorMine::PushButtonDelClicked() {
// 删除显示器上的最后一个数字后再显示
QString textShow = ui->textBrowser->toPlainText();
if (textShow.length() == 0) {
return;
}
textShow.chop(1);
ui->textBrowser->setText(textShow);
}
void CalculatorMine::PushButonPercentClicked() {
// 把显示器上的数字除以100之后再显示
QString textShow = ui->textBrowser->toPlainText();
double percentResult = 0.01 * textShow.toDouble();
textShow = QString::number(percentResult, 'g', numLimit);
ui->textBrowser->setText(textShow);
}
void CalculatorMine::PushButtonReverseClicked() {
// 把显示器上的数字取负之后再显示
QString textShow = ui->textBrowser->toPlainText();
double reverseResult = -1 * textShow.toDouble();
textShow = QString::number(reverseResult, 'g', numLimit);
ui->textBrowser->setText(textShow);
}
void CalculatorMine::PushButtonEqualClicked() {
// 如果符合运算标准,执行运算操作,操作数标志位置假
QString textShow = ui->textBrowser->toPlainText();
if (!isStoredNum || textShow.length() == 0 || isOperatorClicked) {
return;
}
CalculateResult();
isStoredNum = false;
}
void CalculatorMine::PushButtonPointClicked() {
// 如果显示器上已经有了小数点,直接返回
QString textShow = ui->textBrowser->toPlainText();
if (textShow.length() >= (numLimit - 1) || textShow.contains('.', Qt::CaseSensitive)) {
return;
}
// 如果显示器没有数字,那么那么就是0-1之间的小数,先加入一个0
if (textShow.length() == 0) {
textShow = "0";
}
// 给显示器的末尾添加一个小数点
textShow.append('.');
ui->textBrowser->setText(textShow);
}
void CalculatorMine::SearchHistory() {
// 我们获取的数据共有四个列,分别记录了第一个操作数、操作符、第二个操作数、计算结果。
// 这里我们设置最多取200条运算记录,定义4个数组存储
double previousNum[200];
QString operatorChar[200];
double presentNum[200];
double resultNum[200];
// 连接数据库,初始化数据库操作对象
QSqlDatabase db = QSqlDatabase::database("sqliteMine");
QSqlQuery query(db);
// 定义一个SQL语句的字符串,用于获取数据库中所有的记录
// SELECT * FROM: 标准的SQL命令,用于从后面的表 history 中查询所有的数据
QString selectDataString = "SELECT * FROM history";
// 执行查询语句,如果不成功,在显示屏上进行提示
bool success = query.exec(selectDataString);
if (!success) {
ui->textBrowser->setText("can't get history data");
}
// 将获取的查询结果放入我们事先准备好的数组中。
int index = 0;
while (query.next()) {
previousNum[index] = query.value(0).toDouble();
operatorChar[index] = query.value(1).toString();
presentNum[index] = query.value(2).toDouble();
resultNum[index] = query.value(3).toDouble();
index++;
}
// 将历史记录一条一条的插入到我们的显示器中,格式为 IDx:操作数1 操作符 操作数2 = 运算结果。
ui->textBrowser->clear();
for (int i = 0; i < index; i++) {
QString historyString = QString("ID%1: %2 %3 %4 = %5").arg(i + 1).arg(previousNum[i]).arg(operatorChar[i]).arg(presentNum[i]).arg(resultNum[i]);
ui->textBrowser->append(historyString);
}
}
void CalculatorMine::CloseHistory() {
// 直接清空显示器就好了
ui->textBrowser->clear();
}
void CalculatorMine::ClearHistory() {
// 连接数据库,建立数据库操作对象
QSqlDatabase db = QSqlDatabase::database("sqliteMine");
QSqlQuery query(db);
// 定义一个SQL语句的字符串,用来删除表。
// DROP TABLE : 标准SQL语句,用于删除后面的表 history 。
QString dropTableString = "DROP TABLE history";
// 执行删除表的语句,如果未成功,在显示器上提醒。
bool success = query.exec(dropTableString);
if (!success) {
ui->textBrowser->setText("can't clear history data");
}
else {
ui->textBrowser->clear();
}
}
ui_CalculatorMine.h 这个是自动生成的文件,放出来也没啥用,截图给大家认识以下:
我把编译产生的附加文件删了之后,整个工程只有10k,配置好环境后就可以直接运行,下面是资源:
链接: CalculatorMine.zip
提取码:qt6j