程序没有终止(tkinter + openCV + threading)

时间:2017-09-22 03:31:22

标签: python multithreading user-interface tkinter

我尝试构建一个GUI应用程序来从相机中抓取帧并在Tkinter GUI中显示它们。 Tkinter主循环在主线程中执行,而gui的帧抓取和更新则在一个单独的线程中进行。

以下代码可以在我的gui窗口中正确抓取并显示视频流。但是,当我通过单击" x"来调用on_close()方法时关闭gui,gui将关闭,但程序不会完全终止。最后的CLI输出将是" Mainloop停止!",但程序没有像我期望的那样终止。所以,我怀疑额外的线程继续运行,即使我通过stop事件退出线程run()方法中的while循环。

这是我的代码:

import threading    
import cv2
import tkinter
from tkinter import ttk
from PIL import Image
from PIL import ImageTk

import camera


class mainThread(threading.Thread):

    def __init__(self, gui):
        threading.Thread.__init__(self)
        self.stop_event = threading.Event()
        self.gui = gui

    def stop(self):
        print("T: Stop method called...")
        self.stop_event.set()

    def run(self):
        print("Connecting to camera...")
        cam = camera.Camera(resolution=(1280, 960), exposure=-3, bit_depth=12)
        cam.connect(device_id=0)

        while (not self.stop_event.is_set()):
            print("running..., stop event = " + str(self.stop_event.is_set()))
            # retrieve frame and frame time
            frame, _, _ = cam.get_frame()

            # display frame
            self.gui.updater(frame)

            # wait for displaying image
            cv2.waitKey(1)


class gui(object):

    def __init__(self, root):

        self.root = root

        # create and start thread running the main loop
        self.main_thread = mainThread(self)
        self.main_thread.start()

        # bind on close callback that executes on close of the main window
        self.root.protocol("WM_DELETE_WINDOW", self.on_close)

        # change window title
        self.root.title("MCT Laser Welding Quality Control")

        # set min size of root window and make window non resizable
        self.root.minsize(600, 400)
        self.root.resizable(False, False)

        # configure grid layout
        self.root.rowconfigure(0, weight=1)
        self.root.rowconfigure(1, weight=1)
        self.root.columnconfigure(0, weight=1)

        # create image panel
        self.image_panel = None

    def on_close(self):
        self.main_thread.stop()
        self.root.destroy()

    def updater(self, image):

        # TODO: resize frame first

        # convert image to tkinter image format
        image = Image.fromarray(image)
        image = ImageTk.PhotoImage(image)

        # if the panel does not exist, create and pack it
        if self.image_panel is None:

            # show the image in the panel
            self.image_panel = ttk.Label(self.root, image=image)
            self.image_panel.image = image  # keep reference

            # pack object into grid
            self.image_panel.grid(row=0, column=0)

        # just update the image on the panel
        else:
            self.image_panel.configure(image=image)
            self.image_panel.image = image  # keep reference

    def run(self):
        self.root.mainloop()


if __name__ == "__main__":

    # create a main window
    root = tkinter.Tk()

    # set style
    style = ttk.Style()
    style.theme_use("vista")

    # create gui instance
    user_interface = gui(root)

    # run the user interface
    root.mainloop()

    print("Mainloop stopped!")

编辑: 我发现了那条线

image = ImageTk.PhotoImage(image)

阻止线程如上所述停止。当我删除这一行并简单地用递增的数字更新标签文本时,当我关闭gui窗口时,一切都工作为例外并且线程终止。任何想法,为什么ImageTk.PhotoImage()导致线程无法正常终止?

1 个答案:

答案 0 :(得分:0)

你需要加入线程等待它关闭。

function changeColor() {
    if (window.scrollY == sectionClass.offsetTop) {
    document.querySelector('.sectionClass').classList.add("newColorClass");
} else {
    document.querySelector('.sectionClass').classList.remove("newColorClass");
    }
}

此外,您可能需要关闭与相机的开放连接。

def stop(self):
    print("T: Stop method called...")
    self.stop_event.set()
    self.join()