Qt——计算器示例(用户界面与业务逻辑的分离)

1.基本程序架构一般包括:

  • 用户界面模块(UI):接受用户输入及呈现数据
  • 业务逻辑模块:根据用户需求处理数据

2.基本设计原则------强内聚、弱耦合

  • 每个模块应该只实现单一的功能
  • 模块内部的子模块只为整体的单一功能而存在
  • 模块之间通过约定好的接口进行交互

所以将计算器示例代码中的UI界面和业务逻辑模块集成

头文件:

ICalculator.h

复制代码
#ifndef _ICALCULATOR_H_
#define _ICALCULATOR_H_
#include <QString>
class ICalculator{
public:
    virtual bool expression(const QString& exp) = 0; //接受用户输入的四则运算表达式
    virtual QString result() = 0; //接受表达式的结果
};

#endif // _ICALCULATOR_H_

QCalculator.h

复制代码
#ifndef QCALCULATOR_H
#define QCALCULATOR_H

#include "QCalculatorui.h"
#include "QCalculatorDec.h"

class QCalculator
{
protected:
    QCalculatorUI* m_ui;
    QCalculatorDec m_cal;
    QCalculator();
    bool construct();
public:
    static QCalculator* NewInstance();
    void show();
    ~QCalculator();
};

#endif // QCALCULATOR_H

QCalculatorDec.h

复制代码
#ifndef QCALCULATORDEC_H
#define QCALCULATORDEC_H
#include <QString>
#include <QChar>
#include <QStack>
#include <QQueue>

#include "ICalculator.h"

class QCalculatorDec : public ICalculator
{
protected :
    QString m_exp;
    QString m_result;

    bool isDigitOrDot(QChar c);
    bool isSymbol(QChar c);
    bool isSign(QChar c);
    bool isNumber(QString s);
    bool isOperator(QString s);
    bool isLeft(QString s);
    bool isRight(QString s);
    int priority(QString s);
    QQueue<QString> split(const QString& exp); //分离算法,<QString> 就是给这个空盒子贴标签:这个队列只能存放 QString 类型的字符串。
    bool match(QQueue<QString>& exp); //括号匹配算法
    bool transform(QQueue<QString>& exp, QQueue<QString>& output);
    QString calculate(QQueue<QString>& exp); //计算结果
    QString calculate(QString l, QString op, QString r);
public:
    QCalculatorDec();
    ~QCalculatorDec();
    bool expression(const QString& exp);
    QString expression();
    QString result();
};

#endif // QCALCULATORDEC_H

QCalculatorui.h

复制代码
#ifndef QCALCULATORUI_H_
#define QCALCULATORUI_H_

#include <QWidget>
#include <QLineEdit>
#include <QPushButton>
#include "ICalculator.h"

class QCalculatorUI : public QWidget
{
    Q_OBJECT //定义槽的类必须在声明的最开始处使用Q_OBJECT
private:
    QLineEdit* m_edit;
    QPushButton* m_buttons[20];

    ICalculator* m_cal;
    QCalculatorUI(); //使用二阶构造法
    bool construct();
private slots:  //类中声明槽时需要使用slots关键字
    void onButtonClicked(); //由于点击信号的函数无参数,所以槽也不能有参数
public:
    static QCalculatorUI* NewInstance();
    void show();
    void setCalculator(ICalculator* cal);
    ICalculator* getCalculator();

    ~QCalculatorUI();
};

#endif // QCALCULATORUI_H

源文件:

main.cpp

复制代码
#include <QApplication>
#include "QCalculator.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QCalculator* cal = QCalculator::NewInstance();

    int ret = -1;

    if(cal != NULL){
        cal->show();
        ret = a.exec();
        delete cal;
    }
    return ret; //把退出码返回给操作系统。
}

QCalculator.cpp

复制代码
#include "QCalculator.h"

QCalculator::QCalculator()
{

}
bool QCalculator::construct()
{
    m_ui=QCalculatorUI::NewInstance();

    if( m_ui != NULL ){
        m_ui->setCalculator(&m_cal);
    }
    return (m_ui != NULL);
}
QCalculator* QCalculator::NewInstance()
{
    QCalculator* ret = new QCalculator();
    if( (ret == NULL) || !ret->construct() )
    {
        delete ret;
        ret = NULL;
    }
    return ret;
}

void QCalculator::show(){
    m_ui->show();
}

QCalculator::~QCalculator()
{
    delete m_ui;
}

QCalculatorDec.cpp

复制代码
#include "QCalculatorDec.h"

