You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Worker1线程中调用serialSend函数未执行问题求助

问题解决:PyQt5线程中调用主窗口串口发送方法无响应

核心原因

  1. 线程安全违规:PyQt明确规定非UI线程不能直接调用UI线程创建的对象方法,serial对象在CustomMainWindow(UI线程)中初始化,Worker1线程直接调用serialSend会导致方法无法正常执行,甚至触发线程冲突。
  2. 全局变量引用无效:线程启动时mainWindow可能未完成初始化,直接引用全局变量会导致无效调用。

标准解决方案:信号槽通信

利用PyQt的信号槽机制,让线程发送数据信号,主窗口在UI线程中接收并执行串口发送操作,这是线程与主界面通信的规范方式,完全符合线程安全要求。

具体修改步骤

  1. 为Worker1新增串口发送请求信号:
class Worker1(QThread):
    ImageUpdate = pyqtSignal(QImage)
    # 定义信号,用于传递需要发送的串口数据列表
    SerialSendRequest = pyqtSignal(list)
  1. 在主窗口中连接信号与串口发送方法:
class CustomMainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)

        self.Worker = Worker1()
        # 连接Worker的串口发送请求信号到主窗口的serialSend方法
        self.Worker.SerialSendRequest.connect(self.serialSend)
        self.Worker.start()

        self.Worker.ImageUpdate.connect(self.ImageUpdateSlot)
  1. 替换线程中的直接调用为发送信号:
# Worker1的run方法中,替换原有的mainWindow.serialSend调用
if iSee == True:
    self.SerialSendRequest.emit([2, controlXY])

修改后的完整代码

from GoodNessMain import *
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtSerialPort import QSerialPort, QSerialPortInfo
import sys
import cv2
import math


class CustomMainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)

        self.Worker = Worker1()
        # 新增信号连接
        self.Worker.SerialSendRequest.connect(self.serialSend)
        self.Worker.start()

        self.Worker.ImageUpdate.connect(self.ImageUpdateSlot)

        # ------------------------- SerialPort ------------------------- #
        self.serial = QSerialPort()
        self.serial.setBaudRate(115200)
        self.serial.readyRead.connect(self.onRead)
        portList = []
        ports = QSerialPortInfo().availablePorts()
        for port in ports:
            portList.append(port.portName())
        self.comL.addItems(portList)
        for i, port in enumerate(portList):
            comL = self.menuPortList.addAction(str(port))
            comL.triggered.connect(self.onOpen)
            if i >= 2:
                break
        self.actionOpenPort.triggered.connect(self.onOpen)
        self.actionClosePort.triggered.connect(self.onClose)
        self.actionReset.triggered.connect(self.onReset)
        # ------------------------- SerialPort ------------------------- #

        self.speedSlider.valueChanged.connect(self.dSpeed)
        self.servoSlider.valueChanged.connect(self.dServo)
        self.ledC.stateChanged.connect(self.ledControl)
        self.QR_Forward.clicked.connect(self.dFORWARD)
        self.QR_Stop.clicked.connect(self.dSTOP)
        self.QR_Back.clicked.connect(self.dBACKWARD)

    def onReset(self):
        portlist = []
        ports = QSerialPortInfo().availablePorts()
        for port in ports:
            portlist.append(port.portName())
        self.comL.clear()
        self.comL.addItems(portlist)
        self.menuPortList.clear()
        for i, port in enumerate(portlist):
            comL = self.menuPortList.addAction(str(port))
            comL.triggered.connect(self.onOpen)
            if i >= 2:
                break
    def onOpen(self):
        self.serial.setPortName(self.comL.currentText())
        self.serial.open(QIODevice.ReadWrite)
    def onClose(self):
        self.serial.close()
    def onRead(self):
        if not self.serial.canReadLine():
            return
        rx = self.serial.readLine()
        rxs = str(rx, 'utf-8').strip()
        data = rxs.split(',')
        if data[0] == '0':
            self.temperaturL01.display(data[1])
            self.temperaturL02.display(data[2])
    def serialSend(self, data):
        txs = ""
        for val in data:
            txs += str(val)
            txs += ','
        txs = txs[:-1]
        txs += ';'
        self.serial.write(txs.encode())
    def ledControl(self, val):
        if val == 2: val = 1
        self.serialSend([0, val])
    def dSpeed(self, val):
        self.serialSend([1, self.speedSlider.value()])
    def dServo(self, val):
        self.serialSend([2, self.servoSlider.value()])

    def dFORWARD(self):
        self.serialSend([3, 1])

    def dSTOP(self):
        self.serialSend([4, 1])
    def dBACKWARD(self):
        self.serialSend([5, 1])
    def ImageUpdateSlot(self, Image):
        self.QTcamera.setPixmap(QPixmap.fromImage(Image))

class Worker1(QThread):
    ImageUpdate = pyqtSignal(QImage)
    # 新增串口发送请求信号
    SerialSendRequest = pyqtSignal(list)

    def run(self):
        self.ThreadActive = True
        Capture = cv2.VideoCapture(0)
        while self.ThreadActive:
            iSee = False
            controlXY = 0
            success, frame = Capture.read()
            if success:
                height, width = frame.shape[0:2]
                hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
                binary = cv2.inRange(hsv, (0, 100, 250), (20, 255, 255))
                roi = cv2.bitwise_and(frame, frame, mask=binary)
                contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
                if len(contours) != 0:
                    maxcont = max(contours, key=cv2.contourArea)
                    moments = cv2.moments(maxcont)
                    if moments["m00"] > 20:
                        cx = int(moments["m10"] / moments["m00"])
                        cy = int(moments["m01"] / moments["m00"])
                        iSee = True
                        controlXY = 100 * (cx - width / 2) / width
                        cv2.drawContours(frame, maxcont, -1, (0, 255, 0), 2)
                        cv2.line(frame, (cx, 0), (cx, height), (0, 0, 255), 6)
                        cv2.line(frame, (0, cy), (width, cy), (0, 255, 0), 6)
                cv2.putText(frame, 'iSee: {};'.format(iSee), (width - 370, height - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.4,
                            (255, 0, 0), 1, cv2.LINE_AA)
                cv2.putText(frame, 'controlX: {:.2f}'.format(controlXY), (width - 200, height - 10),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 0, 0), 1, cv2.LINE_AA)

                if iSee == True:
                    # 替换直接调用,发送信号
                    self.SerialSendRequest.emit([2, controlXY])

            ConvertToQtFormat = QImage(frame.data, frame.shape[1], frame.shape[0], QImage.Format_BGR888)
            Pic = ConvertToQtFormat.scaled(800, 600, Qt.KeepAspectRatio)
            self.ImageUpdate.emit(Pic)

    def stop(self):
        self.ThreadActive = False
        self.quit()


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    print(f"Класс Worker имеет{Worker1.__dict__}")
    print(f"Класс CustomMainWindow имеет{CustomMainWindow.__dict__}")

    mainWindow = CustomMainWindow()
    mainWindow.show()
    sys.exit(app.exec_())

额外修正:调整了dFORWARD等按钮点击方法的参数定义,避免原代码中信号传递参数不匹配导致的报错。

内容的提问来源于stack exchange,提问作者Rauf 44

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 15:00:54