#include "calculatordesigner.h" #include #include "expressionevaluator.h" #include "ui_widget.h" namespace { bool isOperatorChar(QChar ch) { return ch == QLatin1Char('+') || ch == QLatin1Char('-') || ch == QLatin1Char('*') || ch == QLatin1Char('/'); } bool isDigitOrDotChar(QChar ch) { return ch.isDigit() || ch == QLatin1Char('.'); } } // namespace CalculatorDesigner::CalculatorDesigner(QWidget* parent) : QWidget(parent), ui(new Ui::CalculatorForm) { ui->setupUi(this); // 表单里的控件树 + QGridLayout 全部由 Designer XML 建好 connect(ui->btn0, &QPushButton::clicked, this, [this] { onDigit(0); }); connect(ui->btn1, &QPushButton::clicked, this, [this] { onDigit(1); }); connect(ui->btn2, &QPushButton::clicked, this, [this] { onDigit(2); }); connect(ui->btn3, &QPushButton::clicked, this, [this] { onDigit(3); }); connect(ui->btn4, &QPushButton::clicked, this, [this] { onDigit(4); }); connect(ui->btn5, &QPushButton::clicked, this, [this] { onDigit(5); }); connect(ui->btn6, &QPushButton::clicked, this, [this] { onDigit(6); }); connect(ui->btn7, &QPushButton::clicked, this, [this] { onDigit(7); }); connect(ui->btn8, &QPushButton::clicked, this, [this] { onDigit(8); }); connect(ui->btn9, &QPushButton::clicked, this, [this] { onDigit(9); }); connect(ui->btnDot, &QPushButton::clicked, this, [this] { onDot(); }); connect(ui->btnPlus, &QPushButton::clicked, this, [this] { onOperator(QLatin1Char('+')); }); connect(ui->btnMinus, &QPushButton::clicked, this, [this] { onOperator(QLatin1Char('-')); }); connect(ui->btnMul, &QPushButton::clicked, this, [this] { onOperator(QLatin1Char('*')); }); connect(ui->btnDiv, &QPushButton::clicked, this, [this] { onOperator(QLatin1Char('/')); }); connect(ui->btnLeftParen, &QPushButton::clicked, this, &CalculatorDesigner::onLeftParen); connect(ui->btnRightParen, &QPushButton::clicked, this, &CalculatorDesigner::onRightParen); connect(ui->btnEquals, &QPushButton::clicked, this, &CalculatorDesigner::onEquals); connect(ui->btnClear, &QPushButton::clicked, this, &CalculatorDesigner::onClear); connect(ui->btnBackspace, &QPushButton::clicked, this, &CalculatorDesigner::onBackspace); onClear(); } CalculatorDesigner::~CalculatorDesigner() { delete ui; } void CalculatorDesigner::onDigit(int digit) { if (m_error) onClear(); if (m_resultShown) { m_expression.clear(); m_openParenCount = 0; m_resultShown = false; } const QString token = currentOperandToken(); if (token == QStringLiteral("0")) { m_expression.chop(1); // 用新数字替换孤立的前导 0,避免出现 "007" } else if (currentOperandDigitCount() >= kMaxOperandLength) { return; // 达到单个运算数的长度上限,忽略后续数字 } if (m_expression.size() >= kMaxExpressionLength) return; m_expression.append(QString::number(digit)); refreshDisplays(); } void CalculatorDesigner::onDot() { if (m_error) onClear(); if (m_resultShown) { m_expression.clear(); m_openParenCount = 0; m_resultShown = false; } const QString token = currentOperandToken(); if (token.contains(QLatin1Char('.'))) return; // 当前运算数已有小数点 const int appended = token.isEmpty() ? 2 : 1; // 空token时需要补一个前导 0 if (m_expression.size() + appended > kMaxExpressionLength) return; if (token.isEmpty()) m_expression.append(QLatin1Char('0')); m_expression.append(QLatin1Char('.')); refreshDisplays(); } void CalculatorDesigner::onOperator(QChar op) { if (m_error) return; if (m_resultShown) { m_expression = m_lastResultText; m_resultShown = false; } if (m_expression.isEmpty()) { if (op != QLatin1Char('-')) return; // 表达式开头只允许负号(单目负号) } else { const QChar last = m_expression.at(m_expression.size() - 1); if (last == QLatin1Char('(')) { if (op != QLatin1Char('-')) return; // 左括号后只允许负号 } else if (isOperatorChar(last)) { m_expression.chop(1); // 连续按运算符时,用新的运算符替换上一个 } } if (m_expression.size() >= kMaxExpressionLength) return; m_expression.append(op); refreshDisplays(); } void CalculatorDesigner::onLeftParen() { if (m_error) onClear(); if (m_resultShown) { m_expression.clear(); m_openParenCount = 0; m_resultShown = false; } if (!m_expression.isEmpty()) { const QChar last = m_expression.at(m_expression.size() - 1); if (!isOperatorChar(last) && last != QLatin1Char('(')) { return; // 数字或右括号后不支持隐式乘法,忽略 } } if (m_expression.size() >= kMaxExpressionLength) return; m_expression.append(QLatin1Char('(')); ++m_openParenCount; refreshDisplays(); } void CalculatorDesigner::onRightParen() { if (m_error) return; if (m_resultShown) return; if (m_openParenCount <= 0 || m_expression.isEmpty()) return; const QChar last = m_expression.at(m_expression.size() - 1); if (isOperatorChar(last) || last == QLatin1Char('(')) return; // 括号内需要完整子表达式 if (m_expression.size() >= kMaxExpressionLength) return; m_expression.append(QLatin1Char(')')); --m_openParenCount; refreshDisplays(); } void CalculatorDesigner::onEquals() { if (m_error) return; if (m_expression.isEmpty()) return; QString toEvaluate = m_expression; for (int i = 0; i < m_openParenCount; ++i) toEvaluate.append(QLatin1Char(')')); // 自动补全未闭合括号 const ExpressionEvaluator::Result result = ExpressionEvaluator::evaluate(toEvaluate); if (!result.ok) { showError(); return; } const QString resultText = QString::number(result.value, 'g', ExpressionEvaluator::kResultPrecision); m_expression = toEvaluate; m_lastResultText = resultText; m_openParenCount = 0; m_resultShown = true; ui->expressionEdit->setPlainText(m_expression + QStringLiteral(" = ") + resultText); ui->display->setText(resultText); } void CalculatorDesigner::onClear() { m_expression.clear(); m_lastResultText.clear(); m_openParenCount = 0; m_resultShown = false; m_error = false; ui->expressionEdit->clear(); ui->display->setText(QStringLiteral("0")); } void CalculatorDesigner::onBackspace() { if (m_error) { onClear(); return; } if (m_resultShown) return; // 结果已展示,需先清空或继续输入新表达式 if (m_expression.isEmpty()) return; const QChar removed = m_expression.at(m_expression.size() - 1); m_expression.chop(1); if (removed == QLatin1Char('(')) --m_openParenCount; else if (removed == QLatin1Char(')')) ++m_openParenCount; refreshDisplays(); } QString CalculatorDesigner::displayText() const { return ui->display->text(); } QString CalculatorDesigner::expressionText() const { return ui->expressionEdit->toPlainText(); } QString CalculatorDesigner::currentOperandToken() const { int start = m_expression.size(); while (start > 0 && isDigitOrDotChar(m_expression.at(start - 1))) --start; return m_expression.mid(start); } int CalculatorDesigner::currentOperandDigitCount() const { const QString token = currentOperandToken(); int count = 0; for (const QChar ch : token) { if (ch.isDigit()) ++count; } return count; } QString CalculatorDesigner::lastNumberToken() const { int end = m_expression.size(); while (end > 0 && !isDigitOrDotChar(m_expression.at(end - 1))) --end; // 跳过末尾的运算符/括号 int start = end; while (start > 0 && isDigitOrDotChar(m_expression.at(start - 1))) --start; return m_expression.mid(start, end - start); } void CalculatorDesigner::refreshDisplays() { ui->expressionEdit->setPlainText(m_expression); const QString token = lastNumberToken(); ui->display->setText(token.isEmpty() ? QStringLiteral("0") : token); } void CalculatorDesigner::showError() { ui->display->setText(QStringLiteral("Error")); m_error = true; }