QCalculatorDec::QCalculatorDec()
{
    m_exp = ""; //用户原始输入表达式
    m_result = ""; //计算结果
}
QCalculatorDec::~QCalculatorDec()
{

}
bool QCalculatorDec::isDigitOrDot(QChar c) //判断当前字符是数字或小数点
{
    return (('0' <=c) && (c <= '9')) || (c == '.');
}
bool QCalculatorDec::isSymbol(QChar c) //判断当前字符是不是符号
{
    return isOperator(c) || (c == '(') || (c == ')');
}
bool QCalculatorDec::isSign(QChar c) //判断当前字符是不是正负号
{
    return (c == '+') || (c == '-');
}
bool QCalculatorDec::isNumber(QString s)
{
    bool ret = false;
    s.toDouble(&ret);
    return ret;
}
bool QCalculatorDec::isOperator(QString s)
{
    return (s == "+") || (s == "-") || (s == "*") || (s == "/");
}
bool QCalculatorDec::isLeft(QString s)
{
    return (s == "(");
}
bool QCalculatorDec::isRight(QString s)
{
    return (s == ")");
}
int QCalculatorDec::priority(QString s) //返回四则运算加减乘除的优先级
{
    int ret = 0;
    if( (s == "+") || (s == "-")){
        ret = 1;
    }
    if( (s == "*") || (s == "/")){
        ret = 2;
    }
    return ret;
}

bool QCalculatorDec::expression(const QString& exp)
{
    bool ret = false;
    QQueue<QString> spExp = split(exp);
    QQueue<QString> postExp;
    m_exp = exp;
    if( transform(spExp,postExp) )
    {
        m_result = calculate(postExp);
        ret = (m_result != "Error");
    }
    else{
        m_result = "Error";
    }
    return ret;
}
QString QCalculatorDec::expression()
{

}
QString QCalculatorDec::result()
{
    return m_result;
}
QQueue<QString> QCalculatorDec::split(const QString& exp){
    QQueue<QString> ret;
    QString num = "";
    QString pre = ""; //保存前一个字符
    for(int i = 0; i<exp.length(); i++){
        if( isDigitOrDot(exp[i]) ) //当前字符为小数或小数点
        {
            num += exp[i];
            pre = exp[i];
        }
        else if( isSymbol(exp[i]) )
        {
            if( !num.isEmpty() )
            {
                ret.enqueue(num); //将num插入队列当中
                num.clear(); //清空
            }
            if( isSign(exp[i]) && (pre == "") || (pre == "(") || isOperator(pre) )
            {
                num += exp[i];
            }
            else{
                ret.enqueue(exp[i]);
            }
            pre = exp [i];
        }
    }
    if( !num.isEmpty() ){
        ret.enqueue(num);
    }
    return ret;
}
bool QCalculatorDec::match(QQueue<QString>& exp){
    bool ret = true;
    int len = exp.length();
    QStack<QString> stack;
    for(int i = 0; i<len; i++)
    {
        if( isLeft(exp[i]) )
            stack.push(exp[i]);
        else if( isRight(exp[i]) )
        {
            if( !stack.isEmpty() && isLeft(stack.top()) )
                stack.pop();
            else{
                ret = false;
                break;
            }
        }
    }
    return ret;
}
bool QCalculatorDec::transform(QQueue<QString>& exp, QQueue<QString>& output)
{
    bool ret = true;
    QStack<QString> stack;
    output.clear();
    while( ret && !exp.isEmpty() )
    {
        QString e = exp.dequeue();
        if( isNumber(e) )
        {
            output.enqueue(e);
        }
        else if( isOperator(e) )
        {
            while( !stack.isEmpty() && (priority(e) <= priority(stack.top())) )
            {
                output.enqueue(stack.pop());
            }
            stack.push(e);
        }
        else if( isLeft(e) )
        {
            stack.push(e);
        }
        else if( isRight(e) )
        {
            while( !stack.isEmpty() && !isLeft(stack.top()) )
            {
                output.enqueue(stack.pop());
            }
            if( !stack.isEmpty() )
            {
                stack.pop();
            }
        }
        else{
            ret = false;
        }
    }
    while( !stack.isEmpty() )
    {
        output.enqueue(stack.pop());
    }
    if( !ret )
    {
        output.clear();
    }
    return ret;
}
QString QCalculatorDec::calculate(QString l, QString op, QString r)
{
    QString ret = "Error";
    if( isNumber(l) && isNumber(r) )
    {
        double lp = l.toDouble();
        double rp = r.toDouble();
        if( op == "+" )
        {
            ret.sprintf("%f", lp + rp);
        }
        else if( op == "-" )
        {
            ret.sprintf("%f", lp - rp);
        }
        else if( op == "*" )
        {
            ret.sprintf("%f", lp * rp);
        }
        else if( op == "/" )
        {
            const double P = 0.000000000000001;
            if( (-P < rp) && (rp < P) )
            {
                ret = "Error";
            }
            else{
                ret.sprintf("%f", lp / rp);
            }

        }
        else{
            ret = "Error";
        }
    }
    return ret;

}
QString QCalculatorDec::calculate(QQueue<QString>& exp)
{
    QString ret = "Error";
    QStack<QString> stack;

    while( !exp.isEmpty() )
    {
        QString e = exp.dequeue();
        if( isNumber(e) )
        {
            stack.push(e);
        }
        else if( isOperator(e) )
        {
            QString rp = !stack.isEmpty() ? stack.pop() : "";
            QString lp = !stack.isEmpty() ? stack.pop() : "";
            QString result = calculate(lp, e, rp);
            if( result != "Error" )
            {
                stack.push(result);
            }
            else{
                break;
            }
        }
        else{ //后缀表达式不合法
            break;
        }
    }
    if( exp.isEmpty() && (stack.size() ==1 ) && isNumber(stack.top()) )
    {
        ret = stack.pop();
    }
    return ret;
}

