程序综合设计实践 :用QT实现简易计算器及贷款计算
1,项目概述
该项目目标是设计开发一个支持连续计算的包括括号(
)
,求余%
四则运算+
-
*
/
的计算器 Calculator 以及贷款计算功能 Mortgage。
本程序为本人初学一周QT所作,才疏学亦浅,程序中绝对有意想不到的蜜汁bug😆(逃)欢迎大家评论区交流丫。
项目下载地址
通过单击按钮,输入并完成如4+5.21+6
或 5*(8+16)-2
类似的连续计算,并将运算结果显示在输出文本框 lineEdit
中,计算的式子移至上方的 label
中。
支持负数,小数运算,并且精度为小数点后五位。支持简单的错误性检验。 CE
清空 Del
后退 More
切换至贷款计算。两个界面,贷款计算界面不做过多赘述。
2,设计步骤 (以下只举例 Calculator
界面)
###界面设计部分
界面设计部分如图由
label
(显示计算的式子)
lineedit
(显示结果或者当前输入的式子)
以及各个按钮组成。
创建按钮的时候因为数目有点多,所以我们可以用数组保存创建的按钮 指针
,后面就可以直接用 索引
对按钮进行操控。
QT支持用 css
渲染元素。如label->setStyleSheet("font-size:12px;color:grey;border-bottom:1px solid #f1f1f1;border-radius:5px;text-align:right;");
//from widget.cpp
setFixedSize(400,450); //固定窗口大小
setWindowIcon(QIcon(":/icon.png")); //设置icon
QLineEdit* lineedit = new QLineEdit(this); //创建 lineedit 对象
lineedit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); //将该对象设为宽高可扩展
QLabel* label = new QLabel(this);
label->setText("请在下方输入要计算的式子..."); //设置文本内容
QString btnstr[22] = {"(", ")", "%", "CE", "Del",
"7", "8", "9", "+", "-",
"4", "5", "6", "*", "/",
"1", "2", "3", "More", "=",
"0", "."
};
auto that = this;
QPushButton* btn[22]; //创建22个按钮并用数组储存,并且绑定相应的槽函数。方便后面布局(偷懒
for(int i=0; i<22; i++){
btn[i] = new QPushButton(that);
btn[i]->setText(btnstr[i]);
btn[i]->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
connect(btn[i],&QPushButton::clicked,that,[=](){
that->input(btnstr[i], lineedit, label);
});
}
//设置样式,美化
btn[4]->setStyleSheet("color:red;");
btn[18]->setStyleSheet("color:yellow;");
int cyellow[9] = {0,1,2,3,8,9,13,14};
for(int i=0; i<9; i++) btn[cyellow[i]]->setStyleSheet("color:#38d6d5;");
lineedit->setStyleSheet("color:#333;font-size:21px;font-weight:bolder;border:1px dashed #f1f1f1;border-radius:5px;text-align:right;");
label->setStyleSheet("font-size:12px;color:grey;border-bottom:1px solid #f1f1f1;border-radius:5px;text-align:right;");
lineedit->setAlignment( Qt::AlignRight);
label->setAlignment( Qt::AlignRight);
setStyleSheet("QPushButton {font-size:20px;font-weight:bold;color:ligrhtgrey;background-color: rgba(255, 255, 255, 0%);border:1px solid grey;border-radius:5px;} QPushButton:hover, QLabel:hover {font-size:25px;} Widget {background:qlineargradient(spread:pad,y1:1,y2:0,stop:0 #79f4f3,stop:1 #f8fefe);}");
setWindowTitle("Calculator");
//设置布局
QGridLayout* computelayout = new QGridLayout(this);
computelayout->addWidget(label, 0, 0, 1, 5);
computelayout->addWidget(lineedit, 1, 0, 1, 5);
computelayout->addWidget(btn[0], 2, 0, 1, 1);
computelayout->addWidget(btn[1], 2, 1, 1, 1);
computelayout->addWidget(btn[2], 2, 2, 1, 1);
computelayout->addWidget(btn[3], 2, 3, 1, 1);
computelayout->addWidget(btn[4], 2, 4, 1, 1);
computelayout->addWidget(btn[5], 3, 0, 1, 1);
computelayout->addWidget(btn[6], 3, 1, 1, 1);
computelayout->addWidget(btn[7], 3, 2, 1, 1);
computelayout->addWidget(btn[8], 3, 3, 1, 1);
computelayout->addWidget(btn[9], 3, 4, 1, 1);
computelayout->addWidget(btn[10], 4, 0, 1, 1);
computelayout->addWidget(btn[11], 4, 1, 1, 1);
computelayout->addWidget(btn[12], 4, 2, 1, 1);
computelayout->addWidget(btn[13], 4, 3, 1, 1);
computelayout->addWidget(btn[14], 4, 4, 1, 1);
computelayout->addWidget(btn[15], 5, 0, 1, 1);
computelayout->addWidget(btn[16], 5, 1, 1, 1);
computelayout->addWidget(btn[17], 5, 2, 1, 1);
computelayout->addWidget(btn[18], 5, 3, 2, 1);
computelayout->addWidget(btn[19], 5, 4, 2, 1);
computelayout->addWidget(btn[20], 6, 0, 1, 2);
computelayout->addWidget(btn[21], 6, 2, 1, 1);
###逻辑设计部分
首先先将每个按钮都绑定对应的 槽函数
//from widget.cpp
for(int i=0; i<22; i++){
btn[i] = new QPushButton(that);
btn[i]->setText(btnstr[i]);
btn[i]->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
connect(btn[i],&QPushButton::clicked,that,[=](){ //这里
that->input(btnstr[i], lineedit, label);
});
}
用户按下 =
按钮后,利用按钮的信号和槽调用 input()
函数
//from widget.cpp
void Widget::input(QString word, QLineEdit* lineedit, QLabel* label)
{
if(word == "="){
if(linetxt == "") label->setText("不能为空哦..."); // 首先判断表达式是否合法
else compute(lineedit, label); //合法,跳转到计算函数
}
else if (word == "More") { //切换贷款计算界面
this->hide();
emit calchangewindow();
}
else if (word == "CE") { //清空
linetxt = "";
labeltxt = "";
label->setText("");
lineedit->setText(linetxt);
}
else if (word == "Del") { //后退
QString tmp = "";
for (int i=0; i < linetxt.length()-1; i++)
tmp += linetxt[i];
linetxt = tmp;
lineedit->setText(linetxt);
}
else {
linetxt+=word; //输入为字符
lineedit->setText(linetxt);
}
}
计算时我们需要先将 中缀表达式
转化为 后缀表达式
。
中缀表达式就是我们日常生活中正常使用的那种形如:a+b*c
后缀表达式就是形如abc*+
,操作符在数字后面;
为什么有后缀表达式呢?
因为中缀表达式便于人们的理解与计算
但是后缀表达式更方便计算机的运算(如二叉树、堆栈的方法计算)
因此在读取一个中缀表达式后,我们得办法将他转化为后缀表达式。
那么怎么转化为后缀表达式呢?
我们可以用这种方法将中缀表达式转化为后缀表达式。
以下是节选自项目 backans.cpp
的实现将中缀表达式转化后缀表达式的函数
//from backans.cpp
string getback(string str)//获取后缀表达式的函数
{
char* middle = new char[100];
char* tmp = middle;
char* isFirst = tmp;
for (int i=0; i<str.length(); i++) {
*tmp = str[i]; //将 middle 所指向的字符串内容赋值为传入的 str ,同时保持 middle 指向首地址不变
tmp++;
}
char* back = new char[100]; //同上
char* backend = back;
stack<char> s;
s.push('#');
while (*middle)
{
if( ((middle == isFirst)&& *middle == '-' && Number(*(middle+1))) || ( *(middle-1) == '(' && *middle == '-' && Number(*(middle+1))) || Number(*middle))
{
*back = *middle; //判断是否为负数
back++, middle++;
}
else
{
if (Number(*(back - 1))) *back++ = ' '; //如果前一位是数字的话添加空格 用以区分
if (*middle == ')')//如果右括号的话,输出所有操作符直到遇到左括号,并抛弃相对应的一堆括号
{
while (s.top() != '(' && s.top() != '#')
{
*back = s.top();
s.pop();
back++;
*back++ = ' ';
}
middle++;
s.pop();//抛弃左括号
}
else if (*middle == '(')//遇到左括号,则进入栈
{
s.push(*middle); middle++;
}
else if (priority(*middle) > priority(s.top()))//如果栈内的操作符优先级高于栈外的优先级,则入栈
{
s.push(*middle); middle++;
}
else if (priority(*middle) <= priority(s.top()))
//如果栈内的操作符优先级低于或等于栈外的优先级,输出栈内的符号,并入栈栈外的符号
{
while (priority(*middle) <= priority(s.top()) && s.top() != '#') {
*back = s.top();
back++; //直到栈内的操作符为空 或者 低于栈外的优先级
s.pop();
*back++ = ' ';
}
s.push(*middle);
middle++;
}
}
}
while (isOpe(s.top()))//中缀表达式遍历完成,但是=栈中还有符号存在,一一出栈输出
{
if (Number(*(back - 1))) *back++ = ' ';
*back = s.top();
qDebug("%c",*back);
s.pop();
if(s.top() != '#'){
back++;
*back++ = ' ';
}
}
string tmpresult = ""; //后面这些由于某些蜜汁bug而做出的一些处理。。。请无视充满了妥协
string result = "";
int address = 0;
for (;backend<=back;backend++)
tmpresult += *backend;
while(Number(tmpresult[address]) || isOpe(tmpresult[address]) || tmpresult[address] == ' ')
result += tmpresult[address++];
return result;
}
转化为后缀表达式后怎么计算结果呢?
我们可以用这种方法将后缀表达式求值。
然后将结果打印至 lineEdit
即完成计算啦!
以下是节选自项目 backans.cpp
的实现计算后缀表达式的函数
//from backans.cpp
long double backans::result(string str)
{
string back = getback(str);
stack<long double> s;
for (int i=0; i<back.length(); ) {
if(!isOpe(back[i]) || (back[i] == '-' && Number(back[i+1]) )){ //将数字依次入栈
s.push(turnnum(back, i));
while (back[i] != ' ')
i++;
i++;
}
else {
long double a = s.top();
s.pop();
long double b = s.top();
s.pop();
s.push(Cauculate(back[i], b, a));//遇到符号时,取栈顶的第二个数和第一个数求解,并入栈
i+=2;
}
}
while (s.size() >= 2)//最终栈内存在的数大于2时,继续计算,直到只剩下一个数
{
long double a = s.top();
s.pop();
long double b = s.top();
s.pop();
s.push(Cauculate(back[back.length()-1], b, a));
}
//返回这个数字,既是最终结果
return s.top();
}
以上就是全部内容啦,希望能帮到您嘿嘿🤭
项目压缩包下载地址:https://files-cdn.cnblogs.com/files/Trump-He/Calculator.zip