Qt是C++的一个开发图形用户界面的框架,支持windows、linux、Android等很多平台
这次的作业就是用Qt,模仿着win10系统自带的标准计算器制作的,并运用逆波兰算法实现了优先级加减乘除

1、运行流程
输入表达式后,检查表达式格式是否正确,正确则计算结果,否则提示Error

2、功能介绍
数字键和加、减、乘、除运算符不必说,可进行优先级加减乘除,且可计算小数。
%计算百分数,1/x表示倒数,^2平方,^0.5开平方根,+/-变相反数,C清屏,R退格

当表达式输入完毕,按下=进行计算时,会进行表达式格式检测
例如两个运算符相邻、一个数多个小数点、除0等均为不合法,会提示Error

其他的取百分数、平方、相反数等功能仅能对上次合法表达式所计算的结果进行计算,在输入表达式时使用会提示Error

3、GUI界面设计
Qt也提供了ui界面,可以像C#、Android Studio那样拖动控件进行布局,很直观的修改控件的属性,如名字、字体颜色等

布局使用了Qt的水平布局和垂直布局
5排按键,每一排进行水平布局后,整体进行垂直布局,再和文本框垂直布局,实现窗体内控件尺寸随窗体的变化而变化
我设置的文本框高度限度为120px,文本框高度固定,宽度随窗体变化而变化。控件位置是相对窗体的,不是固定的

4、代码实现

