Qt实现计算器(包含算术运算和逻辑运算),纯干货,一步步超详细过程

一、实验目的和要求

目的:锻炼我们对Qt的掌握和应用,锻炼实际解决问题,完成项目的能力。

要求:在课后作业03,05的基础上,增加图形交互功能,增加三个逻辑运算符&&,||,!,并能处理逻辑运算符和算术运算符的混合运算;增加容错功能,能进行异常处理。

二、实验环境

软件:Qt 5

硬件: Legion

三、实验内容

设计并实现计算器

四、实验过程

用文字、图(流程图等)、表格等方式记录实验过程中分析、设计工作。

Qt实现计算器(包含算术运算和逻辑运算),纯干货,一步步超详细过程_第1张图片

4.1 任务定义和问题分析

         此次需求在于基于课后作业扩充。主要难点在于图形交互界面的建立,需要学习QT,并且将原本的代码转化为Qt可以识别的代码。并且在算术运算的基础上增加逻辑运算,并支持混合运算。

4.2 数据结构的选择和概要设计

         在对Qt一段时间的学习之后,我选择使用ui来进行图形界面的设计简化过程。在设计好计算器的大概视图后,需要更改每个按钮的名称以便于后期的实现。

         之后在代码的编写上,为了增加美观性与实用性,我选择添加背景图片和显示实时时间。之后需要通过connect语句将按钮信号和对应的槽函数相连接,以便于可以通过按按钮来进行处理(这一步非常重要)。

         之后对槽函数的设计上,选择每输入一个,就在textEdit上显示一个,并且将它储存到事先准备好的string里去。在进行计算的时候,代码类似于第三、五次作业内容,故不再赘述。需要注意的是,在按下等于号时,需要将计算的结果显示到textEdit上。

4.3 详细设计

         本人最开始设计的是ui,建立的按钮效果如下

 

Qt实现计算器(包含算术运算和逻辑运算),纯干货,一步步超详细过程_第2张图片

现在我逐一讲解过程:

1、pushButton和textEdit的建立                        

  创建完项目之后,首先看到的肯定是

Qt实现计算器(包含算术运算和逻辑运算),纯干货,一步步超详细过程_第3张图片

Resource和Other files是后加的,刚创建并没有,后面会讲到)

  那么点开forms,就可以进入ui界面。之后拖拽左框的pushbutton按钮和textEdit按钮,然后自行排版(pushbutton是后期自己按按钮用的,textEdit是显示用的,就留一个textEdit)

Qt实现计算器(包含算术运算和逻辑运算),纯干货,一步步超详细过程_第4张图片

Qt实现计算器(包含算术运算和逻辑运算),纯干货,一步步超详细过程_第5张图片

  

