毕业了,整理下思绪,把我之前在实验室弄的一个Qt终端界面开发的程序的整体思路以及源代码共享开来,以便于更好地让Qt为人所知,为开源贡献点我的微薄之力。
一. 项目介绍
项目背景的简要介绍:通过串口从数据采集板传输数据到终端板上面,自行设计一个界面用来显示收集到的数据,以及画出所对应的坐标点。终端显示界面的板子是基于S3C6410芯片的,板子是师兄根据Mini6410开发板电路图画出来的,做了部分裁剪。
由于考虑到跨平台以及移植性问题,我选择了Qt进行开发,分别开发了Windows以及Linux版本,并且移植到开发板上面。以下截图1,图2所示:
图1 Windows版本 图2 Linux版本
编写控制Qt串口通信终端界面时候,串口通信部分是参考yafeilinux的串口通信教程:http://blog.csdn.net/yafeilinux/article/details/4717706 Windows, Linux需要相应改写第三方类,yafeilinux里面已经有介绍。由于我要用的poll查询机制,利用settime(sec),因此更改了"*_qextserialport.cpp"文件里面的对应函数,如下图3所示:
图3 修改*_qextserialport.cpp文件
下图3是Windows下的项目文件截图:
图3 Windows下项目源文件
二. Windows平台编写过程
整个项目我是利用QT的designer和手写代码互相配合完成的。新建了一个带有ui的项目以后,添加第三方串口类源文件。在ui里面画出相应的东西,我是用了tabWidget来切换几个界面,在主界面中还用了tableView表格来显示各个参数,如上图1所示,对应的源程序代码如下。
头文件mainwindow.h,定义声明了各种类和变量。
-
- #ifndef MAINWINDOW_H
- #define MAINWINDOW_H
-
- #include <QMainWindow>
- #include <QString>
- #include <QStandardItemModel>
-
- #include "win_qextserialport.h"
- #include <QDebug>
- #include <QTime>
- #include <QTimer>
-
- namespace Ui {
- class MainWindow;
- }
-
- class MainWindow : public QMainWindow
- {
- Q_OBJECT
-
- public:
- explicit MainWindow(QWidget *parent = 0);
- ~MainWindow();
-
-
- private:
- Ui::MainWindow *ui;
- Win_QextSerialPort *myCom;
-
- QStandardItemModel *model;
- int row, column;
- QTimer *myReadTimer;
-
-
- int flag_startToConvert;
- float QBArray2Float(QByteArray temp);
-
- void draw();
-
-
-
- protected:
- bool eventFilter(QObject *obj, QEvent *e);
-
- private slots:
-
-
- void readMyCom();
- void on_openMyComBtn_clicked();
- void on_closeMyComBtn_clicked();
- void on_helpBtn_clicked();
- void on_inputBtn_clicked();
- };
- #endif // MAINWINDOW_H
mainwindow.cpp文件,具体实现各个子函数的功能。
-
- #include "mainwindow.h"
- #include "ui_mainwindow.h"
- #include <QMessageBox>
- #include <QDebug>
- #include <QtGui>
-
- float QBArray2Float(QByteArray temp);
-
- MainWindow::MainWindow(QWidget *parent) :
- QMainWindow(parent),
- ui(new Ui::MainWindow)
- {
- ui->setupUi(this);
- ui->radarWidget->installEventFilter(this);
- ui->closeMyComBtn->setEnabled(false);
- setWindowTitle(tr("汽车防撞雷达界面显示"));
-
- row = 13;
- column = 3;
- model = new QStandardItemModel(row, column);
- ui->tableView->setModel(model);
- ui->tableView->verticalHeader()->hide();
- model->setHeaderData(0, Qt::Horizontal, tr("目标"));
- model->setHeaderData(1, Qt::Horizontal, tr("距离/ (米)"));
- model->setHeaderData(2, Qt::Horizontal, tr("方位/ (度)"));
- }
-
- MainWindow::~MainWindow(){
- delete ui;
- }
-
-
-
- float floatData[20][2] = {{0, 0}};
- int ti = 0, tj = 0;
-
- int dcFlag = 0;
-
-
- void MainWindow::readMyCom()
- {
- int rflag = 0;
- int serialNum = 0;
- int num = 0;
- static int rNum = 2;
-
- ui->radarWidget->update();
-
-
-
- model->removeRows(0, model->rowCount());
- model->setRowCount(row);
- model->setColumnCount(column);
- ui->tableView->update();
-
-
- QByteArray testFrame = myCom->read(5);
- if(testFrame.startsWith("Star"))
- {
- dcFlag = 1;
- qDebug()<<"Start to go"<<endl;
- rflag = 1;
-
- for(int i = 0; i < 50; i++)
- {
- serialNum = myCom->bytesAvailable();
- if(serialNum >= num)
- {
- serialNum = 0;
- break;
- }
- }
- QByteArray dataFrame = myCom->read(4);
- num = QBArray2Float(dataFrame);
- for(ti = 0; ti < num; ti++)
- {
-
-
- QStandardItem *itemTarget = new QStandardItem(QString::number(ti + 1));
- model->setItem(ti, 0, itemTarget);
-
-
- for(tj = 0; tj < rNum; tj++)
- {
- dataFrame = myCom->read(4);
- floatData[ti][tj] = QBArray2Float(dataFrame);
-
-
- QString str;
- QStandardItem *itemFloat = new QStandardItem(str.sprintf("%0.2f", floatData[ti][tj]));
- model->setItem(ti, tj + 1, itemFloat);
-
- }
-
- }
-
- }
- else
- {
- qDebug()<<"receive"<<endl;
- rflag = 0;
- myCom->readAll();
- dcFlag = 0;
-
- }
- rflag = 0;
-
- }
-
-
-
-
-
-
-
-
-
-
-
- float MainWindow::QBArray2Float(QByteArray temp)
- {
- unsigned int i;
- float f;
- temp.resize(4);
- i=0;
- i|=((unsigned char) temp.at(3));
- i=i<<8;
- i|=((unsigned char) temp.at(2));
- i=i<<8;
- i|=((unsigned char) temp.at(1));
- i=i<<8;
- i|=((unsigned char) temp.at(0));
- f = *(float*)&i;
- return f;
- }
-
-
- void MainWindow::on_openMyComBtn_clicked()
- {
-
- QString portName = ui->portNameComboBox->currentText();
-
- myCom = new Win_QextSerialPort(portName, QextSerialBase::Polling);
-
- if(myCom->open(QIODevice::ReadWrite))
- {
- myCom->flush();
-
-
- if(ui->baudRateComboBox->currentText() == tr("9600"))
- myCom->setBaudRate(BAUD9600);
- else if(ui->baudRateComboBox->currentText() == tr("115200"))
- myCom->setBaudRate(BAUD115200);
-
-
- if(ui->dataBitsComboBox->currentText() == tr("8"))
- myCom->setDataBits(DATA_8);
- else if(ui->dataBitsComboBox->currentText() == tr("7"))
- myCom->setDataBits(DATA_7);
-
-
- if(ui->parityComboBox->currentText() == tr("无"))
- myCom->setParity(PAR_NONE);
- else if(ui->parityComboBox->currentText() == tr("奇校验"))
- myCom->setParity(PAR_ODD);
- else if(ui->parityComboBox->currentText() == tr("偶校验"))
- myCom->setParity(PAR_EVEN);
-
-
- if(ui->stopBitsComboBox->currentText() == tr("1"))
- myCom->setStopBits(STOP_1);
- else if(ui->stopBitsComboBox->currentText() == tr("2"))
- myCom->setStopBits(STOP_2);
-
- myCom->setFlowControl(FLOW_OFF);
-
- myCom->setTimeout(10);
- ui->StatusLabel->setText(tr("串口状态:打开成功"));
- }
- else
- {
- ui->StatusLabel->setText(tr("串口状态:打开失败"));
- return;
- }
-
-
-
- myReadTimer = new QTimer(this);
- myReadTimer->setInterval(10);
- connect(myReadTimer, SIGNAL(timeout()), this, SLOT(readMyCom()));
- this->myReadTimer->start();
-
-
-
-
-
-
-
- ui->openMyComBtn->setEnabled(false);
- ui->closeMyComBtn->setEnabled(true);
- ui->helpBtn->setEnabled(true);
- ui->portNameComboBox->setEnabled(false);
- ui->baudRateComboBox->setEnabled(false);
- ui->dataBitsComboBox->setEnabled(false);
- ui->stopBitsComboBox->setEnabled(false);
- ui->parityComboBox->setEnabled(false);
- }
-
- void MainWindow::on_closeMyComBtn_clicked()
- {
- this->myReadTimer->stop();
-
- myCom->close();
- model->removeRows(0, model->rowCount());
- model->setRowCount(row);
- model->setColumnCount(column);
- dcFlag = 0;
- ui->radarWidget->update();
-
- ui->openMyComBtn->setEnabled(true);
- ui->helpBtn->setEnabled(true);
- ui->portNameComboBox->setEnabled(true);
- ui->baudRateComboBox->setEnabled(true);
- ui->dataBitsComboBox->setEnabled(true);
- ui->stopBitsComboBox->setEnabled(true);
- ui->parityComboBox->setEnabled(true);
- ui->StatusLabel->setText(tr("串口状态:关闭"));
- }
-
-
- void MainWindow::on_helpBtn_clicked()
- {
- QMessageBox::about(this, tr("帮助信息"), tr("1.选定好具体串口设置,点击打开串口即可收到信息" "\n"
- "2.防撞雷达项目小组共同努力制作" "\n"
- "3.Qt界面作者联系方式:周军 QQ: 380774082" "\n"
- "4.欢迎访问个人博客 http://blog.csdn.net/jjzhoujun2010"));
- }
-
-
- void MainWindow::on_inputBtn_clicked()
- {
- QByteArray absoluteByte = ui->absoluteLineEdit->text().toAscii().data();
- QByteArray fastByte = ui->fastLineEdit->text().toAscii().data();
-
-
- QByteArray allByte = absoluteByte + '0' + fastByte + '\r' + '\n';
- myCom->write(allByte);
-
- qDebug()<<absoluteByte<<fastByte<<allByte;
- }
coordinate.cpp文件中,由于我是在tabWidget里面绘图的,不能直接调用QPainter类,相应的解决方法有两种:
方法(1): 在子窗口ui.---派生一个类,在PaintEvent里画图。 我所遇到的问题:不清楚自己构造的派生类如何跟ui.---相关联起来,归根到底就是自己C++使用得不熟悉。
方法(2):事件过滤法。给ui.---安装事件监视器,在QEvent::paint事件时QPainter paint(ui->---)画图。我使用的是第二种方法。
坐标图显示如下图4所示,由于在QT里面慢慢画直线、弧线等各种坐标点很麻烦,工作量大,因此我就确定好尺寸后,用PS自己弄了背景图片,计算好相应的坐标点,直接在上面显示目标点。
图4 坐标图显示
-
- #include "mainwindow.h"
- #include "ui_mainwindow.h"
- #include <QPainter>
- #include <stdlib.h>
- #include <QtCore/qmath.h>
-
-
-
- bool MainWindow::eventFilter(QObject *obj, QEvent *e)
- {
- if(obj == ui->radarWidget)
- {
- if(e->type() == QEvent::Paint)
- {
- draw();
- qDebug()<<"Workinggggggggggggggggggggggggggg................................";
- return true;
- }
- else
- {
- qDebug()<<"Waiting";
- }
- }
- return QMainWindow::eventFilter(obj, e);
- }
-
- void MainWindow::draw()
- {
- extern float floatData[20][2];
- extern int dcFlag;
- extern int ti;
- QPainter painter(ui->radarWidget);
- QPixmap pix;
- pix.load(":/coordinate05-18.png");
- painter.drawPixmap(0, 0, 400, 440, pix);
-
- painter.setBrush(Qt::red);
- painter.translate(194, 432);
-
- float radian = 0.140;
-
-
-
-
-
- qDebug()<<dcFlag<<endl;
- if(dcFlag == 1)
- {
- int x = 0;
-
- for(int j = 0; j < ti; j++)
- {
- x = int(floatData[j][0] / 10);
- switch(x)
- {
- case 0:
- if(floatData[j][1] == 0)
- {
- painter.drawEllipse(0, -(floatData[j][0] * 4.5), 10, 10);
- }
-
- else if(floatData[j][1] == 4)
- {
- painter.drawEllipse((floatData[j][0] * 4.5) * qSin(radian),
- -((floatData[j][0] * 4.5) * qCos(radian)), 10, 10);
- }
-
- qDebug()<<(floatData[j][0] * 4.5)<<(floatData[j][0] * 4.5) * qSin(radian)
- <<((floatData[j][0] * 4.5) * qCos(radian));
- break;
- case 1:
- if(floatData[j][1] == 0)
- painter.drawEllipse(0, -(45 + (floatData[j][0] - 10) * 4.4), 10, 10);
- else if(floatData[j][1] == 4)
- painter.drawEllipse((45 + (floatData[j][0] - 10) * 4.4) * qSin(radian),
- -((45 + (floatData[j][0] - 10) * 4.4) * qCos(radian)), 10, 10);
- break;
- case 2:
- if(floatData[j][1] == 0)
- painter.drawEllipse(0, -(89 + (floatData[j][0] - 20) * 4.2), 10, 10);
- else if(floatData[j][1] == 4)
- painter.drawEllipse((89 + (floatData[j][0] - 20) * 4.2) * qSin(radian),
- -(89 + (floatData[j][0] - 20) * 4.2) * qCos(radian), 10, 10);
- break;
- case 3:
- if(floatData[j][1] == 0)
- painter.drawEllipse(0, -(131 + (floatData[j][0] - 30) * 4.2), 10, 10);
- else if(floatData[j][1] == 4)
- painter.drawEllipse((131 + (floatData[j][0] - 30) * 4.2) * qSin(radian),
- -(131 + (floatData[j][0] - 30) * 4.2) * qCos(radian), 10, 10);
- break;
- case 4:
- if(floatData[j][1] == 0)
- painter.drawEllipse(0, -(174 + (floatData[j][0] - 40) * 4.3), 10, 10);
- else if(floatData[j][1] == 4)
- painter.drawEllipse((174 + (floatData[j][0] - 40) * 4.3) * qSin(radian),
- -(174 + (floatData[j][0] - 40) * 4.3) * qCos(radian), 10, 10);
- break;
- case 5:
- if(floatData[j][1] == 0)
- painter.drawEllipse(0, -(218 + (floatData[j][0] - 50) * 4.4), 10, 10);
- else if(floatData[j][1] == 4)
- painter.drawEllipse((218 + (floatData[j][0] - 50) * 4.4) * qSin(radian),
- -(218 + (floatData[j][0] - 50) * 4.4) * qCos(radian), 10, 10);
- break;
- case 6:
- if(floatData[j][1] == 0)
- painter.drawEllipse(0, -(261 + (floatData[j][0] - 60) * 4.3), 10, 10);
- else if(floatData[j][1] == 4)
- painter.drawEllipse((261 + (floatData[j][0] - 60) * 4.3) * qSin(radian),
- -(261 + (floatData[j][0] - 60) * 4.3) * qCos(radian), 10, 10);
- break;
- case 7:
- if(floatData[j][1] == 0)
- painter.drawEllipse(0, -(305 + (floatData[j][0] - 70) * 4.4), 10, 10);
- else if(floatData[j][1] == 4)
- painter.drawEllipse((305 + (floatData[j][0] - 70) * 4.4) * qSin(radian),
- -(305 + (floatData[j][0] - 70) * 4.4) * qCos(radian), 10, 10);
- break;
- case 8:
- if(floatData[j][1] == 0)
- painter.drawEllipse(0, -(343 + (floatData[j][0] - 80) * 3.8), 10, 10);
- else if(floatData[j][1] == 4)
- painter.drawEllipse((343 + (floatData[j][0] - 80) * 3.8) * qSin(radian),
- -(343 + (floatData[j][0] - 80) * 3.8) * qCos(radian), 10, 10);
- break;
- case 9:
- if(floatData[j][1] == 0)
- painter.drawEllipse(0, -(378 + (floatData[j][0] - 90) * 3.5), 10, 10);
- else if(floatData[j][1] == 4)
- painter.drawEllipse((378 + (floatData[j][0] - 90) * 3.5) * qSin(radian),
- -(378 + (floatData[j][0] - 90) * 3.5) * qCos(radian), 10, 10);
- break;
- case 10:
- if(floatData[j][1] == 0)
- painter.drawEllipse(0, -(410 + (floatData[j][0] - 100) * 3.2), 10, 10);
- else if(floatData[j][1] == 4)
- painter.drawEllipse((410 + (floatData[j][0] - 100) * 3.2) * qSin(radian),
- -(410 + (floatData[j][0] - 100) * 3.2) * qCos(radian), 10, 10);
- break;
- default:
- break;
- }
- qDebug()<<x<<floatData[j][0]<<floatData[j][1]<<j<<endl;
- }
-
-
- qDebug()<<ti<<endl;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- }
- }
main.cpp文件
-
-
-
- #include <QtGui/QApplication>
- #include <QTextCodec> //加入头文件
- #include "mainwindow.h"
-
- int main(int argc, char *argv[])
- {
- QApplication a(argc, argv);
-
- QTextCodec::setCodecForTr(QTextCodec::codecForLocale());
- QTextCodec::setCodecForTr(QTextCodec::codecForName("GB2312"));
-
-
-
-
- MainWindow w;
- w.show();
-
- return a.exec();
- }
三、Linux版本
我是在Fedora9 里面进行Linux版本的编写的,只需把相应的第三方类修改,相对应的定义也改掉,串口名称那里也要改,并添加前缀“/dev”,其他部分相同。
四、移植到Mini6410上面运行
移植前需要修改下项目文件里ui的串口名称,改成ttySAC0, ttySAC1....之类的,然后在安装了交叉编译链,Qt4.7交叉编译环境后,在Qt里面选择对应的编译链进行编译,具体做法参见《Mini6410 Qt4和Qtopia编程开发指南》。
五、总结
从接手项目任务到完成大概花了3个月时间,期间由于其他事情断断续续地编程,Qt知识是从零开始学习的,以及由于个人C++水平有限,编写的程序可能会不合理的地方,忘高手指导。通过完成此次的项目,我对于C++的学习又深入一步,以及学会了Qt这种跨平台的SDK。我会分别把Windows版本,Linux版本的源文件上传上来,供大家参考,互相学习。(备注:Linux版本的由于当初是直接在实验室弄好,跟今天修改的Windows版本略有不同,因为当初是为了考虑嵌入式开发板的屏幕小,把选择串口参数部分去掉,直接在程序里面默认设定好了,需要修改的同学直接在里面修改即可。)
Windows版本:http://download.csdn.net/detail/jjzhoujun2010/4393863
Linux版本:http://download.csdn.net/detail/jjzhoujun2010/4393867
《Mini6410 Qt4和Qtopia编程开发指南》:http://download.csdn.net/detail/jjzhoujun2010/4393908
转自:blog.csdn.net/jjzhoujun2010
作者:Dream Fly