QT实现计算器

代码

widget.h

cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QPushButton>
#include <QGridLayout>
#include <QSizePolicy>
#include <QLineEdit>
#include <QDebug>
#include <QtGlobal>
#include <QStack>
#include <cmath>

class Widget : public QWidget
{
    Q_OBJECT
    QLineEdit *edit = new QLineEdit();
    bool m_clearOnNextInput; // 新增:标志位,下次输入是否清除内容
public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
public slots:
    void onNumberButtonClicked(const QString &number);
    void onEqualClicked();
};
#endif // WIDGET_H

widget.cpp

cpp 复制代码
#include "widget.h"
#include <vector>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
     m_clearOnNextInput = false; // 初始状态:不清除
    QGridLayout *layout = new QGridLayout();
    //按钮
    QPushButton* pqb1= new QPushButton("1");
    QPushButton* pqb2= new QPushButton("2");
    QPushButton* pqb3= new QPushButton("3");
    QPushButton* pqb4= new QPushButton("4");
    QPushButton* pqb5= new QPushButton("5");
    QPushButton* pqb6= new QPushButton("6");
    QPushButton* pqb7= new QPushButton("7");
    QPushButton* pqb8= new QPushButton("8");
    QPushButton* pqb9= new QPushButton("9");
    QPushButton* pqb0= new QPushButton("0");
    QPushButton* pqb10= new QPushButton("+");
    QPushButton* pqb11= new QPushButton("-");
    QPushButton* pqb12= new QPushButton("*");
    QPushButton* pqb13= new QPushButton("/");
    QPushButton* pqb14= new QPushButton(".");
    QPushButton* pqb15= new QPushButton("=");

    pqb1->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    pqb2->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    pqb3->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    pqb4->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    pqb5->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    pqb6->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    pqb7->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    pqb8->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    pqb9->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    pqb10->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    pqb11->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    pqb12->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    pqb13->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    pqb14->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    pqb15->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    pqb0->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);



    layout->addWidget(pqb0,3,0);
    layout->addWidget(pqb1,2,0);
    layout->addWidget(pqb2,2,1);
    layout->addWidget(pqb3,2,2);
    layout->addWidget(pqb4,1,0);
    layout->addWidget(pqb5,1,1);
    layout->addWidget(pqb6,1,2);
    layout->addWidget(pqb7,0,0);
    layout->addWidget(pqb8,0,1);
    layout->addWidget(pqb9,0,2);
    layout->addWidget(pqb10,3,2);
    layout->addWidget(pqb11,0,3);
    layout->addWidget(pqb12,1,3);
    layout->addWidget(pqb13,2,3);
    layout->addWidget(pqb14,3,1);
    layout->addWidget(pqb15,3,3);


    edit->show();
    edit->setMaximumHeight(150);
    QGridLayout *layout1 = new QGridLayout(this);
    edit->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
    edit->setReadOnly(true); // 禁止手动输入


    layout1->addLayout(layout,1,0,4,4);
    layout1->addWidget(edit,0,0,1,4);

    std::vector<QPushButton*> arr = {pqb0,pqb1,pqb2,pqb3,pqb4,pqb5,pqb6,pqb7,pqb8,
                                     pqb9,pqb10,pqb11,pqb12,pqb13,pqb14};

    for(auto &ele : arr){
        connect(ele, &QPushButton::clicked, this, [this,ele](){
            this->onNumberButtonClicked(ele->text());});
    }

    connect(pqb15, &QPushButton::clicked, this, &Widget::onEqualClicked);


}

Widget::~Widget()
{
}
// 槽函数实现:追加数字到显示框
void Widget::onNumberButtonClicked(const QString &number) {
    if (m_clearOnNextInput) {
        // 如果需要清除,则先清空显示框,再输入新数字
        edit->setText(number);
        m_clearOnNextInput = false; // 重置标志位(下次输入恢复追加)
    } else {
        // 不需要清除,则直接追加数字
        edit->setText(edit->text() + number);
    }
}


//等号计算
QString calculateSimpleExpression(const QString &expression);

void Widget::onEqualClicked() {
        QString expr = edit->text();  // 获取QLineEdit中的表达式
        QString result = calculateSimpleExpression(expr);

        edit->setText(result);  // 显示结果
        m_clearOnNextInput = true;
}
// 辅助函数:判断是否为运算符(仅支持 +-*/)
bool isOperator(const QChar &c) {
    return c == '+' || c == '-' || c == '*' || c == '/';
}

