目录
一、选题与前期调查
1.选题
计算数学表达式的程序(2人)等级:C-
功能要求:
1)由用户输入一个简单的四则运算表达式,求出其计算结果后显示。2)允许在表达式中出现常用的数学函数,如绝对值、取整、三角函数、倒数、平方根、平方、立方等。
注意:该题对算法有一定要求,并且如果要得高分,其中的算法一定要自己写。如果被判定为抄袭,得分不会超过C。
提供多线程网络服务与客户端可提供提升至A。
2.前期调查
1.主要使用gpt3.5和cursor来进行课设完成:
gpt3.5:extension://dhoenijjpgpeimemopealfcbiecgceod/chat/index.html
cursor:Cursor - The AI-first Code Editor
二、人员分工
分工 | 姓名 | 任务 |
组长 | 罗明 | 博客编写;GuiMain类;Gui类;Algorithm类;编码规范格式;前期调查和功能设计;git管理代码 |
三、项目描述
1.项目简介
- 该项目是一款简单的计算器,主要用于大家平时进行一些简单的计算。
2.项目采用技术
- GUI图形界面
- 设计模式
- git代码管理
3.功能需求分析
- 支持基本的加减乘除运算
- 支持复杂的四则运算
- 支持常用的数学函数
- 支持清零功能
4.项目亮点
- 用户友好的界面设计,包括按钮和输入框
- 实现在显示框得到计算结果
- 支持历史记录功能,可以查看之前的计算式和计算结果
- 功能完备,有利于大家轻松入手使用
5.系统细节
5.1系统目录
5.2GuiMain类
- 系统的主函数,主要是启动一个GUI界面
5.3Gui类
- 用于加载出计算器的主界面
- 继承自Frame类,实现ActionListener接口
- 登录监听器接口,定义登录成功事件回调方法
- 功能:设计一个GUI界面
5.4Algorithm类
- 用于记录计算器功能的实现方法
- 继承自JFrame类,实现ActionListener接口
- 功能:包含实现计算器每个计算方法和基本数学函数的实现方法
四、功能运行结果展示
1.系统主界面
2.实现基本的运算
2.1实现加法运算
2.2实现减法运算
2.3实现乘法运算
2.4实现除法运算
2.5实现四则运算
3.实现基本的数学函数
3.1实现开方
3.2实现平方
3.3实现倒数
3.4实现百分比转换
3.5实现三角函数
3.5.1实现sin
3.5.2实现cos
3.5.3实现tan
3.6实现绝对值
3.7实现十进制转化为二进制
3.8实现十进制转换为十六进制
3.9实现取整
4.实现查看历史运算式和以及结果
五、关键代码
1.GUI界面设置
public Gui() {
super("Caculator");//“超”关键字,表示调用父类的构造函数,
resultText.setBounds(20, 18, 255, 115); // 设置文本框大小
resultText.setAlignmentX(RIGHT_ALIGNMENT); // 文本框内容右对齐
resultText.setEditable(false); // 文本框不允许修改结果
resultText.setFont(new Font("monospaced", Font.PLAIN, 18)); //设置结果文本框输入文字的字体、类型、大小
History.setFont(new Font("monospaced", Font.PLAIN, 18)); //设置历史记录文本框输入文字的字体、类型、大小
History.setBounds(290, 40, 250, 370); // 设置文本框大小
History.setAlignmentX(LEFT_ALIGNMENT); // 文本框内容右对齐
History.setEditable(false); // 文本框不允许修改结果
label.setBounds(300, 15, 100, 20); //设置标签位置及大小
jp2.setBounds(290, 40, 250, 370); //设置面板窗口位置及大小
jp2.setLayout(new GridLayout());
JPanel jp1 = new JPanel();
jp1.setBounds(20, 18, 255, 115); //设置面板窗口位置及大小
jp1.setLayout(new GridLayout());
resultText.setLineWrap(true); // 激活自动换行功能
resultText.setWrapStyleWord(true); // 激活断行不断字功能
resultText.setSelectedTextColor(Color.RED);
History.setLineWrap(true); //自动换行
History.setWrapStyleWord(true);
History.setSelectedTextColor(Color.blue);
gdt1.setViewportView(resultText); //使滚动条显示出来
gdt2.setViewportView(History);
gdt1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); //设置让垂直滚动条一直显示
gdt2.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); //设置让垂直滚动条一直显示
gdt2.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); //设置让水平滚动条一直显示
jp1.add(gdt1); //将滚动条添加入面板窗口中
jp2.add(gdt2);
this.add(jp1); //将面板添加到总窗体中
this.add(jp2); //将面板添加到总窗体中
this.setLayout(null);
this.add(label); // 新建“历史记录”标签
// 放置按钮 x,y为按钮的横纵坐标
int x = 20, y = 150;
for (int i = 0; i < KEYS.length; i++) {
keys[i] = new JButton();
keys[i].setText(KEYS[i]);
keys[i].setBounds(x, y, 60, 30);
if (x < 215) {
x += 65;
} else {
x = 20;
y += 35;
}
this.add(keys[i]);
}
for (int i = 0; i < KEYS.length; i++) // 每个按钮都注册事件监听器
{
keys[i].addActionListener(this);
}
this.setResizable(false);
this.setBounds(500, 200, 567, 480);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setVisible(true);
}
2.中缀转换为后缀
private String[] houzhui(String infix) { //infix 中缀
String s = "";// 用于承接多位数的字符串
Stack<String> opStack = new Stack<String>(); // 操作符静态栈,对用户输入的操作符进行处理,用于存储运算符
Stack<String> postQueue = new Stack<String>(); // 后缀表达式,为了将多位数存储为独立的字符串
System.out.println("中缀:" + infix);
for (int i = 0; i < infix.length(); i++) // 遍历中缀表达式
// indexof函数,返回字串首次出现的位置;charAt函数返回index位置处的字符;
{
if ("1234567890.".indexOf(infix.charAt(i)) >= 0) { // 遇到数字字符直接入队
//判断并记录多位操作数,比如,中缀表达式:234+4*2,我们扫描这个字符串的时候,s的作用相当于用来存储长度为3个字符的操作数:234
s = "";// 作为承接字符,每次开始时都要清空
for (; i < infix.length() && "0123456789.".indexOf(infix.charAt(i)) >= 0; i++) {
s = s + infix.charAt(i);
}
i--; //避免跳过对非数字字符的处理
postQueue.push(s); // 数字字符直接加入后缀表达式
} else if ("(".indexOf(infix.charAt(i)) >= 0) { // 遇到左括号
opStack.push(String.valueOf(infix.charAt(i))); // 左括号入栈
} else if (")".indexOf(infix.charAt(i)) >= 0) { // 遇到右括号
while (!opStack.peek().equals("(")) { // 栈顶元素循环出栈,直到遇到左括号为止
postQueue.push(opStack.pop());
}
opStack.pop(); //删除左括号
} else if ("*%/+-".indexOf(infix.charAt(i)) >= 0) // 遇到运算符
{
if (opStack.empty() || "(".contains(opStack.peek())) { // 若栈为空或栈顶元素为左括号则直接入栈
opStack.push(String.valueOf(infix.charAt(i)));
} else {
// 当栈顶元素为高优先级或同级运算符时,让栈顶元素出栈进入后缀表达式后,直到符合规则后,当前运算符再入栈
boolean rule = ("*%/+-".contains(opStack.peek()) && "+-".indexOf(infix.charAt(i)) >= 0) || ("*%/".contains(opStack.peek()) && "*%/".indexOf(infix.charAt(i)) >= 0);
while (!opStack.empty() && rule) {
postQueue.push(opStack.peek()); //peek()方法:返回栈顶的元素但不移除它
opStack.pop();
}
opStack.push(String.valueOf(infix.charAt(i))); // 当前元素入栈
}
}
}
while (!opStack.empty()) {// 遍历结束后将栈中剩余元素依次出栈进入后缀表达式
postQueue.push(opStack.pop());
}
//将后缀表达式栈转换为字符串数组格式
String[] suffix = new String[postQueue.size()];
for (int i = postQueue.size() - 1; i >= 0; i--) {
suffix[i] = postQueue.pop();
}
System.out.println("后缀:" + Arrays.toString(suffix.clone()));
return suffix;
}
3.计算后缀表达式,并计算出结果
public double Result(String[] suffix) { //suffix 后缀
Stack<String> Result = new Stack<>();// 顺序存储的栈,数据类型为字符串
int i;
for (i = 0; i < suffix.length; i++) {
if ("1234567890.".indexOf(suffix[i].charAt(0)) >= 0) { //遇到数字,直接入栈
Result.push(suffix[i]);
} else { // 遇到运算符字符,将栈顶两个元素出栈计算并将结果返回栈顶
double x, y, n = 0;
x = Double.parseDouble(Result.pop()); // 顺序出栈两个数字字符串,并转换为double类型
y = Double.parseDouble(Result.pop());
switch (suffix[i]) {
case "*":
n = y * x;
break;
case "/":
if (x == 0) return 000000;
else n = y / x;
break;
case "%":
if (x == 0) return 000000;
else n = y % x;
break;
case "-":
n = y - x;
break;
case "+":
n = y + x;
break;
}
Result.push(String.valueOf(n)); // 将运算结果重新入栈
}
}
System.out.println("return:" + Result.peek());
return Double.parseDouble(Result.peek()); // 返回最终结果
}
4.实现计算器的各种计算方法
public void actionPerformed(ActionEvent e) {
String label = e.getActionCommand(); //获得事件源的标签
if (Objects.equals(label, "AC")) //清空按钮,消除显示屏文本框前面所有的输入和结果
{
input = "";
resultText.setText("0.0"); //更新文本域的显示,显示初始值;
} else if (Objects.equals(label, "sqrt")) {
String m;
double a = Double.parseDouble(input);
if(input.isEmpty()) m="error!"; //加判断,先输入数字再输入开方符号才是合法的
else m = String.valueOf(Math.sqrt(a));
resultText.setText("sqrt" + "(" + input + ")" + "=" + m); //使运算表达式显示在输入界面
History.setText(History.getText() + resultText.getText() + "\n"); //获取输入界面的运算表达式并使其显示在历史记录文本框
input = m;
} else if (Objects.equals(label,"1/x")){
String m;
double a = Double.parseDouble(input);
if(input.isEmpty()) m="error!";
else m = String.valueOf(1.0/a);
resultText.setText("1/" + input + "=" + m);
History.setText(History.getText() + resultText.getText() + "\n");
input = m;
}else if (Objects.equals(label, "x^2")) {
String m;
Double a = Double.parseDouble(input);
if(input.isEmpty()) m="error!";
else m = String.valueOf(Math.pow(a,2));
resultText.setText(input + "^2" + "=" + m);
History.setText(History.getText() + resultText.getText() + "\n");
input = m;
} else if (Objects.equals(label, "%")) {
String m;
double a = Double.parseDouble(input);
if(input.isEmpty()) m="error!";
else m = String.valueOf(0.01*a);
resultText.setText("%" + input + "=" + m);
History.setText(History.getText() + resultText.getText() + "\n");
input = m;
} else if (Objects.equals(label, "=")) {
if (input.isEmpty()) return;
String[] s = houzhui(input); //将中缀表达式转换为后缀表达式
double result = Result(s); //计算后缀表达式得出最终算式结果
resultText.setText(input + "=" + result);
History.setText(History.getText() + resultText.getText() + "\n");
} else if (Objects.equals(label,"abs")) {
String m;
double a = Double.parseDouble(input);
if(input.isEmpty()) m="error!";
else m = String.valueOf(Math.abs(a));
resultText.setText("abs(" + input + ")=" + m);
History.setText(History.getText() + resultText.getText() + "\n");
input = m;
} else if (Objects.equals(label,"sin")) {
String m;
double a = Double.parseDouble(input);
double radians = Math.toRadians(a);
if(input.isEmpty()) m="error!";
else {
DecimalFormat df = new DecimalFormat("#.##");
m = String.valueOf(Math.sin(radians));
}
resultText.setText("sin(" + input + "°)=" + m);
History.setText(History.getText() + resultText.getText() + "\n");
input = m;
} else if (Objects.equals(label,"cos")) {
String m;
double a = Double.parseDouble(input);
double radians = Math.toRadians(a);
if(input.isEmpty()) m="error!";
else {
DecimalFormat df = new DecimalFormat("#.##");
m = String.valueOf(Math.cos(radians));
}
resultText.setText("cos(" + input + "°)=" + m);
History.setText(History.getText() + resultText.getText() + "\n");
input = m;
} else if (Objects.equals(label,"tan")) {
String m;
double a = Double.parseDouble(input);
double radians = Math.toRadians(a);
if(input.isEmpty()) m="error!";
else {
DecimalFormat df = new DecimalFormat("#.##");
m = String.valueOf(Math.tan(radians));
}
resultText.setText("tan(" + input + "°)=" + m);
History.setText(History.getText() + resultText.getText() + "\n");
input = m;
}else if (Objects.equals(label,"bin")) {
String m;
int a = Integer.parseInt(input);
if(input.isEmpty()) m="error!";
else m = Integer.toBinaryString(a);
resultText.setText("("+input+")10" + "=" +"(" + m + ")2");
History.setText(History.getText() + resultText.getText() + "\n");
input = m;
} else if (Objects.equals(label,"hex")) {
String m;
int a = Integer.parseInt(input);
if(input.isEmpty()) m="error!";
else m = Integer.toHexString(a);
resultText.setText("("+input+")10" + "=" +"(" + m + ")16");
History.setText(History.getText() + resultText.getText() + "\n");
input = m;
} else if (Objects.equals(label,"rou")){
String m;
double a = Double.parseDouble(input);
if(input.isEmpty()) m="error!";
else m = String.valueOf(Math.round(a));
resultText.setText("round(" + input + ")" + "=" + m);
History.setText(History.getText() + resultText.getText() + "\n");
input = m;
}else {
if (Objects.equals(label, "e")) {
String m = String.valueOf(2.71828); //将e的值以字符串的形式传给m
label = m;
} else if (Objects.equals(label, "pi")) {
String m = String.valueOf(3.14159);
label = m;
}
input = input + label;
resultText.setText(input);
}
}
六、使用git管理代码
七、遇到的问题
1.无法打开GUI界面以及打开以后的界面按钮选项的排布很乱,导致超出窗口