【Qt 编程入门】如何用 Qt 实现一个基本的盘算器

打印 上一主题 下一主题

主题 541|帖子 541|积分 1623

媒介

QT中实现一个简单的盘算器是一个比较好的练手项目,下面是源码:
Calculator盘算器

计划界面

待实现的界面重要包含两个部门:


  • 输入输出栏
  • 用户点击的按钮
输入输出栏通过QLabel类实现,而用户点击按钮通过QPushButton或QToolButton,按钮的布局我们使用网格布局(grid)

随后给整个页面进行垂直布局,并将程度垂直策略设为expanding(巨细跟随程序页面巨细)
再通过qss进行样式表美化,最终效果为:

用户输入的数字会显示在下面的displayLabel中,当点击等号时,结果会被盘算显示到上面的outputLabel中。

程序计划分析

对于整个盘算器的功能,都封装在一个calculator类中;

下面是头文件:
  1. #ifndef CALCULATOR_H
  2. #define CALCULATOR_H
  3. #include "ui_calculator.h"
  4. #include <QWidget>
  5. #include <QFile>
  6. #include <QMessageBox>
  7. #include <QString>
  8. #include <QStack>
  9. #include <QStack>
  10. #include <QMap>
  11. #include <cmath>
  12. #include <QDebug>
  13. #include <QtMath>
  14. #include <QRegularExpression>
  15. #include <QRegularExpressionMatch>
  16. QT_BEGIN_NAMESPACE
  17. namespace Ui { class Calculator; }
  18. QT_END_NAMESPACE
  19. class Calculator : public QWidget
  20. {
  21.     Q_OBJECT
  22. public:
  23.     Calculator(QWidget *parent = nullptr);
  24.     ~Calculator();
  25.         // 加载qss
  26.     void loadStyleSheetFile(const QString &styleSheetFile);
  27.         // 各种按钮点击的槽函数
  28.     void btnNumClicked(double x);
  29.     void btnOperatorClicked(char op);
  30.     void btnCClicked();
  31.     void btnCEClicked();
  32.     void btnBackClicked();
  33.     void btnSquareClicked();
  34.     void btnSqrtClicked();
  35.     void btnSpotClicked();
  36.     void btnFractionClicked();
  37.     void btnPerClicked();
  38.     void btnReverseClicked();
  39.     bool btnEqualClicked();
  40. private:
  41.         // 初始化以及其他功能
  42.     void initConnections();
  43.     void initMembers();
  44.     bool isPureNumber(const QString& sen);
  45.     // int getDecimalPlaces(const QString &text) const;
  46. private slots:
  47. private:
  48.     Ui::Calculator *ui;
  49.     size_t decimalCount;
  50.     double num;
  51.     double decimalPlaces;
  52.     double result;
  53.     char operation;
  54.     QString sentence;
  55.     bool isNewOperation;
  56.     bool isFirstElement;
  57.     bool hasDecimalPoint;
  58.     bool EqualBtnClicked;
  59. };
  60. #endif // CALCULATOR_H
复制代码
在创建calculator对象时,对槽函数以及相干变乱进行绑定连接,随后就可以正式编写代码:

代码部门

按钮点击

该槽函数用于当用户点击按钮时的操纵,将用户输入的数字存储到成员变量num中即可,通过判定用户是否点击小数点,进行小数点的统计与显示。
  1. void Calculator::btnNumClicked(double x)
  2. {
  3.     if(EqualBtnClicked) {
  4.         sentence = "";
  5.         num = '\0';
  6.     }
  7.     // 处理数字输入
  8.     if (isFirstElement) {
  9.         num = x;
  10.         isFirstElement = false;
  11.         ui->displayLabel->setText(QString::number(num, 'f', decimalCount));
  12.     } else {
  13.         if (hasDecimalPoint) {
  14.            // 处理小数部分
  15.            num += x * decimalPlaces;
  16.            decimalPlaces /= 10; // 减少小数点后面的位数
  17.            decimalCount++;
  18.            qDebug() << "decimalConut: " << decimalCount;
  19.            ui->displayLabel->setText(QString::number(num, 'f', decimalCount));
  20.        } else {
  21.            // 处理整数部分
  22.            num = num * 10 + static_cast<int>(x);
  23.            ui->displayLabel->setText(QString::number(num, 'f', decimalCount));
  24.        }
  25.     }
  26. }
复制代码

小数点点击

