从 PyQt GUI 连接到串口
Posted
技术标签:
【中文标题】从 PyQt GUI 连接到串口【英文标题】:Connect to serial from a PyQt GUI 【发布时间】:2019-03-08 20:21:51 【问题描述】:我编写了一个程序来从串口发送和接收数据,但是我有一个问题,我想创建一个函数“connect()”或一个类,当我按下按钮时,该函数被执行,但是如果我在“MainWindow”类中创建这个函数,“TestThread”类中的变量“ser”变得未初始化,你能帮帮我吗?
import sys
import serial
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.uic import loadUi
ser = serial.Serial('/dev/tty.usbmodem14201', 9600, timeout=1)
class TestThread(QThread):
serialUpdate = pyqtSignal(str)
def run(self):
while ser.is_open:
QThread.sleep(1)
value = ser.readline().decode('ascii')
self.serialUpdate.emit(value)
ser.flush()
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
loadUi('/Users/bogdanvesa/P2A_GUI/mainwindow.ui', self)
self.thread = TestThread(self)
self.thread.serialUpdate.connect(self.handleSerialUpdate)
self.connect_btn.clicked.connect(self.connectSer)
self.lcd_EBtn.clicked.connect(self.startThread)
def startThread(self):
self.thread.start()
def handleSerialUpdate(self, value):
print(value)
self.lcd_lineEdit.setText(value)
def main():
app = QApplication(sys.argv)
form = MainWindow()
form.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
【问题讨论】:
@eyllanesc 好的,当我按下一个按钮时,我希望建立与 Arduino 的连接(打开串行),当我按下另一个按钮时关闭连接(关闭串行)但我不知道如何制作这个 【参考方案1】:与其使用 pySerial + 线程,不如使用与 Qt 事件循环一起使用的 QSerialPort
:
from PyQt5 import QtCore, QtWidgets, QtSerialPort
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.message_le = QtWidgets.QLineEdit()
self.send_btn = QtWidgets.QPushButton(
text="Send",
clicked=self.send
)
self.output_te = QtWidgets.QTextEdit(readOnly=True)
self.button = QtWidgets.QPushButton(
text="Connect",
checkable=True,
toggled=self.on_toggled
)
lay = QtWidgets.QVBoxLayout(self)
hlay = QtWidgets.QHBoxLayout()
hlay.addWidget(self.message_le)
hlay.addWidget(self.send_btn)
lay.addLayout(hlay)
lay.addWidget(self.output_te)
lay.addWidget(self.button)
self.serial = QtSerialPort.QSerialPort(
'/dev/tty.usbmodem14201',
baudRate=QtSerialPort.QSerialPort.Baud9600,
readyRead=self.receive
)
@QtCore.pyqtSlot()
def receive(self):
while self.serial.canReadLine():
text = self.serial.readLine().data().decode()
text = text.rstrip('\r\n')
self.output_te.append(text)
@QtCore.pyqtSlot()
def send(self):
self.serial.write(self.message_le.text().encode())
@QtCore.pyqtSlot(bool)
def on_toggled(self, checked):
self.button.setText("Disconnect" if checked else "Connect")
if checked:
if not self.serial.isOpen():
if not self.serial.open(QtCore.QIODevice.ReadWrite):
self.button.setChecked(False)
else:
self.serial.close()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
【讨论】:
非常感谢,我将使用您的代码,但是为了我的安宁,可以从我的“主窗口”类中的按钮连接到串行吗?同时我的“TestThread”类运行良好..你能给我举个例子吗?【参考方案2】:我使用了重新设计的上述代码,所以它有一个mainwindow
、一个menubar
和一个statusbar
。我还添加了 QSerialPortInfo 类。此版本将找到活动端口并将其显示在状态栏上。
我只在 rpi 4 和 Windows 10 上对此进行了测试。
import sys
from PyQt5 import QtCore, QtWidgets, QtSerialPort
from PyQt5.QtWidgets import QApplication, QMainWindow ,QWidget ,QToolBar ,QHBoxLayout, QAction ,QStatusBar ,QLineEdit ,QPushButton ,QTextEdit , QVBoxLayout
from PyQt5.QtCore import Qt , pyqtSignal
from PyQt5.QtSerialPort import QSerialPortInfo
class AddComport(QMainWindow):
porttnavn = pyqtSignal(str)
def __init__(self, parent , menu):
super().__init__(parent)
menuComporte = menu.addMenu("Comporte")
info_list = QSerialPortInfo()
serial_list = info_list.availablePorts()
serial_ports = [port.portName() for port in serial_list]
if(len(serial_ports)> 0):
antalporte = len(serial_ports)
antal = 0
while antal < antalporte:
button_action = QAction(serial_ports[antal], self)
txt = serial_ports[antal]
portinfo = QSerialPortInfo(txt)
buttoninfotxt = " Ingen informationer"
if portinfo.hasProductIdentifier():
buttoninfotxt = ("Produkt specifikation = " + str(portinfo.vendorIdentifier()))
if portinfo.hasVendorIdentifier():
buttoninfotxt = buttoninfotxt + (" Fremstillers id = "+ str(portinfo.productIdentifier()))
button_action = QAction( txt , self)
button_action.setStatusTip( buttoninfotxt)
button_action.triggered.connect(lambda checked, txt = txt: self.valgAfComportClick(txt))
menuComporte.addAction(button_action)
antal = antal +1
else:
print("Ingen com porte fundet")
def valgAfComportClick(self , port):
self.porttnavn.emit(port)
def closeEvent(self, event):
self.close()
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
portname = "None"
self.setStatusBar(QStatusBar(self))
menu = self.menuBar()
comfinder = AddComport(self , menu)
comfinder.porttnavn.connect(self.valgAfComport)
self.setWindowTitle("Serial port display / send")
self.message_le = QLineEdit()
self.send_btn = QPushButton(
text="Send",
clicked=self.send
)
self.output_te = QTextEdit(readOnly=True)
self.button = QPushButton(
text="Connect",
checkable=True,
toggled=self.on_toggled
)
lay = QVBoxLayout(self)
hlay = QHBoxLayout()
hlay.addWidget(self.message_le)
hlay.addWidget(self.send_btn)
lay.addLayout(hlay)
lay.addWidget(self.output_te)
lay.addWidget(self.button)
widget = QWidget()
widget.setLayout(lay)
self.setCentralWidget(widget)
self.serial = QtSerialPort.QSerialPort(
portname,
baudRate=QtSerialPort.QSerialPort.Baud9600,
readyRead=self.receive)
@QtCore.pyqtSlot()
def receive(self):
while self.serial.canReadLine():
text = self.serial.readLine().data().decode()
text = text.rstrip('\r\n')
self.output_te.append(text)
@QtCore.pyqtSlot()
def send(self):
self.serial.write(self.message_le.text().encode())
@QtCore.pyqtSlot(bool)
def on_toggled(self, checked):
self.button.setText("Disconnect" if checked else "Connect")
if checked:
if not self.serial.isOpen():
self.serial.open(QtCore.QIODevice.ReadWrite)
if not self.serial.isOpen():
self.button.setChecked(False)
else:
self.button.setChecked(False)
else:
self.serial.close()
def valgAfComport(self , nyport):
seropen = False
if self.serial.isOpen():
seropen = True
self.serial.close()
self.serial.setPortName(nyport)
if seropen:
self.serial.open(QtCore.QIODevice.ReadWrite)
if not self.serial.isOpen():
self.button.setChecked(False)
print(nyport)
def closeEvent(self, event):
self.serial.close()
print("Comport lukket")
# print(comporttxt)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
【讨论】:
以上是关于从 PyQt GUI 连接到串口的主要内容,如果未能解决你的问题,请参考以下文章