功能
基于Qt的软件框架设计,
软件分层


GUI层
这里我们由于基于Qt的,所以这一层都是用Qt的类库来实现了,所有的主界面都会从这三个类:QWiget,QMainWindow,QDialog中的一个继承而来,并在main函数里生成实例并show出来,进入主消息循环。
Models层
model层主要实现我们的软件的业务逻辑,这里的模块因整个软件的业务逻辑来合理划分模块,达到高内聚松耦合的效果。在这里我们就涉及到一个这些业务逻辑类,在那里new出来的问题了。我的做法是:做一个bridge类,让所有的业务逻辑类都在这个bridge类里实例化。
Communication 层
- Communication 层是实现对数据的采集,通信的方式 有多种多样,有串口,有网口,有CAN口等。这一层收到的数据向models层发,用户的输入也通过这一层向执行机构发。
那么这一层的类,在那里实例化呢?我同样选择在bridge类里,这样,bridge类其实是一个什么业务功能都没有的类,只是提供了各个类之间可以相互connect(信号槽)的载体。
下层与GUI层的沟通
至此,我们的除GUI层的类(通信与模块)都在bridge类里能通过信号槽的机制沟通起来了。那么我们的的模型怎么和GUI进行通信呢?
我们可以通过这个bridge类来进行,因为我们的其它 的类都是在bridge类里进行实例化的,所以这个类里可以拥有类的引用,我们可以在QWidget里的构造函数里传一个bridge类的指针进去,这样,我们的所有类,都可以和GUI进行通信了。
运行架构
当我们把所有的层次的代码都设计好了,并且知道在那个类里,那个层级来实现后,我们要使用线程让整个软件欢快的运行进来 。这就是我们要说的运行架构。
- 一般而言,我们把所有的communication的类都 会单独出来成为一个线程,这样,不会阻塞GUI线程,也能够更快的响应采集信息和向下位机传递控制信息。所以communication这一层,一般有几个端口就会有几个线程。当然有些比较轻量级的可以合在一个线程里。
- bridge类收到GUI线程里,为什么要这么做呢,我们界面弹出来时,要从model里get最新的值显示在界面上,(用信号槽实现也可以但是会显得比较繁琐),所以要调用get的方法,有这样的需求, 我们最好把所有的model都放到一个线程里——-即GUI线程。
- 这样我们的一个小型的信息采集与控制系统的主框架就搭建好了。
- 最后强调一点的是:当使用线程时建议使用movetothread的方法。如果不使用这个方法的话,所有的实例都必须在线程的run()函数里new出来,才能保证这个类的槽函数在这个线程的消息循环里执行
实例
通信
通信口不局限于串口,如果有多种接口可以抽象。
#ifndef CPORT_H#define CPORT_H#include <QObject>#include <QSerialPort>#include <QThread>#include <QTimer>class Message;//当然这个结构得自己定义并注册,这里使用了前置申明class CPort : public QObject{Q_OBJECTpublic:explicit CPort(QObject *parent = 0);~CPort();signals://向上层传送消息void signalUpwardMsg(const Message&);private slots://上层传过来的消息发送到串口void onDownwardMsg(const Message&);//读串口数据void onReadData();//在这里定时的解析收到的数据void onParse();private:QSerialPort *port;QThread worker;QTimer *timer;QByteArray data;};#endif // CPORT_H
#include "CPort.h"#include "message.h"#include <QDebug>CPort::CPort(QObject *parent) : QObject(parent){port = new QSerialPort(this);#ifdef __arm__port->setPortName("/dev/ttymxc1");#elseport->setPortName("COM2");#endifport->setBaudRate(QSerialPort::Baud115200);port->setDataBits(QSerialPort::Data8);port->setParity(QSerialPort::EvenParity);port->setStopBits(QSerialPort::OneStop);connect(port,SIGNAL(readyRead()),this,SLOT(onReadData()));if(!port->open(QIODevice::ReadWrite)){qDebug()<<"Port Can't open";}timer = new QTimer(this);timer->setInterval(10000);connect(timer,SIGNAL(timeout()),SLOT(onParse()));timer->start();this->moveToThread(&worker);worker.start(QThread::HighPriority);}CPort::~CPort(){}void CPort::onDownwardMsg(const Message & msg){QByteArray array(msg.content);port->write(array);}void CPort::onReadData(){data.append(port->readAll());}void CPort::onParse(){for(int i = 0 ; i < data.size();i++){//协议解析后得到 message}Message msg;signalUpwardMsg(msg);}
业务逻辑
#ifndef CMSGBUSINESS_H#define CMSGBUSINESS_H#include <QObject>class Message;class CMsgBusiness : public QObject{Q_OBJECTpublic:explicit CMsgBusiness(QObject *parent = 0);~CMsgBusiness();public://这里可以在UI里来直接调用QString get()const;signals://处理完的Msg发到界面上去显示void signalSomethingComing(QString);private slots://收到下层来的msgvoid onCommingMsg(const Message&);};#endif // CMSGBUSINESS_H
#include "CMsgBusiness.h"CMsgBusiness::CMsgBusiness(QObject *parent) : QObject(parent){}CMsgBusiness::~CMsgBusiness(){}void CMsgBusiness::onCommingMsg(const Message &msg){Q_UNUSED(msg)//这里可以对收到的msg进行处理signalSomethingComing(QString("sss"));}
Bridge
#ifndef CBRIDGE_H#define CBRIDGE_H#include <QObject>class CMsgBusiness;class CPort;class CBridge : public QObject{Q_OBJECTpublic:explicit CBridge(QObject *parent = 0);~CBridge();//这里可以得到所有的实例CMsgBusiness *business()const;CPort *port()const;signals://这里可以转发信号public slots:private:CMsgBusiness *pBusiness;CPort *pPort;};#endif // CBRIDGE_H
#include "CBridge.h"#include "CPort.h"#include "CMsgBusiness.h"CBridge::CBridge(QObject *parent) : QObject(parent){pBusiness = new CMsgBusiness(this);pPort = new CPort();//这里的实例化得注意了//通信层与业务层的通信connect(pPort,SIGNAL(signalUpwardMsg(Message)),pBusiness,SLOT(onCommingMsg(Message)));}CBridge::~CBridge(){}CMsgBusiness *CBridge::business() const{return pBusiness;}CPort *CBridge::port() const{return pPort;}
MainWindow
#include "mainwindow.h"#include "CBridge.h"#include "CMsgBusiness.h"MainWindow::MainWindow(QWidget *parent,CBridge* bridge): QMainWindow(parent),pb(bridge){//这里完成业务层与UI层的通信connect(pb->business(),SIGNAL(signalSomethingComing(QString)),SLOT(onComing(QString)));}MainWindow::~MainWindow(){}void MainWindow::onComing(QString content){Q_UNUSED(content)//SHOW ON UI}
Client
#include "mainwindow.h"#include <QApplication>#include "CBridge.h"int main(int argc, char *argv[]){QApplication a(argc, argv);MainWindow w(0,new CBridge);w.show();return a.exec();}