这里提一句,在排版时,除了可以自行调节按钮位置外,更可以选中你所需要排列的按钮,通过上方的

 这两个按钮进行水平或者垂直布局。

     此时你将会获得一个已经排版好的计算器样式,双击pushbutton按钮可以更改名称,可以将pushbutton改为‘(’、‘=’等任意符号,方便之后计算器运行。

2、对pushbutton和textEdit的修改

    在大概的界面设计结束后,接下来的步骤是对pushbutton和textEdit属性的修改。点开ui界面,任意点击一个按钮,会在右下角看到这样的界面

Qt实现计算器(包含算术运算和逻辑运算),纯干货,一步步超详细过程_第6张图片

      其中,objectName就是指按钮信号的名称,因为在后期需要设置相应的槽函数和信号连接,所以信号必须有一个容易区分的名字(默认都是pushbutton_i。i=1,2…n)。例如,可以点击‘+’,将它的名字改为pushButton_plus或者pushButton_jia,怎么改都行,只要自己可以理解。此外,顺便提一句,因为pushbutton都是有默认大小的,如果不满意,可以在geometry的宽度高度进行修改。

    既然改了,就改到底!我在设计时嫌默认按钮太小,就将按钮放大,但是放大后却感到字体好小啊…所以,就要将字体调大,那干脆字体格式也改了吧。咋改呢?回到myWidget.cpp文件中(我构造的类名称叫myWidget),在这个

Qt实现计算器(包含算术运算和逻辑运算),纯干货,一步步超详细过程_第7张图片

  Ui->setupUi(this)下面接着写这样几句话。

ui->pushButton_0->setFont(QFont("宋体", 18));

 

//此处,pushbutton_0是我定义按钮0的名称,那么我设置的就是宋体18号的大小
//但是为什么同样的代码,你写会报错呢?
//包含Qfont头文件了嘛…没有就去包含吧,就不会报错了。
//同理
        QFont textEditFont ( "Microsoft YaHei", 25, 75);
        ui->textEdit->setFont(textEditFont);

这两行代码,就把输入输出时显示在textEdit框里数字的大小和格式修改了。

3、myWidget.h的编写

  点开myWidget.h文件,映入眼帘的是public、private slots、private几个单词,在private下还有

Ui::myWidget *ui;

一行代码对不对?

  好的,接下来需要做的是,在public 中写几个函数

Qt实现计算器(包含算术运算和逻辑运算),纯干货,一步步超详细过程_第8张图片

我写了这几个函数,is_num的作用是判断字符是不是数字,Cal的作用是进行局部运算(了解计算器机制的都清楚,储存在栈中,按优先级进行入栈或者局部运算操作),prior作用是判断运算符优先级,Do就是最核心的部分,将表达式字符串进行计算,return出结果。这些函数因人而异,每个人写的函数,写的方式都不同,但因为我写这篇博客在于我已经完成了前一次在vs中编写的过程,并且我此篇博客是面向的合肥工业大学同学们的大作业,所以对于这些函数的介绍,我只会在篇末给出大概思路。在后期修改中,我又加了一个

void on_actionRight_triggered();

用来将textEdit显示的数字进行右对齐(因为我看到计算器都是这样的,所以这一步很可以省略)

  在private slots中,我们需要编写槽函数,来和按钮匹配

Qt实现计算器(包含算术运算和逻辑运算),纯干货,一步步超详细过程_第9张图片

我定义了这么多槽函数,前面那么多个,可以看出分别匹配了我ui中的按钮。那么后面四个五个槽函数呢?分别是四个报错函数和一个显示时间的函数,可以根据需要添加。最后,在private中,我添加了一个Qstring strin,即建立了一个Qstring类的对象叫strin。到这里,就结束了myWidget.h的编写。

4、myWidget.cpp的编写

  这是一个非常浩大的工程。第一步,我想将窗口的名称改了,毕竟我要证明是我做的,可以使用setWindowTitle(“我的窗口”),你可以“”中写入任何东西,产生出的窗口就会显示这个名称 

  接着要做的是,将textEdit上显示的数字设置为不可更改,不然,产生的数字就可以被delete了!那可得了?果断

ui->textEdit->setReadOnly(true);

设置为只可读。然后再来一个

ui->textEdit->setText("0");

用于将初始啥也没按时,设置为0。

  第二步,strin=“#”。啥意思呢,strin是我在.h里建立的Qstring对象,它干什么用?就是讲你输入的字符统统储存到里面,便于之后计算,之所以要初始化为“#”,是因为储存在栈中时,需要一个字符与接下来第一个进入的字符做优先级比较,工大同学应该都懂,不再赘述。

  第三步,连接槽函数和信号,这一步非常简单,就是将你按按钮的行为和槽函数的调用联系在一起,只需要写一行,然后Ctrl C,Ctrl V粘贴就行。

//绑定按键0与处理函数   
 connect(ui->pushButton_0,&QPushButton::clicked,[this](){pushButton_0();});

这就将按钮0和槽函数void pushbutton_0联系了。注意,第一个pushbutton_0指的是信号,第二个指的是槽函数,只是我把槽函数设置成和信号同名便于查找而已。下面解释一下connect括号内的内容:ui->pushButton_0 表示信号发送者,&QpushButton::clicked表示行为,也就是当产生click的行为后发送信号,[this]表示信号是传给本身的,随后那个指的是产生的效果,也就是点击产生的槽函数的调用。

  第四步,这几行代码

   Timer *timer = new QTimer(this);

   connect(timer,SIGNAL(timeout()),this,SLOT(timerUpdate()));

   timer->start(1000);

  这是产生时间所需要的三行代码

   on_actionRight_triggered();这是对齐代码(可以忽略

 OK!已经完成很多了!起码可以编写其他函数了

  第五步,编写 void on_actionRight_triggered();上面说了这个可以根据个人需要进行右对齐,所以我直接放代码了,ui->textEdit->setAlignment(Qt::AlignRight);。左对齐就是AlignLeft…
  第六步timerUpdate(void)的编写,(记得包含#include #include


    QDateTime time = QDateTime::currentDateTime();

    QString str = time.toString("yyyy-MM-dd hh:mm:ss dddd");

    ui->label->setText(str);

    QFont textEditFont ( "Microsoft YaHei", 15, 30);

    ui->label->setFont(textEditFont);

第一行,设置当前时间,转化成年月日时分秒形式,底下几行是设置字体大小和样式的。这里注意一点,代码为ui->label ,也就是说你要回到ui界面,在左侧找到label,拖一个出来就行了,时间就会显示在上面。

  槽函数的编写!!!

第七步!数字按钮的编写

这里从注意按钮0到9都是差不多的,写一个然后复制粘贴就行,以1为例。

if(strin=="#")    ui->textEdit->clear();

   bracketdigiterror();

    strin+="1";

    on_actionRight_triggered();

    ui->textEdit->textCursor().insertText("1");

    如果strin==“#”,就将textEdit清空,这是啥意思?因为,每进行一次运算,摁下等于号之后textEdit会显示结果,然后要将strin初始化为#便于下一步运算,此时在按任何按钮时,上一步运算结果还停留在textEdit上,很不方便,所以要通过这一步消除。Bracketdigiterror是一个判错的槽函数,暂时不理它。Strin+=“1”指的是将1加入,因为我们要得到一个完整的表达式来带入Do中计算,而strin就是储存表达式的,所以每按一个按钮,都会让表达式多一位,也就需要接入strin中。下面一行,右对齐。最后一行,就是将1显示出来。之后的几个数字按钮与此同理。

   第八步,现在进行左括号槽函数编写

  void myWidget::pushButton_lkuohao()

  {

    if(strin=="#")    ui->textEdit->clear();

        char*s=strin.toLocal8Bit().data();

        digitbracketerror();

        strin+="(";

        on_actionRight_triggered();

        ui->textEdit->textCursor().insertText("(");

}

第二行,是定义一个char*类型变量s,将strin转化。为什么要转化?与底下判错函数有关。

   第九步,现在进行右括号函数编写

void myWidget::pushButton_rkuohao()

{



    if(strin=="#")

        ui->textEdit->clear();

    if(!bracketerror())//检查左括号,左括号缺失

    {

        QMessageBox::about(this,"输入错误","请检查一下左括号数量");

        //这一步要包含QmessageBox头文件,指的是,如果出现这样的错误,    //会弹出弹窗弹窗标题为“输入错误”,内容为“请检查左括号数量”

        digitbracketerror();//判错函数

        strin+="(";

        on_actionRight_triggered();//对齐

        ui->textEdit->textCursor().insertText("(");//补齐缺失的左括号

    }

    Else//正常输入,无错误的情况

    {

        char*s=strin.toLocal8Bit().data();//转化为char*

        if(s[strlen(s)-1]=='(')//如果‘)’前一个是‘(’,证明括号里什么                           //也没有,需要提示并补充个1.

        {

            QMessageBox::about(this,"输入错误","括号里面没有计算或者数字");

            strin+="1";

           ui->textEdit->textCursor().insertText("1");

        }

        strin+=")";//一切正常的话,则将‘)’加入strin

        on_actionRight_triggered();

      ui->textEdit->textCursor().insertText(")");

    }



}

代码较长,我将解释都在上面以注释形式传递

   第十步,加减乘除槽函数(类似,可Ctrl)

  void myWidget::pushButton_jia()

  {

    strin+="+";

    ui->textEdit->textCursor().insertText("+");

    on_actionRight_triggered();

    operror();

}

    第十一步,关闭函数

void myWidget::pushButton_guanbi()
{
    close();
}

 

这一步很好理解,就是在按turn off按钮时,直接退出计算器。

   第十二步!(快结束了

void myWidget::pushButton_C()

{

    strin="#";

    ui->textEdit->setText("0");

    on_actionRight_triggered();

}

这一步是将所有数据都归零的意思,所以直接将strin变为初始的#,并且将textEdit清空,最后显示初始时的0,且右对齐。

  第十三步 ,back函数编写

void myWidget::pushButton_jiantou()

{

    strin.chop(1);

    ui->textEdit->textCursor().deletePreviousChar();

    if(strin=="#")ui->textEdit->setText("0");

}

    首先,点击这个按钮,就是会删除一个数。删除所需要的首先是将它从strin中取出,就是chop函数,里面的1指的是取出一个。之后则需要把它在textEdit上显示的部分也删除,最后一步是啥?也就是如果本来就一个数的话,删除之后strin就空了,那就需要形成初始状况的0。

   第十四步,“&&”和“||”编写

void myWidget::pushButton_and()
{
        strin+="&&";
        ui->textEdit->textCursor().insertText("&&");
        //operror(1);
}
void myWidget::pushButton_huo()
{
        strin+="||";
        on_actionRight_triggered();
       ui->textEdit->textCursor().insertText("||");
     //   operror(2);
}

对于逻辑运算,应该只有工大老师要求,所以就直接挂代码了

老规矩,先储存在strin中然后进行运算符正确性判断。

   第十五步!!:逻辑非运算,有点不一样,单独讲

void myWidget::pushButton_fei()

{

    if(strin=="#")    ui->textEdit->clear();

       char*s=strin.toLocal8Bit().data();

       if(isdigit(s[strlen(s)-1])||s[strlen(s)-1]==')')

       {

           QMessageBox::about(this,"输入有误","逻辑否符号前是数");

           strin+="*";

           ui->textEdit->textCursor().insertText("*");

       }

       strin+="!";

       on_actionRight_triggered();

      ui->textEdit->textCursor().insertText("!");

}

   这个需要先转化为char* 类型,之后判断“!”前面是不是数字或者右括号,如果是的话,例如“4!”或者“)!”都是错误的,所以给出弹窗并且给塔补充一个乘号:“4*!”,这样就可以通过在!后面加数字来避免错误。之后的内容同理,不再赘述。

好的!!!

那么四个判错函数到底是啥呢,为啥几乎每个槽函数下面都有?

第十六步,判错槽函数编写

void myWidget::operror(int num)

{

    char*s=strin.toLocal8Bit().data();

    if(!isdigit(s[strlen(s)-2])&&s[strlen(s)-2]!=')')

    {

        QMessageBox::about(this,"输入有误","您输入的双目运算符无左值");

        strin.chop(1);

        for(int i=1;i<=num;i++)

            ui->textEdit->textCursor().deletePreviousChar();



    }

}

细心的你会发现,我只有在输入运算符时才会调用这个槽函数

   我们分析一下,先转化,如果strin中,我输入了一个+号或者任意符号,如果它前面一个符号不是数字也不是右括号,会是什么情形?很明显错误了,所以要爆出一个这样的错误。之后呢就需要把它在strin中弹出来,并且在textEdit进行删除处理。

   倒数第3步!!!

int myWidget::bracketerror()

{

    int l=0,r=0;

    for(int i=0;i

这个函数主要测试括号数量是否对的上。

  倒数第二步!!!

void myWidget::digitbracketerror()

{

    char*s=strin.toLocal8Bit().data();

       if(isdigit(s[strlen(s)-1])||s[strlen(s)-1]==')')

       {

           QMessageBox::about(this,"输入有误","您要输入的左括号前是数");

           strin+="*";

           ui->textEdit->textCursor().insertText("*");

       }

}

void myWidget::bracketdigiterror()

{

    char* s=strin.toLocal8Bit().data();

        if(s[strlen(s)-1]==')')

        {

            QMessageBox::about(this,"输入有误","您要输入的数字前是右括号");

            strin+="*";

            ui->textEdit->textCursor().insertText("*");



        }
}

这些可以照着之前所说的自行理解哦~~~

最后一步!!运行!!

我想现在运行的话,你就可以得到一颗可以运行的计算器啦!!!恭喜你!!!

    如果你没有计算器具体编写方法的话,给下面这个链接,可以去看看,之后带着你写好的函数,来运行吧!

https://blog.csdn.net/weixin_45769058/article/details/106944806?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162236564916780261968904%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=162236564916780261968904&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~rank_v29-4-106944806.pc_search_result_cache&utm_term=C%2B%2B%E5%AE%9E%E7%8E%B0%E8%AE%A1%E7%AE%97%E5%99%A8&spm=1018.2226.3001.4187

 

4、添加背景

现在,你已经有了一个属于你的计算器,但是有没有感觉不太好看?这时候,你需要按照以下步骤

(1)挑一张你觉得好看的照片放在一个文件夹里

(2)将文件夹移动到你计算器的文件下

(3)点击左上角   文件->新建文件或项目->

Qt实现计算器(包含算术运算和逻辑运算),纯干货,一步步超详细过程_第10张图片

然后就多了个resource

(4)回到ui界面,在左边找到frame,拖一个出来,将它放大到和你的计算器一样大,然后消除里面的文字,之后在右边找到stylesheet

Qt实现计算器(包含算术运算和逻辑运算),纯干货,一步步超详细过程_第11张图片

 右键点击->添加资源->border-images

(5)之后你就可以找到你添加的图片啦!

Ps:如果没有,试试先编译一下。

5、总结

    (1)这次饱含泪水的大作业着实耗费了大量的时间和精力,从啥也不会到逐步探索,经历了很多。思来想去,还是应该写一篇博客,虽然我们的大作业已经过去,但是希望以后的学弟学妹们能少一点辛酸

     (2)本人弱鸡一枚,如有错误,请多指正。

         不要跟我说下次一定,请直接告诉我你要白嫖(狗头)

你可能感兴趣的:(数据结构,栈,qt)