Python 从PyQt GUI连接到串行

Python 从PyQt GUI连接到串行,python,python-3.x,pyqt5,pyserial,Python,Python 3.x,Pyqt5,Pyserial,我写了一个程序来发送和接收串行数据,但我有一个问题,我想创建一个函数“connect()”或一个类,当我按下一个按钮时,该函数被执行,但是如果我在“MainWindow”类中创建这个函数,来自“TestThread”类的变量“ser”将无法初始化,你能帮助我吗 import sys import serial from PyQt5.QtWidgets import QMainWindow, QApplication from PyQt5.QtCore import QThread, pyqt

我写了一个程序来发送和接收串行数据,但我有一个问题,我想创建一个函数“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()

与其使用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_())

我使用了上面重新设计的代码,因此它有一个
主窗口
,带有
菜单栏
状态栏
。我还添加了QSerialPortInfo类。此版本将查找活动端口并在状态栏上显示它们

我只在RPI4和Windows10上测试过这个

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):
        selv.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_())
导入系统 从PyQt5导入QtCore、QtWidgets、QtSerialPort 从PyQt5.qtwidts导入QApplication、QMainWindow、QWidget、QToolBar、QHBoxLayout、QAction、QStatusBar、QLineEdit、QPushButton、qtexedit、QVBoxLayout 从PyQt5.QtCore导入Qt,pyqtSignal 从PyQt5.QtSerialPort导入QSerialPortInfo 类AddComport(QMainWindow): porttnavn=pyqtSignal(str) 定义初始化(自、父、菜单): super()。\uuuu init\uuuu(父级) menuComporte=menu.addMenu(“Comporte”) info_list=QSerialPortInfo() serial\u list=info\u list.availableport() serial_ports=[port.portName()表示串行_列表中的端口] 如果(len(串行_端口)>0): antalporte=len(串行端口) antal=0 而安塔尔<安塔尔波特: 按钮动作=QAction(串行端口[antal],自身) txt=串行端口[antal] portinfo=QSerialPortInfo(txt) buttoninfotxt=“Ingen informationer” 如果portinfo.hasProductIdentifier(): buttoninfotxt=(“产品规范=“+str(portinfo.vendoriIdentifier())) 如果portinfo.hasVendorIdentifier(): buttoninfotxt=buttoninfotxt+(“Fremstillers id=“+str(portinfo.productIdentifier())) 按钮动作=QAction(txt,自我) button_action.setStatusIP(buttoninfotxt) 按钮\u action.triggered.connect(选中lambda,txt=txt:self.valgAfComportClick(txt)) menuComporte.addAction(按钮操作) antal=antal+1 其他: 打印(“Ingen com porte fundet”) def VALGAFCOMPORT单击(自身,端口): self.porttnavn.emit(端口) def关闭事件(自身、事件): selv.close() 类主窗口(QMainWindow): 定义初始化(自): 超级(主窗口,自我)。\uuuu初始化 portname=“无” self.setStatusBar(QStatusBar(self)) menu=self.menuBar() comfinder=AddComport(自我,菜单) comfinder.porttnavn.connect(self.valgAfComport) self.setWindowTitle(“串口显示/发送”) self.message_le=QLineEdit() self.send\u btn=QPushButton( text=“发送”, 单击=self.send ) self.output_te=QTextEdit(readOnly=True) self.button=QPushButton( text=“连接”, 可检查=正确, toggled=self.on\u切换 ) lay=QVBoxLayout(自身) hlay=QHBoxLayout() hlay.addWidget(self.message_le) hlay.addWidget(self.send\u btn) 布局。添加布局(hlay) lay.addWidget(自输出) lay.addWidget(self.button) widget=QWidget() widget.setLayout(lay) self.setCentralWidget(小部件) self.serial=QtSerialPort.QSerialPort( portname, 波特率=QtSerialPort.QSerialPort.Baud9600, readyRead=self.receive) @QtCore.pyqtSlot() def接收(自我): 而self.serial.canReadLine() text=self.serial.readLine().data().decode() text=text.rstrip('\r\n') self.output_te.append(文本) @QtCore.pyqtSlot() def发送(自我): self.serial.write(self.message_.le.text().encode()) @QtCore.pyqtlot(bool) def on_已切换(自检、已检查): self.button.setText(“断开”如果选中,否则“连接”) 如果选中: 如果不是self.serial.isOpen(): self.serial.open(QtCore.QIODevice.ReadWrite) 如果不是self.serial.isOpen(): self.button.setChecked(False) 其他: self.button.setChecked(False) 其他: self.serial.close() def valgAfComport(自身,nyport): seropen=False 如果self.serial.isOpen(): seropen=True self.serial.close() self.serial.setPortName(nyport) 如果是seropen: self.serial.open(QtCore.QIODevice.ReadWrite) 如果不是self.serial.isOpen(): self.button.setChecked(False) 打印(nyport) def关闭事件(自身、事件): self.serial.close() 印刷品(“康波特卢克特”) 打印(comporttxt) 如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu': app=qtwidts.QApplication(sys.argv) w=主窗口() w、 show() sys.exit(app.exec_())
解释你自己better@eyllanesc好的,当我按下一个按钮时,我想连接到Arduino(打开串行),当我按下另一个按钮时关闭连接(关闭串行),但我不知道如何实现这一点非常感谢,我将使用您的代码,但为了我的和平,可以从“主窗口”的按钮连接到串行上课?同时,我的“TestThread”类运行良好。。你能给我举个例子吗?