判定用户点击小数点前是否有数字 / 点击过了,随后将标识符设为true
  1. void Calculator::btnSpotClicked()
  2. {
  3.     if(isFirstElement || hasDecimalPoint) {
  4.         return;
  5.     }
  6.     hasDecimalPoint = true;
  7. }
复制代码

操纵符点击

sentence 变量负责存储算式,末了由equal盘算,用户点击操纵符后,会根据情况将操纵符加载到算是后面。
  1. void Calculator::btnOperatorClicked(char op)
  2. {
  3.     if(EqualBtnClicked) {
  4.         sentence = "";
  5.         num = '\0';
  6.     }
  7.     // 如果上一次操作后有数字,则将其加入 sentence
  8.     if (!isFirstElement) {
  9.         if ((!sentence.isEmpty() && !sentence.back().isDigit()) || sentence.isEmpty())
  10.             sentence += QString::number(num, 'f', decimalCount);
  11.         qDebug() << "num: " << num;
  12.         num = 0;  // 重置 num 以便输入新的数字
  13.         isFirstElement = true;  // 标记新的数字输入开始
  14.     }
  15.     // 检查 sentence 是否以运算符结尾
  16.     if (!sentence.isEmpty() && (sentence.endsWith('+') || sentence.endsWith('-') || sentence.endsWith('*') || sentence.endsWith('/'))) {
  17.         // 如果最后一位是运算符,替换运算符
  18.         sentence.chop(1);  // 删除最后一个运算符
  19.     }
  20.     // 追加新的运算符
  21.     sentence += op;
  22.     // 更新显示
  23.     ui->outputLabel->setText(sentence);
  24.     ui->displayLabel->setText("");
  25.     size_t tmp = decimalCount;
  26.     initMembers();
  27.     decimalCount = tmp;
  28. }
复制代码

退格

退格键是对用户当前正在输入的数字进行尾删,根据情况更新num,并重设displayLabel上显示的内容
  1. void Calculator::btnBackClicked()
  2. {
  3.     if(EqualBtnClicked) {
  4.         sentence = "";
  5.         num = '\0';
  6.     }
  7.     QString currentText = ui->displayLabel->text(); // 获取当前显示的文本
  8.     if (currentText.isEmpty()) {
  9.         return; // 如果没有文本,直接返回
  10.     }
  11.     // 移除最后一个字符
  12.     currentText.chop(1); // 或者使用 remove() 方法:currentText.remove(currentText.length() - 1, 1);
  13.     // 如果移除后的文本为空,重置显示为 0
  14.     if (currentText.isEmpty()) {
  15.         currentText = "0";
  16.     }
  17.     // 更新显示标签
  18.     ui->displayLabel->setText(currentText);
  19.     // 更新 num 变量
  20.     bool ok;
  21.     num = currentText.toDouble(&ok);
  22.     if (!ok) {
  23.         num = 0; // 如果转换失败,则将 num 设置为 0
  24.     }
  25. }
复制代码

等号(盘算)

