计算器功能如下:
1.支持运行时更换界面皮肤,水印和按钮形状等外观显示。
2.支持sin, cos, tan, cot, arcsin, arccos, arctan, arccot, !, log, ln, +, -, *, /, %, 乘方等基本运算,支持连续运算并自带PI,E的精确值。
3.支持表达式计算,支持设置变量,可以轻松的实现公式运算。
4.无限的结果保存,完全对用户透明,用户可以轻松的存储和读取先前的操作结果.
5.能够分析用户操作尤其是表达式中的语法错误,提醒用户而不是返回错误结果。
设计思路:
整个程序的 GUI 由两部分组成。结果存储由 JDialog 完成, JDialog 内含一个 JList 存放结果。主界面由 JMenuBar 和 JTabbedPane 组成, JTabbedPane 的两个子界面分别用作按键式计算器和表达式式计算器。
关于界面的构建工作全部由 CalculatorFace 类完成,同时按键式计算器的后台计算工作也有该类完成。 Parser 类则负责解析表达式返回计算结果。 CalculatorFace 调用 Parser 进行表达式计算工作。
在处理按键式计算时,各种计算操作符号显然可以分为两类:单操作数和双操作数。单操作数计算只需要取出显示框中内容,进行计算再将结果显示即可。双操作数计算则显得比较麻烦。因为需要实现连续计算,所以必须保存上次的操作符号和上次输入的结果。为此增加lastOperator字段和number1,number2字段分别存储前次操作符和对应的操作数。
运行机制如下:
当按下新键时,将number2->number1,显示框中内容取出作为number2,同时根据lastOperator中内容进行计算(number1 lastOperator number2),计算完成后,将这次按键内容保存到lastOperator中。要注意的是,当“=”按下时,将取消连续运算。因此添加boolean字段continued用来判断是否进行连续运算。
考虑到用户的误操作,即可能不小心按错了按钮,因此每次运算应以输入新数字前最后一个操作符号为准。因此,continued事实上也控制运算是否进行,每次双操作运算后,continued赋值为false,输入数字后才重新为真。当continued为false时,程序只是简单的把按键值赋予lastOperator而不进行运算工作。
而对于数字按键,唯一需要注意的是按下操作键后,再输入数据需要将显示框清空后从头输入;否则新数字将在显示框原有内容尾部添加。completed字段用于控制该情形。上述例外在于,第一次使用计算器时,即使没有按下操作键,也应该重新显示数字。First字段将处理这一情形。
表达式解析则通过Parser类完成。getToken方法负责将表达式中基本的单元取出。这些基本单元包括:变量,数字和操作符号。运行时,首先将表达式中变量替换成其值,对处理好的表达式从左至右取出单元进行计算。计算采用递归下降的流式处理,调用按计算优先级排列好的各种函数,每种函数处理同一优先级的运算。
优先级列表为:
括号,阶乘,正负号,三角函数/对数函数/反三角函数,乘方,乘/除/模运算,加减运算。
下面举例说明表达式的解析过程。表达式
10 + 5 * B
有两个项:10和5*B,第二项包括两个因数:5和B,分别是一个数字和一个变量
再看另外一个例子。表达式
14 * (7 – C)
有两个因数:14和(7-C),分别是一个数字和一个圆括号表达式。圆括号表达式包括两个项:一个数字和一个变量。
上述过程形成了递归下降解析器的基础。递归下降解析器是一组互相递归的方法,这些方法以一种链式方式实现生成规则。在每个适当的步骤上,解析器以代数学规定的正确顺序执行指定的操作。为了解释如何使用生成规则来解析表达式,采用下面的表达式来跟踪解析过程:
9/3 – (100 + 56)
整个解析过程如下:
(1) 获得第一项9/3;
(2) 获得第一项的两个因数并完成除法运算,得到结果3;
(3) 获得第二项(100+56)。在这一步启动递归分析过程处理括号内的子表达式;
(4) 获得其中的两项并完成加法运算,得到结果156;
(5) 从第二项的递归计算过程中返回;
(6) 3减去156,答案是-153。
Bugs:
经过同学的友情测试,现发现bug如下:
(1)无法将――2形式识别为2
(2)乘方运算时,当数字和’!’中出现空格或者括号时无法处理
(3)类似sinlog10形式无法识别,只能识别sin(log10)
以上将在下一版本中修订。
Uncompleted features:
(1)任意进制的计算和转换
(2)角度与弧度的转换
(3)扩大计算范围(即应该使用BigDemical而不是double)
以上同样会在下一版本中实现。
下面是程序的源代码:
package cn.com.w4t.calculator;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Vector;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.border.EtchedBorder;
import javax.swing.border.LineBorder;
import javax.swing.border.TitledBorder;
import org.jvnet.substance.SubstanceLookAndFeel;
import org.jvnet.substance.theme.SubstancePurpleTheme;
import org.jvnet.substance.watermark.SubstanceBinaryWatermark;

