C++ Qt学习笔记 (1) 简易计算器设计

最近开始学习c++ qt, 按照教材上的例程设计一个简易的桌面计算器:

        Qt是一个基于C++语言的跨平台应用程序和UI开发框架,主要包含一个类库,和跨平台开发及国际化的工具,最初由挪威的Trolltech公司开发,后来被诺基亚收购,现在属于Digia公司。qt最大的特点是其跨平台的属性,同一份代码可以在不同平台上编译运行。qt中使用信号/槽机制来代替传统UI设计中的回调函数。

       信号和槽是Qt中对象通信的一种方式,它代替了传统的GUI编程中利用回调函数传递消息的机制。回调函数的原理是:将处理函数传递到按钮控件中去,在用户点击按钮对象之后,按钮控件对象调用传进去的处理函数,这种将一个函数传进去,在将来某个时刻调用的方式称为回调。信号与槽机制:同样需要编写处理函数(称为槽函数),但是不需要将处理函数传递给控件,只需要将按钮的单击信号(如clicked(),这是Qt种已经比那些好的函数)和槽函数连接起来即可。使用更加方便,但是也带来了效率上的损耗。

      QWidget是所有图像用户将界面的基类,QWidget包含所有界面共有的特征:1. 接受鼠标点击事件 2. 接受键盘事件 3. 区域渲染

同时,QWidget也可以包含其他的界面控件。

(由于目前没有找到好的Qt教材,所以只对Qt有一个简单的了解,后续会进行更加全面的学习)

1. 计算器界面设计:

这里界面设计采用了Qt designer, 在Qt Creator种新建项目,进入.ui文件。进行界面设计,如下图所示:

C++  Qt学习笔记 (1)  简易计算器设计

对界面中的控件进行命名,以及属性设置:

C++  Qt学习笔记 (1)  简易计算器设计

将计算器的按钮与相应的槽函数进行连接:
    这里使用Qt中的connect方法,将按钮和相应的槽函数连接在一起:

connect(btn, SIGNAL(clicked()), this, SLOT(onDigitClicked()));

connect函数的形式如上所示:

在计算器设计中,采用的方式为,将数字按钮0~9连接到同一个槽函数中,将加减乘除连接到同一个槽函数中,将其它的按钮连接到对应的槽函数中,首先定义在widget.h文件中定义相应的槽函数:

private slots:
    void onDigitClicked();     // 数字键按下对应的槽函数
    void onOperatorClicked();  // 运算符键按下对应的槽函数
    void onEqualBtnClicked();  // 按下运算键对应的槽函数
    void onDotBtnClicked();    // 按下小数点对应的槽函数
    void onClearBtnClicked();  // 清除按钮对应的槽函数
    void onClearAllBtnClicked();  // 清除所有按钮对应的槽函数
    void onSignBtnClicked();   // 正负号按键所对应的槽函数

定义函数connectSlots(),将连接槽函数的过程进行封装:

private:
    void connectSolts();       // 将数字键以及符号键连接到槽函数

实现connectSlots()函数:

void Widget::connectSolts()
{
    // 链接槽函数
    // 将十个数字键连接到槽函数onDigitClicked();
    QPushButton *digit_btns[10] =
    {
        ui->btn_0,
        ui->btn_1,
        ui->btn_2,
        ui->btn_3,
        ui->btn_4,
        ui->btn_5,
        ui->btn_6,
        ui->btn_7,
        ui->btn_8,
        ui->btn_9
    };

    for(auto btn : digit_btns)
    //for(int i=0; i<10; i++)
    {
         // 将按键连接到槽函数
        connect(btn, SIGNAL(clicked()), this, SLOT(onDigitClicked()));
    }

    //  对应的按钮为指针
    QPushButton *operatorBtn[4] =
    {
        ui->btn_add,
        ui->btn_subtract,
        ui->btn_multiply,
        ui->btn_divide
    };

    for(auto btn : operatorBtn)
    // for(int i=0; i<4; i++)
    {
        connect(btn, SIGNAL(clicked()), this, SLOT(onOperatorClicked()));
    }

    connect(ui->btn_equal, SIGNAL(clicked()), this, SLOT(onEqualBtnClicked()));   // 等号键按下
    connect(ui->btn_dot, SIGNAL(clicked()), this, SLOT(onDotBtnClicked()));       // 小数点键安按下
    connect(ui->btn_clear, SIGNAL(clicked()), this, SLOT(onClearBtnClicked()));    // 清除键按下
    connect(ui->btn_clear_all, SIGNAL(clicked()), this, SLOT(onClearAllBtnClicked()));  // 清除所有 按钮
    connect(ui->btn_neg_pos, SIGNAL(clicked()),this, SLOT(onSignBtnClicked()));    // 符号按键
}

