Multithreading PyQt5将第二个函数添加到线程但不工作

Multithreading PyQt5将第二个函数添加到线程但不工作,multithreading,pyqt5,Multithreading,Pyqt5,之前,我尝试使用烧瓶同时执行以下操作: 显示实时视频流 显示实时数据流 控制机器人车 由于以上只是演示,视频流性能不够好,我决定将整个应用程序更改为PyQt5,以便进一步开发和生产。现在我可以创建GUI来很好地显示实时视频流,而实时数据流无法很好地完成。错误是 QObject::startTimer:计时器只能用于以QThread启动的线程 以下是整个节目。请帮助查看添加线程问题中的错误。谢谢 import sys from PyQt5 import QtWidgets, QtCore, Q

之前,我尝试使用烧瓶同时执行以下操作:

  • 显示实时视频流
  • 显示实时数据流
  • 控制机器人车
由于以上只是演示,视频流性能不够好,我决定将整个应用程序更改为PyQt5,以便进一步开发和生产。现在我可以创建GUI来很好地显示实时视频流,而实时数据流无法很好地完成。错误是

QObject::startTimer:计时器只能用于以QThread启动的线程

以下是整个节目。请帮助查看添加线程问题中的错误。谢谢

import sys
from PyQt5 import QtWidgets, QtCore, QtGui
import cv2
from vidgear.gears import CamGear
from random import random
data_list=[]

fps=60
options_cam={"CAP_PROP_FRAME_WIDTH":640,"CAP_PROP_FRAME_HEIGHT":480,"CAP_PROP_FPS":fps}
stream=CamGear(source=0,logging=False,**options_cam).start()

class MainWindow(QtWidgets.QWidget):
    def __init__(self,*args):
        super(MainWindow, self).__init__()
        self.setWindowTitle('Vehicle control')
        self.grid_layout=QtWidgets.QGridLayout()
        self.video_label = QtWidgets.QLabel('Video streaming',self)
        self.video_frame = QtWidgets.QLabel(self)
        self.grid_layout.addWidget(self.video_label,0,0)
        self.grid_layout.addWidget(self.video_frame,1,0)

        self.data_label = QtWidgets.QLabel('Data streaming',self)
        self.data_frame = QtWidgets.QListWidget(self)
        self.grid_layout.addWidget(self.data_label,0,1)
        self.grid_layout.addWidget(self.data_frame,1,1)
        self.setLayout(self.grid_layout)

        #self.thread=QtCore.QThread()
        #self.thread.started.connect(self.nextFrameSlot)
        #self.thread.start()

        self.timer=QtCore.QTimer()
        self.timer.timeout.connect(self.video_stream)
        self.timer.start(0)

        self.thread=QtCore.QThread()
        self.thread.start()

        self.timer2=QtCore.QTimer()
        self.timer2.moveToThread(self.thread)
        self.timer2.timeout.connect(self.data_stream)
        self.timer2.start(0)

    def video_stream(self):
        frame = stream.read()

        # My webcam yields frames in BGR format
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        img = QtGui.QImage(frame, frame.shape[1], frame.shape[0], QtGui.QImage.Format_RGB888)
        pix = QtGui.QPixmap.fromImage(img)
        self.video_frame.setPixmap(pix)
        QtCore.QThread.sleep(0)

    def data_stream(self):
        print("data stream")
        stream_data=round(random()*10,3)
        data_list.insert(0,str(stream_data)+'\n')
        if len(data_list)>10:
            del data_list[-1]
        for i in range(len(data_list)):
            self.data_frame.addItem(data_list[i])
        self.data_frame.show()
        QtCore.QThread.sleep(1000)

if __name__ == '__main__':

    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())
编辑: 谢谢@musicamante的回答。我已经按照如下方式更新了代码,但是对于视频流仍然存在错误“segmentation fault”,而如果我只针对数据流运行,则可以显示更新的列表。那么setPixmap函数有什么问题呢?再次感谢

import sys
from PyQt5 import QtWidgets, QtCore, QtGui
import cv2
from vidgear.gears import CamGear
from random import random

fps=60
options_cam={"CAP_PROP_FRAME_WIDTH":480,"CAP_PROP_FRAME_HEIGHT":480,"CAP_PROP_FPS":fps}
stream=CamGear(source=0,logging=False,**options_cam).start()

class CamGrabber(QtCore.QThread):
    frame = QtCore.pyqtSignal(QtGui.QImage)

    def run(self):
        while True:
            new_frame = stream.read()
            new_frame = cv2.cvtColor(new_frame, cv2.COLOR_BGR2RGB)
            img = QtGui.QImage(new_frame, new_frame.shape[1], new_frame.shape[0], QtGui.QImage.Format_RGB888)
            self.frame.emit(img)

class DataProvider(QtCore.QThread):
    data = QtCore.pyqtSignal(object)

    def run(self):
        while True:
            newData = round(random()*10,3)
            self.data.emit(newData)
            QtCore.QThread.sleep(1)

