功能

基于Qt的软件框架设计,


软件分层

📀 程序架构 - 图1
📀 程序架构 - 图2

GUI层

这里我们由于基于Qt的,所以这一层都是用Qt的类库来实现了,所有的主界面都会从这三个类:QWiget,QMainWindow,QDialog中的一个继承而来,并在main函数里生成实例并show出来,进入主消息循环。

Models层

model层主要实现我们的软件的业务逻辑,这里的模块因整个软件的业务逻辑来合理划分模块,达到高内聚松耦合的效果。在这里我们就涉及到一个这些业务逻辑类,在那里new出来的问题了。我的做法是:做一个bridge类,让所有的业务逻辑类都在这个bridge类里实例化。

Communication 层

  1. Communication 层是实现对数据的采集,通信的方式 有多种多样,有串口,有网口,有CAN口等。这一层收到的数据向models层发,用户的输入也通过这一层向执行机构发。
  2. 那么这一层的类,在那里实例化呢?我同样选择在bridge类里,这样,bridge类其实是一个什么业务功能都没有的类,只是提供了各个类之间可以相互connect(信号槽)的载体。

    下层与GUI层的沟通

  3. 至此,我们的除GUI层的类(通信与模块)都在bridge类里能通过信号槽的机制沟通起来了。那么我们的的模型怎么和GUI进行通信呢?

  4. 我们可以通过这个bridge类来进行,因为我们的其它 的类都是在bridge类里进行实例化的,所以这个类里可以拥有类的引用,我们可以在QWidget里的构造函数里传一个bridge类的指针进去,这样,我们的所有类,都可以和GUI进行通信了。

    运行架构

  5. 当我们把所有的层次的代码都设计好了,并且知道在那个类里,那个层级来实现后,我们要使用线程让整个软件欢快的运行进来 。这就是我们要说的运行架构。

  6. 一般而言,我们把所有的communication的类都 会单独出来成为一个线程,这样,不会阻塞GUI线程,也能够更快的响应采集信息和向下位机传递控制信息。所以communication这一层,一般有几个端口就会有几个线程。当然有些比较轻量级的可以合在一个线程里。
  7. bridge类收到GUI线程里,为什么要这么做呢,我们界面弹出来时,要从model里get最新的值显示在界面上,(用信号槽实现也可以但是会显得比较繁琐),所以要调用get的方法,有这样的需求, 我们最好把所有的model都放到一个线程里——-即GUI线程。
  8. 这样我们的一个小型的信息采集与控制系统的主框架就搭建好了。
  9. 最后强调一点的是:当使用线程时建议使用movetothread的方法。如果不使用这个方法的话,所有的实例都必须在线程的run()函数里new出来,才能保证这个类的槽函数在这个线程的消息循环里执行

实例

Demo

通信

通信口不局限于串口,如果有多种接口可以抽象。

  1. #ifndef CPORT_H
  2. #define CPORT_H
  3. #include <QObject>
  4. #include <QSerialPort>
  5. #include <QThread>
  6. #include <QTimer>
  7. class Message;//当然这个结构得自己定义并注册,这里使用了前置申明
  8. class CPort : public QObject
  9. {
  10. Q_OBJECT
  11. public:
  12. explicit CPort(QObject *parent = 0);
  13. ~CPort();
  14. signals:
  15. //向上层传送消息
  16. void signalUpwardMsg(const Message&);
  17. private slots:
  18. //上层传过来的消息发送到串口
  19. void onDownwardMsg(const Message&);
  20. //读串口数据
  21. void onReadData();
  22. //在这里定时的解析收到的数据
  23. void onParse();
  24. private:
  25. QSerialPort *port;
  26. QThread worker;
  27. QTimer *timer;
  28. QByteArray data;
  29. };
  30. #endif // CPORT_H
  1. #include "CPort.h"
  2. #include "message.h"
  3. #include <QDebug>
  4. CPort::CPort(QObject *parent) : QObject(parent)
  5. {
  6. port = new QSerialPort(this);
  7. #ifdef __arm__
  8. port->setPortName("/dev/ttymxc1");
  9. #else
  10. port->setPortName("COM2");
  11. #endif
  12. port->setBaudRate(QSerialPort::Baud115200);
  13. port->setDataBits(QSerialPort::Data8);
  14. port->setParity(QSerialPort::EvenParity);
  15. port->setStopBits(QSerialPort::OneStop);
  16. connect(port,SIGNAL(readyRead()),this,SLOT(onReadData()));
  17. if(!port->open(QIODevice::ReadWrite))
  18. {
  19. qDebug()<<"Port Can't open";
  20. }
  21. timer = new QTimer(this);
  22. timer->setInterval(10000);
  23. connect(timer,SIGNAL(timeout()),SLOT(onParse()));
  24. timer->start();
  25. this->moveToThread(&worker);
  26. worker.start(QThread::HighPriority);
  27. }
  28. CPort::~CPort()
  29. {
  30. }
  31. void CPort::onDownwardMsg(const Message & msg)
  32. {
  33. QByteArray array(msg.content);
  34. port->write(array);
  35. }
  36. void CPort::onReadData()
  37. {
  38. data.append(port->readAll());
  39. }
  40. void CPort::onParse()
  41. {
  42. for(int i = 0 ; i < data.size();i++)
  43. {
  44. //协议解析后得到 message
  45. }
  46. Message msg;
  47. signalUpwardMsg(msg);
  48. }

