1.项目架构
1.图形化界面
2.widget.h
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QString>
#include <QStack>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_oneButton_clicked();
void on_twoButton_clicked();
void on_threeButton_clicked();
void on_fourButton_clicked();
void on_fiveButton_clicked();
void on_sixButton_clicked();
void on_sevenButton_clicked();
void on_eightButton_clicked();
void on_nineButton_clicked();
void on_leftButton_clicked();
void on_rightButton_clicked();
void on_addButton_clicked();
void on_reduceButton_clicked();
void on_mulButton_clicked();
void on_divButton_clicked();
void on_zeroButton_clicked();
void on_clearButton_clicked();
void on_backButton_clicked();
void on_equalButton_clicked();
int evaluateExpression(const QString &expr);
int applyOperator(double a, double b, QChar op);
private:
Ui::Widget *ui;
QString expression;
};
#endif // WIDGET_H
3.main.cpp
cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
3.widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
this->setMaximumSize(210,295);
this->setMinimumSize(210,295);
this->setWindowTitle("计算器");
QFont f("仿宋",14);//字体对象
ui->mainLineEdit->setFont(f);
//按钮上放图片
QIcon con("/data/wzh/QT/Qt_1/calculate/Back.png");
ui->backButton->setIcon(con);
//改变按钮背景色
ui->equalButton->setStyleSheet("background:green");
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_oneButton_clicked()
{
expression += "1";
ui->mainLineEdit->setText(expression);
}
void Widget::on_twoButton_clicked()
{
expression += "2";
ui->mainLineEdit->setText(expression);
}
void Widget::on_threeButton_clicked()
{
expression += "3";
ui->mainLineEdit->setText(expression);
}
void Widget::on_fourButton_clicked()
{
expression += "4";
ui->mainLineEdit->setText(expression);
}
void Widget::on_fiveButton_clicked()
{
expression += "5";
ui->mainLineEdit->setText(expression);
}
void Widget::on_sixButton_clicked()
{
expression += "6";
ui->mainLineEdit->setText(expression);
}
void Widget::on_sevenButton_clicked()
{
expression += "7";
ui->mainLineEdit->setText(expression);
}
void Widget::on_eightButton_clicked()
{
expression += "8";
ui->mainLineEdit->setText(expression);
}
void Widget::on_nineButton_clicked()
{
expression += "9";
ui->mainLineEdit->setText(expression);
}
void Widget::on_leftButton_clicked()
{
expression += "(";
ui->mainLineEdit->setText(expression);
}
void Widget::on_rightButton_clicked()
{
expression += ")";
ui->mainLineEdit->setText(expression);
}
void Widget::on_addButton_clicked()
{
expression += "+";
ui->mainLineEdit->setText(expression);
}
void Widget::on_reduceButton_clicked()
{
expression += "-";
ui->mainLineEdit->setText(expression);
}
void Widget::on_mulButton_clicked()
{
expression += "*";
ui->mainLineEdit->setText(expression);
}
void Widget::on_divButton_clicked()
{
expression += "/";
ui->mainLineEdit->setText(expression);
}
void Widget::on_zeroButton_clicked()
{
expression += "0";
ui->mainLineEdit->setText(expression);
}
void Widget::on_clearButton_clicked()
{
expression.clear();
ui->mainLineEdit->clear();
}
void Widget::on_backButton_clicked()
{
expression.chop(1);
ui->mainLineEdit->setText(expression);
}
void Widget::on_equalButton_clicked()
{
// 获取表达式
QString expr = ui->mainLineEdit->text();
// 检查表达式是否为空
if (expr.isEmpty()) {
ui->mainLineEdit->setText("Error: Empty expression");
return;
}
// 解析和计算表达式
double result = evaluateExpression(expr);
// 显示结果
ui->mainLineEdit->setText(QString::number(result));
}
int Widget::evaluateExpression(const QString &expr)
{
// 定义运算符优先级
QMap<QChar, int> precedence;
precedence['+'] = 1;
precedence['-'] = 1;
precedence['*'] = 2;
precedence['/'] = 2;
// 操作数栈
QStack<double> values;
// 运算符栈
QStack<QChar> ops;
int i = 0;
while (i < expr.length()) {
QChar c = expr[i];
if (c.isDigit() || c == '.') {
// 解析数字(包括小数)
double val = 0;
int j = i;
bool hasDecimal = false;
// 判断小数点错误
while (j < expr.length() && (expr[j].isDigit() || expr[j] == '.')) {
if (expr[j] == '.') {
if (hasDecimal) {
// 多个小数点,非法表达式
return 0;
}
hasDecimal = true;}
j++;}
// 提取数字字符串
QString numStr = expr.mid(i, j - i);
bool ok;
val = numStr.toDouble(&ok);
if (!ok) {
return 0;} // 转换失败
values.push(val);
i = j;}
else if (c == '(') {
// 左括号直接入栈
ops.push(c);
i++;}
else if (c == ')') {
// 右括号,弹出运算符直到遇到左括号
while (!ops.isEmpty() && ops.top() != '(') {
double val2 = values.pop();
double val1 = values.pop();
QChar op = ops.pop();
double res = applyOperator(val1, val2, op);
values.push(res);}
if (!ops.isEmpty()) {
ops.pop();} // 弹出左括号
i++;}
else if (precedence.contains(c)) {
// 处理运算符
while (!ops.isEmpty() && ops.top() != '(' && precedence[ops.top()] >= precedence[c]) {
double val2 = values.pop();
double val1 = values.pop();
QChar op = ops.pop();
double res = applyOperator(val1, val2, op);
values.push(res);}
ops.push(c);
i++;}
else {
// 非法字符
return 0;}
}
// 处理剩余的运算符
while (!ops.isEmpty()) {
double val2 = values.pop();
double val1 = values.pop();
QChar op = ops.pop();
double res = applyOperator(val1, val2, op);
values.push(res);
}
return values.pop();
}
int Widget::applyOperator(double a, double b, QChar op)
{
switch (op.toLatin1()) {
case '+':
return a + b;
case '-':
return a - b;
case '*':
return a * b;
case '/':
if (b == 0) {
// 除以零错误
return 0;
}
return a / b;
default:
return 0;
}
}
2. 程序讲解
-
主窗口类 :
Widget
继承自QWidget
,负责计算器的UI和逻辑。 -
UI文件 :通过
ui_widget.h
自动生成,包含计算器的界面元素(按钮、文本框等)。 -
核心功能:
-
数字和运算符的输入
-
表达式的解析和计算
-
结果的显示
-
1. 初始化代码
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
this->setMaximumSize(210,295);
this->setMinimumSize(210,295);
this->setWindowTitle("计算器");
QFont f("仿宋",14);//字体对象
ui->mainLineEdit->setFont(f);
//按钮上放图片
QIcon con("/data/wzh/QT/Qt_1/calculate/Back.png");
ui->backButton->setIcon(con);
//改变按钮背景色
ui->equalButton->setStyleSheet("background:green");
}
-
窗口设置:
-
固定窗口大小为210x295像素。
-
设置窗口标题为"计算器"。
-
-
字体设置:
- 使用
QFont
设置mainLineEdit
的字体为"仿宋",字号为14。
- 使用
-
按钮图标:
- 为
backButton
设置图片图标。
- 为
-
按钮样式:
- 为
equalButton
设置背景颜色为绿色。
- 为
2.按钮事件处理
1.数字按钮:每个数字按钮(0-9)将对应的数字添加到expression
字符串中,并更新mainLineEdit
的显示。
2.运算符按钮 :+
、-
、*
、/
按钮将对应的运算符添加到expression
中。
3.括号按钮 :(
和)
按钮将括号添加到expression
中。
cpp
void Widget::on_divButton_clicked()
{
expression += "/";
ui->mainLineEdit->setText(expression);
}
4.清除按钮 :清空expression
和mainLineEdit
。
cpp
void Widget::on_clearButton_clicked()
{
expression.clear();
ui->mainLineEdit->clear();
}
5.退格按钮 :删除expression
的最后一个字符,并更新显示。
cpp
void Widget::on_backButton_clicked()
{
expression.chop(1);
ui->mainLineEdit->setText(expression);
}
6.等号按钮:
cpp
void Widget::on_equalButton_clicked()
{
// 获取表达式
QString expr = ui->mainLineEdit->text();
// 检查表达式是否为空
if (expr.isEmpty()) {
ui->mainLineEdit->setText("Error: Empty expression");
return;
}
// 解析和计算表达式
double result = evaluateExpression(expr);
// 显示结果
ui->mainLineEdit->setText(QString::number(result));
}
-
获取
mainLineEdit
中的表达式。 -
如果表达式为空,显示错误信息。
-
调用
evaluateExpression
计算结果,并显示结果。
3. 表达式解析和计算
cpp
int Widget::evaluateExpression(const QString &expr)
{
// 定义运算符优先级
QMap<QChar, int> precedence;
precedence['+'] = 1;
precedence['-'] = 1;
precedence['*'] = 2;
precedence['/'] = 2;
// 操作数栈
QStack<double> values;
// 运算符栈
QStack<QChar> ops;
int i = 0;
while (i < expr.length()) {
QChar c = expr[i];
if (c.isDigit() || c == '.') {
// 解析数字(包括小数)
double val = 0;
int j = i;
bool hasDecimal = false;
// 判断小数点错误
while (j < expr.length() && (expr[j].isDigit() || expr[j] == '.')) {
if (expr[j] == '.') {
if (hasDecimal) {
// 多个小数点,非法表达式
return 0;
}
hasDecimal = true;}
j++;}
// 提取数字字符串
QString numStr = expr.mid(i, j - i);
bool ok;
val = numStr.toDouble(&ok);
if (!ok) {
return 0;} // 转换失败
values.push(val);
i = j;}
else if (c == '(') {
// 左括号直接入栈
ops.push(c);
i++;}
else if (c == ')') {
// 右括号,弹出运算符直到遇到左括号
while (!ops.isEmpty() && ops.top() != '(') {
double val2 = values.pop();
double val1 = values.pop();
QChar op = ops.pop();
double res = applyOperator(val1, val2, op);
values.push(res);}
if (!ops.isEmpty()) {
ops.pop();} // 弹出左括号
i++;}
else if (precedence.contains(c)) {
// 处理运算符
while (!ops.isEmpty() && ops.top() != '(' && precedence[ops.top()] >= precedence[c]) {
double val2 = values.pop();
double val1 = values.pop();
QChar op = ops.pop();
double res = applyOperator(val1, val2, op);
values.push(res);}
ops.push(c);
i++;}
else {
// 非法字符
return 0;}
}
// 处理剩余的运算符
while (!ops.isEmpty()) {
double val2 = values.pop();
double val1 = values.pop();
QChar op = ops.pop();
double res = applyOperator(val1, val2, op);
values.push(res);
}
return values.pop();
}
-
运算符优先级 :使用
QMap
定义运算符的优先级(+
、-
优先级为1,*
、/
优先级为2)。 -
栈结构:
-
values
栈存储操作数。 -
ops
栈存储运算符。
-
-
解析流程:
-
遍历表达式中的每个字符。
-
如果是数字或小数点,解析整个数字并压入
values
栈。 -
如果是左括号
(
,直接压入ops
栈。 -
如果是右括号
)
,弹出ops
栈中的运算符直到遇到左括号,并计算结果。 -
如果是运算符,根据优先级处理
ops
栈中的运算符,并将当前运算符压入栈。 -
如果遇到非法字符,返回0。
-
-
计算剩余运算符 :处理完所有字符后,弹出
ops
栈中的剩余运算符并计算结果。
4. 运算符应用
cpp
int Widget::applyOperator(double a, double b, QChar op)
{
switch (op.toLatin1()) {
case '+':
return a + b;
case '-':
return a - b;
case '*':
return a * b;
case '/':
if (b == 0) {
// 除以零错误
return 0;
}
return a / b;
default:
return 0;
}
}
-
根据运算符执行对应的数学运算。
-
如果是除法且除数为0,返回0(表示错误)。