class MainWindow(QtWidgets.QWidget):
    def __init__(self,*args):
        super(MainWindow, self).__init__()
        self.setWindowTitle('Vehicle control')
        self.grid_layout=QtWidgets.QGridLayout()
        self.video_label = QtWidgets.QLabel('Video streaming',self)
        self.video_frame = QtWidgets.QLabel(self)
        self.grid_layout.addWidget(self.video_label,0,0)
        self.grid_layout.addWidget(self.video_frame,1,0)

        self.data_label = QtWidgets.QLabel('Data streaming',self)
        self.data_frame = QtWidgets.QListWidget(self)
        self.grid_layout.addWidget(self.data_label,0,1)
        self.grid_layout.addWidget(self.data_frame,1,1)
        self.setLayout(self.grid_layout)

        self.camObject = CamGrabber()
        self.camObject.frame.connect(self.newFrame)
        self.camObject.start()

        self.dataProvider = DataProvider()
        self.dataProvider.data.connect(self.newData)
        self.dataProvider.start()

    def newFrame(self, img):
        self.video_frame.setPixmap(QtGui.QPixmap.fromImage(img))

    def newData(self, data):
        self.data_frame.insertItem(0,str(data))
        if self.data_frame.count() > 10:
            self.data_frame.takeItem(9)

if __name__ == '__main__':

    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())```

QTimer错误基本上意味着QTimer只能从其存在的线程启动

除此之外,GUI元素应始终直接从主线程访问或修改,而不是从另一个线程访问或修改。
为了实现这一点,您需要创建一个单独的“工作线程”,并利用信号/插槽机制与主线程通信

class CamGrabber(QtCore.QThread):
    frame = QtCore.pyqtSignal(QtGui.QImage)
    def run(self):
        while True:
            frame = stream.read()
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            img = QtGui.QImage(frame, frame.shape[1], frame.shape[0], QtGui.QImage.Format_RGB888)
            self.frame.emit(img)


class DataProvider(QtCore.QThread):
    data = QtCore.pyqtSignal(object)

    def run(self):
        while True:
            newData = round(random()*10,3)
            self.data.emit(newData)
            # note that QThread.sleep unit is seconds, not milliseconds
            QtCore.QThread.sleep(1)


class MainWindow(QtWidgets.QWidget):
    def __init__(self,*args):
        super(MainWindow, self).__init__()
        self.setWindowTitle('Vehicle control')
        self.grid_layout=QtWidgets.QGridLayout()
        self.video_label = QtWidgets.QLabel('Video streaming',self)
        self.video_frame = QtWidgets.QLabel(self)
        self.grid_layout.addWidget(self.video_label,0,0)
        self.grid_layout.addWidget(self.video_frame,1,0)

        self.data_label = QtWidgets.QLabel('Data streaming',self)
        self.data_frame = QtWidgets.QListWidget(self)
        self.grid_layout.addWidget(self.data_label,0,1)
        self.grid_layout.addWidget(self.data_frame,1,1)
        self.setLayout(self.grid_layout)

        self.camObject = CamGrabber()
        self.camObject.frame.connect(self.newFrame)
        self.camObject.start()

        self.dataProvider = DataProvider()
        self.dataProvider.data.connect(self.newData)
        self.dataProvider.start()

    def newFrame(self, img):
        self.video_frame.setPixmap(QtGui.QPixmap.fromImage(img))

    def newData(self, data):
        self.data_frame.addItem(str(data))
        if self.data_frame.count() > 10:
            self.data_frame.takeItem(0)
如果出于任何原因,您希望控制通过QTimer从主线程获取数据,则可以使用队列:

from queue import Queue

class DataProvider(QtCore.QObject):
    data = QtCore.pyqtSignal(object)

    def __init__(self):
        super().__init__()
        self.queue = Queue()

    def run(self):
        while True:
            multi = self.queue.get()
            # simulate a time consuming process
            QtCore.QThread.sleep(5)
            newData = round(multi * 10, 3)
            self.data.emit(newData)

    def pushData(self, data):
        self.queue.put(data)


class MainWindow(QtWidgets.QWidget):
    def __init__(self,*args):
        # ...
        self.requestTimer = QtCore.QTimer()
        self.requestTimer.setInterval(1000)
        self.requestTimer.timeout.connect(self.requestData)
        # this will cause the timer to be executed only once after each time
        # start() is called, so no new requests will overlap
        self.requestTimer.setSingleShot(True)
        self.requestTimer.start()

    def requestData(self):
        value = random()
        print('requesting data with value {}'.format(value))
        self.dataProvider.pushData(value)
        print('waiting for result')

    def newFrame(self, img):
        self.video_frame.setPixmap(QtGui.QPixmap.fromImage(img))

    def newData(self, data):
        print('data received')
        self.data_frame.addItem(str(data))
        if self.data_frame.count() > 10:
            self.data_frame.takeItem(0)
        # restart the timer
        self.requestTimer.start()

首先谢谢你的回答。但在我的代码中实现它时,发现对于数据流,它可以工作,而对于视频流,它不能与错误消息“segmentation fault”一起工作。检查后,我发现错误应该来自
setPixmap(QtGui.QPixmap.fromImage(img)
,但仍然无法修复。那么我能做什么?再次感谢!@mckingwan我不确定,因为我无法使用您的库对其进行测试,但请尝试为设置pixmap的函数设置pyqtSlot修饰符:
@QtCore.pyqtSlot(QtGui.QImage)
。我已经试过了,但还是一样。虽然我看到视频流有时只能进行一秒钟,然后显示错误。我不知道CamGear,我相信
read()
应该等待可用的数据,但可能没有。我稍后会进行一些搜索。不要紧,我发现了这里的错误:视频流的分辨率应该缩小。最后我有以下设置:在选项中,我将分辨率设置为1280x720,然后在执行
self.frame.emit(img)之前
我添加了行
img=img.scaled(640480)
来修复它。无论如何,感谢您的大力帮助!