实现功能
由于本人水平有限,仅用了最简单的一些pushButton和comboBox实现,主要功能:
- 串口扫描、刷新、设置
- 串口连接
- 串口数据发送
- 开始、停止手动灌溉
- 开始、停止自动灌溉
页面实现效果
代码目录结构
代码案例
- 代码已经全部添加注释,故不再做单独解释。
Main.py
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
from ui.Ui_meanu import *
from drivers.driver_serial import *
import threading
class MainWidget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.ui = Ui_Form()
self.ui.setupUi(self)
# 固定窗口大小
self.setFixedSize(650,400)
# 点击按钮会触发的槽函数
self.init_ui()
# 实例化属性
self.serial_ports=[] # 串口列表
self.sp = None # 串口对象
self.scan_show_uart()
self.baud_set()
def init_ui(self):
# 刷新串口的槽函数连接
self.ui.pushButton_6.clicked.connect(self.scan_show_uart)
# 链接串口的槽函数连接
self.ui.pushButton.clicked.connect(self.connect_uart_slot)
# 开始手动灌溉的槽函数连接
self.ui.pushButton_2.clicked.connect(self.irrigation_slot)
# 停止手动灌溉的槽函数连接
self.ui.pushButton_3.clicked.connect(self.irrigation_slot)
# 开始自动灌溉的槽函数连接
self.ui.pushButton_4.clicked.connect(self.irrigation_slot)
# 停止自动灌溉的槽函数连接
self.ui.pushButton_5.clicked.connect(self.irrigation_slot)
def irrigation_slot(self):
'''灌溉的槽函数定义'''
# 如果串口连接且是打开的才发数据
if self.sp and self.sp.is_open():
sender = self.sender()
# 开始手动灌溉
if sender is self.ui.pushButton_2:
res,msg = self.sp.write(b'\x01') # 发送十六进制数据
self.sp.write(b'\n')
# 停止手动灌溉
elif sender is self.ui.pushButton_3:
res,msg = self.sp.write(b'\x00') # 发送十六进制数据
self.sp.write(b'\n')
# 开始自动灌溉
elif sender is self.ui.pushButton_4:
res,msg = self.sp.write(b'\x02') # 发送十六进制数据
self.sp.write(b'\n')
# 停止自动灌溉
elif sender is self.ui.pushButton_5:
res,msg = self.sp.write(b'\x03') # 发送十六进制数据
self.sp.write(b'\n')
# 如果发串口数据失败弹出警告
if not res:
QMessageBox.warning(self,'警告','发送失败,错误信息:'+msg)
# 如果发送成功弹出提示
else:
QMessageBox.information(self,'提示','指令发送成功')
# 如果串口对象没连接弹出警告提示
else:
QMessageBox.warning(self,'警告','请先连接串口')
def connect_uart_slot(self):
'''串口连接槽函数定义'''
# 如果串口已经连接,再次点击就要关闭串口连接
if self.sp:
self.sp.close()
self.sp = None
self.ui.pushButton.setText('点击连接串口')
self.ui.pushButton.setIcon(QIcon(":/img/img/disc.png"))
return
# 如果串口列表为空,弹出警告提示
if not self.serial_ports:
QMessageBox.warning(self,'警告','请先选择串口')
return
# 获取串口选中项
index = self.ui.comboBox_3.currentIndex()
uart_name = self.serial_ports[index][0]
# 获取当前波特率选中项
baud_rate = int(self.ui.comboBox_2.currentText())
self.sp = SerialDevice(uart_name, baud_rate=baud_rate) # 替换为您的串口名称、波特率和超时时间
# 进行串口连接
res, msg = self.sp.open()
if not res:
QMessageBox.warning(self,'警告',"串口无法连接,请检查是否被占用")
return
# 链接成功后改变状态
self.ui.pushButton.setText('已连接(点击断开连接)')
self.ui.pushButton.setIcon(QIcon(":/img/img/conn.png"))
# 连接成功后就接受数据
t = threading.Thread(target=self.read_thread,daemon=True)
t.start()
def read_thread(self):
'''串口读取数据的线程函数'''
while True:
# 如果串口连接成功,就接收数据
if self.sp and self.sp.is_open():
data = self.sp.readline(1024)
if data:
print(data)
else:
break
def scan_show_uart(self):
# 如果已经存在,需要清空串口列表和下拉选项,否则主动刷新时会增加多个
if self.serial_ports:
self.serial_ports = []
self.ui.comboBox_3.clear()
self.serial_ports = scan_serial_ports()
# 如果存在串口,添加到下拉列表中
if len(self.serial_ports) != 0:
for device, description in self.serial_ports:
self.ui.comboBox_3.addItem(description)
def baud_set(self):
'''波特率默认设置'''
self.ui.comboBox_2.addItems(['9600','19200','115200','256000','500000'])
# 默认显示选中“115200”
self.ui.comboBox_2.setCurrentIndex(2)
if __name__ == '__main__':
app = QApplication(sys.argv)
widget = MainWidget()
widget.show()
sys.exit(app.exec_())
Driver_serial.py
import serial
from serial import Serial
from serial.tools import list_ports
def scan_serial_ports():
# 查看串口列表
ports = list_ports.comports()
# for port in ports: # ListPortInfo
# # 设备名称、描述、硬件ID、供应商ID、产品ID、序列号、位置
# print(port.device, port.description, port.hwid, port.vid, port.pid, port.serial_number, port.location)
return [(port.device, port.description) for port in ports]
class SerialDevice:
def __init__(self, port, baud_rate=9600, timeout=None):
self.port = port
self.baud_rate = baud_rate
self.timeout = timeout
self.serial: Serial = None
def open(self):
try:
self.serial = serial.Serial(self.port, self.baud_rate, timeout=self.timeout)
if self.serial.is_open:
# print(f"Serial port {self.port} opened successfully.")
return True, f"Serial port {self.port} opened successfully."
except serial.SerialException as e:
print("请检查设备是否连接,或端口被其他软件占用,Failed to open serial port:", str(e))
return False, str(e)
return False, f"Serial port {self.port} open failed."
def close(self):
try:
if self.serial and self.serial.is_open:
self.serial.close()
print("Serial port closed.")
self.serial = None
else:
print("Serial port is not open.")
except:
pass
def write(self, data):
if not self.serial or not self.serial.is_open:
print("Serial port is not open.")
return False,"Serial port is not open."
try:
self.serial.write(data)
print("Data written:", data)
return True,"Data send successful"
except serial.SerialException as e:
print("Failed to write data:", str(e))
return False,str(e)
def flush(self):
if not self.serial or not self.serial.is_open:
print("Serial port is not open.")
return None
self.serial.flush()
def read(self, num_bytes):
if not self.serial or not self.serial.is_open:
print("Serial port is not open.")
return None
try:
return self.serial.read(num_bytes)
except serial.SerialException as e:
print("Failed to read data:", str(e))
return None
def readline(self):
if not self.serial or not self.serial.is_open:
print("Serial port is not open.")
return
try:
return self.serial.readline()
except serial.SerialException as e:
print("Failed to read data:", str(e))
return None
def is_open(self):
if not self.serial:
return False
return self.serial.is_open
if __name__ == '__main__':
# 示例用法
serial_ports = scan_serial_ports()
if len(serial_ports) > 0:
print("Available serial serial_ports:")
for device, description in serial_ports:
print(device, "->", description)
else:
print("No serial serial_ports found.")
# 示例用法
sp = SerialDevice("COM4", baud_rate=115200) # 替换为您的串口名称、波特率和超时时间
res, msg = sp.open()
print("res = ", res)
print("msg = ", msg)
if res == True:
# sp.write(b"Hello, Serial!\n") # 发送数据
sp.write(b"\x12\n") # 发送数据
# print(sp.read(10)) # 读取10个字节的数据
print(sp.readline()) # 读取一行
sp.close()
Ui_meanu.py(由QT页面编译而成)
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'c:\Users\Windows10\Desktop\02上课代码步骤\自动灌溉系统\ui\meanu.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(646, 447)
self.label_3 = QtWidgets.QLabel(Form)
self.label_3.setGeometry(QtCore.QRect(280, 40, 76, 22))
font = QtGui.QFont()
font.setFamily("Arial")
font.setPointSize(14)
self.label_3.setFont(font)
self.label_3.setObjectName("label_3")
self.label_2 = QtWidgets.QLabel(Form)
self.label_2.setGeometry(QtCore.QRect(190, 140, 78, 16))
self.label_2.setObjectName("label_2")
self.comboBox_2 = QtWidgets.QComboBox(Form)
self.comboBox_2.setGeometry(QtCore.QRect(270, 130, 171, 31))
self.comboBox_2.setObjectName("comboBox_2")
self.pushButton = QtWidgets.QPushButton(Form)
self.pushButton.setGeometry(QtCore.QRect(190, 180, 251, 31))
font = QtGui.QFont()
font.setFamily("Arial")
font.setPointSize(10)
font.setBold(True)
font.setWeight(75)
self.pushButton.setFont(font)
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(":/img/img/disc.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.pushButton.setIcon(icon)
self.pushButton.setObjectName("pushButton")
self.pushButton_4 = QtWidgets.QPushButton(Form)
self.pushButton_4.setGeometry(QtCore.QRect(190, 300, 111, 51))
self.pushButton_4.setObjectName("pushButton_4")
self.pushButton_2 = QtWidgets.QPushButton(Form)
self.pushButton_2.setGeometry(QtCore.QRect(190, 230, 111, 51))
self.pushButton_2.setObjectName("pushButton_2")
self.label = QtWidgets.QLabel(Form)
self.label.setGeometry(QtCore.QRect(190, 90, 78, 16))
self.label.setTextFormat(QtCore.Qt.PlainText)
self.label.setObjectName("label")
self.comboBox_3 = QtWidgets.QComboBox(Form)
self.comboBox_3.setGeometry(QtCore.QRect(270, 80, 171, 31))
self.comboBox_3.setObjectName("comboBox_3")
self.pushButton_3 = QtWidgets.QPushButton(Form)
self.pushButton_3.setGeometry(QtCore.QRect(330, 230, 111, 51))
self.pushButton_3.setObjectName("pushButton_3")
self.pushButton_5 = QtWidgets.QPushButton(Form)
self.pushButton_5.setGeometry(QtCore.QRect(330, 300, 111, 51))
self.pushButton_5.setObjectName("pushButton_5")
self.label_4 = QtWidgets.QLabel(Form)
self.label_4.setGeometry(QtCore.QRect(450, 80, 54, 31))
self.label_4.setText("")
self.label_4.setObjectName("label_4")
self.pushButton_6 = QtWidgets.QPushButton(Form)
self.pushButton_6.setEnabled(True)
self.pushButton_6.setGeometry(QtCore.QRect(450, 80, 31, 31))
self.pushButton_6.setText("")
icon1 = QtGui.QIcon()
icon1.addPixmap(QtGui.QPixmap(":/img/img/refresh.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.pushButton_6.setIcon(icon1)
self.pushButton_6.setObjectName("pushButton_6")
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "智慧农业灌溉系统"))
self.label_3.setText(_translate("Form", "串口设置"))
self.label_2.setText(_translate("Form", "波特率设置 :"))
self.pushButton.setText(_translate("Form", "点击连接串口"))
self.pushButton_4.setText(_translate("Form", "开启自动灌溉"))
self.pushButton_2.setText(_translate("Form", "开始手动灌溉"))
self.label.setText(_translate("Form", "选 择 串 口:"))
self.pushButton_3.setText(_translate("Form", "停止手动灌溉"))
self.pushButton_5.setText(_translate("Form", "停止自动灌溉"))
from ui import img_rc
代码资源下载
- 只有上位机界面,没有C代码。