等号功能进行实际的运算,通过将用户输入的sentence进行运算:
盘算结果后将内容回显到显示器中。
  1. bool Calculator::btnEqualClicked() {
  2.     if(isPureNumber(sentence)) {
  3.         ui->outputLabel->setText(QString::number(num, 'f', decimalCount));
  4.         sentence = QString::number(num, 'f', decimalCount);
  5.         return true;
  6.     }
  7.     // 将当前数字(num)转换为字符串,并添加到当前计算表达式(sentence)中
  8.     sentence += QString::number(num, 'f', decimalCount);
  9.     QString str = sentence; // 将表达式字符串复制到局部变量
  10.     QStack<double> numbers; // 用于存储操作数的栈
  11.     QStack<QChar> operators; // 用于存储操作符的栈
  12.     if(!sentence.begin()->isDigit()) {
  13.         ui->outputLabel->setText("Error: There are no numbers before the operator."); // 显示结果(保留两位小数)
  14.         ui->displayLabel->setText("");
  15.         initMembers();
  16.         sentence = "";
  17.         return false;
  18.     }
  19.     // 定义操作符的优先级
  20.     QMap<QChar, int> precedence = {{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}};
  21.     str = str.trimmed(); // 去除前后的空白字符
  22.     int length = str.length(); // 获取表达式的长度
  23.     for (int i = 0; i < length; ++i) {
  24.         QChar ch = str[i]; // 当前字符
  25.         // 处理数字或小数点
  26.         if (ch.isDigit() || (ch == '.' && (i > 0 && str[i - 1].isDigit()))) {
  27.             QString numberStr;
  28.             while (i < length && (str[i].isDigit() || str[i] == '.')) {
  29.                 numberStr += str[i++]; // 读取完整的数字(包括小数点)
  30.             }
  31.             --i; // 回退一个字符,因为循环结束时 i 已经多加了一次
  32.             numbers.push(numberStr.toDouble()); // 将数字转换为 double 并压入操作数栈
  33.         }
  34.         // 处理负号(负数)
  35.         else if (ch == '-' && (i == 0 || (i > 0 && str[i - 1] == '('))) {
  36.             QString numberStr;
  37.             numberStr += ch; // 负号
  38.             while (i + 1 < length && (str[i + 1].isDigit() || str[i + 1] == '.')) {
  39.                 numberStr += str[++i]; // 读取负数后的数字
  40.             }
  41.             numbers.push(numberStr.toDouble()); // 将负数转换为 double 并压入操作数栈
  42.         }
  43.         // 处理操作符(+ - * /)
  44.         else if (precedence.contains(ch)) {
  45.             // 处理当前操作符前已有的操作符(具有相同或更高优先级的)
  46.             while (!operators.isEmpty() && precedence[operators.top()] >= precedence[ch]) {
  47.                 double right = numbers.pop(); // 从操作数栈弹出右操作数
  48.                 double left = numbers.pop(); // 从操作数栈弹出左操作数
  49.                 QChar op = operators.pop(); // 从操作符栈弹出操作符
  50.                 double result;
  51.                 if (op == '+') result = left + right; // 计算加法
  52.                 else if (op == '-') result = left - right; // 计算减法
  53.                 else if (op == '*') result = left * right; // 计算乘法
  54.                 else if (op == '/') {
  55.                     if (right == 0) {  // 处理除以零的情况
  56.                         ui->outputLabel->setText("Error: Division by zero");
  57.                         return false;
  58.                     }
  59.                     result = left / right; // 计算除法
  60.                 }
  61.                 numbers.push(result); // 将结果压入操作数栈
  62.             }
  63.             operators.push(ch); // 将当前操作符压入操作符栈
  64.         }
  65.     }
  66.     // 处理剩余的操作符
  67.     while (!operators.isEmpty()) {
  68.         double right = numbers.pop(); // 从操作数栈弹出右操作数
  69.         double left = numbers.pop(); // 从操作数栈弹出左操作数
  70.         QChar op = operators.pop(); // 从操作符栈弹出操作符
  71.         double result;
  72.         if (op == '+') result = left + right; // 计算加法
  73.         else if (op == '-') result = left - right; // 计算减法
  74.         else if (op == '*') result = left * right; // 计算乘法
  75.         else if (op == '/') {
  76.             if (right == 0) {  // 处理除以零的情况
  77.                 ui->outputLabel->setText("Error: Division by zero");
  78.                 return false;
  79.             }
  80.             result = left / right; // 计算除法
  81.         }
  82.         numbers.push(result); // 将结果压入操作数栈
  83.     }
  84.     EqualBtnClicked = true;
  85.     // 确保栈中仅剩一个操作数,即计算结果
  86.     if (numbers.size() == 1) {
  87.         double result = numbers.pop(); // 获取计算结果
  88.         qDebug() << "ret: " << result;
  89.         ui->outputLabel->setText(QString::number(result, 'f', decimalCount)); // 显示结果(保留两位小数)
  90.         ui->displayLabel->setText("");
  91.         sentence = QString::number(result, 'f', decimalCount);
  92.         num = result;
  93.         operation = '\0';
  94.         // isNewOperation = true;
  95.         isFirstElement = true;
  96.         hasDecimalPoint = false;
  97.         decimalPlaces = 0.1;
  98.         decimalCount = 0;
  99.         EqualBtnClicked = false;
  100.         return true;
  101.     } else {
  102.         ui->outputLabel->setText("Error"); // 显示错误信息
  103.         return false;
  104.     }
  105. }
  106. void Calculator::initConnections()
  107. {
  108.     // 初始化数字按钮
  109.     for (int i = 0; i <= 9; ++i)
  110.     {
  111.         connect(findChild<QToolButton*>(QString("btn%1").arg(i)), &QToolButton::clicked, [this, i]() { btnNumClicked(i); });
  112.     }
  113.     // 初始化运算符
  114.     connect(ui->btnAdd, &QToolButton::clicked, [this]() { btnOperatorClicked('+'); });
  115.     connect(ui->btnSub, &QToolButton::clicked, [this]() { btnOperatorClicked('-'); });
  116.     connect(ui->btnMul, &QToolButton::clicked, [this]() { btnOperatorClicked('*'); });
  117.     connect(ui->btnDiv, &QToolButton::clicked, [this]() { btnOperatorClicked('/'); });
  118.     // 绑定 等于按钮
  119.     connect(ui->btnEqual, &QToolButton::clicked, [this]() { btnEqualClicked(); });
  120.     // 绑定其他功能按钮
  121.     connect(ui->btnC, &QToolButton::clicked, [this]() { btnCClicked(); });
  122.     connect(ui->btnCE, &QToolButton::clicked, [this]() { btnCEClicked(); });
  123.     connect(ui->btnBack, &QToolButton::clicked, [this]() { btnBackClicked(); });
  124.     connect(ui->btnSquare, &QToolButton::clicked, [this]() { btnSquareClicked(); });
  125.     connect(ui->btnFraction, &QToolButton::clicked, [this]() { btnFractionClicked(); });
  126.     connect(ui->btnSqrt, &QToolButton::clicked, [this]() { btnSqrtClicked(); });
  127.     connect(ui->btnPer, &QToolButton::clicked, [this]() { btnPerClicked(); });
  128.     connect(ui->btnSpot, &QToolButton::clicked, [this]() { btnSpotClicked(); });
  129.     connect(ui->btnReverse, &QToolButton::clicked, [this]() { btnReverseClicked(); });
  130. }