上面通过循环的方式,将相应的按钮与槽函数连接。相应槽函数的实现如下:
1. 数字键按下对应的槽函数:

void Widget::onDigitClicked()
{
    // std::cout << "数字键按下" << std::endl;
    qDebug() << "digit key pressed" << endl;
    QPushButton *digitBtn = static_cast<QPushButton*>(sender());   // sender()表示信号的发送者
    QString value = digitBtn->text();    // 获取按钮的text属性
    // 判断按键
    if(ui->result->text() == "0" && value == "0")    // 按键为0
        return;
    if(waitForOperator)                  // 等在操作数 状态为真
    {
        ui->result->setText(value);
        waitForOperator = false;         // 此时不再需要等待操作数
    }
    else
    {
        ui->result->setText(ui->result->text() + value);
    }
}

2. 运算符键按下对应的槽函数实现:

void Widget::onOperatorClicked()
{
    // qDebug() << "operator key pressed" << endl;
    // 判断按下的运算符键
    QPushButton *clickedBtn = static_cast<QPushButton*>(sender());    // 将信号源转化为QpusuBytton指针
    QString value = clickedBtn->text();      // 获取运算符
    // 此时的状态是按下了运算符,所以需要获取第一个运算数
    double operand = ui->result->text().toDouble();     // 获取运算数
    if(pendingOperator.isEmpty())   // 运算符为空
    {
        result = operand;
    }
    else
    {
        if(!calculate(operand, value))
        {
            abortOperation();
            return;
        }
        ui->result->setText(QString::number(result));
    }
    // 更新运算符
    pendingOperator = value;
    waitForOperator = true;    // 等待新的输入数字
}

函数calculate(operand, value)用于获取运算符和操作数之后进行计算,具体的实现如下所示:

bool Widget::calculate(double operand, QString pendingOperator)
{
    if(pendingOperator == "+")
    {
        result += operand;     // 加法运算
    }
    else if(pendingOperator == "-")
    {
        result -= operand;
    }
    else if(pendingOperator == "*")
    {
        result *= operand;
    }
    else
    {
        if(operand == 0)
        {
            return false;
        }
        result /= operand;
    }
    return true;
}

Qt中的键盘事件:

Qt中使用QKeyEvent来描述键盘事件,当键盘按下胡哦这松开的时候,键盘事件便会传递给拥有键盘输入焦点的控件,key()函数可以用来获取具体的按键值。在简易计算器设计中中加入键盘事件,使得在键盘上输入数据和在计算器上输入具有相同的效果,给按钮添加快捷键的方式主要有两种方法:
1. 重写键盘事件函数:
首先,在widget.h文件中,键盘事件函数必须定义为protected类型: 

#include <QKeyEvent>

protected:
    void keyPressEvent(QKeyEvent* event);

键盘事件函数的定义:
(未解决的bug, emit ui->btn_0->clicked报错问题)

void Widget::keyPressEvent(QKeyEvent *event)
{
    switch (event->key())
    {
        case Qt::Key_0:
            emit ui->btn_0->clicked();
            break;
        case Qt::Key_1:
            emit ui->btn_1->clicked();
            break;
        case Qt::Key_2:
            emit ui->btn_2->clicked();
            break;
        case Qt::Key_3:
            emit ui->btn_3->clicked();
            break;
        case Qt::Key_4:
            emit ui->btn_4->clicked();
            break;
        case Qt::Key_5:
            emit ui->btn_5->clicked();
            break;
        case Qt::Key_6:
            emit ui->btn_6->clicked();
            break;
        case Qt::Key_7:
            emit ui->btn_7->clicked();
            break;
        case Qt::Key_8:
            emit ui->btn_8->clicked();
            break;
        case Qt::Key_9:
            emit ui->btn_9->clicked();
            break;

        case Qt::Key_Plus:
            emit ui->btn_add->clicked();
            break;
        case Qt::Key_Minus:
            emit ui->btn_subtract->clicked();
            break;
        case Qt::Key_Asterisk:
            emit ui->btn_multiply->clicked();
            break;
        case Qt::Key_Slash:
            emit ui->btn_divide->clicked();
            break;

        case Qt::Key_Enter:
        case Qt::Key_Equal:
            emit ui->btn_equal->clicked();
            break;

        case Qt::Key_Period:
            emit ui->btn_dot->clicked();
            break;

        case Qt::Key_M:
            emit ui->btn_neg_pos->clicked();
            break;

        case Qt::Key_Backspace:
            emit ui->btn_clear->clicked();
            break;

        default:
            break;
    }
}

