万恶的项目上有一点需求,需要我们收集底层采集到的数据,本地存储,再发送到云端,被迫搬砖写了个小型的UI,记录下。
完整的下载代码地址:https://download.csdn.net/download/weixin_39749553/11026494
首先是环境,开发的工具是Pycharm,python的解释器使用 anaconda3,辅助包主要是PyQt5,会稍微用到一点QTDesigner(不用也可以),数据库使用的是SQlite,数据库的查看工具是DB Browser。流程如下
一、环境配置
安装pycharm很简单,就不说了,但是这里一定要安装专业版的,因为社区版的pycharm再使用数据库时非常不方便。安装好之后我们按照下图配置环境 File - settings - project - Project interpreter
点击加号,输入pyqt,把圈中的两个包都安装上,后边需要用到的包也是同样的安装方法。注意解释器呀,要用anaconda3的,可以少安装很多东西。
二、UI界面
环境配好就可以动手写东西了,首先分析需求,我们要采集数据,然后再界面上显示数据,并做出图像,所以,首先要有数据和UI界面,这里先做界面。
正常来讲,UI界面应该单独的放在一个模块里,然后在主模块中调用,这里我写的不好,就直接写在一起了,界面的主要部分如下所示:
operatorLayout = QHBoxLayout()
temperatureLayout = QHBoxLayout()
pressureLayout = QHBoxLayout()
flowLayout = QHBoxLayout()
addressLayout = QHBoxLayout()
# 传感器显示布局
self.temperature = QLabel('0')
self.temperature.setFixedWidth(200)
self.temperature_status = QLineEdit('NO')
self.temperature_status.setFixedWidth(200)
self.pressure = QLabel('0')
self.pressure.setFixedWidth(200)
self.pressure_status = QLineEdit('NO')
self.pressure_status.setFixedWidth(200)
self.flow = QLabel('0')
self.flow.setFixedWidth(200)
self.flow_status = QLineEdit('NO')
self.flow_status.setFixedWidth(200)
self.address = QLabel('0')
self.address.setFixedWidth(200)
self.address_status = QLineEdit('NO')
self.address_status.setFixedWidth(200)
# 所有的状态标签
temperature_show1 = QLabel("温度传感器:")
temperature_show2 = QLabel("温度传感器工作状态:")
pressure_show1 = QLabel("压力传感器:")
pressure_show2 = QLabel("压力传感器工作状态:")
flow_show1 = QLabel("流量传感器:")
flow_show2 = QLabel("流量传感器工作状态:")
address_show1 = QLabel("地址信息 :")
address_show2 = QLabel("角度传感器工作状态:")
# 添加布局,使用的是水平布局,然后垂直摆放
temperatureLayout.addWidget(temperature_show1)
temperatureLayout.addWidget(self.temperature)
temperatureLayout.addWidget(temperature_show2)
temperatureLayout.addWidget(self.temperature_status)
pressureLayout.addWidget(pressure_show1)
pressureLayout.addWidget(self.pressure)
pressureLayout.addWidget(pressure_show2)
pressureLayout.addWidget(self.pressure_status)
flowLayout.addWidget(flow_show1)
flowLayout.addWidget(self.flow)
flowLayout.addWidget(flow_show2)
flowLayout.addWidget(self.flow_status)
addressLayout.addWidget(address_show1)
addressLayout.addWidget(self.address)
addressLayout.addWidget(address_show2)
addressLayout.addWidget(self.address_status)
self.prevButton = QPushButton("前一页")
self.nextButton = QPushButton("后一页")
self.switchPageButton = QPushButton("Go")
self.switchPageLineEdit = QLineEdit('0')
self.switchPageLineEdit.setFixedWidth(40)
switchPage = QLabel("转到第")
page = QLabel("页")
operatorLayout.addWidget(self.prevButton)
operatorLayout.addWidget(self.nextButton)
operatorLayout.addWidget(switchPage)
operatorLayout.addWidget(self.switchPageLineEdit)
operatorLayout.addWidget(page)
operatorLayout.addWidget(self.switchPageButton)
operatorLayout.addWidget(QSplitter())
# 状态布局
statusLayout = QHBoxLayout()
self.totalPageLabel = QLabel()
self.totalPageLabel.setFixedWidth(70)
self.currentPageLabel = QLabel()
self.currentPageLabel.setFixedWidth(70)
self.totalRecordLabel = QLabel()
self.totalRecordLabel.setFixedWidth(70)
statusLayout.addWidget(self.totalPageLabel)
statusLayout.addWidget(self.currentPageLabel)
statusLayout.addWidget(QSplitter())
statusLayout.addWidget(self.totalRecordLabel)
# 设置表格属性
self.tableView = QTableView()
# 表格宽度的自适应调整
self.tableView.horizontalHeader().setStretchLastSection(True)
self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
# 创建图形
self.graphicsView = pg.GraphicsLayoutWidget()
# 创建界面
mainLayout = QVBoxLayout(self)
mainLayout.addLayout(operatorLayout)
mainLayout.addWidget(self.tableView) #加入表格
mainLayout.addLayout(temperatureLayout)
mainLayout.addLayout(pressureLayout)
mainLayout.addLayout(flowLayout)
mainLayout.addLayout(addressLayout)
mainLayout.addWidget(self.graphicsView) #加入曲线
mainLayout.addLayout(statusLayout)
self.setLayout(mainLayout)
这段代码看不懂关系也不大,完全可以用QTDesigner来画。
三、数据采集
使用python做数据采集需要使用 serial 这个包,然后只需要行代码就可以读取。
import serial
serialport = serial.Serial(GL.comport, int(GL.baudrate) ,timeout=0.5,parity=serial.PARITY_NONE, rtscts=1)
class WorkThread(QThread):
receive = pyqtSignal()
def run(self):
while True:
if (GL.bytes == 51):
data = serialport.readline()
if data != '':
data1 = data.decode()
data1 = data1.strip().split()
data1 = [str(i) for i in data1[:6]]
GL.data2 = data1
self.receive.emit() #发送消息
首先载入serial,定义串口为 serialport ,然后写一个QThread的子类,使用另外一个线程做数据的读取(因为主线程是UI的显示,如果直接使用主线程读取,会造成阻塞)。这里使用了 pyqt 的消息和槽的机制,发送一个 receive 消息,然后再UI的初始化中绑定这个消息和处理方法(也就是槽),最后编写处理该消息的槽。
四、存入数据库并画图
有了UI界面、又完成了数据采集,我们就可以将数据存入数据库,并使用它们来画图了
def update_data(self):
time = QDateTime.currentDateTime()
timeDisplay = time.toString("yyyyMMdd hh:mm:ss dddd")
if GL.data2 != []:
self.temperature.setText(GL.data2[0])
self.pressure.setText(GL.data2[1])
self.flow.setText(GL.data2[2])
self.address.setText(GL.data2[5])
# 获取采集数据
GL.list[0] = int(GL.list[0] + 1)
GL.list[1] = timeDisplay[9:17]
GL.list[2] = int(GL.data2[1])
GL.list[3] = int(GL.data2[0])
GL.list[4] = int(GL.data2[2])
GL.list[5] = GL.data2[5]
GL.x_plot.append(GL.list[0])
GL.y_plot.append(GL.list[2])
GL.blue_plot.append(GL.list[3])
if len(GL.x_plot) == 100:
GL.x_plot = [0]
GL.y_plot = [0]
GL.blue_plot = [0] # 用刚刚采集到的100个点,画图
self.plt.clear()
self.plt.plot( GL.x_plot,GL.y_plot, pen= pg.mkPen(color='r', width=3),name="Red curve")
self.plt.plot( GL.x_plot,GL.blue_plot, pen= pg.mkPen(color='b', width=3),name="Blue curve")
self.db.open()
query = QSqlQuery()
# 存入数据库
sql_1 = """insert into device(id,time,name,sex,age,deparment) values(%d,"%s",%d,%d,%d,"%s")""" % (GL.list[0], GL.list[1], GL.list[2], GL.list[3], GL.list[4], GL.list[5])
query.exec(sql_1)
self.db.close()
上边这段代码就是刚才的 receive 消息对应的槽函数,当接收到receive消息时,我们将串口读到的数据存入全局变量,然后将全局变量按照顺序丢到数据库中。当然,要想使用数据库,肯定要在开头创建一个,如下:
def createTableAndInit(self):
# 添加数据库
self.db = QSqlDatabase.addDatabase('QSQLITE')
# 设置数据库名称
self.db.setDatabaseName('./db/database2.db')
# 判断是否打开
if not self.db.open():
return False
# 声明数据库查询对象
query = QSqlQuery()
# 创建表
query.exec("create table device(id int , time int , name vchar, sex vchar, age int, deparment int )")
# 添加记录
sql = "insert into device(id,time,name,sex,age,deparment) values(%d,%d,%d,%d,%d,%d)"%(GL.list[0],GL.list[1],GL.list[2],GL.list[3],GL.list[4],GL.list[5])
query.exec(sql)
return True
如此,我们就完成了数据的采集、存储和显示。
当然,这个代码可以优化的地方有很多,望能指正。