如何在PyQt5GUI中运行两个无限循环?

2022-02-23 00:00:00 python pyqt5

问题描述

我正在尝试使用pyqt5构建GUI。在图形用户界面中,我需要通过WiFi从树莓圆周率摄像头流传输一段视频。我需要向树莓派发送游戏板输入。我的代码如下:

import sys,time,math
import cv2
import numpy as np
from PyQt5 import QtCore,QtGui,QtWidgets
from PyQt5 import uic
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtGui import QImage,QPixmap
from PyQt5.QtWidgets import QDialog,QApplication
import urllib.request
import threading
import paho.mqtt.client as mqtt
import inputs

m1="0"
broker_ip="computerIP"
url="http://raspberrypiIP/html/cam_pic.php"

#this function get raspberry pi camera video to laptop via WiFi

def url_to_image(url): 
with urllib.request.urlopen(url) as resp:
    image = np.asarray(bytearray(resp.read()),dtype ="uint8")
    image = cv2.imdecode(image,cv2.IMREAD_COLOR)        
return image

def on_connect(client, userdata, flags, rc):
   client.subscribe("arm")
 
def on_message(client, userdata, msg):
    print (msg.payload)

#mqtt is used to send data from laptop to raspberrypi
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect(broker_ip, 1883, 60)
threading.Thread(target=client.loop_forever).start()

def Stop1():
    m1="0,0,0,0"
    client.publish("arm",str(m1))
def Forward1():
    m1="0,0,100,100"
    client.publish("arm",str(m1))
def Reverse1():
    m1="0,0,-100,-100"
    client.publish("arm",str(m1))

def cnsl(): # this function detect the gamepad input
    while True:
        events=inputs.get_gamepad()
        for event in events:
            if event.code=="ABS_Y" and event.state>1000:
                Forward1()
            elif event.code=="ABS_Y" and event.state<-1000:
                Reverse1()
            elif event.code=="ABS_Y" and event.state==128:
                Stop1()
      
class test1(QtWidgets.QMainWindow):

    def __init__(self,*args,**kwargs):
        super().__init__(*args,**kwargs)
        uic.loadUi("test1.ui",self)
    
    def slot1(self): # this function runs when Pushbutton is clicked
        threadVideo=threading.Thread(target=self.onClicked())
        threadConsol=threading.Thread(target=self.cnsl())
        threadVideo.start()
        threadConsol.start()
        threadVideo.join()
        threadConsol.join()
     
    @pyqtSlot()
# this function display the raspberrypi video in the buited GUI
    def onClicked(self): 
    print ("video")
    while (True):
        img=url_to_image(url)
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        qformat=QImage.Format_Indexed8
        if len(img.shape)==3:
            if (img.shape[2])==4:
                qformat=QImage.Format_RGBA888
            else:
                qformat=QImage.Format_RGB888 
        img=QImage(img,img.shape[1],img.shape[0],qformat)
        img=img.rgbSwapped()
        self.label_3.setPixmap(QPixmap.fromImage(img))
          
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

app=QtWidgets.QApplication(sys.argv)
window=test1()
window.show()
app.exec_()

当我运行此代码时,GUI中显示视频流。但我不能发送游戏板输入。我也试过用两个纽扣。但这两个按钮并不同时工作。有谁知道吗?


解决方案

在操作码中有一些小错误,如:

threading.Thread(target=foo())

因为它相当于:

res = foo()
threading.Thread(target=res)

如您所见,任务不是在辅助线程中执行,而是在初始线程中执行。

另一个错误是使用join(),因为此方法会阻塞初始线程的执行,直到辅助线程中执行的线程结束,而这显然不是您想要的。

另一个小错误是,不需要在辅助线程中使用LOOP_EVERVER方法,只需执行start()函数即可。

此外,还应考虑不应从另一个线程更新GUI,而应通过信号将信息发送到主线程(其中必须更新GUI)。

综合以上考虑,解决方案为:

import sys
import threading

from PyQt5 import QtCore, QtGui, QtWidgets, uic
import sip

import paho.mqtt.client as mqtt

import inputs
import cv2
import numpy as np
import urllib.request


broker_ip = "computerIP"
url = "http://raspberrypiIP/html/cam_pic.php"


def url_to_image(url):
    with urllib.request.urlopen(url) as resp:
        image = np.asarray(bytearray(resp.read()), dtype="uint8")
        image = cv2.imdecode(image, cv2.IMREAD_COLOR)
    return image


class GamepadManager:
    def __init__(self):
        self.client = mqtt.Client()
        self.client.on_connect = self.on_connect
        self.client.on_message = self.on_message
        self.client.connect(broker_ip, 1883, 60)

    def start(self):
        self.client.start()
        threading.Thread(target=self._init_bucle, daemon=True).start()

    def on_connect(self, client, userdata, flags, rc):
        self.client.subscribe("arm")

    def on_message(self, client, userdata, msg):
        print(msg.payload)

    def stop_arm(self):
        m1 = "0,0,0,0"
        self.client.publish("arm", m1)

    def forward_arm(self):
        m1 = "0,0,100,100"
        self.client.publish("arm", m1)

    def reverse_arm(self):
        m1 = "0,0,-100,-100"
        self.client.publish("arm", m1)

    def _init_bucle(self):
        while True:
            events = inputs.get_gamepad()
            for event in events:
                if event.code == "ABS_Y" and event.state > 1000:
                    self.forward_arm()
                elif event.code == "ABS_Y" and event.state < -1000:
                    self.reverse_arm()
                elif event.code == "ABS_Y" and event.state == 128:
                    self.stop_arm()


class VideoManager(QtCore.QObject):
    imageChanged = QtCore.pyqtSignal(QtGui.QImage)

    def start(self):
        threading.Thread(target=self._request_video, daemon=True).start()

    def _request_video(self):
        while True:
            img = url_to_image(url)
            qformat = QtGui.QImage.Format_Indexed8
            if len(img.shape) == 3:
                if (img.shape[2]) == 4:
                    qformat = QtGui.QImage.Format_RGBA888
                else:
                    qformat = QtGui.QImage.Format_RGB888
            img = QtGui.QImage(img, img.shape[1], img.shape[0], qformat)
            img = img.rgbSwapped()
            if not sip.isdeleted(self):
                self.imageChanged.emit(img)


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        uic.loadUi("test1.ui", self)

    @QtCore.pyqtSlot(QtGui.QImage)
    def update_label(self, image):
        pixmap = QtGui.QPixmap.fromImage(image)
        self.label_3.setPixmap(pixmap)


def main():
    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()

    gamepad_manager = GamepadManager()
    video_manager = VideoManager()

    video_manager.imageChanged.connect(w.update_label)

    gamepad_manager.start()
    video_manager.start()

    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

相关文章