2. 通过按钮控件的setShortCut()函数

在widget.h中定义快捷键设置的函数setShortCut()

public:
    void setShortCut();        // 设计快捷键
void Widget::setShortCut()
{
    Qt::Key key[18] = {
        Qt::Key_0, Qt::Key_1, Qt::Key_2,
        Qt::Key_3, Qt::Key_4, Qt::Key_5,
        Qt::Key_6, Qt::Key_7, Qt::Key_8,
        Qt::Key_9,
        Qt::Key_Plus, Qt::Key_Minus, Qt::Key_Asterisk, Qt::Key_Slash,
        Qt::Key_Enter, Qt::Key_Period, Qt::Key_Backspace, Qt::Key_M

    };

    QPushButton *btn[18] = {
        ui->btn_0, ui->btn_1, ui->btn_2,
        ui->btn_3, ui->btn_4, ui->btn_5,
        ui->btn_6, ui->btn_7, ui->btn_8,
        ui->btn_9,
        ui->btn_add, ui->btn_subtract, ui->btn_multiply, ui->btn_divide,
        ui->btn_equal, ui->btn_dot, ui->btn_clear, ui->btn_neg_pos
    };

    for(int i=0; i<18; i++)
    {
        btn[i]->setShortcut(QKeySequence(key[i]));
    }
    ui->btn_clear_all->setShortcut(QKeySequence("Ctrl+Backspace"));
}

Qt中的鼠标事件:

Qt中用一个对象表示一个事件(event), 继承自QEvent。QMouseEvent事件用来表示鼠标事件,它可以检测到当前哪个键被按下了,或者鼠标的当前位置。QWheelEvent用来表示鼠标滚轮事件,用来获取滚轮的滑动方向和距离,共有5个鼠标事件处理函数,在.h文件中这些事件处理函数必须为protected类型,同时需要包含头文件<QMouseEvent><QwheelEvent>

mousePressEvent()

mouseReleaseEvent()

mouseDoubleClickedEvent()

mouseMoveEvent()

wheelEvent()

---------------------------------------------------------------------------------------------------------------

简易计算器完整代码:

widget.h文件

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QString>
#include <QKeyEvent>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();


private:
    Ui::Widget *ui;

private:
    bool calculate(double operand, QString pendingOperator);  // 做算数运算
    void abortOperation();     // 结束运算
    void connectSolts();       // 将数字键以及符号键连接到槽函数

    QString pendingOperator;   // 存储运算符
    double result;             // 存储运算结果
    bool waitForOperator;      // 标志位,是否等待操作数

private slots:
    void onDigitClicked();     // 数字键按下对应的槽函数
    void onOperatorClicked();  // 运算符键按下对应的槽函数
    void onEqualBtnClicked();  // 按下运算键对应的槽函数
    void onDotBtnClicked();    // 按下小数点对应的槽函数
    void onClearBtnClicked();  // 清除按钮对应的槽函数
    void onClearAllBtnClicked();  // 清除所有按钮对应的槽函数
    void onSignBtnClicked();   // 正负号按键所对应的槽函数

public:
    void setShortCut();        // 设计快捷键

//protected:
//    void keyPressEvent(QKeyEvent* event);

};

#endif // WIDGET_H

widget.cpp文件:

#include "widget.h"
#include "ui_widget.h"
#include <QString>
#include <QLayout>
#include <QMessageBox>
#include <iostream>
#include <QDebug>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    waitForOperator = true;  // 初始状态等待操作数
    result = 0.0;            // 初始状态结果为0
    ui->result->setText("0");

    connectSolts();    // 连接所有的按键与对应的槽函数
    setShortCut();

}