复制代码

初始化 C / CE

C、CE分别为完全初始化与初始化当前输入,通过调用自实现的initMembers()并更改显示器上的显示内容;
  1. oid Calculator::btnCClicked()
  2. {
  3.     // 清空全部内容
  4.     initMembers();
  5.     sentence = "";
  6.     ui-> displayLabel->setText("");
  7.     ui->outputLabel->setText("");
  8. }
  9. void Calculator::btnCEClicked()
  10. {
  11.     initMembers();
  12.     ui->displayLabel->setText("");
  13. }
  14. void Calculator::initMembers()
  15. {
  16.     num = 0;
  17.     operation = '\0';
  18.     isNewOperation = true;
  19.     isFirstElement = true;
  20.     hasDecimalPoint = false;
  21.     decimalPlaces = 0.1;
  22.     decimalCount = 0;
  23.     EqualBtnClicked = false;
  24. }
复制代码

其他功能

其他的功能诸如平方数、开根号、取分数等,都只需要简单盘算一下再设置num即可。
  1. void Calculator::btnCClicked()
  2. {
  3.     // 清空全部内容
  4.     initMembers();
  5.     sentence = "";
  6.     // ui-> displayLabel->setText("displayLabel");
  7.     // ui->outputLabel->setText("outputLabel");
  8.     ui-> displayLabel->setText("");
  9.     ui->outputLabel->setText("");
  10. }
  11. void Calculator::btnCEClicked()
  12. {
  13.     initMembers();
  14.     ui->displayLabel->setText("");
  15. }
  16. void Calculator::btnSquareClicked()
  17. {
  18.     num *= num;
  19.     ui->displayLabel->setText(QString::number(num, 'f', decimalCount + 6));
  20. }
  21. void Calculator::btnSqrtClicked()
  22. {
  23.     if (num >= 0) { // 防止负数开根号的错误
  24.         num = sqrt(num); // 计算平方根
  25.         ui->displayLabel->setText(QString::number(num, 'f', decimalCount + 6)); // 显示6位小数
  26.     } else {
  27.         ui->displayLabel->setText("Error: 负数不能开根号"); // 处理负数开根号的情况
  28.     }
  29. }
  30. void Calculator::btnFractionClicked()
  31. {
  32.     if (num != 0) { // 防止除以0的错误
  33.         num = 1.0 / num;
  34.         ui->displayLabel->setText(QString::number(num, 'f', decimalCount + 6)); // 显示6位小数
  35.     } else {
  36.         ui->displayLabel->setText("Error: 除数不能为0"); // 处理除以0的情况
  37.     }
  38. }
  39. void Calculator::btnPerClicked()
  40. {
  41.     num = num / 100.0; // 将当前值转换为百分比
  42.     ui->displayLabel->setText(QString::number(num, 'f', decimalCount + 6)); // 显示6位小数
  43. }
  44. void Calculator::btnReverseClicked()
  45. {
  46.     num = -num;
  47.     ui->displayLabel->setText(QString::number(num, 'f', decimalCount)); // 更新显示,6位小数
  48. }
复制代码

结果演示



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

兜兜零元

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表