基于QT5的计算器简单实现

    科技2022-08-11  98

    本人实验室培训作业

    需求:实现必要的计算器功能,合理化布局

    UI布局:

    逻辑思维:由于组件中的text()值多为QSTring类型,

    1.将槽的操作都写成,往一个大字符串追加信息

    2.将字符串解析,利用栈实现数据的先后弹出,操作符的优先顺序

    具体实现代码如下:

    由于代码中注释足够详细,不再做文字说明:

    mainwindow.h

    #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); QString numInfo; void caculate(); int is_First(char a, char b); struct node{ int flag; union{ double a; char op; }num; //public slots: //空写slots,编译时会出错,导致无法编译, //下次务必不要踩雷 }; private: Ui::MainWindow *ui; }; #endif // MAINWINDOW_H

    mainwindow.cpp

    //9月29号9:12 //10月3号0:13 //完成基本功能,运算逻辑均正确 //至于科学计算器其它复杂的功能,可日后添加 #include "mainwindow.h" #include "ui_mainwindow.h" #include "QPixmap" #include "QPushButton" #include "QIcon" #include "QPixmap" #include "QSize" #include "QDebug" #include "QFont" #include "QVector" #include "QStack" #include "math.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); //设置固定尺寸 this->setFixedSize(1061,800); this->setWindowIcon(QPixmap(":/res/caculater_icon.jpg")); this->setWindowTitle("爷的科学计算器"); //计算符号图标 //Qicon会自动缩小,这里用Qpixmap先放大 ui->ride_btn->setIconSize(QSize(100,100)); ui->ride_btn->setIcon(QIcon(QPixmap(":/res/ride.png"))); ui->plus_btn->setIconSize(QSize(100,100)); ui->plus_btn->setIcon(QIcon(QPixmap(":/res/plus.png"))); ui->reduce_btn->setIconSize(QSize(100,100)); ui->reduce_btn->setIcon(QIcon(QPixmap(":/res/reduce.png"))); ui->equal_btn->setIconSize(QSize(100,100)); ui->equal_btn->setIcon(QIcon(QPixmap(":/res/equal.png"))); ui->clear_btn->setIconSize(QSize(100,100)); ui->clear_btn->setIcon(QIcon(QPixmap(":/res/clear.png"))); ui->back_btn->setIconSize(QSize(100,100)); ui->back_btn->setIcon(QIcon(QPixmap(":/res/back.png"))); ui->division_btn->setIconSize(QSize(100,100)); ui->division_btn->setIcon(QIcon(QPixmap(":/res/division.png"))); ui->left_btn->setIconSize(QSize(100,100)); ui->left_btn->setIcon(QIcon(QPixmap(":/res/left.png"))); ui->right_btn->setIconSize(QSize(100,100)); ui->right_btn->setIcon(QIcon(QPixmap(":/res/right.png"))); ui->mod_btn->setIconSize(QSize(100,100)); ui->mod_btn->setIcon(QIcon(QPixmap(":/res/mod.png"))); if(this->ui->history_label->text() == ""){ this->ui->history_label->setText("尚无历史记录"); } //上面发现label字体太小 QFont font; font.setPointSize(20); ui->screen_label->setFont(font); //通过按钮的text获得按钮代表数字 this->numInfo = ""; //写好获取text值,追加字符串之后 //将btn的clicked与this.slots{}连接 //不写槽了,用lamda表达式直接模式粘贴复制 //可以提模板,但是已经写完了 connect(ui->num1_btn,&QPushButton::clicked,[=](){ this->numInfo += this->ui->num1_btn->text(); this->ui->screen_label->setText(this->numInfo); qDebug() << this->numInfo <<endl; }); connect(ui->num2_btn,&QPushButton::clicked,[=](){ this->numInfo += this->ui->num2_btn->text(); this->ui->screen_label->setText(this->numInfo); qDebug() << this->numInfo <<endl; }); connect(ui->num3_btn,&QPushButton::clicked,[=](){ this->numInfo += this->ui->num3_btn->text(); this->ui->screen_label->setText(this->numInfo); qDebug() << this->numInfo <<endl; }); connect(ui->num4_btn,&QPushButton::clicked,[=](){ this->numInfo += this->ui->num4_btn->text(); this->ui->screen_label->setText(this->numInfo); qDebug() << this->numInfo <<endl; }); connect(ui->num5_btn,&QPushButton::clicked,[=](){ this->numInfo += this->ui->num5_btn->text(); this->ui->screen_label->setText(this->numInfo); qDebug() << this->numInfo <<endl; }); connect(ui->num6_btn,&QPushButton::clicked,[=](){ this->numInfo += this->ui->num6_btn->text(); this->ui->screen_label->setText(this->numInfo); qDebug() << this->numInfo <<endl; }); connect(ui->num7_btn,&QPushButton::clicked,[=](){ this->numInfo += this->ui->num7_btn->text(); this->ui->screen_label->setText(this->numInfo); qDebug() << this->numInfo <<endl; }); connect(ui->num8_btn,&QPushButton::clicked,[=](){ this->numInfo += this->ui->num8_btn->text(); this->ui->screen_label->setText(this->numInfo); qDebug() << this->numInfo <<endl; }); connect(ui->num9_btn,&QPushButton::clicked,[=](){ this->numInfo += this->ui->num9_btn->text(); this->ui->screen_label->setText(this->numInfo); qDebug() << this->numInfo <<endl; }); connect(ui->num0_btn,&QPushButton::clicked,[=](){ this->numInfo += this->ui->num0_btn->text(); this->ui->screen_label->setText(this->numInfo); qDebug() << this->numInfo <<endl; }); connect(ui->point_btn,&QPushButton::clicked,[=](){ if(this->numInfo != " "){ this->numInfo += this->ui->point_btn->text(); this->ui->screen_label->setText(this->numInfo); qDebug() << this->numInfo <<endl; } }); connect(ui->equal_btn ,&QPushButton::clicked,[=](){ if(this->numInfo != " "){ this->caculate(); this->ui->history_label->setText(this->numInfo); qDebug() << this->numInfo <<endl; } }); connect(ui->left_btn,&QPushButton::clicked,[=](){ if(this->numInfo != " "){ this->numInfo += this->ui->left_btn->text(); this->ui->screen_label->setText(this->numInfo); qDebug() << this->numInfo <<endl; } }); connect(ui->right_btn,&QPushButton::clicked,[=](){ if(this->numInfo != " "){ this->numInfo += this->ui->right_btn->text(); this->ui->screen_label->setText(this->numInfo); qDebug() << this->numInfo <<endl; } }); connect(ui->plus_btn,&QPushButton::clicked,[=](){ if(this->numInfo != " "){ this->numInfo += this->ui->plus_btn->text(); this->ui->screen_label->setText(this->numInfo); qDebug() << this->numInfo <<endl; } }); connect(ui->ride_btn,&QPushButton::clicked,[=](){ if(this->numInfo != " "){ this->numInfo += this->ui->ride_btn->text(); this->ui->screen_label->setText(this->numInfo); qDebug() << this->numInfo <<endl; } }); connect(ui->reduce_btn,&QPushButton::clicked,[=](){ if(this->numInfo != " "){ this->numInfo += this->ui->reduce_btn->text(); this->ui->screen_label->setText(this->numInfo); qDebug() << this->numInfo <<endl; } }); connect(ui->division_btn,&QPushButton::clicked,[=](){ if(this->numInfo != " "){ this->numInfo += this->ui->division_btn->text(); this->ui->screen_label->setText(this->numInfo); qDebug() << this->numInfo <<endl; } }); connect(ui->clear_btn,&QPushButton::clicked,[=](){ this->numInfo = ""; this->ui->screen_label->setText(this->numInfo); }); connect(ui->back_btn,&QPushButton::clicked,[=](){ this->numInfo = this->numInfo.left(this->numInfo.size()-1); ui->screen_label->setText(this->numInfo); }); connect(ui->mod_btn,&QPushButton::clicked,[=](){ if(this->numInfo != " "){ this->numInfo += this->ui->mod_btn->text(); this->ui->screen_label->setText(this->numInfo); qDebug() << this->numInfo <<endl; } }); } //字符串算式解析的方法实现 void MainWindow::caculate(){ // QVector<double> numlist; node nodeList[20]; double num = 0; //设置位置 int position = 0; //设置浮点数标志 int float_flog = 0; //浮点数的小数数量,整数数量 int num_of_float = 0; int num_of_int = 0; //获取字符串长度 int len = this->numInfo.size(); //老子不用vector了 // double numlist[len]; //提取整数 while(position < len){ if(numInfo[position] >= '0' && numInfo[position] <= '9'){ if(!float_flog){ //将字符串的元素转化为ASCII码 /*返回相当于QChar的拉丁语-1字符,或0。这主要用于非国际化软件。 注意:不可能区分非拉丁1字符和拉丁10(NUL)字符。更喜欢使用unicode(),它没有这种模糊性。 百度结果*/ num = num*10 + (numInfo[position].toLatin1()-'0'); ++position; } else{ //这一块是算小数部分 double p = 1; for(int k = 0; k < num_of_float;k++){ p *= 0.1; } num += (numInfo[position].toLatin1()-'0')*p; num_of_float++; ++position; } } //这块是读到小数点,设置标志,设置小数数量(含小数点) else if (numInfo[position] == '.') { float_flog = 1; num_of_float = 1; ++position; } //下面是读到运算符的情况 else { if(num){ nodeList[num_of_int].flag = 0; nodeList[num_of_int].num.a = num; qDebug() <<nodeList[num_of_int].num.a; num = 0; ++num_of_int; float_flog = 0; num_of_float = 0; //莫名其妙的bug // qDebug() << QString("%1").arg((numlist.at(1)+numlist.at(2))); // this->numInfo += QString("%1").arg((numlist.at(1)+numlist.at(2))); // while(i>=1){ // qDebug() << QString("%1").arg((numlist[i]+numlist[i-1])); // break; // } } nodeList[num_of_int].flag = 1; nodeList[num_of_int].num.op = numInfo[position].toLatin1(); ++num_of_int; ++position; } } //最后一个操作数 if(num){ nodeList[num_of_int].flag = 0; nodeList[num_of_int].num.a = num; qDebug() <<nodeList[num_of_int].num.a; ++num_of_int; num = 0; } //利用栈的存储特性,将符号存入并按后缀的顺序读出 //创建一个Qstack类型的,保存操作符的栈 QStack<node> signList; //临时性出栈操作符 node temporarySign_node[20]; int j = 0; for(int m = 0; m < num_of_int;){ if(nodeList[m].flag){ if(signList.isEmpty()){ signList.push(nodeList[m++]); } else{ node temp = signList.top(); //判断运算符优先级 int res = is_First(temp.num.op,nodeList[m].num.op); switch (res) { case -1: signList.push(nodeList[m++]); break; case 1: temporarySign_node[j++] = signList.top(); signList.pop(); break; default: signList.pop(); m++; break; } } } else { temporarySign_node[j++] = nodeList[m++]; } } //处理算式中的括号 while(!signList.isEmpty()){ node tem = signList.top(); if(tem.num.op != '(' &&tem.num.op != ')'){ temporarySign_node[j++] = tem; } signList.pop(); } QStack<double> last_signList; double d1 = 0,d2 = 0; for(int n = 0; n < j ; n++){ //遇到符号弹出数字,弹出前两个数字 if(temporarySign_node[n].flag){ d2 = last_signList.top(); last_signList.pop(); d1 = last_signList.top(); last_signList.pop(); switch(temporarySign_node[n].num.op) { case '+': d1 += d2; break; case '-': d1 -= d2; break; case '*': d1 *= d2; break; case '/': d1 /= d2; break; case '%': //注意,这块是浮点数去模,一定要注意用fmod方法,头文件为math.h //切记不要再次踩雷 d1 = fmod(d1,d2); break; default: break; } last_signList.push(d1); } else { last_signList.push(temporarySign_node[n].num.a); } } qDebug() << d1; this->numInfo += '='; this->numInfo += QString::number(d1); this->ui->screen_label->setText(this->numInfo); } //返回整形标志的运算符优先级比对结果 int MainWindow::is_First(char a, char b){ char aim[7][8] = {{ ">><<<>>" },{ ">><<<>>" },{ ">>>><>>" },{ ">>>><>>" },{ "<<<<<=1" },{ ">>>>1>>" },{ "<<<<<1=" }}; char sta[7] = { '+','-','*','/','(',')','#' }; char result; int i,pa,pb; for (i = 0; i<6; i++) { if (a == sta[i]) { pa = i; } if (b == sta[i]) { pb = i; } } result = aim[pa][pb]; if(result == '>')return 1; else if(result == '<')return -1; else return 0; } MainWindow::~MainWindow() { delete ui; }

    不可描述的雷:

    第一遍运行时,系统会一直报构建方法出错,还不告诉你第几行出错,经查询,把build_debug的文件夹删了就可通顺运行,原因是第一遍程序已经qmake,后面在debug后,再次qmake,build_debug文件夹中的debug文件会与程序重现qmake的步骤相冲突

    运行结果图:

    最大的点:

    可以用toDoule方法直接解析字符串算式,不用上面那种繁琐的方法,但是!!!

    我已经写完了,淦

    Processed: 0.012, SQL: 8