Widget::~Widget()
{
    delete ui;
}

bool Widget::calculate(double operand, QString pendingOperator)
{
    if(pendingOperator == "+")
    {
        result += operand;     // 加法运算
    }
    else if(pendingOperator == "-")
    {
        result -= operand;
    }
    else if(pendingOperator == "*")
    {
        result *= operand;
    }
    else
    {
        if(operand == 0)
        {
            return false;
        }
        result /= operand;
    }
    return true;
}

void Widget::abortOperation()
{
    // 终止运算,清除数据,输出错误信息
    result = 0;
    pendingOperator.clear();      // 清除运算符
    ui->result->setText("0");     // 清除显示结果
    waitForOperator = true;       // 等待一个操作数输入
    QMessageBox::warning(this, "运算错误", "除数不能为零");
}

void Widget::connectSolts()
{
    // 链接槽函数
    // 将十个数字键连接到槽函数onDigitClicked();
    QPushButton *digit_btns[10] =
    {
        ui->btn_0,
        ui->btn_1,
        ui->btn_2,
        ui->btn_3,
        ui->btn_4,
        ui->btn_5,
        ui->btn_6,
        ui->btn_7,
        ui->btn_8,
        ui->btn_9
    };

    for(auto btn : digit_btns)
    //for(int i=0; i<10; i++)
    {
         // 将按键连接到槽函数
        connect(btn, SIGNAL(clicked()), this, SLOT(onDigitClicked()));
    }

    QPushButton *operatorBtn[4] =
    {
        ui->btn_add,
        ui->btn_subtract,
        ui->btn_multiply,
        ui->btn_divide
    };

    for(auto btn : operatorBtn)
    // for(int i=0; i<4; i++)
    {
        connect(btn, SIGNAL(clicked()), this, SLOT(onOperatorClicked()));
    }

    connect(ui->btn_equal, SIGNAL(clicked()), this, SLOT(onEqualBtnClicked()));   // 等号键按下
    connect(ui->btn_dot, SIGNAL(clicked()), this, SLOT(onDotBtnClicked()));       // 小数点键安按下
    connect(ui->btn_clear, SIGNAL(clicked()), this, SLOT(onClearBtnClicked()));    // 清除键按下
    connect(ui->btn_clear_all, SIGNAL(clicked()), this, SLOT(onClearAllBtnClicked()));  // 清除所有 按钮
    connect(ui->btn_neg_pos, SIGNAL(clicked()),this, SLOT(onSignBtnClicked()));    // 符号按键
}


void Widget::onDigitClicked()
{
    // std::cout << "数字键按下" << std::endl;
    qDebug() << "digit key pressed" << endl;
    QPushButton *digitBtn = static_cast<QPushButton*>(sender());   // sender()表示信号的发送者
    QString value = digitBtn->text();    // 获取按钮的text属性
    // 判断按键
    if(ui->result->text() == "0" && value == "0")    // 按键为0
        return;
    if(waitForOperator)                  // 等在操作数 状态为真
    {
        ui->result->setText(value);
        waitForOperator = false;         // 此时不再需要等待操作数
    }
    else
    {
        ui->result->setText(ui->result->text() + value);
    }
}

void Widget::onOperatorClicked()
{
    // qDebug() << "operator key pressed" << endl;
    // 判断按下的运算符键
    QPushButton *clickedBtn = static_cast<QPushButton*>(sender());    // 将信号源转化为QpusuBytton指针
    QString value = clickedBtn->text();      // 获取运算符
    // 此时的状态是按下了运算符,所以需要获取第一个运算数
    double operand = ui->result->text().toDouble();     // 获取运算数
    if(pendingOperator.isEmpty())   // 运算符为空
    {
        result = operand;
    }
    else
    {
        if(!calculate(operand, value))
        {
            abortOperation();
            return;
        }
        ui->result->setText(QString::number(result));
    }
    // 更新运算符
    pendingOperator = value;
    waitForOperator = true;    // 等待新的输入数字
}

void Widget::onEqualBtnClicked()
{
    // 等号键按下,需要计算最终的结果
    double operand = ui->result->text().toDouble();   // 获取运算数
    if(pendingOperator.isEmpty())   // 没有输入加减乘除运算符,直接按了等号
    {
        return;
    }
    if(!calculate(operand, pendingOperator))
    {
        abortOperation();
        return;
    }
    ui->result->setText(QString::number(result));
    waitForOperator = true;
    result = 0.0;
    pendingOperator.clear();
}