/** */ /**
* Main Application Interface
*
* @author td
*
*/
@SuppressWarnings( " serial " )
public class CalculatorFace extends JFrame implements ActionListener
{

/** */ /**
* 按键计算的结果
*/
private double result1;

/** */ /**
* 表达式计算的结果
*/
private double result2;

/** */ /**
* 二元计算是否完成,完成后下次输入数据时将从头开始而不是在原有数据后添加
*/
private boolean completed = false ;

/** */ /**
* 是否是第一次进行操作
*/
private boolean first = true ;

/** */ /**
* 是否进行连续计算,比如"="将取消连续运算
*/
private boolean continued = false ;

/** */ /**
* 被保存的先前计算结果集
*/
private Vector < String > resultVector = new Vector < String > ();

/** */ /**
* 计算表达式
*/
private String express;

/** */ /**
* 变量值
*/
private String variables;

/** */ /**
* 表达式解析式
*/
private Parser parser = new Parser();

/** */ /**
* 操作按键
*/
private JButton[] operatorBtns = new JButton[ 20 ];

/** */ /**
* 操作符号
*/
private String[] op =
{ " + " , " - " , " * " , " / " , " % " , " ^ " , " ! " , " = " , " ln " ,
" log " , " sin " , " cos " , " tan " , " cot " , " arcsin " , " arccos " , " arctan " ,
" arccot " , " PI " , " E " } ;

/** */ /**
* 数字按键
*/
private JButton[] numberBtns = new JButton[ 12 ];

/** */ /**
* 保持结果的对话框
*/
private JDialog resultHolder;

/** */ /**
* 保持结果的列表
*/
private JList resultList;

/** */ /**
* 一些系统功能按键,显示、隐藏结果记录对话框,存储计算结果,清屏,退格
*/
private JButton clearBtn, saveBtn, saveBtn2, backBtn, shBtn, shBtn2,
submitBtn;

/** */ /**
* 主界面
*/
private JTabbedPane mainTab;

/** */ /**
* 输入框
*/
private JTextField resultField1, resultField2, inputField;

/** */ /**
* 错误标签
*/
private JLabel errorLabel1, errorLabel2;

/** */ /**
* 监听器
*/
private ActionListener numberListener = new NumberListener(),
oneOperatorListener = new OneOperatorListener(),
twoOperatorListener = new TwoOperatorListener();

/** */ /**
* 菜单
*/
private JMenu fileM, setM, helpM;

/** */ /**
* 菜单栏
*/
private JMenuBar menu;

/** */ /**
* 变量输入框
*/
private JTextArea variableArea;

/** */ /**
* 操作数
*/
private String number1 = " 0.0 " , number2 = " 0.0 " ;

/** */ /**
* 上一次操作符号
*/
private String lastOperator;
// private JMenuItem fileItem, settingsItem, helpItem;
public CalculatorFace()
{
super ();
init();
}
/** */ /**
* 初始化属性
*
*/
private void initReference()
{
// init all buttons
for ( int i = 0 ; i < 10 ; i ++ )
{
numberBtns[i] = new JButton(Integer.toString(i));
numberBtns[i].addActionListener(numberListener);
numberBtns[i].setBorder( null );
}
numberBtns[ 10 ] = new JButton( " . " );
numberBtns[ 10 ].addActionListener(oneOperatorListener);
numberBtns[ 10 ].setBorder( null );
numberBtns[ 11 ] = new JButton( " +/- " );
numberBtns[ 11 ].setBorder( null );
numberBtns[ 11 ].addActionListener(oneOperatorListener);
for ( int i = 0 ; i < op.length; i ++ )
{
operatorBtns[i] = new JButton(op[i]);
if (i < 6 )
operatorBtns[i].addActionListener(twoOperatorListener);
else
operatorBtns[i].addActionListener(oneOperatorListener);
operatorBtns[i].setBorder( null );
}
clearBtn = new JButton( " Clear " );
clearBtn.addActionListener( this );
clearBtn.setBorder( new EtchedBorder(EtchedBorder.LOWERED));
saveBtn = new JButton( " Save " );
saveBtn.addActionListener( this );
saveBtn.setBorder( new EtchedBorder(EtchedBorder.LOWERED));
saveBtn2 = new JButton( " Save " );
saveBtn2.addActionListener( this );
saveBtn2.setBorder( new EtchedBorder(EtchedBorder.LOWERED));
backBtn = new JButton( " Back " );
backBtn.addActionListener( this );
backBtn.setBorder( new EtchedBorder(EtchedBorder.LOWERED));
shBtn = new JButton( " S/H " );
shBtn.addActionListener( this );
shBtn.setBorder( new EtchedBorder(EtchedBorder.LOWERED));
shBtn2 = new JButton( " S/H " );
shBtn2.addActionListener( this );
shBtn2.setBorder( new EtchedBorder(EtchedBorder.LOWERED));
submitBtn = new JButton( " Submit " );
ParserListener p = new ParserListener();
submitBtn.addActionListener(p);
submitBtn.setBorder( new EtchedBorder(EtchedBorder.LOWERED));
// init textfileds
resultField1 = new JTextField( " 0.0 " );
resultField1.setEditable( false );
resultField2 = new JTextField( " 0.0 " );
resultField2.setEditable( false );
inputField = new JTextField();
inputField.addActionListener(p);
inputField.setToolTipText( " 输入表达式,可以含有变量,但是变量必须在下面定义 " );
variableArea = new JTextArea();
variableArea.setToolTipText( " 输入变量值,约定形式为a=1,b=2,请勿使用s,c,t等关键字作为变量名 " );
// init labels
errorLabel1 = new JLabel();
errorLabel1.setForeground(Color.red);
errorLabel2 = new JLabel();
errorLabel2.setForeground(Color.red);
mainTab = new JTabbedPane();
// init menu
menu = new JMenuBar();
fileM = new JMenu( " File " );
fileM.setMnemonic( ' F ' );
setM = new JMenu( " Settings " );
setM.setMnemonic( ' S ' );
helpM = new JMenu( " Help " );
helpM.setMnemonic( ' H ' );
}
/** */ /**
* 初始化菜单
*
*/
private void initMenu()
{
menu.add(fileM);
menu.add(setM);
menu.add(helpM);
fileM.add( new JMenuItem( " Exit " ));
setM.add( new JMenuItem( " 二进制 " ));
setM.add( new JMenuItem( " 八进制 " ));
setM.add( new JMenuItem( " 十进制 " ));
setM.add( new JMenuItem( " 十六进制 " ));
helpM.add( new JMenuItem( " Manual " ));
helpM.add( new JMenuItem( " About " ));
}
/** */ /**
* 初始化主界面
*
*/
private void initTabbedPane()
{
JPanel p1 = new JPanel();
JPanel p10 = new JPanel();
JPanel p12 = new JPanel();
JPanel p11 = new JPanel();
JPanel pc1 = new JPanel();
JPanel p2 = new JPanel();
JPanel p20 = new JPanel();
JPanel p22 = new JPanel();
JPanel p21 = new JPanel();
p10.setLayout( new GridLayout( 2 , 1 ));
p10.setBorder( new TitledBorder( new LineBorder(Color.DARK_GRAY, 1 ),
" Result: " ));
p12.setLayout( new GridLayout( 6 , 8 ));
p11.setLayout( new GridLayout( 2 , 8 ));
p1.setLayout( new BorderLayout());
pc1.setLayout( new BorderLayout());
pc1.setBorder( new TitledBorder( new LineBorder(Color.DARK_GRAY, 1 ),
" Input Buttons: " ));
p20.setLayout( new GridLayout( 3 , 1 ));
p21.setLayout( new BorderLayout());
p22.setLayout( new GridLayout( 1 , 7 ));
p2.setLayout( new BorderLayout());
p10.add(resultField1);
p10.add(errorLabel1);

for ( int i = 0 ; i < 4 ; i ++ )
{
p12.add(operatorBtns[i]);
}
for ( int i = 0 ; i < 4 ; i ++ )
{
p12.add( new JLabel());
}
for ( int i = 0 ; i < 4 ; i ++ )
{
for ( int j = i * 4 ; j < i * 4 + 4 ; j ++ )
p12.add(operatorBtns[j + 4 ]);
p12.add( new JLabel( "" ));
for ( int j = i * 3 ; j < i * 3 + 3 ; j ++ )
p12.add(numberBtns[j]);
}
for ( int i = 0 ; i < 8 ; i ++ )
{
p12.add( new JLabel());
}
p11.add(shBtn);
p11.add(saveBtn);
p11.add(backBtn);
p11.add(clearBtn);
for ( int i = 0 ; i < 12 ; i ++ )
p11.add( new JLabel());
p1.add(p10, " North " );
pc1.add(p11, " Center " );
pc1.add(p12, " South " );
p1.add(pc1, " Center " );
JLabel tip = new JLabel(
" 支持:sin,cos,tan,cot,arcsin,arccos,arctan,arccot,!,log,ln " );
p20.add(tip);
p20.add(resultField2);
&nb
您现在的位置: