一句废话:之前说不搞PyQT了,可是python的库实在太强大,QT的界面实在太方便,二者结合,实在太牛逼,我还是继续踏实地往下做吧
Pycharm新建工程,就不细表了。安装一些依赖库,WIN+R调出cmd,输入
pip3 install modbus_tk
就OK了
但是有个问题,pip安装的库默认在python安装路径下,而Pycharm新建工程则默认去识别当前工程所在的目录包含的库。因此,会出现无法import库的情况,更改如下
选择Python路径作为project interpreter,一切OK
开工,在Qt Designer下绘制UI,如下图
此时在工程目录下,会出现相应的ui文件,比如我的是MBcomQT.ui,右键选择PyUIC,将会在当前工程目录下,生成 MBcomQT.py:
大致看一下生成的文件内容:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'MBcomQT.ui'
#
# Created by: PyQt5 UI code generator 5.6
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.setEnabled(True)
MainWindow.resize(800, 600)
#还有很多
该py文件由ui文件自动生成,每次QT的Ui改动,记得生成一次py文件就好。
然后,在主文件中包含文件,新建窗口类包含Ui类就好。如下:
from MBcomQT import Ui_MainWindow
###
#此处省略N行无关代码
###
class MyWindow(QMainWindow,Ui_MainWindow):
def __init__(self):
super(MyWindow,self).__init__()
self.inspectData = {'adcValue':0,'adcRefValue':0,'pTomValue':0,'meaPlsCnt':0,'refPlsCnt':0,'meaTRatio':0}
###
#此处再省略N行无关代码
###
最简单的测试代码可以这样写,
import sys
from MBcomQT import Ui_MainWindow
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5 import QtCore, QtGui
class MyWindow(QMainWindow,Ui_MainWindow):
def __init__(self):
super(MyWindow,self).__init__()
self.setupUi(self)
if __name__ == "__main__":
app = QApplication(sys.argv)
myshow = MyWindow()
myshow.show()
print('程序终止')
sys.exit(app.exec_())
出错:
这个时候会报错,原因是在Ui生成的py文件末尾,有rc文件的引用,而这个文件并不存在,即缺少相应的rc文件
办法:
在PyCharm的Terminal下,输入
pyrcc5 -o MBcomSrc_rc.py MBcomSrc.qrc
关于UI文件中用到图标资源的几点注意(本处非必要,可略过不看)
所有的图标资源都在这里管理,点开铅笔
这几个小图标分别是:新建资源文件、打开资源文件、移除资源文件;添加前缀、添加文件、删除文件。见名知意,不再赘述。只是想说的是,这里的前缀决定了图标的索引,举个例子:
我的文件结构是,主py文件与icons文件夹并列,所有ico和png都在icons文件夹下。因此,前缀应该是./
,文件路径是icons/logo.ico
,即从工程目录开始,以 ./icons/logo.ico来索引资源,以此类推
到这里,软件的基本操作就算告一段落了,下面讲点业务上的事情
1. modbus_tk的基本使用
可以参考另一篇文章
python下通过modbus_tk实现modbus主机
2.业务逻辑
如前面UI所示,有端口选择框和一些pushbutton,主要有 建立连接BuidLink、观测数据InspectData、清除数据ClearData和远端加热FarHeat。这些pushbutton都连接了相应的槽函数,分别如下:
self.timeThread.oneSecondTriger.connect(self.timeUpdate)
self.pushButton_BuidLink.clicked.connect(self.MBusBuidLink)
self.pushButton_InspectData.clicked.connect(self.InspectData)
self.pushButton_ClearData.clicked.connect(self.TextBrowserClear)
self.pushButton_FarHeat.clicked.connect(self.FarHeatCtrl)
列出几个关键的函数:
def InspectData(self):
try:
x = self.MBusMaster.execute(35, cst.READ_HOLDING_REGISTERS, 25, 11)
self.inspectData['adcValue'] = AddrIntToFloat(x[0],x[1])
self.inspectData['adcRefValue'] = AddrIntToFloat(x[2],x[3])
self.inspectData['pTomValue'] = AddrIntToFloat(x[4],x[5])
self.inspectData['meaPlsCnt'] = (x[6] << 8)| (x[7])
self.inspectData['refPlsCnt'] = (x[8] << 8)| (x[9])
self.inspectData['meaTRatio'] = x[10]
print(self.inspectData)
self.TextBrowserAdd()
except modbus_tk.modbus_rtu.ModbusInvalidResponseError as err:
print(err)
以及
def FarHeatCtrl(self):
if self.pushButton_FarHeat.text() == '远端加热':
self.pushButton_FarHeat.setText('中断加热')
self.label_PortHeatInfo.setText('加热中')
self.graphicsView_Heat.setStyleSheet("background-color: rgb(255, 0, 0);")
try:
print(self.MBusMaster.execute(35, cst.WRITE_MULTIPLE_REGISTERS, 9, output_value=[1]))
except modbus_tk.modbus_rtu.ModbusInvalidResponseError as err:
print(err)
else:
self.pushButton_FarHeat.setText('远端加热')
self.label_PortHeatInfo.setText('未加热')
self.graphicsView_Heat.setStyleSheet("background-color: rgb(184, 134, 11);")
try:
print(self.MBusMaster.execute(35, cst.WRITE_MULTIPLE_REGISTERS, 9, output_value=[0]))
except modbus_tk.modbus_rtu.ModbusInvalidResponseError as err:
print(err)
着重说一下,多线程的刷新问题,关于Python多线程以及PyQT多线程,可参考这篇文章:Python多线程与PyQT多线程实例讲解
在UI中,有checkbox,这个是否勾选将决定线程是否向从机读数据。代码如下:
class MBThread(QThread):
oneSecondTriger = pyqtSignal()
def __init__(self):
super(MBThread,self).__init__()
def run(self):
while True:
self.oneSecondTriger.emit()
time.sleep(1)
槽函数定义
def timeUpdate(self):
self.label_Time.setText(time.strftime('%H:%M:%S'))
if self.checkBox_Refresh.isChecked() == True:
try:
x = self.MBusMaster.execute(35, cst.READ_HOLDING_REGISTERS, 4, 2)
self.rh = AddrIntToFloat(x[0], x[1])
x = self.MBusMaster.execute(35, cst.READ_HOLDING_REGISTERS, 5, 2)
self.t = AddrIntToFloat(x[0], x[1])
except modbus_tk.modbus_rtu.ModbusInvalidResponseError as err:
print(err)
else:
self.rh = 0
self.t = 0
self.label_RHValue.setText(str(round(self.rh,2)))
self.label_TValue.setText(str(round(self.t,2)))
当然,在类内部构造函数,应该做相应的初始化工作的
self.timeThread = MBThread()
self.timeThread.oneSecondTriger.connect(self.timeUpdate)
self.timeThread.start()
好的,运行测试
细节功能就不一一展示了。
好,本篇结束