void Widget::onDotBtnClicked()
{
    if(waitForOperator)
    {
        ui->result->setText("0");
    }
    if(ui->result->text().contains("."))
    {
        // no operation
    }
    else
    {
        ui->result->setText(ui->result->text() + ".");
    }
    waitForOperator = false;       // 当前数字的输入还未结束
}

void Widget::onClearBtnClicked()
{
    ui->result->setText("0");     // 输入的数字清零
    waitForOperator = true;       // 重新输入
}

void Widget::onClearAllBtnClicked()
{
    ui->result->setText("0");
    waitForOperator = true;
    result = 0.0;
    pendingOperator.clear();
}

void Widget::onSignBtnClicked()
{
    QString text = ui->result->text();
    double value = text.toDouble();
    if(value > 0)
    {
        text.prepend("-");
    }
    else
    {
        text.remove(0, 1);
    }
    ui->result->setText(text);
}


void Widget::setShortCut()
{
    Qt::Key key[18] = {
        Qt::Key_0, Qt::Key_1, Qt::Key_2,
        Qt::Key_3, Qt::Key_4, Qt::Key_5,
        Qt::Key_6, Qt::Key_7, Qt::Key_8,
        Qt::Key_9,
        Qt::Key_Plus, Qt::Key_Minus, Qt::Key_Asterisk, Qt::Key_Slash,
        Qt::Key_Enter, Qt::Key_Period, Qt::Key_Backspace, Qt::Key_M

    };

    QPushButton *btn[18] = {
        ui->btn_0, ui->btn_1, ui->btn_2,
        ui->btn_3, ui->btn_4, ui->btn_5,
        ui->btn_6, ui->btn_7, ui->btn_8,
        ui->btn_9,
        ui->btn_add, ui->btn_subtract, ui->btn_multiply, ui->btn_divide,
        ui->btn_equal, ui->btn_dot, ui->btn_clear, ui->btn_neg_pos
    };

    for(int i=0; i<18; i++)
    {
        btn[i]->setShortcut(QKeySequence(key[i]));
    }
    ui->btn_clear_all->setShortcut(QKeySequence("Ctrl+Backspace"));
}


/*
void Widget::keyPressEvent(QKeyEvent *event)
{
    switch (event->key())
    {
        case Qt::Key_0:
            emit ui->btn_0->clicked();
            break;
        case Qt::Key_1:
            emit ui->btn_1->clicked();
            break;
        case Qt::Key_2:
            emit ui->btn_2->clicked();
            break;
        case Qt::Key_3:
            emit ui->btn_3->clicked();
            break;
        case Qt::Key_4:
            emit ui->btn_4->clicked();
            break;
        case Qt::Key_5:
            emit ui->btn_5->clicked();
            break;
        case Qt::Key_6:
            emit ui->btn_6->clicked();
            break;
        case Qt::Key_7:
            emit ui->btn_7->clicked();
            break;
        case Qt::Key_8:
            emit ui->btn_8->clicked();
            break;
        case Qt::Key_9:
            emit ui->btn_9->clicked();
            break;

        case Qt::Key_Plus:
            emit ui->btn_add->clicked();
            break;
        case Qt::Key_Minus:
            emit ui->btn_subtract->clicked();
            break;
        case Qt::Key_Asterisk:
            emit ui->btn_multiply->clicked();
            break;
        case Qt::Key_Slash:
            emit ui->btn_divide->clicked();
            break;

        case Qt::Key_Enter:
        case Qt::Key_Equal:
            emit ui->btn_equal->clicked();
            break;

        case Qt::Key_Period:
            emit ui->btn_dot->clicked();
            break;

        case Qt::Key_M:
            emit ui->btn_neg_pos->clicked();
            break;

        case Qt::Key_Backspace:
            emit ui->btn_clear->clicked();
            break;

        default:
            break;
    }
}
*/

后续还会对简易计算器进行改进。。。(未完待续)

       

原文链接: https://www.cnblogs.com/ncepubye/p/12724001.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    C++  Qt学习笔记 (1)  简易计算器设计

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/396458

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月6日 上午11:20
下一篇 2023年4月6日 上午11:20

相关推荐