Reputation: 23
I`m trying to built a GUI using pyqt5. In the GUI I need to stream a video from the raspberry pi camera via WiFi. And I need to send game-pad inputs to raspberry pi. my code as follows:
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_()
When I run this code, video stream display in the GUI. But I cannot send game-pad inputs. I have tried with 2 buttons also. But both buttons did not work simultaneously. Is any one have any idea??
Upvotes: 2
Views: 431
Reputation: 243897
In the OP code there are trivial errors like:
threading.Thread(target=foo())
since it is equivalent to:
res = foo()
threading.Thread(target=res)
and as you can see the task is not executed in the secondary thread but in the initial thread.
Another mistake is to use join() since this method blocks the execution of the initial thread until what is executed in the secondary thread ends, and obviously that is not what you want.
And another minor error is that it is not necessary to use the loop_forever method in a secondary thread, but just execute the start() function.
In addition, it should be considered that the GUI should not be updated from another thread but send the information to the main thread (where the GUI must be updated) through signals.
Considering the above, the solution is:
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()
Upvotes: 2