如何在Python中使用tkinter通过线程显示多个视频?

问题描述 投票:0回答:2

我最近创建了一个程序,使用 opencv 显示来自 2 个网络摄像机的多视频源。 但我决定为我的应用程序创建 UI,现在,我不太清楚如何使用多线程方法来实现它。

这是我用来在 TKinter GUI 中仅显示一台摄像机的代码:

import tkinter
import cv2
import PIL.Image, PIL.ImageTk
import time

class App:
    def __init__(self, window, window_title, video_source=0):
        self.window = window
        self.window.title(window_title)
        self.video_source = video_source
        
        # open video source (by default this will try to open the computer webcam)
        self.vid = MyVideoCapture(self.video_source)
        
        # Create a canvas that can fit the above video source size
        self.canvas = tkinter.Canvas(window, width = self.vid.width, height = self.vid.height)
        self.canvas.pack()
         
        # Button that lets the user take a snapshot
        self.btn_snapshot=tkinter.Button(window, text="Snapshot", width=50, command=self.snapshot)
        self.btn_snapshot.pack(anchor=tkinter.CENTER, expand=True)
         
        # After it is called once, the update method will be automatically called every delay milliseconds
        self.delay = 15
        self.update()
         
        self.window.mainloop()
     
    def snapshot(self):
        # Get a frame from the video source
        ret, frame = self.vid.get_frame()
        
        if ret:
            cv2.imwrite("frame-" + time.strftime("%d-%m-%Y-%H-%M-%S") + ".jpg", cv2.cvtColor(frame, cv2.COLOR_RGB2BGR))
    
    def update(self):
        # Get a frame from the video source
        ret, frame = self.vid.get_frame()
        
        if ret:
            self.photo = PIL.ImageTk.PhotoImage(image = PIL.Image.fromarray(frame))
            self.canvas.create_image(0, 0, image = self.photo, anchor = tkinter.NW)
        
        self.window.after(self.delay, self.update)
    
     