// 辅助函数:获取运算符优先级(乘除 > 加减)
int getPriority(const QChar &op) {
    if (op == '+' || op == '-') return 1;  // 优先级低
    if (op == '*' || op == '/') return 2;  // 优先级高
    return 0;
}

// 步骤1:中缀表达式转后缀表达式(无括号版本)
QVector<QString> infixToPostfix(const QString &expression) {
    QVector<QString> postfix;  // 存储后缀表达式
    QStack<QChar> opStack;     // 运算符栈
    QString numBuffer;         // 缓存数字(支持多位数和小数)

    for (int i = 0; i < expression.size(); ++i) {
        QChar c = expression[i];

        // 跳过空格
        if (c.isSpace()) continue;

        // 处理数字或小数点
        if (c.isDigit() || c == '.') {
            numBuffer += c;
        }
        // 处理运算符
        else if (isOperator(c)) {
            // 先将缓存的数字加入后缀表达式
            if (numBuffer.isEmpty()) {
                return {};  // 连续运算符(如"1++2"),返回错误
            }
            postfix.append(numBuffer);
            numBuffer.clear();

            // 弹出栈中优先级 >= 当前运算符的运算符
            while (!opStack.isEmpty() && getPriority(opStack.top()) >= getPriority(c)) {
                postfix.append(opStack.top());
                opStack.pop();
            }
            opStack.push(c);  // 当前运算符入栈
        }
        // 非法字符(非数字、非运算符、非小数点)
        else {
            return {};  // 含非法字符,返回错误
        }
    }

    // 处理最后一个数字
    if (numBuffer.isEmpty()) {
        return {};  // 表达式以运算符结尾(如"1+"),返回错误
    }
    postfix.append(numBuffer);

    // 弹出栈中剩余运算符
    while (!opStack.isEmpty()) {
        postfix.append(opStack.top());
        opStack.pop();
    }

    return postfix;
}

// 步骤2:计算后缀表达式结果
QString calculatePostfix(const QVector<QString> &postfix) {
    if (postfix.isEmpty()) return "错误";

    QStack<double> numStack;  // 数字栈

    for (const QString &token : postfix) {
        // 如果是数字,入栈
        if (!isOperator(token[0]) || (token.size() > 1 && token[0] == '-')) {
            bool ok;
            double num = token.toDouble(&ok);
            if (!ok) return "错误";  // 数字格式错误(如多小数点)
            numStack.push(num);
        }
        // 如果是运算符,计算
        else {
            if (numStack.size() < 2) return "错误";  // 操作数不足

            double b = numStack.pop();  // 注意顺序:后弹出的是第一个操作数
            double a = numStack.pop();
            double result = 0;

            if (token == "+") {
                result = a + b;
            } else if (token == "-") {
                result = a - b;
            } else if (token == "*") {
                result = a * b;
            } else if (token == "/") {
                if (qFuzzyCompare(b, 0.0)) return "错误";  // 除零错误
                result = a / b;
            }

            numStack.push(result);
        }
    }

    // 最终栈中应只有一个结果
    if (numStack.size() != 1) return "错误";

    // 优化显示:整数去掉".0"
    double finalResult = numStack.pop();
    if (finalResult == floor(finalResult)) {
        return QString::number((long long)finalResult);  // 整数显示
    } else {
        return QString::number(finalResult);  // 小数显示
    }
}

// 对外接口:计算无括号的表达式
QString calculateSimpleExpression(const QString &expression) {
    // 空表达式处理
    if (expression.trimmed().isEmpty()) return "";

    // 中缀转后缀
    QVector<QString> postfix = infixToPostfix(expression);
    if (postfix.isEmpty()) return "错误";  // 转换失败(格式错误)

    // 计算结果
    return calculatePostfix(postfix);
}

测试结果

相关推荐
用户8055336980317 小时前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner17 小时前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz6 天前
QML Hello World 入门示例
qt
xcyxiner9 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner9 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner10 天前
DicomViewer (添加模型类)3
qt
xcyxiner10 天前
DicomViewer (目录调整) 2
qt
xcyxiner11 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00612 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术12 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript