所有线程都不是 运行 QThread 以及 QtSerialPort(对于 Arduino)和 Matplotlib

All threads are not running with QThread along with QtSerialPort (for Arduino) and Matplotlib

我正在尝试从 arduino 获取输出以在两个 pyqt windows 中更新,其中一个是 matplotlib 图。如果我注释掉非情节线索 window,情节就会奏效。但是当我同时尝试 运行 时,我收到消息

3 Device is already open
3 Device is already open

但情节中没有显示任何内容,尽管 window 在那里。而非情节 window 更新了值。我不确定是否应该使用 QThreadpool 而不是 QThread。另外,如果我尝试重新 运行 代码,我会收到消息

2 Access is denied.
2 Access is denied.
2 Access is denied.

所以我必须拔下并重新插入 arduino,再次上传它的代码,然后 运行 再次调用 python 脚本,window 都没有更新值。 我使用的 arduino 代码是

int PinOutput = 13;
int PinInput = A0;
int inph;
int inpl;

void setup() {
  // put your setup code here, to run once:
Serial.begin(9600);
pinMode(PinInput, INPUT);
pinMode(PinOutput, OUTPUT);

}

void loop() {
  // put your main code here, to run repeatedly:
inpl = analogRead(PinInput)/4;
Serial.println(inpl);
analogWrite(PinOutput,255);
delay(1000);
inph = analogRead(PinInput)/4;
Serial.println(inph);
analogWrite(PinOutput,0);
delay(1000);
}

其中引脚 13 连接到 arduino 上的 A0。 python代码是

import sys

from PyQt5 import QtCore,  QtWidgets, QtSerialPort

from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg

seril_port_connection = QtSerialPort.QSerialPort("COM3")
seril_port_connection.setBaudRate(QtSerialPort.QSerialPort.Baud9600)

class MyThread2(QtCore.QThread):
    ard_signal = QtCore.pyqtSignal(str)

    
    def __init__(self):
        QtCore.QThread.__init__(self)
        self.serial_port = seril_port_connection
        self.serial_port.errorOccurred.connect(self.handle_error)
        self.serial_port.readyRead.connect(self.run)
        self.serial_port.open(QtCore.QIODevice.ReadWrite)
        
    def run(self):

        while self.serial_port.canReadLine():
            line = self.serial_port.readLine().data().decode().strip().strip('\x00')

            try:
                self.ard_signal.emit(str(line))

            except ValueError as e:
                print("error", e)
        
    def handle_error(self, error):
        if error == QtSerialPort.QSerialPort.NoError:
            return
        print(error, self.serial_port.errorString())

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(325, 237)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(110, 20, 61, 16))
        self.label.setObjectName("label")
        self.textEdit = QtWidgets.QTextEdit(self.centralwidget)
        self.textEdit.setGeometry(QtCore.QRect(90, 60, 104, 71))
        self.textEdit.setObjectName("textEdit")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(100, 150, 75, 23))
        self.pushButton.setObjectName("pushButton")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 325, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)


    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "test window"))
        self.label.setText(_translate("MainWindow", "pyqt5 tests"))
        self.pushButton.setText(_translate("MainWindow", "test button"))
        self.pushButton.clicked.connect(self.label_change)  
        self.thread_start = MyThread()
        self.thread_start.ard_signal.connect(self.label.setText)        
        self.thread_start.start()
    
  
    def label_change(self):
        self.pushButton.setText('Button Clicked!')
        self.textEdit.setText('taco')

class MainWindowm(QtWidgets.QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindowm, self).__init__(*args, **kwargs)

        self.canvas = FigureCanvasQTAgg(Figure(figsize=(5, 4), dpi=100))
        self.setCentralWidget(self.canvas)

        self.axes = self.canvas.figure.subplots()

        n_data = 10
        self.xdata = list(range(n_data))
        self.ydata = [0 for i in range(n_data)]

        self.thread_start = MyThread2()
        self.thread_start.ard_signal.connect(self.update_plot)
        self.thread_start.start()
        
    def handle_error(self, error):
        if error == QtSerialPort.QSerialPort.NoError:
            return
        print(error, self.serial_port.errorString())

    def update_plot(self, value):
        self.ydata = self.ydata[1:] + [float(value)]
        self.axes.cla()
        self.axes.plot(self.xdata, self.ydata, "r")
        self.canvas.draw()

class MyThread(QtCore.QThread):
    ard_signal = QtCore.pyqtSignal(str)

    
    def __init__(self):
        QtCore.QThread.__init__(self)
        self.serial_port = seril_port_connection
        self.serial_port.errorOccurred.connect(self.handle_error)
        self.serial_port.readyRead.connect(self.run)
        self.serial_port.open(QtCore.QIODevice.ReadWrite)
        
    def run(self):

        while self.serial_port.canReadLine():
            line = self.serial_port.readLine().data().decode().strip().strip('\x00')
            try:
                self.ard_signal.emit(str(line))
            except ValueError as e:
                print("error", e)
        
    def handle_error(self, error):
        if error == QtSerialPort.QSerialPort.NoError:
            return
        print(error, self.serial_port.errorString())


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    w = MainWindowm()
    w.show()

    sys.exit(app.exec_())

所以我试图让 arduino 输出在全球范围内可用,然后被不同的线程同时使用,在两个 windows 中更新,一个带有绘图。任何有关如何执行此操作的信息都会有所帮助,谢谢。

我假设 OP 正在使用 my old answer,并且在该解决方案中表明 QSerialPort 的使用避免了不必要的线程使用,这也适用于这种情况。另一方面,一个串行端口不能由多个 classes 处理。在这种情况下,您只需创建一个 class 来管理串行端口并将信息分发给其他元素。

import sys

from PyQt5 import QtCore, QtWidgets, QtSerialPort

from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(325, 237)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(110, 20, 61, 16))
        self.label.setObjectName("label")
        self.textEdit = QtWidgets.QTextEdit(self.centralwidget)
        self.textEdit.setGeometry(QtCore.QRect(90, 60, 104, 71))
        self.textEdit.setObjectName("textEdit")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(100, 150, 75, 23))
        self.pushButton.setObjectName("pushButton")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 325, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "test window"))
        self.label.setText(_translate("MainWindow", "pyqt5 tests"))
        self.pushButton.setText(_translate("MainWindow", "test button"))


class MainWindowm(QtWidgets.QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindowm, self).__init__(*args, **kwargs)

        self.canvas = FigureCanvasQTAgg(Figure(figsize=(5, 4), dpi=100))
        self.setCentralWidget(self.canvas)

        self.axes = self.canvas.figure.subplots()

        n_data = 10
        self.xdata = list(range(n_data))
        self.ydata = [0 for i in range(n_data)]

    def update_plot(self, value):
        self.ydata = self.ydata[1:] + [value]
        self.axes.cla()
        self.axes.plot(self.xdata, self.ydata, "r")
        self.canvas.draw()


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupUi(self)

    def update_text(self, value):
        self.label.setNum(value)
        self.label.adjustSize()


class SerialManager(QtCore.QObject):
    valueChanged = QtCore.pyqtSignal(float)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.serial_port = QtSerialPort.QSerialPort("COM3")
        self.serial_port.setBaudRate(QtSerialPort.QSerialPort.Baud9600)
        self.serial_port.errorOccurred.connect(self.handle_error)
        self.serial_port.readyRead.connect(self.handle_ready_read)
        self.serial_port.open(QtCore.QIODevice.ReadWrite)

    def handle_ready_read(self):
        while self.serial_port.canReadLine():
            codec = QtCore.QTextCodec.codecForName("UTF-8")
            line = codec.toUnicode(self.serial_port.readLine()).strip().strip("\x00")
            try:
                print(line)
                value = float(line)
            except ValueError as e:
                print("error", e)
            else:
                self.valueChanged.emit(value)

    def handle_error(self, error):
        if error == QtSerialPort.QSerialPort.NoError:
            return
        print(error, self.serial_port.errorString())


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    w1 = MainWindow()
    w1.show()

    w = MainWindowm()
    w.show()

    manager = SerialManager()
    manager.valueChanged.connect(w1.update_text)
    manager.valueChanged.connect(w.update_plot)

    sys.exit(app.exec_())