QT的学习(一)

发布于:2025-09-09 ⋅ 阅读:(20) ⋅ 点赞:(0)

前言:距离上一次摸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);
    }
});
  • 核心功能:实现计算器,采用逆波兰式
      1. 先了解中缀表达式:形如A+B运算符在两个操作数中间,所以就是中缀表达式,日常生活中使用的就是这种
      1. 后缀表达式:运算符在操作数后面就是,如A B +
      1. 逆波兰式的核心:就是将中缀表达式转变为后缀表达式,然后对后缀表达式进行计算
      1. 一般要使用两个栈,一个栈存放操作数,另外一个栈存放运算符。
      1. 计算终止条件,运算符栈为空,就弹出操作数栈的栈顶元素
        在这里插入图片描述
//核心代码如下
// 等号的槽函数
void Widget::on_equval_Button_clicked()
{
    if(express.isEmpty()){
        express += '0';
        ui->display_LineEdit->setText(express);
        return;
    }
    //逆波兰式进行计算
    try{
        //替换掉表达式中的X
        QString 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


网站公告

今日签到

点亮在社区的每一天
去签到