class MyVideoCapture:
    def __init__(self, video_source=0):
        # Open the video source
        self.vid = cv2.VideoCapture(video_source)
        if not self.vid.isOpened():
            raise ValueError("Unable to open video source", video_source)
    
        # Get video source width and height
        self.width = self.vid.get(cv2.CAP_PROP_FRAME_WIDTH)
        self.height = self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT)
    
    def get_frame(self):
        if self.vid.isOpened():
            ret, frame = self.vid.read()
            if ret:
                # Return a boolean success flag and the current frame converted to BGR
                return (ret, cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
            else:
                return (ret, None)
        else:
            return (ret, None)
    
    # Release the video source when the object is destroyed
    def __del__(self):
        if self.vid.isOpened():
            self.vid.release()
 
 # Create a window and pass it to the Application object
App(tkinter.Tk(), "Tkinter and OpenCV")

这是我之前的应用程序,它在不同线程中显示多个视频源:

from threading import Thread
import cv2
import time

class VideoWriterWidget(object):
    def __init__(self, video_file_name, src=0):
        # Create a VideoCapture object
        self.frame_name = str(src)
        self.video_file = video_file_name
        self.video_file_name = video_file_name + '.avi'
        self.capture = cv2.VideoCapture(src)

        # Default resolutions of the frame are obtained (system dependent)
        self.frame_width = int(self.capture.get(3))
        self.frame_height = int(self.capture.get(4))

        # Set up codec and output video settings
        self.codec = cv2.VideoWriter_fourcc('M','J','P','G')
        self.output_video = cv2.VideoWriter(self.video_file_name, self.codec, 30, (self.frame_width, self.frame_height))

        # Start the thread to read frames from the video stream
        self.thread = Thread(target=self.update, args=())
        self.thread.daemon = True
        self.thread.start()

        # Start another thread to show/save frames
        self.start_recording()
        print('initialized {}'.format(self.video_file))

    def update(self):
        # Read the next frame from the stream in a different thread
        while True:
            if self.capture.isOpened():
                (self.status, self.frame) = self.capture.read()

    def show_frame(self):
        # Display frames in main program
        if self.status:
            cv2.imshow(self.frame_name, self.frame)

        # Press Q on keyboard to stop recording
        key = cv2.waitKey(1)
        if key == ord('q'):
            self.capture.release()
            self.output_video.release()
            cv2.destroyAllWindows()
            exit(1)

    def save_frame(self):
        # Save obtained frame into video output file
        self.output_video.write(self.frame)

    def start_recording(self):
        # Create another thread to show/save frames
        def start_recording_thread():
            while True:
                try:
                    self.show_frame()
                    self.save_frame()
                except AttributeError:
                    pass
        self.recording_thread = Thread(target=start_recording_thread, args=())
        self.recording_thread.daemon = True
        self.recording_thread.start()

if __name__ == '__main__':
    src1 = 'Your link1'
    video_writer_widget1 = VideoWriterWidget('Camera 1', src1)
    src2 = 'Your link2'
    video_writer_widget2 = VideoWriterWidget('Camera 2', src2)
    src3 = 'Your link3'
    video_writer_widget3 = VideoWriterWidget('Camera 3', src3)

    # Since each video player is in its own thread, we need to keep the main thread alive.
    # Keep spinning using time.sleep() so the background threads keep running
    # Threads are set to daemon=True so they will automatically die 
    # when the main thread dies
    while True:
        time.sleep(5)

有人可以帮助我如何使用带有线程的 tkinter 在我的新应用程序中使用我以前的代码(显示多摄像头)吗?

python multithreading opencv tkinter
2个回答
6
投票

tkinter
(像许多其他 GUI 一样)不喜欢在线程中使用小部件,所以首先我会尝试在没有线程的情况下在主进程中运行所有组件。

在示例中,我将大部分代码移至基于

tkinter.Frame
的类来创建小部件 我可以在不同的流中多次使用它。因为我只有一台相机(并且系统不能多次使用同一台相机),所以我找到了一些外部流/文件来测试它。因为流发送非常大的图像,所以我将大小更改为
400, 300

无需调整图像大小,代码运行速度很快。
当它必须调整图像大小时,有时会出现问题,但仍然没问题。


import tkinter
import cv2
import PIL.Image, PIL.ImageTk
import time

# widgets with canvas and camera

class tkCamera(tkinter.Frame):

    def __init__(self, window, video_source=0):
        super().__init__(window)
        
        self.window = window
        
        #self.window.title(window_title)
        self.video_source = video_source
        self.vid = MyVideoCapture(self.video_source)

        self.canvas = tkinter.Canvas(window, width=self.vid.width, height=self.vid.height)
        self.canvas.pack()
         
        # Button that lets the user take a snapshot
        self.btn_snapshot = tkinter.Button(window, text="Snapshot", width=50, command=self.snapshot)
        self.btn_snapshot.pack(anchor=tkinter.CENTER, expand=True)
         
        # After it is called once, the update method will be automatically called every delay milliseconds
        self.delay = 15
        self.update_widget()

    def snapshot(self):
        # Get a frame from the video source
        ret, frame = self.vid.get_frame()
        
        if ret:
            cv2.imwrite("frame-" + time.strftime("%d-%m-%Y-%H-%M-%S") + ".jpg", cv2.cvtColor(frame, cv2.COLOR_RGB2BGR))
    
    def update_widget(self):
        # Get a frame from the video source
        ret, frame = self.vid.get_frame()
        
        if ret:
            self.image = PIL.Image.fromarray(frame)
            self.photo = PIL.ImageTk.PhotoImage(image=self.image)
            self.canvas.create_image(0, 0, image = self.photo, anchor = tkinter.NW)
        
        self.window.after(self.delay, self.update_widget)


class App:

    def __init__(self, window, window_title, video_source1=0, video_source2=0):
        self.window = window

        self.window.title(window_title)
        
        # open video source (by default this will try to open the computer webcam)
        self.vid1 = tkCamera(window, video_source1)
        self.vid1.pack()
        
        self.vid2 = tkCamera(window, video_source2)
        self.vid2.pack()
        
        # Create a canvas that can fit the above video source size
         
        self.window.mainloop()
     
    
     
class MyVideoCapture:
    def __init__(self, video_source=0):
        # Open the video source
        self.vid = cv2.VideoCapture(video_source)
        if not self.vid.isOpened():
            raise ValueError("Unable to open video source", video_source)
    
        # Get video source width and height
        self.width = self.vid.get(cv2.CAP_PROP_FRAME_WIDTH)
        self.height = self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT)
    
        self.width = 400
        self.height = 300
    
    def get_frame(self):
        if self.vid.isOpened():
            ret, frame = self.vid.read()
            if ret:
                frame = cv2.resize(frame, (400, 300))
                # Return a boolean success flag and the current frame converted to BGR
                return (ret, cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
            else:
                return (ret, None)
        else:
            return (ret, None)
    
    # Release the video source when the object is destroyed
    def __del__(self):
        if self.vid.isOpened():
            self.vid.release()
 
# Create a window and pass it to the Application object
App(tkinter.Tk(), "Tkinter and OpenCV", 0, 'https://imageserver.webcamera.pl/rec/krupowki-srodek/latest.mp4')

如果您打算处理框架 - 即。检测运动或面部 - 然后来自

get_frame
的代码可以在单独的线程中运行。线程将始终处理帧并分配给
self.frame
,并且
get_frame()
应该只返回当前
self.frame

在博客

pyImageSearch
中查看类似的想法 使用 Python 和 OpenCV 提高网络摄像头 FPS

也许你甚至可以使用

 from imutils.video import WebcamVideoStream

编辑:

版本仍然没有线程,但带有源列表,因此它可以显示许多摄像机。但对于超过 2 个源,显示时会出现问题 - 所以这需要使用

threads

顺便说一句:tkinter

中的小部件和窗口已经有方法
update()
,所以我将其重命名为
update_frame()

snapshot

 中,我使用了 
pilow.image.save()
,因此我不必读取新帧并转换为 
BGR
 - 而且我可以在流停止时拍摄快照。 
Button
 仅停止替换画布上的图像,但不停止从线程中的流中读取帧 - 因此其他函数仍然可以处理或记录流。


import tkinter import cv2 import PIL.Image, PIL.ImageTk import time class MyVideoCapture: def __init__(self, video_source=0, width=None, height=None): # Open the video source self.vid = cv2.VideoCapture(video_source) if not self.vid.isOpened(): raise ValueError("Unable to open video source", video_source) self.width = width self.height = height # Get video source width and height if not self.width: self.width = int(self.vid.get(cv2.CAP_PROP_FRAME_WIDTH)) # convert float to int if not self.height: self.height = int(self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT)) # convert float to int self.ret = False self.frame = None def process(self): ret = False frame = None if self.vid.isOpened(): ret, frame = self.vid.read() if ret: frame = cv2.resize(frame, (self.width, self.height)) frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) self.ret = ret self.frame = frame def get_frame(self): self.process() # later run in thread return self.ret, self.frame # Release the video source when the object is destroyed def __del__(self): if self.vid.isOpened(): self.vid.release() class tkCamera(tkinter.Frame): def __init__(self, window, video_source=0, width=None, height=None): super().__init__(window) self.window = window #self.window.title(window_title) self.video_source = video_source self.vid = MyVideoCapture(self.video_source, width, height) self.canvas = tkinter.Canvas(window, width=self.vid.width, height=self.vid.height) self.canvas.pack() # Button that lets the user take a snapshot self.btn_snapshot = tkinter.Button(window, text="Snapshot", width=50, command=self.snapshot) self.btn_snapshot.pack(anchor='center', expand=True) # After it is called once, the update method will be automatically called every delay milliseconds self.delay = 15 self.update_widget() def snapshot(self): # Get a frame from the video source ret, frame = self.vid.get_frame() if ret: cv2.imwrite("frame-" + time.strftime("%d-%m-%Y-%H-%M-%S") + ".jpg", cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)) def update_widget(self): # Get a frame from the video source ret, frame = self.vid.get_frame() if ret: self.image = PIL.Image.fromarray(frame) self.photo = PIL.ImageTk.PhotoImage(image=self.image) self.canvas.create_image(0, 0, image = self.photo, anchor = tkinter.NW) self.window.after(self.delay, self.update_widget) class App: def __init__(self, window, window_title, video_sources): self.window = window self.window.title(window_title) self.vids = [] for source in video_sources: vid = tkCamera(window, source, 400, 300) vid.pack() self.vids.append(vid) # Create a canvas that can fit the above video source size self.window.mainloop() if __name__ == '__main__': sources = [ 0, #'https://imageserver.webcamera.pl/rec/krupowki-srodek/latest.mp4', #'https://imageserver.webcamera.pl/rec/skolnity/latest.mp4', 'https://imageserver.webcamera.pl/rec/krakow4/latest.mp4', ] # Create a window and pass it to the Application object App(tkinter.Tk(), "Tkinter and OpenCV", sources)


编辑

使用

threads

读取和处理帧的版本。我仅在需要时添加 
time(1/fps)
 来处理它,以便工作更顺畅。由于延迟
15
有时会冻结。

我使用的源只有 24 秒,所以几秒钟后它们就会停止。


import tkinter import cv2 import PIL.Image, PIL.ImageTk import time import threading class MyVideoCapture: def __init__(self, video_source=0, width=None, height=None, fps=None): self.video_source = video_source self.width = width self.height = height self.fps = fps # Open the video source self.vid = cv2.VideoCapture(video_source) if not self.vid.isOpened(): raise ValueError("[MyVideoCapture] Unable to open video source", video_source) # Get video source width and height if not self.width: self.width = int(self.vid.get(cv2.CAP_PROP_FRAME_WIDTH)) # convert float to int if not self.height: self.height = int(self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT)) # convert float to int if not self.fps: self.fps = int(self.vid.get(cv2.CAP_PROP_FPS)) # convert float to int # default value at start self.ret = False self.frame = None # start thread self.running = True self.thread = threading.Thread(target=self.process) self.thread.start() def process(self): while self.running: ret, frame = self.vid.read() if ret: # process image frame = cv2.resize(frame, (self.width, self.height)) frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) else: print('[MyVideoCapture] stream end:', self.video_source) # TODO: reopen stream self.running = False break # assign new frame self.ret = ret self.frame = frame # sleep for next frame time.sleep(1/self.fps) def get_frame(self): return self.ret, self.frame # Release the video source when the object is destroyed def __del__(self): # stop thread if self.running: self.running = False self.thread.join() # relase stream if self.vid.isOpened(): self.vid.release() class tkCamera(tkinter.Frame): def __init__(self, window, text="", video_source=0, width=None, height=None): super().__init__(window) self.window = window #self.window.title(window_title) self.video_source = video_source self.vid = MyVideoCapture(self.video_source, width, height) self.label = tkinter.Label(self, text=text) self.label.pack() self.canvas = tkinter.Canvas(self, width=self.vid.width, height=self.vid.height) self.canvas.pack() # Button that lets the user take a snapshot self.btn_snapshot = tkinter.Button(self, text="Start", command=self.start) self.btn_snapshot.pack(anchor='center', side='left') self.btn_snapshot = tkinter.Button(self, text="Stop", command=self.stop) self.btn_snapshot.pack(anchor='center', side='left') # Button that lets the user take a snapshot self.btn_snapshot = tkinter.Button(self, text="Snapshot", command=self.snapshot) self.btn_snapshot.pack(anchor='center', side='left') # After it is called once, the update method will be automatically called every delay milliseconds # calculate delay using `FPS` self.delay = int(1000/self.vid.fps) print('[tkCamera] source:', self.video_source) print('[tkCamera] fps:', self.vid.fps, 'delay:', self.delay) self.image = None self.running = True self.update_frame() def start(self): if not self.running: self.running = True self.update_frame() def stop(self): if self.running: self.running = False def snapshot(self): # Get a frame from the video source #ret, frame = self.vid.get_frame() #if ret: # cv2.imwrite(time.strftime("frame-%d-%m-%Y-%H-%M-%S.jpg"), cv2.cvtColor(self.frame, cv2.COLOR_RGB2BGR)) # Save current frame in widget - not get new one from camera - so it can save correct image when it stoped if self.image: self.image.save(time.strftime("frame-%d-%m-%Y-%H-%M-%S.jpg")) def update_frame(self): # widgets in tkinter already have method `update()` so I have to use different name - # Get a frame from the video source ret, frame = self.vid.get_frame() if ret: self.image = PIL.Image.fromarray(frame) self.photo = PIL.ImageTk.PhotoImage(image=self.image) self.canvas.create_image(0, 0, image=self.photo, anchor='nw') if self.running: self.window.after(self.delay, self.update_frame) class App: def __init__(self, window, window_title, video_sources): self.window = window self.window.title(window_title) self.vids = [] columns = 2 for number, source in enumerate(video_sources): text, stream = source vid = tkCamera(self.window, text, stream, 400, 300) x = number % columns y = number // columns vid.grid(row=y, column=x) self.vids.append(vid) self.window.protocol("WM_DELETE_WINDOW", self.on_closing) self.window.mainloop() def on_closing(self, event=None): print('[App] stoping threads') for source in self.vids: source.vid.running = False print('[App] exit') self.window.destroy() if __name__ == '__main__': sources = [ ('me', 0), ('Zakopane, Poland', 'https://imageserver.webcamera.pl/rec/krupowki-srodek/latest.mp4'), ('Kraków, Poland', 'https://imageserver.webcamera.pl/rec/krakow4/latest.mp4'), ('Warszawa, Poland', 'https://imageserver.webcamera.pl/rec/warszawa/latest.mp4'), #('Baltic See, Poland', 'https://imageserver.webcamera.pl/rec/chlopy/latest.mp4'), #('Mountains, Poland', 'https://imageserver.webcamera.pl/rec/skolnity/latest.mp4'), ] # Create a window and pass it to the Application object App(tkinter.Tk(), "Tkinter and OpenCV", sources)


编辑

可以录制视频的版本。

cv2

 需要具有 BGR 颜色的帧才能正确保存它,因此我必须在帧转换为 RGB 之前保存它。

我将大部分代码移至

MyVideoCapture

,这样即使没有
tkinter
也可以使用。我还在 
MyVideoCapture
 中添加了选项,以将图像获取为 
cv2 array
pillow.image
 - 所以现在它会转换为 
pillow
 内的 
thread
,因此主线程不必执行此操作。


import tkinter import cv2 import PIL.Image, PIL.ImageTk import time import threading class MyVideoCapture: def __init__(self, video_source=0, width=None, height=None, fps=None): self.video_source = video_source self.width = width self.height = height self.fps = fps # Open the video source self.vid = cv2.VideoCapture(video_source) if not self.vid.isOpened(): raise ValueError("[MyVideoCapture] Unable to open video source", video_source) # Get video source width and height if not self.width: self.width = int(self.vid.get(cv2.CAP_PROP_FRAME_WIDTH)) # convert float to int if not self.height: self.height = int(self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT)) # convert float to int if not self.fps: self.fps = int(self.vid.get(cv2.CAP_PROP_FPS)) # convert float to int # default value at start self.ret = False self.frame = None self.convert_color = cv2.COLOR_BGR2RGB #self.convert_color = cv2.COLOR_BGR2GRAY self.convert_pillow = True # default values for recording self.recording = False self.recording_filename = 'output.mp4' self.recording_writer = None # start thread self.running = True self.thread = threading.Thread(target=self.process) self.thread.start() def start_recording(self, filename=None): if self.recording: print('[MyVideoCapture] already recording:', self.recording_filename) else: # VideoWriter constructors #.mp4 = codec id 2 if filename: self.recording_filename = filename else: self.recording_filename = time.strftime("%Y.%m.%d %H.%M.%S", time.localtime()) + ".avi" #fourcc = cv2.VideoWriter_fourcc(*'I420') # .avi #fourcc = cv2.VideoWriter_fourcc(*'MP4V') # .avi fourcc = cv2.VideoWriter_fourcc(*'MP42') # .avi #fourcc = cv2.VideoWriter_fourcc(*'AVC1') # error libx264 #fourcc = cv2.VideoWriter_fourcc(*'H264') # error libx264 #fourcc = cv2.VideoWriter_fourcc(*'WRAW') # error --- no information --- #fourcc = cv2.VideoWriter_fourcc(*'MPEG') # .avi 30fps #fourcc = cv2.VideoWriter_fourcc(*'MJPG') # .avi #fourcc = cv2.VideoWriter_fourcc(*'XVID') # .avi #fourcc = cv2.VideoWriter_fourcc(*'H265') # error self.recording_writer = cv2.VideoWriter(self.recording_filename, fourcc, self.fps, (self.width, self.height)) self.recording = True print('[MyVideoCapture] started recording:', self.recording_filename) def stop_recording(self): if not self.recording: print('[MyVideoCapture] not recording') else: self.recording = False self.recording_writer.release() print('[MyVideoCapture] stop recording:', self.recording_filename) def record(self, frame): # write frame to file if self.recording_writer and self.recording_writer.isOpened(): self.recording_writer.write(frame) def process(self): while self.running: ret, frame = self.vid.read() if ret: # process image frame = cv2.resize(frame, (self.width, self.height)) # it has to record before converting colors if self.recording: self.record(frame) if self.convert_pillow: frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) frame = PIL.Image.fromarray(frame) else: print('[MyVideoCapture] stream end:', self.video_source) # TODO: reopen stream self.running = False if self.recording: self.stop_recording() break # assign new frame self.ret = ret self.frame = frame # sleep for next frame time.sleep(1/self.fps) def get_frame(self): return self.ret, self.frame # Release the video source when the object is destroyed def __del__(self): # stop thread if self.running: self.running = False self.thread.join() # relase stream if self.vid.isOpened(): self.vid.release() class tkCamera(tkinter.Frame): def __init__(self, window, text="", video_source=0, width=None, height=None): super().__init__(window) self.window = window #self.window.title(window_title) self.video_source = video_source self.vid = MyVideoCapture(self.video_source, width, height) self.label = tkinter.Label(self, text=text) self.label.pack() self.canvas = tkinter.Canvas(self, width=self.vid.width, height=self.vid.height) self.canvas.pack() # Button that lets the user take a snapshot self.btn_snapshot = tkinter.Button(self, text="Start", command=self.start) self.btn_snapshot.pack(anchor='center', side='left') self.btn_snapshot = tkinter.Button(self, text="Stop", command=self.stop) self.btn_snapshot.pack(anchor='center', side='left') # Button that lets the user take a snapshot self.btn_snapshot = tkinter.Button(self, text="Snapshot", command=self.snapshot) self.btn_snapshot.pack(anchor='center', side='left') # After it is called once, the update method will be automatically called every delay milliseconds # calculate delay using `FPS` self.delay = int(1000/self.vid.fps) print('[tkCamera] source:', self.video_source) print('[tkCamera] fps:', self.vid.fps, 'delay:', self.delay) self.image = None self.running = True self.update_frame() def start(self): #if not self.running: # self.running = True # self.update_frame() self.vid.start_recording() def stop(self): #if self.running: # self.running = False self.vid.stop_recording() def snapshot(self): # Get a frame from the video source #ret, frame = self.vid.get_frame() #if ret: # cv2.imwrite(time.strftime("frame-%d-%m-%Y-%H-%M-%S.jpg"), cv2.cvtColor(self.frame, cv2.COLOR_RGB2BGR)) # Save current frame in widget - not get new one from camera - so it can save correct image when it stoped if self.image: self.image.save(time.strftime("frame-%d-%m-%Y-%H-%M-%S.jpg")) def update_frame(self): # widgets in tkinter already have method `update()` so I have to use different name - # Get a frame from the video source ret, frame = self.vid.get_frame() if ret: #self.image = PIL.Image.fromarray(frame) self.image = frame self.photo = PIL.ImageTk.PhotoImage(image=self.image) self.canvas.create_image(0, 0, image=self.photo, anchor='nw') if self.running: self.window.after(self.delay, self.update_frame) class App: def __init__(self, window, window_title, video_sources): self.window = window self.window.title(window_title) self.vids = [] columns = 2 for number, source in enumerate(video_sources): text, stream = source vid = tkCamera(self.window, text, stream, 400, 300) x = number % columns y = number // columns vid.grid(row=y, column=x) self.vids.append(vid) self.window.protocol("WM_DELETE_WINDOW", self.on_closing) self.window.mainloop() def on_closing(self, event=None): print('[App] stoping threads') for source in self.vids: source.vid.running = False print('[App] exit') self.window.destroy() if __name__ == '__main__': sources = [ ('me', 0), ('Zakopane, Poland', 'https://imageserver.webcamera.pl/rec/krupowki-srodek/latest.mp4'), ('Kraków, Poland', 'https://imageserver.webcamera.pl/rec/krakow4/latest.mp4'), ('Warszawa, Poland', 'https://imageserver.webcamera.pl/rec/warszawa/latest.mp4'), #('Baltic See, Poland', 'https://imageserver.webcamera.pl/rec/chlopy/latest.mp4'), #('Mountains, Poland', 'https://imageserver.webcamera.pl/rec/skolnity/latest.mp4'), ] # Create a window and pass it to the Application object App(tkinter.Tk(), "Tkinter and OpenCV", sources)


编辑:

我创建了可以选择源的版本 - 因此它可以显示录制的视频。

这段代码很混乱。对话框窗口可以位于单独的类中。

我无法在此处放置代码,因为答案的字符数限制为 30000 个字符。

我把它放在 GitHub 上:

python-cv2-streams-viewer



0
投票
我很高兴看到您高质量的回答,我还有一个问题。也就是说,我想在新窗口(顶级 tkinter)中同时显示 1 或 2 个摄像头,而主窗口仍然显示所有 4.5 个摄像头,我该怎么做?我尝试过,但是当我在另一个窗口中显示它时,主窗口中的 2 个视频中的 1 个会冻结(除非我关闭窗口)。如果我没有说清楚的话,这是我的问题的链接:

How to Display 1 Camera on 2 tkinter windows

© www.soinside.com 2019 - 2024. All rights reserved.