Tkinter зависает с несколькими потоками - PullRequest
0 голосов
/ 09 июля 2020

Я разработал программу производитель-потребитель в Python с одним производителем (который считывает кадры с камеры на высокой скорости) и двумя потребителями (один для отображения видео в формате * 1003). * cv2 window , а другой для сохранения каждого кадра на диск). Три потока связаны двумя очередями (q1 и q2), по одной для каждого потребителя, поскольку ничто не должно блокировать получение и отображение изображения. Я пробовал эту часть самостоятельно, и она отлично работает. Здесь я адаптировал код класса, так что получение теперь "подделывается" путем помещения фреймов с нулями в очереди.

import multiprocessing 
from tkinter import *
from tkinter import messagebox
from tkinter import filedialog
import PIL.Image, PIL.ImageTk
import cv2
from threading import Thread,Lock,Semaphore
import queue
import time
from tifffile import imsave
import numpy as np 
import os

class VideoGet():  
    
        def __init__(self,folder,record):

            self.record = record
            self.counter=0
            self.folder = folder
            
        def displayer(self,q2):
            while q2.empty() is False:
                    framedisplay = q2.get()
                    cv2.imshow("Video", framedisplay)
                    cv2.waitKey(1)
                    q2.task_done()
            cv2.destroyAllWindows()
            
        def consumer(self,q):
         
             while q.empty() is False:
                frame_get = q.get()
                imsave(os.path.join(self.folder,(str(self.counter)+'.tiff')), frame_get)
                self.counter=self.counter+1
                q.task_done()
        
        def producer(self,buffer,q,q2):

            while self.record is True:
               frame=np.zeros_like(buffer)
               q.put(frame)
               q2.put(frame)
               del frame

        def run(self,buffer,q,q2):
            
                prod_thread=Thread(target=self.producer,args=(buffer,q,q2,))
                display_thread=Thread(target=self.displayer,args=(q2,))
                con_thread= Thread(target=self.consumer, args=(q,))
                
                prod_thread.start()
                display_thread.start()
                con_thread.start()
                
                prod_thread.join()
                display_thread.join()
                con_thread.join()
                

Я хочу создать небольшой tkinter GUI всего с тремя кнопками, одна запрос папки, другой для начала сбора данных (при этом появится окно cv2) и последний для остановки и полного уничтожения системы. Я разработал класс, который вызывает потоки при правильном нажатии кнопок. Появляется cv2 windows и кадры сохраняются, но GUI зависает и больше не отвечает, поэтому я больше не могу нажимать кнопку «Стоп» . Я предполагаю, что проблемы в том, что потоки не работают так, как я ожидал.

Есть идеи, как правильно найти или вызвать потоки ?. И, может быть, как отображать кадры в GUI вместо этого, а не в окне cv2?

PS: Я знаю, что это немного длинный код, но я не знал, как его лучше сжать

class VideoGUI():
    
    def __init__(self, window, window_title):

        self.window = window
        self.window.title(window_title)

        bottom_frame = Frame(self.window)
        
        bottom_frame.pack(side=BOTTOM, pady=5)

        self.record = True   # Parameter that controls pause button
        self.btn_select=Button(bottom_frame, text="Select a folder", width=15, command=self.open_file)
        self.btn_select.grid(row=0, column=0)

        # Play Button
        self.btn_play=Button(bottom_frame, text="Play", width=15, command=self.play_video)
        self.btn_play.grid(row=0, column=1)

        # Pause Button
        self.btn_pause=Button(bottom_frame, text="Stop", width=15, command=self.pause_video)
        self.btn_pause.grid(row=0, column=2)
        self.buffer=np.zeros(shape=(513,640), dtype=np.uint16)
        self.disp_queue=queue.Queue()
        self.save_queue=queue.Queue()
        self.window.mainloop()

    def open_file(self):

        self.folder=filedialog.askdirectory(title="Select folder")
        self.video_object=VideoGet(self.folder,self.record)

    def play_video(self):
 
        try:
             self.video_object.run(self.buffer,self.save_queue,self.disp_queue)
        except:
            print('no play')

    def pause_video(self):
        
        try:    
            self.video_object.record=False
        except:
            self.window.destroy()
    
# Create a window and pass it to videoGUI Class
if __name__=='__main__':
    
    video_object=VideoGUI(Tk(),"camera")
...