项目总结-上位机
前几天受朋友之托,给他们的项目写个上位机。有些经验分享给大家
要求是上位机收到通过串口发送的数据,根据数据显示空车位的数量。
*最终上位机拥有的值得一谈的功能:
- 串口通信的相关设置
- 数据保存
- 界面随窗口缩放的实现
- 软件打包
- 。。。然后就没有了
1.开始写软件之前首先明确目的,软件应该实行什么功能,然后设计界面,最后才开始写代码。
界面设计(Visio设计,当然其他工具也可以):
2.软件结构设计
整个软件只有一个窗口tabwidget,在其中嵌入三个继承自Qwidgetd的类,一个用于串口通信相关设置,一个用于显示车位数,一个是帮助页面。
3.软件页面的设计与实现
之前看过其他人写的上位机,有的界面做得不是很爽,主要是界面固定,不能进行窗口缩放。
说一下如何让软件的界面能够大小根据口的大小缩放?窗
- 首先要知道,软件界面的自动缩放,可以由窗口控制。就是说设置好界面后把控制权交给QT的程序框架就行了,框架会进行调整。值得一提的是,个人感觉qt设计师不好用,当界面的控件多的时候,布局起来往往不如人意,特别是要实现窗口缩放的功能,很麻烦。推荐用代码布局,可操作性强,配置起来也简单明了。如果是小项目,界面简单,那就无所谓了。
- 把所有的布局最终嵌入到一个布局里面,然后设置这个布局为窗口的布局,剩下的缩放就交给qt控件了。
4.串口通信的实现
qt5提供了串口类,所以我们只需要调用修改函数进行配置就能够实现通信了。qt5的示例程序里面有查看程序,输入"serial"就可以检索出来。
对串口的操作类似文件操作。过程就是:打开串口—>使用串口—>关闭串口。完整的过程:检索串口—>打开串口—>配置波特率、停止位等等—>对串口读写—>不使用时关闭。
5.代码有些是有注释,有些是没有注释的,还有一些是自注释(通过函数名称就知道是干啥的)。
6.一些需要注意的问题
1.要想使用串口类需要在工程文件添加serialport
eg. QT += core gui serialport
2.为软件的使用方便性,可以设置"伙伴"(setBuddy)
3.注意控件的宽度,有时候界面缩放效果不理想,并不是布局不好,而是控件的一些属性(高度、宽度)影响了布局。
7.这个程序车位显示模块做得真菜,判断有无车位居然使用的是if()else()……..
他们做的下位机连个通信协议都定。我这边也是没办法,把老师忽悠过关就行了。反正不是我的项目,O(∩_∩)O~
8.结果展示:
9.软件打包
1.思考一个问题:为什么要打包?
通俗来说,因为用户可能没有Qt SDK,可能有也不知道如何使用,所以需要打包(不同意的同志不要笑)。
2.打包工具:
3.打包原料:1.qt生成的可执行文件(一般使用release版的,因为其占用空间少,使用debug对应用户来说没必要,其占用空间也大)
2.qt的一些dll(如果不知道你的可执行程序需要那些,就运行可执行程序根据提示去安装目录下的bin文件夹复制过来就行。eg. C:\Qt\Qt5.5.0\5.5\mingw492_32\bin)
4.打包步骤:
- 把程序用到的资源,可执行程序、相关dll放置一个文件夹里
- 打开打包工具,
完成后可以发给用户了。
10.上代码:
KelySerialPort.pro(项目文件)
#-------------------------------------------------
#
# Project created by QtCreator 2015-10-01T15:33:42
#
#-------------------------------------------------
QT += core gui serialport
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = KelySerialPort
TEMPLATE = app
SOURCES += main.cpp \
KelySerialPort.cpp
HEADERS += \
KelySerialPort.h
DISTFILES += \
Readme.txt
RC_FILE += app.rc
KelySerialPort.h(头文件):
#ifndef
WIDGET_H
#define
WIDGET_H
#include
<QWidget>
#include
<QTime>
#include
<QTimer>
#include
<QDebug>
#include
<QWidget>
#include
<QPushButton>
#include
<QLineEdit>
#include
<QLabel>
#include
<QGridLayout>
#include
<QtDebug>
#include
<QComboBox>
#include
<QMessageBox>
#include
<QtGui>
#include
<QTableView>
#include
<QHeaderView>
#include
<QTextEdit>
#include
<QTextBrowser>
#include
<QtSerialPort/QSerialPort>
class
PositionShow
:
public
QWidget
{
Q_OBJECT
public:
PositionShow(QWidget
*parent =
0);
~PositionShow();
QPushButton
*
pb0;
QPushButton
*
pb1;
QPushButton
*
pb2;
QPushButton
*
pb3;
QGridLayout
*
gl0;
#define
Posi0_0
"carport0"
#define
Posi0_1
"carport1"
#define
Posi1_0
"carport2"
#define
Posi1_1
"carport3"
public
slots:
};
class
MySerialPort
:
public
QWidget
{
Q_OBJECT
public:
MySerialPort(QWidget
*parent =
0);
~MySerialPort();
QHBoxLayout
*
hl0;
QHBoxLayout
*
hl1;
QHBoxLayout
*
hl2;
QHBoxLayout
*
hl3;
QHBoxLayout
*
hl4;
QHBoxLayout
*
hl5;
QHBoxLayout
*
hl6;
QHBoxLayout
*
hl7;
QHBoxLayout
*
hl8;
QVBoxLayout
*
vl0;
QVBoxLayout
*
vl1;
QGridLayout
*
gl0;
QLabel
*
lb0;
QLabel
*
lb1;
QLabel
*
lb2;
QLabel
*
lb3;
QLabel
*
lb4;
QLabel
*
lb5;
QLabel
*
lb6;
QComboBox
*
cb0;
QComboBox
*
cb1;
QComboBox
*
cb2;
QComboBox
*
cb3;
QComboBox
*
cb4;
QPushButton
*
pb0;
QPushButton
*
pb1;
QPushButton
*
pb2;
QPushButton
*
pb3;
QPushButton
*
pb4;
QTextEdit*
te0;
QLineEdit*
le0;
public
slots:
bool
readFromSerial(void);
bool
sendToSerial(void);
void
openSerial(void);
void
clearSendArea(void);
void
clearGetArea(void);
void
saveData(void);
public:
QSerialPort
serial;
QByteArray
readBuf;
QTimer
timer;
QString
msg;
void
UIload(void);
void
UIlayout(void);
void
setBaudRate(void);
void
setDataBit(void);
void
setParity(void);
void
setStopBits(void);
void
setComponemtAttribute(void);
};
class
Widget
:
public
QWidget
{
Q_OBJECT
public:
Widget(QWidget
*parent =
0);
~Widget();
QGridLayout
*
gl_tab;
QTabWidget
*
QTab_UI;
MySerialPort
*
QTab_SerP;
PositionShow
*
QTab_PosiUI;
QWidget
*
help;
QTextBrowser
*
tw0;
QVBoxLayout
*
v0_for_help;
QTimer
*timer;
public
slots:
void
showEmptyPosi(void);
public:
void
loadDocument(void);
};
#endif
//
WIDGET_H
KelySerialPort.cpp(正文):
#include "KelySerialPort.h"
#include <QFile>
#include <QTextStream>
#include <QtSerialPort/QSerialPortInfo>
#include <QTime>
#include <QDate>
#include <QTimer>
#include <QString>
#include <QComboBox>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
this->setWindowTitle(tr("上位机:空位显示"));
QTab_UI= new QTabWidget;
QTab_SerP = new MySerialPort;
QTab_PosiUI = new PositionShow;
help = new QWidget;
gl_tab = new QGridLayout;
v0_for_help = new QVBoxLayout;
QTab_UI->addTab(QTab_SerP,tr("(&T)串口设置界面"));
QTab_UI->addTab(QTab_PosiUI,tr("(&S)车位显示界面"));
QTab_UI->addTab(help,tr("(&H)帮助页面"));
gl_tab->addWidget(QTab_UI);
gl_tab->setContentsMargins(0,0,0,0);
this->setLayout(gl_tab); // 为窗口设置布局
timer = new QTimer(this); // 定时器 1秒 用于更新车位的状态
timer->start(1000);
connect(timer, SIGNAL(timeout()), this, SLOT(showEmptyPosi()));
loadDocument(); // 加载帮助界面的文档
}
Widget::~Widget()
{
}
MySerialPort::MySerialPort(QWidget *parent)
{
UIload();
UIlayout();
setComponemtAttribute();
foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
cb0->addItem(info.portName());
}
connect(pb0,SIGNAL(clicked(bool)),this,SLOT(openSerial()));
connect(pb3,SIGNAL(clicked(bool)),this,SLOT(sendToSerial()));
connect(&serial,SIGNAL(readyRead()),this,SLOT(readFromSerial()));
connect(pb1,SIGNAL(clicked(bool)),this,SLOT(clearSendArea()));
connect(pb4,SIGNAL(clicked(bool)),this,SLOT(clearGetArea()));
connect(pb2,SIGNAL(clicked(bool)),this,SLOT(saveData()));
}
MySerialPort::~MySerialPort()
{
}
bool MySerialPort::readFromSerial()
{
readBuf.append(serial.readAll());
te0->append(QString(readBuf));
msg = QString(readBuf);
readBuf.clear();
}
bool MySerialPort::sendToSerial()
{
serial.write(le0->text().toLocal8Bit());
}
void MySerialPort::openSerial()
{
if(pb0->text()=="(&O)打开串口"){
pb0->setText(tr("(&O)关闭串口"));
serial.setPortName(cb0->currentText());
serial.open(QIODevice::ReadWrite);
setBaudRate();
setDataBit();
setParity();
setStopBits();
cb0->setEnabled(false);
cb1->setEnabled(false);
cb2->setEnabled(false);
cb3->setEnabled(false);
cb4->setEnabled(false);
}else{
serial.close();
pb0->setText(tr("(&O)打开串口"));
cb0->setEnabled(true);
cb1->setEnabled(true);
cb2->setEnabled(true);
cb3->setEnabled(true);
cb4->setEnabled(true);
}
}
void MySerialPort::clearSendArea()
{
te0->clear();
}
void MySerialPort::clearGetArea()
{
le0->clear();
}
void MySerialPort::saveData()
{ QTime tim;
qDebug()<<tim.currentTime().toString();
QDate td;
qDebug()<<td.currentDate().toString();
QFile data("SerialPortRecord.txt");
if (data.open(QFile::WriteOnly | QFile::Append)) {
QTextStream out(&data);
out<<"Save time:"<<td.currentDate().toString()<<" " \
<<tim.currentTime().toString()\
<<"\nData:"<<te0->document()->toPlainText()<<"\n\n";
}
}
void MySerialPort::UIload()
{
hl0 = new QHBoxLayout;
hl1 = new QHBoxLayout;
hl2 = new QHBoxLayout;
hl3 = new QHBoxLayout;
hl4 = new QHBoxLayout;
hl5 = new QHBoxLayout;
hl6 = new QHBoxLayout;
hl7 = new QHBoxLayout;
hl8 = new QHBoxLayout;
vl0 = new QVBoxLayout;
vl1 = new QVBoxLayout;
gl0 = new QGridLayout;
lb0 = new QLabel(tr("(&B)串口号:"));
lb1 = new QLabel(tr("(&V)波特率:"));
lb2 = new QLabel(tr("(&E)数据位:"));
lb3 = new QLabel(tr("(&P)停止位:"));
lb4 = new QLabel(tr("(&J)校验位:"));
lb5 = new QLabel(tr("(&G)数据接收区:"));
lb6 = new QLabel(tr("(&D)数据发送区:"));
cb0 = new QComboBox;
cb1 = new QComboBox;
cb2 = new QComboBox;
cb3 = new QComboBox;
cb4 = new QComboBox;
pb0 = new QPushButton(tr("(&O)打开串口"));
pb1 = new QPushButton(tr("(&L)清空数据发送区"));
pb2 = new QPushButton(tr("(&A)保存数据"));
pb3 = new QPushButton(tr("(&E)发送数据"));
pb4 = new QPushButton(tr("(&G)清空数据接收区"));
te0 = new QTextEdit;
le0 = new QLineEdit;
}
void MySerialPort::UIlayout()
{
hl0->addWidget(lb0);
hl0->addWidget(cb0);
hl1->addWidget(lb1);
hl1->addWidget(cb1);
hl2->addWidget(lb2);
hl2->addWidget(cb2);
hl3->addWidget(lb3);
hl3->addWidget(cb3);
hl4->addWidget(lb4);
hl4->addWidget(cb4);
hl5->addWidget(pb1);
hl5->addWidget(pb2);
hl6->addWidget(pb3);
hl6->addWidget(pb4);
vl0->addWidget(pb0);
vl0->addLayout(hl0);
vl0->addLayout(hl1);
vl0->addLayout(hl2);
vl0->addLayout(hl3);
vl0->addLayout(hl4);
vl1->addWidget(lb6);
vl1->addLayout(hl6);
vl1->addWidget(le0);
vl1->addWidget(lb5);
vl1->addLayout(hl5);
vl1->addWidget(te0);
gl0->addLayout(vl0,0,0);
gl0->addLayout(vl1,0,1);
gl0->setSpacing(20);
this->setLayout(gl0);
}
void MySerialPort::setBaudRate()
{
if(cb1->currentText() == tr("9600"))
serial.setBaudRate(QSerialPort::Baud9600);
else if(cb1->currentText() == tr("1200"))
serial.setBaudRate(QSerialPort::Baud1200);
else if(cb1->currentText() == tr("2400"))
serial.setBaudRate(QSerialPort::Baud2400);
else if(cb1->currentText() == tr("4800"))
serial.setBaudRate(QSerialPort::Baud4800);
else if(cb1->currentText() == tr("19200"))
serial.setBaudRate(QSerialPort::Baud19200);
else if(cb1->currentText() == tr("38400"))
serial.setBaudRate(QSerialPort::Baud38400);
else if(cb1->currentText() == tr("57600"))
serial.setBaudRate(QSerialPort::Baud57600);
else if(cb1->currentText() == tr("115200"))
serial.setBaudRate(QSerialPort::Baud115200);
}
void MySerialPort::setDataBit()
{
if(cb2->currentText() == tr("Data5"))
serial.setDataBits(QSerialPort::Data5);
else if(cb2->currentText() == tr("Data6"))
serial.setDataBits(QSerialPort::Data6);
else if(cb2->currentText() == tr("Data7"))
serial.setDataBits(QSerialPort::Data7);
else if(cb2->currentText() == tr("Data8"))
serial.setDataBits(QSerialPort::Data8);
}
void MySerialPort::setParity()
{
if(cb4->currentText() == tr("NoParity"))
serial.setParity(QSerialPort::NoParity);
else if(cb4->currentText() == tr("EvenParity"))
serial.setParity(QSerialPort::EvenParity);
else if(cb4->currentText() == tr("OddParity"))
serial.setParity(QSerialPort::OddParity);
else if(cb4->currentText() == tr("SpaceParity"))
serial.setParity(QSerialPort::SpaceParity);
else if(cb4->currentText() == tr("MarkParity"))
serial.setParity(QSerialPort::MarkParity);
}
void MySerialPort::setStopBits()
{
if(cb3->currentText() == tr("OneStop"))
serial.setStopBits(QSerialPort::OneStop);
else if(cb3->currentText() == tr("OneAndHalfStop"))
serial.setStopBits(QSerialPort::OneAndHalfStop);
else if(cb3->currentText() == tr("TwoStop"))
serial.setStopBits(QSerialPort::TwoStop);
}
void MySerialPort::setComponemtAttribute()
{
cb1->addItem(tr("9600"));
cb1->addItem(tr("1200"));
cb1->addItem(tr("2400"));
cb1->addItem(tr("4800"));
cb1->addItem(tr("19200"));
cb1->addItem(tr("38400"));
cb1->addItem(tr("57600"));
cb1->addItem(tr("115200"));
cb2->addItem(tr("Data8"));
cb2->addItem(tr("Data5"));
cb2->addItem(tr("Data6"));
cb2->addItem(tr("Data7"));
cb3->addItem(tr("OneStop"));
cb3->addItem(tr("OneAndHalfStop"));
cb3->addItem(tr("TwoStop"));
cb4->addItem(tr("NoParity"));
cb4->addItem(tr("EvenParity"));
cb4->addItem(tr("OddParity"));
cb4->addItem(tr("SpaceParity"));
cb4->addItem(tr("MarkParity"));
pb0->setFixedWidth(120);
pb1->setFixedWidth(120);
pb2->setFixedWidth(120);
pb3->setFixedWidth(120);
pb4->setFixedWidth(120);
te0->setEnabled(false);
cb0->setFixedWidth(100);
cb1->setFixedWidth(100);
cb2->setFixedWidth(100);
cb3->setFixedWidth(100);
cb4->setFixedWidth(100);
lb5->setBuddy(te0);
lb6->setBuddy(le0);
lb0->setBuddy(cb0);
lb1->setBuddy(cb1);
lb2->setBuddy(cb2);
lb3->setBuddy(cb3);
lb4->setBuddy(cb4);
}
PositionShow::PositionShow(QWidget *parent)
{
gl0 = new QGridLayout;
pb0 = new QPushButton(tr("无车"));
pb1 = new QPushButton(tr("无车"));
pb2 = new QPushButton(tr("无车"));
pb3 = new QPushButton(tr("无车"));
pb0->setFixedSize(250,250);
pb1->setFixedSize(250,250);
pb2->setFixedSize(250,250);
pb3->setFixedSize(250,250);
gl0->addWidget(pb0,0,0);
gl0->addWidget(pb1,0,1);
gl0->addWidget(pb2,1,0);
gl0->addWidget(pb3,1,1);
pb0->setStyleSheet(QString::fromUtf8("font: 75 36pt \"\351\273\221\344\275\223\";"));
pb1->setStyleSheet(QString::fromUtf8("font: 75 36pt \"\351\273\221\344\275\223\";"));
pb2->setStyleSheet(QString::fromUtf8("font: 75 36pt \"\351\273\221\344\275\223\";"));
pb3->setStyleSheet(QString::fromUtf8("font: 75 36pt \"\351\273\221\344\275\223\";"));
setLayout(gl0);
}
PositionShow::~PositionShow()
{
}
void Widget::showEmptyPosi()
{
QString str = QTab_SerP->msg;
if(str == "END_A_0")
QTab_PosiUI->pb0->setText(tr("空车位:\n0个"));
if(str == "END_A_1")
QTab_PosiUI->pb0->setText(tr("空车位:\n1个"));
if(str == "END_A_2")
QTab_PosiUI->pb0->setText(tr("空车位:\n2个"));
if(str == "END_B_0")
QTab_PosiUI->pb1->setText(tr("空车位:\n0个"));
if(str == "END_B_1")
QTab_PosiUI->pb1->setText(tr("空车位:\n1个"));
if(str == "END_C_0")
QTab_PosiUI->pb2->setText(tr("空车位:\n0个"));
if(str == "END_C_1")
QTab_PosiUI->pb2->setText(tr("空车位:\n1个"));
if(str == "END_D_0")
QTab_PosiUI->pb3->setText(tr("空车位:\n0个"));
if(str == "END_D_1")
QTab_PosiUI->pb3->setText(tr("空车位:\n1个"));
}
void Widget::loadDocument()
{
tw0 = new QTextBrowser;
v0_for_help->addWidget(tw0);
help->setLayout(v0_for_help);
QByteArray str;
QFile file("Readme.txt");
if(file.open(QFile::ReadOnly)){
str.append(file.readAll());
file.close();
}
tw0->setText(str);
}
main.cpp(主函数):
#include
"KelySerialPort.h"
#include
<QApplication>
int
main(int
argc,
char
*argv[])
{
QApplication
a(argc,
argv);
Widget
w;
w.show();
return
a.exec();
}
Readme.txt(相关的。。):
1.enum
QSerialPort::StopBits:
The
default
value
is
OneStop,
i.e.
1
stop
bit.
Constant Value Description
QSerialPort::OneStop 1 1
stop
bit.
QSerialPort::OneAndHalfStop 3 1.5
stop
bits.
This
is
only
for
the
Windows
platform.
QSerialPort::TwoStop 2 2
stop
bits.
QSerialPort::UnknownStopBits -1 Unknown
number
of
stop
bits.
This
value
is
obsolete.
It
is
provided
to
keep
old
source
code
working.
We
strongly
advise
against
using
it
in
new
code.
2.enum
QSerialPort::Parity
The
default
value
is
NoParity,
i.e.
no
parity.
Constant Value Description
QSerialPort::NoParity 0 No
parity
bit
it
sent.
This
is
the
most
common
parity
setting.
Error
detection
is
handled
by
the
communication
protocol.
QSerialPort::EvenParity 2 The
number
of
1
bits
in
each
character,
including
the
parity
bit,
is
always
even.
QSerialPort::OddParity 3 The
number
of
1
bits
in
each
character,
including
the
parity
bit,
is
always
odd.
It
ensures
that
at
least
one
state
transition
occurs
in
each
character.
QSerialPort::SpaceParity 4 Space
parity.
The
parity
bit
is
sent
in
the
space
signal
condition.
It
does
not
provide
error
detection
information.
QSerialPort::MarkParity 5 Mark
parity.
The
parity
bit
is
always
set
to
the
mark
signal
condition
(logical
1).
It
does
not
provide
error
detection
information.
QSerialPort::UnknownParity -1 Unknown
parity.
This
value
is
obsolete.
It
is
provided
to
keep
old
source
code
working.
We
strongly
advise
against
using
it
in
new
code.
3.enum
QSerialPort::DataBits
The
default
value
is
Data8,
i.e.
8
data
bits.
This
enum
describes
the
number
of
data
bits
used.
Constant Value Description
QSerialPort::Data5 5 The
number
of
data
bits
in
each
character
is
5.
It
is
used
for
Baudot
code.
It
generally
only
makes
sense
with
older
equipment
such
as
teleprinters.
QSerialPort::Data6 6 The
number
of
data
bits
in
each
character
is
6.
It
is
rarely
used.
QSerialPort::Data7 7 The
number
of
data
bits
in
each
character
is
7.
It
is
used
for
true
ASCII.
It
generally
only
makes
sense
with
older
equipment
such
as
teleprinters.
QSerialPort::Data8 8 The
number
of
data
bits
in
each
character
is
8.
It
is
used
for
most
kinds
of
data,
as
this
size
matches
the
size
of
a
byte.
It
is
almost
universally
used
in
newer
applications.
QSerialPort::UnknownDataBits -1 Unknown
number
of
bits.
This
value
is
obsolete.
It
is
provided
to
keep
old
source
code
working.
We
strongly
advise
against
using
it
in
new
code.
4.enum
QSerialPort::BaudRate
The
default
value
is
Baud9600,
i.e.
9600
bits
per
second.
This
enum
describes
the
baud
rate
which
the
communication
device
operates
with.
Note:
Only
the
most
common
standard
baud
rates
are
listed
in
this
enum.
Constant Value Description
QSerialPort::Baud1200 1200 1200
baud.
QSerialPort::Baud2400 2400 2400
baud.
QSerialPort::Baud4800 4800 4800
baud.
QSerialPort::Baud9600 9600 9600
baud.
QSerialPort::Baud19200 19200 19200
baud.
QSerialPort::Baud38400 38400 38400
baud.
QSerialPort::Baud57600 57600 57600
baud.
QSerialPort::Baud115200 115200 115200
baud.
QSerialPort::UnknownBaud -1 Unknown
baud.
This
value
is
obsolete.
It
is
provided
to
keep
old
source
code
working.
We
strongly
advise
against
using
it
in
new
code.
通信协议?