'How to use Threading correctly with a GUI and Threads in my Case
I use a 2 Thread Webcam Stream Class to show a Live Stream on my GUI. (As an Overlay, I didn't find an other Way with high FPS) And I use a GUI Function for my GUI. (No Class yet, because i am not good in classes)
I need to join() the 2 Stream Threads, because they depend on an Event. So I could speed up the Stream, I think.
How can I join() them without blocking my "Mainthread" with the GUI ?
Using the GUI as Daemon didn't work.
May be I can split the Threads in to different Pools in some Way, to be able to use join()?
I would be happy if Someone could help me to understand what to to.
Here is my minimalistic Code:
import sys
sys.path.append('C:\\Users\\User\\Python\\pyproj\\project2')
sys.path.append('C:\\Users\\User\\Python\\pyproj\\project1\\Lib\\site-packages\\paho_mqtt-1.6.1-py3.10.egg')
sys.path.append('C:\\Users\\User\\Python\\modules\\')
sys.path.append('C:\\Users\\User\\Python\\pyproj\\project1\\Lib\\site-packages\\pyparsing-3.0.7-py3.10.egg')
sys.path.append('C:\\Users\\User\\Python\\pyproj\\project1\\Lib\\site-packages\\packaging-21.3-py3.10.egg')
sys.path.append('C:\\Users\\User\\Python\\Medien\\Bilder')
from imutils.video import FPS
import threading
from threading import Thread
from threading import Event
import numpy as np
import argparse
import imutils
from videoclasses import FPS
from videoclasses import WebcamVideoStream
import cv2
import tkinter as tk
import time
if sys.version_info >= (3, 0):
from queue import Queue
else:
from Queue import Queue
import PIL.Image as Image
import PIL.ImageTk as ImageTk
import logging
cam1fullurl="http://User:password@IP/axis-cgi/mjpg/video.cgi"
#cam1fullurl='C:/Users/User/Python/Medien/Videos/example.mp4'
streamcap = cv2.VideoCapture(cam1fullurl)
def start():
print('start pressed')
global streamactivekey
streamactivekey = True
#global waspaused
#paused = True
#T_ImVideo = threading.Thread(target=ImVideo)
#T_ImVideo.start()
ImVideo()
def start2():
print('start pressed')
global streamactivekey
streamactivekey = True
mts=multi_thread_stream(ready)
#mts.t1.join()
#mts.t2.join()
def stop():
print('stop pressed')
global streamactivekey
streamactivekey = False
global waspaused
waspaused = True
ImVideo()
window = tk.Tk()
window.title('Test')
#main thread id
print('Main Thread ID = ', threading.get_ident())
ready = threading.Event()
ready = ready
global videoframe
global videolabel1
global win_name
def framework():
print('framework Thread ID = ', threading.get_ident())
window.geometry("800x600+10+20")
frame1 = tk.Frame(window,bg='green')
frame1.place(relheight=1,relwidth=1)
global videoframe
videoframe = tk.Frame(window,bg="red",highlightbackground="black", highlightthickness=1)
videoframe.place(relx = 0.25, rely=0.25, relheight = 0.5, relwidth = 0.5)
global videolabel1
videolabel1 = tk.Label(videoframe,fg='#000000', bg = 'yellow')
videolabel1.place(relx = 0.5, rely = 0.5, anchor = "center")
videolabel1.config(font=("Courier bold", 20))
#basic setup
if streamcap.isOpened():
width = streamcap.get(cv2.CAP_PROP_FRAME_WIDTH) # float `width`
height = streamcap.get(cv2.CAP_PROP_FRAME_HEIGHT) # float `height`
size = "Breite: "+str(int(width))+" Höhe :"+str(int(height))
print('Videosize :',size)
global win_name
win_name = 'Stream'
streamactivekey = True
global waspaused
waspaused = False
button1 = tk.Button(frame1,text='Start',command=start2)
button1.place(rely=0.9,relx=0,relheight=0.1,relwidth=0.1)
button2 = tk.Button(frame1,text='Stop',command=stop)
button2.place(rely=0.9,relx=0.9,relheight=0.1,relwidth=0.1)
class multi_thread_stream:
#global streamactivekey
def __init__(self, ready=None):
self.ready = ready
self.cap = cv2.VideoCapture(cam1fullurl)
self.frame = {}
#Create the Threads
#self.t1 = threading.Thread(target=self.capture_stream,daemon=True)
#self.t2 = threading.Thread(target=self.display_image,daemon=True)
self.t1 = threading.Thread(target=self.capture_stream)
self.t2 = threading.Thread(target=self.display_image)
self.t1.name = 'capture_thread'
self.t2.name = 'display_thread'
self.t1.start()
self.t2.start()
def capture_stream(self):
while True:
# Capture frame-by-frame
self.ret, self.frame = self.cap.read()
self.ready.set()
if cv2.waitKey(1) & 0xFF == ord('q'):
break
def display_image(self):
while True:
#global waspaused
#global streamactivekey
#if streamactivekey:
# Display the resulting frame
self.ready.wait()
fwidth = videoframe.winfo_width()
fheight = videoframe.winfo_height()
frootx = videoframe.winfo_rootx()
frooty = videoframe.winfo_rooty()
global picnostreamsize
picnostreamsize = (frootx, frooty)
cv2.namedWindow(win_name, cv2.WINDOW_NORMAL)
cv2.resizeWindow(win_name,fwidth, fheight)
cv2.moveWindow(win_name, frootx, frooty)
cv2.imshow(win_name,self.frame)
#cv2.imshow('frame_2nd_trhead', self.frame)
cv2.setWindowProperty('Stream', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
self.ready.clear()
if cv2.waitKey(1) & 0xFF == ord('q'):
break;
#window.after(1,ImVideo)
def __del__(self):
# When everything done, release the capture
self.cap.release()
cv2.destroyAllWindows()
def ImVideo():
global waspaused
if streamactivekey:
ret, frameiv = streamcap.read()
#frameiv = threading.Thread(target=capread).start()
fwidth = videoframe.winfo_width()
fheight = videoframe.winfo_height()
frootx = videoframe.winfo_rootx()
frooty = videoframe.winfo_rooty()
fx = videoframe.winfo_x()
fy = videoframe.winfo_y()
global picnostreamsize
picnostreamsize = (frootx, frooty)
#print(f'frootx:{frootx} frooty:{frooty}')
#print(f'fx:{fx} fy:{fy}')
cv2.namedWindow(win_name, cv2.WINDOW_NORMAL)
cv2.resizeWindow(win_name,fwidth, fheight)
cv2.moveWindow(win_name, frootx, frooty)
cv2.imshow(win_name,frameiv)
if waspaused:
print('waspaused')
cv2.setWindowProperty('Stream', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
cv2.setWindowProperty('Stream', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_NORMAL)
waspaused = False
cv2.setWindowProperty('Stream', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
window.after(1,ImVideo)
def setpicnostream():
global picnostream
global videoframe
global videolabel1
img = Image.open(r'C:\Users\User\Python\Medien\Bilder\streamoff.png')
x = videoframe.winfo_width()
y = videoframe.winfo_height()
print('x :',x)
print('y :',y)
img = img.resize((x,y), Image.ANTIALIAS)
picnostream = ImageTk.PhotoImage(img)
videolabel1.config(font=("Courier bold", 20),image = picnostream)
#framework()
T_framework = threading.Thread(target=framework,daemon=True)
T_framework.start()
window.after(50,setpicnostream)
window.mainloop()
As far as I understand the .read() Operation is an IO Blocker. So the .read() and the imshow() should be in the same "Threadpool" depending on the Event .ready().
Later I want to use up to 3 Webcams, so I would need 4 "Threadpools" that dont block each other. GUI, Cam1, Cam2, Cam3.
Solution 1:[1]
there is no code here, so it is very hard to say. You need some way to send a message into the code running in the threads so that its terminated from "inside" (for example, returning from the entry function). This can be something as sophisticated as a queue, or you can just use a dictionary (or other variable) at global level: you set this var in the control thread, and test and act upon its value in the target thread.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | jsbueno |