仅仅设计了一个类Calculator,利用Qt的信号和槽实现按钮点击效果,slot函数即点击按钮后执行的函数
rescource.qrc为资源文件,仅存储了程序图标一张图片
头文件如下:
#ifndef COMPUTER_H
#define COMPUTER_H
#include <QWidget>
#include <QPushButton>
#include <QDebug>
#include <cmath>
#include <string>
#include <stack>
using namespace std;
namespace Ui {
class Computer;
}
class Calculator : public QWidget {
Q_OBJECT
public:
explicit Calculator(QWidget *parent = 0);
~Calculator();
private:
Ui::Computer *ui;//ui界面指针
QString calculate_;//当前表达式
QString cal_ret = ""; //上一个表达式的计算结果
QString error_qstr[4] = {"", "= Empty", "= Error N/0", "= Error"};//错误类型
size_t check_calculate_(const QString &qstr) const;//检查表达式是否合法
QString get_suffix(const QString &qstr) const;//计算表达式后缀表示形式
QString get_result(const QString &qstr) const;//计算后缀式结果
private slots:
void edit_append_num();//按数字
void edit_append_oper();//按运算符
void edit_append_oth();//按下其他 平方等
void edit_append_ret();//计算结果
void edit_change();//C和R
};
#endif // COMPUTER_H 防卫
Qt对stl几乎整体重构,大部分容器做了改变,QString代替string,我用string习惯了,所以很多时候转化为string后实现算法‘
实现文件如下,标注了注释:
#include "Calculator.h"
#include "ui_computer.h"
Calculator::Calculator(QWidget *parent)
: QWidget(parent), ui(new Ui::Computer) {
ui->setupUi(this);
ui->edit_ret->setWordWrapMode(QTextOption::WrapAnywhere);
connect(ui->bon_num_0, &QPushButton::clicked, this, &Calculator::edit_append_num);//数字0
connect(ui->bon_num_1, &QPushButton::clicked, this, &Calculator::edit_append_num);//数字1
connect(ui->bon_num_2, &QPushButton::clicked, this, &Calculator::edit_append_num);//数字2
connect(ui->bon_num_3, &QPushButton::clicked, this, &Calculator::edit_append_num);//数字3
connect(ui->bon_num_4, &QPushButton::clicked, this, &Calculator::edit_append_num);//数字4
connect(ui->bon_num_5, &QPushButton::clicked, this, &Calculator::edit_append_num);//数字5
connect(ui->bon_num_6, &QPushButton::clicked, this, &Calculator::edit_append_num);//数字6
connect(ui->bon_num_7, &QPushButton::clicked, this, &Calculator::edit_append_num);//数字7
connect(ui->bon_num_8, &QPushButton::clicked, this, &Calculator::edit_append_num);//数字8
connect(ui->bon_num_9, &QPushButton::clicked, this, &Calculator::edit_append_num);//数字9
connect(ui->bon_decimal, &QPushButton::clicked, this, &Calculator::edit_append_num);//小数点
connect(ui->bon_add, &QPushButton::clicked, this, &Calculator::edit_append_oper);//加
connect(ui->bon_del, &QPushButton::clicked, this, &Calculator::edit_append_oper);//减
connect(ui->bon_pro, &QPushButton::clicked, this, &Calculator::edit_append_oper);//乘
connect(ui->bon_fix, &QPushButton::clicked, this, &Calculator::edit_append_oper);//除
connect(ui->bon_ret, &QPushButton::clicked, this, &Calculator::edit_append_ret);//计算结果
connect(ui->bon_era, &QPushButton::clicked, this, &Calculator::edit_change);//删除全部
connect(ui->bon_rem, &QPushButton::clicked, this, &Calculator::edit_change);//表达式退格
connect(ui->bon_neg, &QPushButton::clicked, this, &Calculator::edit_append_oth); //相反数
connect(ui->bon_sqr, &QPushButton::clicked, this, &Calculator::edit_append_oth); //开根号
connect(ui->bon_squ, &QPushButton::clicked, this, &Calculator::edit_append_oth); //平方
connect(ui->bon_con, &QPushButton::clicked, this, &Calculator::edit_append_oth); //倒数
connect(ui->bon_bai, &QPushButton::clicked, this, &Calculator::edit_append_oth); //百分数
}
//按下数字
void Calculator::edit_append_num() {
QPushButton *bon = qobject_cast<QPushButton *>(sender());
calculate_ += bon->text();//更新表达式状态
QString str = ui->edit_ret->toPlainText();
ui->edit_ret->clear();
ui->edit_ret->append(str + bon->text());
}
//按下+-*/运算符
void Calculator::edit_append_oper() {
QPushButton *bon = qobject_cast<QPushButton *>(sender());
QString qstr;
if(bon == ui->bon_add) {
qstr = "+";
} else if(bon == ui->bon_del) {
qstr = "-";
} else if(bon == ui->bon_pro) {
qstr = "*";
} else if(bon == ui->bon_fix) {
qstr = "/";
} else {//程序出错
exit(0);
}
calculate_ += " " + qstr + " ";//更新表达式状态
QString str = ui->edit_ret->toPlainText();
ui->edit_ret->clear();
ui->edit_ret->append(str + bon->text());
}
//按下其他,平方等
void Calculator::edit_append_oth() {
if(calculate_ > 0) { //有正在输入的表达式
calculate_.clear();
ui->edit_ret->append(error_qstr[3]);
} else {
if(cal_ret.size() <= 0) { //没有计算结果
ui->edit_ret->append(error_qstr[1]);
} else {
QPushButton *bon = qobject_cast<QPushButton *>(sender());//获取信号发送对象
double ret = cal_ret.toDouble();
if(bon == ui->bon_neg) { //相反数
ui->edit_ret->insertPlainText("negate(" + cal_ret + ")");
ret = -ret;
} else if(bon == ui->bon_sqr) { //平方
ui->edit_ret->insertPlainText("sqrt(" + cal_ret + ")");
ret = pow(ret, 2);
} else if(bon == ui->bon_squ) { //开平方根
ui->edit_ret->insertPlainText("√(" + cal_ret + ")");
if(ret < 0) { //数小于0,不能开平方根
ui->edit_ret->append(error_qstr[3]);
ui->edit_ret->append("");
return;
} else {
ret = pow(ret, 0.5);
}
} else if(bon == ui->bon_bai) { //百分数
ui->edit_ret->insertPlainText("(" + cal_ret + ")*100%");
ret = ret / 100;
} else if(bon == ui->bon_con) { //倒数
ui->edit_ret->insertPlainText("1/(" + cal_ret + ")");
ret = ret == 0.0 ? 0 : 1 / ret;
}
cal_ret = QString::number(ret);
ui->edit_ret->append("= " + cal_ret);
}
}
ui->edit_ret->append("");
}
//按下=
void Calculator::edit_append_ret() {
size_t flag = check_calculate_(calculate_);
if(flag == 0) {//运算正常
cal_ret = get_result(get_suffix(calculate_));
ui->edit_ret->append("= " + cal_ret);
calculate_.clear();
} else {
switch (flag) {
case 1:
ui->edit_ret->append("= Empty");//表达式为空
break;
case 2:
ui->edit_ret->append("= Error N/0");//除0
break;
case 3:
ui->edit_ret->append("= Error");//表达式错误
break;
default:
ui->edit_ret->append("= N/A");//内存漏了
break;
}
}
ui->edit_ret->append("");//另起一行
}
//退格和清屏
void Calculator::edit_change() {
QPushButton *bon = qobject_cast<QPushButton *>(sender());
if(bon == ui->bon_era) { //全部删除
ui->edit_ret->clear();
calculate_.clear();
} else {//删除表达式末尾
if(calculate_.size() > 0) {//当前表达式不为空
QTextCursor tc = ui->edit_ret->textCursor();//获取表达式末尾光标
tc.deletePreviousChar();//删除该光标前一个字符
if(calculate_[calculate_.size() - 1] == ' ') {//退格遇到运算符
calculate_.chop(3);//删去最后3个
} else {
calculate_.chop(1);//删去最后1个
}
}
}
}
//检查表达式是否合法
size_t Calculator::check_calculate_(const QString &qstr) const {
string str = qstr.toStdString();//转为标准字符串
if(str.empty()) {
return 1;
}
size_t i = 0;
string last_part;//当前部分的上一部分
while(i < (str.size())) {
if(str[i] == ' ') {
i++;
} else if((str[i] >= '0' && str[i] <= '9') || str[i] == '.') {//数字部分
string t;
int cnt_dec = 0;
while((str[i] >= '0' && str[i] <= '9') || str[i] == '.') {
if(str[i] == '.') {
cnt_dec++;
}
t += str[i];
i++;
}
if(t.front() == '.' || t.back() == '.' || cnt_dec > 1) {//小数点不合法
return 3;
}
if(last_part == "/" && stod(t) == 0.0) {//除0错误
return 2;
}
last_part = t;//更新上一部分
} else {//运算符部分
if(last_part.empty() || last_part.back() < '0' || last_part.back() > '9') { //前一个不是数字或为空
return 3;
}
last_part = string(1, str[i]);
i++;
}
}
return 0;
}
//中缀转后缀
QString Calculator::get_suffix(const QString &qstr) const {
string str = qstr.toStdString();//转为标准字符串
stack<string> st_oper;
string ret;
for (size_t i = 0; i < str.size(); i++) {
if(str[i] == ' ') {
i++;
}
if ((str[i] >= '0' && str[i] <= '9') || str[i] == '.') {
size_t index_back = i + 1;
while ((str[index_back] >= '0' && str[index_back] <= '9') || str[index_back] == '.') { //支持小数
index_back++;
}
ret += str.substr(i, index_back - i) + " ";//数字直接输出至后式
i = index_back - 1;//注意每次循环结束的i++
} else {
string oper = string(1, str[i]); //运算符转为字符串,便于判断
if ((oper == "+" || oper == "-") && str[i - 1] != '(') { //低优先级运算符向前弹栈直到栈空或遇左括号
while (!st_oper.empty() && st_oper.top() != "(") {
ret += st_oper.top() + " ";
st_oper.pop();
}
st_oper.push(oper);//低优先级运算符最后要入栈
} else if (oper == "*" || oper == "/" || oper == "(") {//高优先级的运算符直接入栈
st_oper.push(oper);
} else if (oper == ")") {//开始匹配左括号
while (st_oper.top() != "(") {
ret += st_oper.top() + " ";//括号之间栈内的运算符输出
st_oper.pop();
}
st_oper.pop();//记得最后弹出左括号,但不要输出至后缀式
}
}
}
while (!st_oper.empty()) {//弹出栈内最后剩余运算符
ret += st_oper.top();
if (st_oper.size() != 1) {
ret += " ";
}
st_oper.pop();
}
return QString::fromStdString(ret);
}
//后缀计算结果
QString Calculator::get_result(const QString &qstr) const {
string str = qstr.toStdString();//转为标准字符串
stack<double> st_num;
for(size_t i = 0; i < str.size(); i++) {
while (str[i] == ' ') {
i++;
}
string t;
while(str[i] != ' ') {
t += str[i];
i++;
}
double num_a, num_b;
switch (t[0]) {
case '+':
num_a = st_num.top();
st_num.pop();
num_b = st_num.top();
st_num.pop();
st_num.push(num_a + num_b);
break;
case '-':
num_a = st_num.top();
st_num.pop();
num_b = st_num.top();
st_num.pop();
st_num.push(num_b - num_a);
break;
case '*':
num_a = st_num.top();
st_num.pop();
num_b = st_num.top();
st_num.pop();
st_num.push(num_a * num_b);
break;
case '/':
num_a = st_num.top();
st_num.pop();
num_b = st_num.top();
st_num.pop();
st_num.push(num_b / num_a);
break;
default:
st_num.push(stod(t));//普通数字
break;
}
}
return QString::number(st_num.top());
}
Calculator::~Calculator() {
delete ui;
}
main函数没有做出改变
#include "Calculator.h"
#include <QApplication>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
Calculator w;
w.show();//显示窗体
return a.exec();//阻塞
}
5、程序导出与缺陷
有的功能不好靠截图展示出来,例如清屏和退格
所以我绑定了dll,导出了程序exe,并发送成了csdn的资源,可以运行看看,当然是免费的,0 C币 这是链接
目前的程序有很多缺陷,也可以说是功能不完全,以后可能周六日慢慢写吧
1、不支持负数和括号的输入
2、百分号、平方等特殊的按键运算只能用在表达式结果中,不能用在表达式输入中
3、表达式格式检验时,错误类型少,不能很好的提示表达式输入错误的类型