QCalculatorui.cpp

复制代码
#include "QCalculatorui.h"
#include <QDebug>

QCalculatorUI::QCalculatorUI() : QWidget(NULL, Qt::WindowCloseButtonHint) //QWidget没有父类,所以为NULL
{
    m_cal = NULL;
}
bool QCalculatorUI::construct(){
    bool ret = true;
    const char* btnText[20] =
    {
            "7", "8", "9", "+", "(",
            "4", "5", "6", "-", ")",
            "1", "2", "3", "*", "<-",
            "0", ".", "=", "/", "C",
    };
    m_edit = new QLineEdit(this);

    if( m_edit != NULL){
        m_edit->move(10, 10);
        m_edit->resize(240, 30);
        m_edit->setReadOnly(true); //设置文本框属性为只读,不可编辑
        m_edit->setAlignment(Qt::AlignRight); //设置字符串在文本框中向右对齐
    }
    else{
        ret = false;
    }
    for(int i = 0; (i<4) && ret; i++) {
        for(int j = 0; (j < 5) && ret; j++){
            m_buttons[i*5 + j] = new QPushButton(this); //指定这个按钮的父窗口
            if( m_buttons[i*5 + j] != NULL ){
                m_buttons[i*5 + j]->resize(40, 40);
                m_buttons[i*5 + j]->move(10 + (10 + 40)*j, 50 + (10 + 40)*i);
                m_buttons[i*5 + j]->setText(btnText[i*5 + j]);
                connect(m_buttons[i*5 + j], SIGNAL(clicked()), this, SLOT(onButtonClicked()));
            }
            else{
                ret = false;
            }
        }
    }
    return ret;
}

QCalculatorUI* QCalculatorUI::NewInstance(){
    QCalculatorUI* ret = new QCalculatorUI();
    if( (ret== NULL) || !ret->construct() )
    {
        delete ret;
        ret = NULL;
    }
    return ret;
}
void QCalculatorUI::show(){
    QWidget::show();
    setFixedSize(width(), height());
}
void QCalculatorUI::onButtonClicked(){
    // sender() 返回的是通用父类指针 QObject*,强转成 QPushButton*,才能调用按钮的方法
    QPushButton* btn = dynamic_cast<QPushButton*>(sender()); //sender() 是 QObject 里的成员函数,返回当前信号的发送者

    if( btn !=NULL )
    {
        QString clickText = btn->text();
        if( clickText == "<-" ){ //删除一个字符
            QString text = m_edit->text();
            if( text.length() > 0){
                text.remove(text.length()-1, 1);
                m_edit->setText(text);
            }
        }
        else if( clickText == "C" ) {//清空字符
            m_edit->setText("");
        }
        else if( clickText == "=" ){
            if( m_cal !=NULL )
            {
                m_cal->expression(m_edit->text());
                m_edit->setText(m_cal->result());
            }
        }
        else{
            m_edit->setText(m_edit->text() + clickText);
        }
    }
}
void QCalculatorUI::setCalculator(ICalculator* cal)
{
    m_cal=cal;
}
ICalculator* QCalculatorUI::getCalculator()
{
    return m_cal;
}
QCalculatorUI::~QCalculatorUI(){

}
相关推荐
用户805533698031 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner1 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz6 天前
QML Hello World 入门示例
qt
xcyxiner9 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner10 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner10 天前
DicomViewer (添加模型类)3
qt
xcyxiner11 天前
DicomViewer (目录调整) 2
qt
xcyxiner11 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00613 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术13 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript