前言:距离上一次摸QT已经快10年了,时光匆匆,现在已经到6.9版本了
一、安装QT
1.1、下载链接
https://mirrors.tuna.tsinghua.edu.cn/qt/official_releases/online_installers/
这是国内镜像,比官网快很多了,官网那个可以用龟速来形如。
目前已采用在线下载方式,类似visual studio 2022,依稀记得,最开始接触时,需要下载源代码,然后自己适配,编译。
二、计算器
先写个计算器先熟悉下
-
功能包含:四则运算,含小数点,可进行浮点数计算,包含括号
-
界面设计:使用QT的UI工具进行布局
大致如图所示,由于控件较多,也比较密集,所以采用网格布局(grid layout),然后为每个控件设置信号与对应的槽<=============>Windows下的win32或者MFC的事件与相关回调函数
当然除了在ui界面一个一个设置相关的槽,还可以直接通过代码来进行设置
//连接信号与槽,对C按钮使用
connect(ui->clear_Button, &QPushButton::clicked, this, [this](){express.clear();ui->display_LineEdit->setText(express);
});//连接信号与槽,对Del按钮使用
connect(ui->del_Button, &QPushButton::clicked, this, [this](){if(!express.isEmpty()){express.chop(1);ui->display_LineEdit->setText(express);}
});
- 核心功能:实现计算器,采用逆波兰式
-
- 先了解中缀表达式:形如
A+B
运算符在两个操作数中间,所以就是中缀表达式,日常生活中使用的就是这种
- 先了解中缀表达式:形如
-
- 后缀表达式:运算符在操作数后面就是,如
A B +
- 后缀表达式:运算符在操作数后面就是,如
-
- 逆波兰式的核心:就是将中缀表达式转变为后缀表达式,然后对后缀表达式进行计算
-
- 一般要使用两个栈,一个栈存放操作数,另外一个栈存放运算符。
-
- 计算终止条件,运算符栈为空,就弹出操作数栈的栈顶元素
- 计算终止条件,运算符栈为空,就弹出操作数栈的栈顶元素
-
//核心代码如下
// 等号的槽函数
void Widget::on_equval_Button_clicked()
{if(express.isEmpty()){express += '0';ui->display_LineEdit->setText(express);return;}//逆波兰式进行计算try{//替换掉表达式中的XQString str = express;str.replace('X', '*');//先将中缀表达式转为后缀表达式QString post_str = infixToPostFix(str);//计算后缀表达式的值double result = calPostFix(post_str);QString temp = QString::number(result, 'g', 10); //将其转为10位有效数字ui->display_LineEdit->setText(temp);}catch(...){express = "Error";ui->display_LineEdit->setText(express);}express.clear();
}
// 中缀转后缀
QString Widget::infixToPostFix(const QString &str)
{QStack<QChar> ops;QString ans = "";auto priority = [](const QChar c)->int{ //需要进行比较运算符的优先级,优先计算优先级高的if(c == '+' || c== '-') return 1;if(c == '*' || c== '/') return 2;return 0;};int len = str.length();for(int i = 0; i < len; ++i){QChar c= str[i];//处理数字和小数点if(c == '.' || c.isDigit()){while(i < len && (str[i] == '.' || str[i].isDigit())){ans += str[i];++i;}i--; //再回退一格ans += ' '; //用空格分隔数字}else if(c == '('){ops.push(c);}else if(c == ')'){while(!ops.isEmpty() && ops.top() != '('){ans += ops.top();ops.pop();ans += ' ';}if(!ops.isEmpty() && ops.top() == '('){ops.pop();}}else{ //处理四则运算符while(!ops.isEmpty() && (priority(ops.top()) > priority(c))){ans += ops.top();ops.pop();ans += ' ';}ops.push(c);}}//将剩下的运算符全部弹出while(!ops.isEmpty()){ans += ops.top();ops.pop();ans += ' ';}return ans.trimmed();
}
// 计算后缀表达式的值
double Widget::calPostFix(const QString &str)
{QStack<double> nums;QString num_str;int len = str.length();auto isOperator = [](const QChar& c)->bool{ if(c == '+' || c == '-' || c == '*' || c == '/') return true;return false;};for(int i = 0; i < len; ++i){QChar c = str[i];//先处理数字和小数点if(c == '.' || c.isDigit()){num_str.clear();while(i < len && (str[i].isDigit() || str[i] == '.')){num_str += str[i];++i;}i--;bool flag = false;double num = num_str.toDouble(&flag);if(flag){nums.push(num);}}else if(isOperator(c)){//操作如果<2不允许计算if(nums.size() < 2){return 0;}double num_B = nums.top();nums.pop();double num_A = nums.top();nums.pop();double result = 0.0f;switch(c.unicode()){case '+': result = num_A + num_B; break;case '-': result = num_A - num_B; break;case '*': result = num_A * num_B; break;case '/': {if(num_B == 0){return 0;}result = num_A / num_B;}break;}nums.push(result);}}if(nums.isEmpty()){return 0;}return nums.top();
}
之前逆波兰式说是两个栈,但这边优化了下,在中缀转后缀以及计算后缀时,都分别只使用了一个栈和一个字符串,用字符串模拟栈操作,减少点代码
三、小结:
3.1. 从代码中可以看到,对于堆上申请的内存,比如ui
,我没有进行手动释放内存,难道不怕造成内存泄漏吗?
QT拥有自动删除机制:- 当QT父对象被删除时,它会自动删除所有子对象- 只有当对象没有父对象时,才需要手动delete, 不过大部分对象都会被QT接管,所以不必过分关注,使用`QObject::deleteLater`来进行手动删除<br>
3.2. 没使用C++原生的STL容器,string, stack
,就连基础类型char
都没使用,使用的是QT这边二次封装的QString,QStack,QChar
,这对于不经常写QT的人来说,确实一下子有点懵,常用的STL算法在QT这边要变形,比如原生的判断字符是否是数字,isdigit(c)
,在QT这边没有,而是c.isdigit()
, switch
表达式不能直接使用QChar
,要这样使用switch(c.unicode())
。
Code