业务逻辑

  1. #ifndef CMSGBUSINESS_H
  2. #define CMSGBUSINESS_H
  3. #include <QObject>
  4. class Message;
  5. class CMsgBusiness : public QObject
  6. {
  7. Q_OBJECT
  8. public:
  9. explicit CMsgBusiness(QObject *parent = 0);
  10. ~CMsgBusiness();
  11. public:
  12. //这里可以在UI里来直接调用
  13. QString get()const;
  14. signals:
  15. //处理完的Msg发到界面上去显示
  16. void signalSomethingComing(QString);
  17. private slots:
  18. //收到下层来的msg
  19. void onCommingMsg(const Message&);
  20. };
  21. #endif // CMSGBUSINESS_H
  1. #include "CMsgBusiness.h"
  2. CMsgBusiness::CMsgBusiness(QObject *parent) : QObject(parent)
  3. {
  4. }
  5. CMsgBusiness::~CMsgBusiness()
  6. {
  7. }
  8. void CMsgBusiness::onCommingMsg(const Message &msg)
  9. {
  10. Q_UNUSED(msg)
  11. //这里可以对收到的msg进行处理
  12. signalSomethingComing(QString("sss"));
  13. }

Bridge

  1. #ifndef CBRIDGE_H
  2. #define CBRIDGE_H
  3. #include <QObject>
  4. class CMsgBusiness;
  5. class CPort;
  6. class CBridge : public QObject
  7. {
  8. Q_OBJECT
  9. public:
  10. explicit CBridge(QObject *parent = 0);
  11. ~CBridge();
  12. //这里可以得到所有的实例
  13. CMsgBusiness *business()const;
  14. CPort *port()const;
  15. signals:
  16. //这里可以转发信号
  17. public slots:
  18. private:
  19. CMsgBusiness *pBusiness;
  20. CPort *pPort;
  21. };
  22. #endif // CBRIDGE_H
  1. #include "CBridge.h"
  2. #include "CPort.h"
  3. #include "CMsgBusiness.h"
  4. CBridge::CBridge(QObject *parent) : QObject(parent)
  5. {
  6. pBusiness = new CMsgBusiness(this);
  7. pPort = new CPort();//这里的实例化得注意了
  8. //通信层与业务层的通信
  9. connect(pPort,SIGNAL(signalUpwardMsg(Message))
  10. ,pBusiness,SLOT(onCommingMsg(Message)));
  11. }
  12. CBridge::~CBridge()
  13. {
  14. }
  15. CMsgBusiness *CBridge::business() const
  16. {
  17. return pBusiness;
  18. }
  19. CPort *CBridge::port() const
  20. {
  21. return pPort;
  22. }

MainWindow

  1. #include "mainwindow.h"
  2. #include "CBridge.h"
  3. #include "CMsgBusiness.h"
  4. MainWindow::MainWindow(QWidget *parent,CBridge* bridge)
  5. : QMainWindow(parent),pb(bridge)
  6. {
  7. //这里完成业务层与UI层的通信
  8. connect(pb->business(),SIGNAL(signalSomethingComing(QString))
  9. ,SLOT(onComing(QString)));
  10. }
  11. MainWindow::~MainWindow()
  12. {
  13. }
  14. void MainWindow::onComing(QString content)
  15. {
  16. Q_UNUSED(content)
  17. //SHOW ON UI
  18. }

Client

  1. #include "mainwindow.h"
  2. #include <QApplication>
  3. #include "CBridge.h"
  4. int main(int argc, char *argv[])
  5. {
  6. QApplication a(argc, argv);
  7. MainWindow w(0,new CBridge);
  8. w.show();
  9. return a.exec();
  10. }