8、QLineEdit用于接受用户输入,QLineEdit能够获取用户输入的字符串,是功能性组件,需要父组件作为容器,能够在父组件中进行定位。
Qwidget w; //生成QWidget对象,顶级组件
QLineEdit le(&w); //生成QLineEdit对象,其父组件为QWidget
le.setAlignment(QT::AlignRight); //设置显示的字符串向右边对齐
le.move(10,10); //移动到坐标(10,10)
le.resize(240,30); //设置大小width=240,height=30
界面设计:
定义组件间的间隔:Space=10px //10像素
定义按钮组件的大小:Width=40px,Height=40px
定义文本框组件的大小:Width=5*40px+4*10px,Height=30px
#include <QtGui/QApplication>
#include "Qwidget"
#include <QLineEdit>
#include <QPushButton>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget* w=new QWidget(NULL,Qt::WindowCloseButtonHint);//第一个问题,只有一个关闭
QLineEdit* le=new QLineEdit(w);
QPushButton* button[20]={0};
const char* btnText[20]=
{
"7","8","9","+","(",
"4","5","6","-",")",
"1","2","3","*","<-",
"0",".","=","/","C",
};
int ret=0;
le->move(10,10);
le->resize(240,30);
le->setReadOnly(true);//不能从键盘输入
for(int i=0;i<4;i++)
{
for(int j=0;j<5;j++)
{
button[i*5+j]=new QPushButton(w);
button[i*5+j]->resize(40,40);
button[i*5+j]->move(10+(10+40)*j,50+(10+40)*i);
button[i*5+j]->setText(btnText[i*5+j]);
}
}
w->show();
w->setFixedSize(w->width(),w->height());//固定长宽
ret=a.exec();
delete w;
return ret;
}
//存在的问题,最大化有问题,拖动有问题,文本框只是显示结果,不能从键盘输入
小结:GUI应用程序开发前应该必须先进行界面设计,
GUI应用程序界面需要考虑各个细节:界面决定最终用户的体验,界面细节是GUI应用程序品质的重要体现。
Qt库有能力实现各种GUI应用程序需求
Qt帮助文档的使用对于开发是非常重要的。
9、计算机界面重构
重构:以改善代码质量为目地的代码重写:使其软件的设计和构架更加合理,提高软件的扩展性和维护性。
代码实现与代码重构不同:
代码实现:按照设计编程实现,重心在于功能实现。
代码重构:以提高代码质量为目地的软件架构优化。
区别:代码实现时不考虑架构的好坏,只考虑功能的实现。
代码重构时不能影响已实现的功能,只考虑架构的改善。
软件开发过程:从工程的角度对软件开发中的活动进行定义和管理。
需求分析-》功能分析-功能实现-功能测试(重构)-系统测试-最终发布
什么样的代码需要重构:当发现项目中重复的代码越来越多时,当发现项目中代码功能越来越不清晰时,当发现项目中代码离设计越来越来远时。
重构是维持代码质量在可接受范围内的重要方式。
main函数功能不唯一,应该把生成按钮的代码剥离出去。
用QCalculatorUI类生成界面,并且继承QWidget,组合了QLineEdit,QPushButton。
#include <QtGui/QApplication>
#include "QCalculatorUI.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QCalculatorUI* cal=QCalculatorUI::NewInstance();
int ret=-1;
if(cal !=NULL)
{
cal->show();
ret=a.exec();
delete cal;
}
return ret;
}
#ifndef _QCALCULATORUI_H_
#define _QCALCULATORUI_H_
#include <QWidget>
#include <QLineEdit>
#include <QPushButton>
class QCalculatorUI : public QWidget
{
private:
QLineEdit* m_edit;
QPushButton* m_buttons[20];
QCalculatorUI();
bool construct();
public:
static QCalculatorUI* NewInstance();
void show();
~QCalculatorUI();
};
#endif
#include "QCalculatorUI.h"
QCalculatorUI::QCalculatorUI():QWidget(NULL,Qt::WindowCloseButtonHint)//顶层窗口 没有父类
{
}
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);//不能从键盘输入
}
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]);
}
else
{
ret=false;
}
}
}
return ret;
}
QCalculatorUI* QCalculatorUI::NewInstance()
{
QCalculatorUI* ret=new QCalculatorUI();
if(!(ret&&ret->construct()))//((ret==NULL)||!ret->connect())
{
delete ret;
ret=NULL;
}
return ret;
}
void QCalculatorUI::show()
{
QWidget::show();
setFixedSize(width(),height());//固定长宽
}
QCalculatorUI::~QCalculatorUI()
{
}
重构是软件开发中的重要概念,重构是以提高代码质量为目地的软件开发活动,重构不能影响已有的软件功能,当软件功能的实现进行到了一定阶段时就需要考虑重构,重构可简单的理解为对软件系统进行重新构架。
这节课主要就是重构。
10、消息处理
Qt封装了具体操作系统的消息机制,Qt遵循经典的GUI消息驱动事件模型。
用户事件-->操作系统-->应用系统消息(应用程序,消息处理函数)
思考:
Qt中如何表示用户消息?
Qt中如何映射用户消息到消息处理函数?
Qt中消息映射需要遵循什么规则?
Qt中定义了与系统消息相关的概念:
信号(Signal):由操作系统产生的消息。
槽(Slot):程序中的消息处理函数。
连接(connect):将系统消息绑定到消息处理函数。
QObject_1(发送信号)->connect<-- QObject_2(消息处理函数)
信号到槽的连接必须发生在两个qt类对象之间 !
Qt的核心-QObject::connect函数
bool connect(const QObject* sender, //发送对象
const char* signal, //消息名
const QObject* receiver, //接收对象
const char* method, //接受对象的成员函数
Qt::ConnectionType type=Qt::AutoConnection);
Node:在qt中,消息用字符串进行描述,connect函数在消息名和处理函数之间建立映射
qt中的“新”关键字:
SIGNAL:用于指定消息名。
SLOT:用于指定消息处理函数名。
Q_OBJECT:所有自定义槽的类必须在类声明的开始处加上Q_OBJECT。
slots:用于在类中声明消息处理函数。
#include <QtGui/QApplication>
#include <QPushButton>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QPushButton b;
b.setText("Click me to quit!");
b.show();
QObject::connect(&b, SIGNAL(clicked())【连接信号与槽】, &a, SLOT(quit()));//按钮对象点击发送消息映射的a的quit函数
return a.exec();
}
quit是预定义的函数,如果自定义函数该怎么办?
自定义槽:只有QObject的子类才能自定义槽
定义槽的类必须在声明的最开始处使用Q_OBJECT.
类中声明槽时需要使用slots关键字,槽与所处理的信号在函数签名上必须一致(要没参数都没参数)。
SIGNAL和SLOT所指定的名称中可以包含参数类型,不能包含具体的参数名。
为计算机添加信号处理函数:
connect( m_buttons[i*5+j],SIGNAL(clicked()),this,SLOT(onButtonClicked()));
void QCalculatorUI::onButtonClicked()//消息处理函数
{
QPushButton* btn=(QPushButton*)sender();
qDebug()<<" void onButtonClicked();";
qDebug()<<btn->text();
}
小贴士:问题Object:: connect: No such slot ...
1、检查类是否继承与QObject
2、检查类声明的开始处是否添加Q_OBJECT.
3、检查是否使用slots关键字进行槽声明。
4.检查槽的名称是否拼音错误
5、重新执行qmake.
小结:信号与槽是qt中的核心机制,不同的qt对象可以通过信号和槽进行通信,只有QObject的子类才能自定义信号和槽,使用信号和槽的类必须在声明的最开始处使用Q_OBJECT,信号与处理函数在函数签名上必须一致。
关键:信号与槽的机制只能发生在qt对象之间。普通c++类对象不能使用信号与槽机制。
11、字符串类
Qt vs STL
STL的具体实现依赖于编译器生成厂商,
STL的标准只是其接口是标准的:相同的全局函数,相同的算法类和数据结构类,相同的类成员函数。
不同厂商的编译器所带的STL存在差异:依赖于STL开发的c++程序在不同平台上的行为可能出现差异。
项目是否需要使用现有库的支持?STL?QT?MFC?私有库?
项目是否需要在不同平台间移植? Linux?Windows? Android?
项目是否需要图形用户界面?GUI应用程序?命令行应用程序?后台服务程序?
Qt中的字符串类:
采用Unicode编码(支持中文,韩文),使用隐式共享技术来节省内存和不必要的数据拷贝,扩平台使用,不必考虑字符串的平台兼容性。
QString直接支持字符串和数字的相互转换,支持字符串的大小比较,支持不同字符编码间的相互转换,支持std::string和std::wstring的相互转换。支持正则表达式的应用。
#include <QDebug>
void Sample_1()
{
QString s = "add";
s.append(" "); // "add "
s.append("Qt"); // "add Qt"
s.prepend(" "); // " add Qt"
s.prepend("C++"); // "C++ add Qt"
qDebug() << s;
s.replace("add", "&"); // "C++ & Qt"
qDebug() << s;
}
void Sample_2()
{
QString s = "";
int index = 0;
s.sprintf("%d. I'm %s, thank you!", 1, "Delphi Tang"); //格式化函数:'m Delphi Tang, thank you!"
qDebug() << s;
index = s.indexOf(","); //找逗号下标
s = s.mid(0, index); // "1. I'm Delphi Tang" 取子串
qDebug() << s;
index = s.indexOf(".");
s = s.mid(index + 1, s.length()); // " I'm Delphi Tang"
s = s.trimmed(); // "I'm Delphi Tang" 去掉前后空格
qDebug() << s;
index = s.indexOf(" ");
s = s.mid(index + 1, s.length()); // "Delphi Tang"
qDebug() << s;
}
void Sample_3(QString* a, int len)
{
for(int i=0; i<len; i++) //选择排序字符串
{
for(int j=i+1; j<len; j++)
{
if( a[j] < a[i] )
{
QString tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
}
}
}
int main()
{
qDebug() << "Sample_1:"; //输出字符串
Sample_1();
qDebug() << endl;
qDebug() << "Sample_2:";
Sample_2();
qDebug() << endl;
qDebug() << "Sample_3:";
QString company[5] =
{
QString("Oracle"),
QString("Borland"),
QString("Microsoft"),
QString("IBM"),
QString("D.T.Software")
};
Sample_3(company, 5);
for(int i=0; i<5; i++)
{
qDebug() << company[i];
}
return 0;
}
QString在Qt库中几乎无所不在的,所有的qt图形用户组件都依赖于QString。
应用开发中大多数的情况都在进行字符串处理,Qt比STL更适合于扩平台开发的场景,Qt中的QString比STL中string更强大易用,Qt图形用户组件都依赖于QSstring,项目开发时需要综合各种选择需要使用的库。
m_edit->setAlignment(Qt::AlignRight);//输入右对齐
void QCalculatorUI::onButtonClicked()//消息处理函数
{
QPushButton* btn=(QPushButton*)sender();
QString clickTest=btn->text();
if(clickTest=="<-")
{
QString text=m_edit->text();
if(text.length()>0)
{
text.remove(text.length()-1,1);
m_edit->setText(text);
}
}
else if(clickTest=="C")
{
m_edit->setText("");
}
else if(clickTest=="=")
{
}
else
{
m_edit->setText(m_edit->text()+clickTest);
}
}
12、实现
计算机如何读懂四则运算表达式?
“9.3+(3--0.11)*5”
后缀表达式:
人类习惯的数学表达式叫做中缀表达式,另外,还有一种将运算符放在数字后面的后缀表达式。
5+3->53+
1+2*3->123*+
9+(3-1)*5->931-5*+
后缀表达式符合计算机的运算方式:消除了中缀表达式中的括号,同时保留中缀表达式中的运算优先级。
解决方案:
1、将中缀表达式进行数字和运算符的分离。
2、将中缀表达式转换为后缀表达式。
3、通过后缀表达式计算最终结果。
所要计算的中缀表达式中包含:
数字和小数点([0-9或.]
符号位[+ -]
运算符[+,-,*,/ ]
括号()
思想:以符号作为标志对表达式中的字逐个访问
定义累计变量num(字符串)
当前字符exp[i]为数字或小数点时:累计:num+=exp[i]
当前字符exp[i]为符号时:分离num
num为运算数,分离并保存,
若exp[i]为正负号:累计符号位+和-:num+=exp[i]。若exp[i]为运算符:分离并保存。
伪代码:
for(int i=0;i<exp.length();i++)
{ if(exp[i]为数字或小数点) 累计:num+=exp[i];
else if(exp[i]为符号)
{ if(num !="") 分离并保存运算数:num;
if(exp[i]为正号或负号) 符号位累计:num+=exp[i];
else {分离并保存运算符:exp[i]; }
}}
难点:如何区分正负号与加号和减号?
正负号:
+和-在表达式的第一个位置
括号后的+和-(左括号)
运算符后的+和-
#ifndef _CALCULATORCORE_H_
#define _CALCULATORCORE_H_
#include <QString>
#include <QStack>
#include <QQueue>
class QCalculatorDec
{
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);//分离算法
public:
QCalculatorDec();
~QCalculatorDec();
bool expression(const QString& exp);
QString expression();
QString result();
};
#endif
#include "QCalculatorDec.h"
#include <QDebug>
QCalculatorDec::QCalculatorDec()
{
m_exp = "";
m_result = "";
QQueue<QString> r = split("+9.11 + ( -3 - 1 ) * -5 ");
for(int i=0; i<r.length(); i++)
{
qDebug() << r[i];
}
}
QCalculatorDec::~QCalculatorDec()
{
}
bool QCalculatorDec::isDigitOrDot(QChar c)//Qchar unicode字符
{
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);//成功ret为true 否则false
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;
return ret;
}
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.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;
}
小结:QString中的每个字符为QChar。
QT中提供了开发中不可或缺的数据结构类。
四则运算表达式的计算分三个步骤:
数字和符号分离,中缀表达式转后缀表达式,根据后缀表达式计算结果。
13、中缀表达式转后缀表达式
过程类似编译过程:
四则运算表达式中的括号必须匹配,根据运算符优先级进行转换,转换后的表达式中没有括号,转换后的表达式中没有括号,转换后可以顺序的计算出最终结果。
转换过程:当前元素e为数字:输出
当前元素e为运算符:1、与栈顶运算符进行优先级比较。2、小于等于:将栈顶元素输出,转1。3、大于:将当前元素e入栈。
当前元素e为左括号:入栈
当前元素e为右括号:1、弹出栈顶元素并输出,直至栈顶元素为左括号。2、将栈顶的左括号从栈中弹出。
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=match(exp);
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;
}
小结:后缀表达式是程序计算复杂表达式的基础,中缀到后缀的转换时基于栈数据结构的,转换过程能够发现表达式中的语法错误。
14、核心算法下
遍历后缀表达式中的数字和运算符
当前元素为数字:进栈
当前元素为运算符:
1、从栈中弹出右操作数
2、从栈中弹出做操作数
3、根据符号进行运算
4、将运算结果压入栈中
遍历结束:栈中唯一的数字为运算结果。
遇到运算符就是把栈中的两个数据运算成一个数字。
注意:与数学计算相关的算法都需要考虑除0的情况,若是浮点运算,避免代码中直接与0做相等比较
不能和0做直接相等运算,因为浮点数在内存中是不精确的。
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.00001;
if((-p<rp)&&(rp<p))
{
ret="Error";
}
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;
}
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;
}
测试:
QCalculatorDec c;
c.expression("(5-8)*(5-6)");
qDebug()<<c.result();
return 0;
小结:计算方法由3个不同的子算法构成,Qt项目在整体上采用面向对象分析与设计,局部的算法设计依旧采用面向过程的方法完成,qt开发是各种开发技术的综合运用。
15、用户界面与业务逻辑的分离
基本程序架构一般包含:
用户界面模块(UI):接受用户输入及呈现数据。
业务逻辑模块(business logic):根据用户需求处理数据。
用户界面与业务逻辑如何交互?
基本设计原则:
功能模块之间需要进行解耦
核心思想:强内聚,弱耦合:
每个模块应该只实现单一的功能,模块内部的子模块只为整体的单一功能而存在,模块之间通过约定好的接口进行交互。
接口:
广义:一种契约(协议,语法,格式)
狭义:面向过程:接口是一组预定义的函数原型。面向对象:接口是纯虚类(java直接支持接口)
用户界面与业务逻辑的交互:
用户界面(使用接口) -->业务接口 < --业务逻辑(实现接口)
模块之间仅通过接口进行关联:必然存在模块会使用接口,必然存在模块实现对应的接口。
模块间的关系是单项依赖的:避免模块间存在循环依赖的情况,循环依赖是糟糕设计标准之一。
计算机整体架构:Icalculator<---QCalculatorUI,QCalculatorDec-->QCalculator
#ifndef _ICALCULATOCH_H_//接口
#define _ICALCULATOCH_H_
#include <QString>
class ICalculator
{
public:
virtual bool expression(const QString& exp)=0;
virtual QString result() = 0;
};
#endif
#ifndef _QCALCULATOR_H_
#define _QCALCULATOR_H_
#include "QCalculatorUI.h"
#include "QCalculatorDec.h"
class QCalculator
{
protected:
QCalculatorUI* m_ui;//因为ui类是二阶构造构造的,如果成员变量是二阶构造构造的,使用的类也用二阶构造
QCalculatorDec m_cal;
QCalculator();
bool construct();
public:
static QCalculator* NewInstance();
void show();
~QCalculator();
};
#endif
#include "QCalculator.h"
QCalculator::QCalculator()
{
}
bool QCalculator::construct()
{
m_ui=QCalculatorUI::NewInstance();
if(m_ui !=NULL)
{
m_ui->setCalculator(&m_cal);//将ui跟核心算法关联
}
return (m_ui!=NULL);
}
QCalculator* QCalculator::NewInstance()
{
QCalculator* ret=new QCalculator();
if(!(ret&&ret->construct()))//if((ret==NULL)||!ret->construct())
{
delete ret;
ret=NULL;
}
return ret;
}
void QCalculator::show()
{
m_ui->show();
}
QCalculator::~QCalculator()
{
delete m_ui;
}
#include <QtGui/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;
}
void QCalculatorUI::onButtonClicked()//消息处理函数
{
QPushButton* btn=dynamic_cast<QPushButton*>(sender());
if(btn !=NULL)
{
QString clickTest=btn->text();
if(clickTest=="<-")
{
QString text=m_edit->text();
if(text.length()>0)
{
text.remove(text.length()-1,1);
m_edit->setText(text);
}
}
else if(clickTest=="C")
{
m_edit->setText("");
}
else if(clickTest=="=")
{
if(m_cal !=NULL)
{
m_cal->expression(m_edit->text());
m_edit->setText(m_cal->result());
}
}
else
{
m_edit->setText(m_edit->text()+clickTest);
}
}
小结:
模块之间的交互需要通过接口完成
接口是开发中模块之间的一种契约
模块之间不能出现循环依赖,基本设计原则,强内聚,弱耦合。