Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
创建一个GUI,可以使用Python 3和tkinter打开/关闭相机图像_Python_Python 3.x_Tkinter_Opencv3.0 - Fatal编程技术网

创建一个GUI,可以使用Python 3和tkinter打开/关闭相机图像

创建一个GUI,可以使用Python 3和tkinter打开/关闭相机图像,python,python-3.x,tkinter,opencv3.0,Python,Python 3.x,Tkinter,Opencv3.0,我想做什么 使用两个按钮“开始按钮”和“停止按钮”创建主窗口 按下“启动按钮”时,连接的USB摄像头的图像将显示在主窗口中 按“停止按钮”擦除[2]中显示的USB摄像头图像(离开主窗口) 麻烦 [1] [2]已经完成。但是,无法使用[3]擦除USB摄像头的图像。错误消息: Exception in thread Thread-8: Traceback (most recent call last): File "C:\Users\usr\Anaconda3\lib\threading.py", 

我想做什么

  • 使用两个按钮“开始按钮”和“停止按钮”创建主窗口

  • 按下“启动按钮”时,连接的USB摄像头的图像将显示在主窗口中

  • 按“停止按钮”擦除[2]中显示的USB摄像头图像(离开主窗口)

  • 麻烦

    [1] [2]已经完成。但是,无法使用[3]擦除USB摄像头的图像。错误消息:

    Exception in thread Thread-8:
    Traceback (most recent call last):
    File "C:\Users\usr\Anaconda3\lib\threading.py", line 916, in _bootstrap_inner self.run()
    File "C:\Users\usr\Anaconda3\lib\threading.py", line 864, in run
    self._target(*self._args, **self._kwargs)
    TypeError: destroy() missing 1 required positional argument: 'panel' 
    
    代码


    您得到的错误实际上说明了代码到底出了什么问题
    TypeError:destroy()缺少1个必需的位置参数:“panel”
    字面意思是必须将参数
    panel
    传递给函数
    destroy()。使用
    按钮2\u clicked()
    的套件中的
    thread.start()
    隐式调用函数。要解决此问题,应修改线程对象创建:

    thread = threading.Thread(target=destroy, args=(panel,))
    
    您还应该将
    面板
    传递给
    按钮2\u clicked()
    函数。这里出现了另一个问题,因为函数
    videoloop()
    返回了
    panel
    。因此,
    面板
    永远不会返回,因为
    videoloop()
    包含无限while循环。要解决这个问题,您需要一种在代码的操作种类之间传递数据的方法。例如,您可以这样做(简单但不可靠的方法):

    我无法完全测试代码。尽管代码的框架(启动线程并通过切换来停止它)可以工作


    我没有使用
    tkinter
    的经验。所以我不知道什么是
    面板
    ,也不知道这种方法是否可行。可能最好在代码的主线程中创建
    面板
    ,并将新创建的
    面板
    传递给函数
    按钮1\u clicked()
    ,然后传递给函数
    videoLoop()
    。这将允许直接从主线程控制
    panel
    ,但是
    videoLoop()
    应该进行重大修改(包括在
    panel
    被主线程禁用的情况下的检查/异常处理)。

    这里的操作存在一些基本问题。基本前提是不正确的,因为您不应该持续销毁和重新创建标签小部件以显示图像。相反,只需使用新图像调用其configure()方法来更新附加到现有小部件的图像。这是一个性能修复程序,与这里的线程问题无关。通常,创建一次小部件并更新它们。这避免了从UI树中删除和添加小部件时发生的几何体更改事件的级联

    这里的螺纹设计不正确。您不应该从工作线程进行Tk调用。Tk绑定到单个线程,只有事件应该在线程之间传递。为了说明如何更好地构造它,我修改了代码,使用
    queue.queue()
    将图像帧从opencv阅读器线程传递到Tk线程。我们可以发布一个自定义事件来通知UI有一个新的框架就绪(

    最后一个问题是,您应该保留对添加到Tk标签中的图像的引用,否则它会在您不期望的情况下被垃圾收集。因此,我们用每个新图像更新
    self.photo
    成员

    import sys
    import cv2
    import threading
    import tkinter as tk
    import tkinter.ttk as ttk
    from queue import Queue
    from PIL import Image
    from PIL import ImageTk
    
    
    class App(tk.Frame):
        def __init__(self, parent, title):
            tk.Frame.__init__(self, parent)
            self.is_running = False
            self.thread = None
            self.queue = Queue()
            self.photo = ImageTk.PhotoImage(Image.new("RGB", (800, 600), "white"))
            parent.wm_withdraw()
            parent.wm_title(title)
            self.create_ui()
            self.grid(sticky=tk.NSEW)
            self.bind('<<MessageGenerated>>', self.on_next_frame)
            parent.wm_protocol("WM_DELETE_WINDOW", self.on_destroy)
            parent.grid_rowconfigure(0, weight = 1)
            parent.grid_columnconfigure(0, weight = 1)
            parent.wm_deiconify()
    
        def create_ui(self):
            self.button_frame = ttk.Frame(self)
            self.stop_button = ttk.Button(self.button_frame, text="Stop", command=self.stop)
            self.stop_button.pack(side=tk.RIGHT)
            self.start_button = ttk.Button(self.button_frame, text="Start", command=self.start)
            self.start_button.pack(side=tk.RIGHT)
            self.view = ttk.Label(self, image=self.photo)
            self.view.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
            self.button_frame.pack(side=tk.BOTTOM, fill=tk.X, expand=True)
    
        def on_destroy(self):
            self.stop()
            self.after(20)
            if self.thread is not None:
                self.thread.join(0.2)
            self.winfo_toplevel().destroy()
    
        def start(self):
            self.is_running = True
            self.thread = threading.Thread(target=self.videoLoop, args=())
            self.thread.daemon = True
            self.thread.start()
    
        def stop(self):
            self.is_running = False
    
        def videoLoop(self, mirror=False):
            No=0
            cap = cv2.VideoCapture(No)
            cap.set(cv2.CAP_PROP_FRAME_WIDTH, 800)
            cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 600)
    
            while self.is_running:
                ret, to_draw = cap.read()
                if mirror is True:
                    to_draw = to_draw[:,::-1]
                image = cv2.cvtColor(to_draw, cv2.COLOR_BGR2RGB)
                self.queue.put(image)
                self.event_generate('<<MessageGenerated>>')
    
        def on_next_frame(self, eventargs):
            if not self.queue.empty():
                image = self.queue.get()
                image = Image.fromarray(image)
                self.photo = ImageTk.PhotoImage(image)
                self.view.configure(image=self.photo)
    
    
    def main(args):
        root = tk.Tk()
        app = App(root, "OpenCV Image Viewer")
        root.mainloop()
    
    if __name__ == '__main__':
        sys.exit(main(sys.argv))
    
    导入系统 进口cv2 导入线程 将tkinter作为tk导入 将tkinter.ttk导入为ttk 从队列导入队列 从PIL导入图像 从PIL导入ImageTk 类应用程序(tk.Frame): 定义初始(自我、父母、头衔): tk.Frame.\uuuu init\uuuuu(自,父) self.is_running=False self.thread=None self.queue=queue() self.photo=ImageTk.PhotoImage(Image.new(“RGB”),(800600),“白色”)) parent.wm_draw() 父.wm_标题(标题) self.create_ui() self.grid(sticky=tk.NSEW) self.bind(“”,self.on_下一帧) wm_协议(“wm_删除_窗口”,self.on_销毁) parent.grid_rowconfigure(0,权重=1) parent.grid\u columnconfigure(0,权重=1) parent.wm_deiconify() def创建用户界面(自我): self.button_frame=ttk.frame(self) self.stop\u button=ttk.button(self.button\u框架,text=“stop”,command=self.stop) 自动停止按钮组件(侧=右侧) self.start\u button=ttk.button(self.button\u框架,text=“start”,command=self.start) 自启动按钮组件(侧面=右侧) self.view=ttk.Label(self,image=self.photo) self.view.pack(side=tk.TOP,fill=tk.BOTH,expand=True) self.button_frame.pack(side=tk.BOTTOM,fill=tk.X,expand=True) def on_销毁(自): self.stop() 赛尔夫(20) 如果self.thread不是None: self.thread.join(0.2) self.winfo_toplevel().destroy() def启动(自): self.is_running=True self.thread=threading.thread(target=self.videoLoop,args=()) self.thread.daemon=True self.thread.start() def停止(自): self.is_running=False def videoLoop(自身,镜像=假): 否=0 cap=cv2.视频捕获(否) 封盖套件(cv2.cap\u PROP\u FRAME\u WIDTH,800) 盖组件(cv2.cap\u PROP\u FRAME\u HEIGHT,600) 当self.u运行时: ret,to_draw=cap.read() 如果镜像为真: to_draw=to_draw[:,::-1] 图像=cv2.CVT颜色(要绘制,cv2.COLOR\U BGR2RGB) self.queue.put(图像) 自我事件_生成(“”) 下一帧上的def(自身、事件参数): 如果不是self.queue.empty(): image=self.queue.get() image=image.fromarray(image) self.photo=ImageTk.PhotoImage(图像) self.view.configure(image=self.photo) def主(args): root=tk.tk() app=app(根,“OpenCV图像查看器”) root.mainloop()
    import cv2
    from PIL import Image
    from PIL import ImageTk
    import threading
    import tkinter as tk
    
    
    def button1_clicked(videoloop_stop):
        threading.Thread(target=videoLoop, args=(videoloop_stop,)).start()
    
    
    def button2_clicked(videoloop_stop):
        videoloop_stop[0] = True
    
    
    def videoLoop(mirror=False):
        No = 0
        cap = cv2.VideoCapture(No)
        cap.set(cv2.CAP_PROP_FRAME_WIDTH, 800)
        cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 600)
    
        while True:
            ret, to_draw = cap.read()
            if mirror is True:
                to_draw = to_draw[:, ::-1]
    
            image = cv2.cvtColor(to_draw, cv2.COLOR_BGR2RGB)
            image = Image.fromarray(image)
            image = ImageTk.PhotoImage(image)
            panel = tk.Label(image=image)
            panel.image = image
            panel.place(x=50, y=50)
    
            # check switcher value
            if videoloop_stop[0]:
                # if switcher tells to stop then we switch it again and stop videoloop
                videoloop_stop[0] = False
                panel.destroy()
                break
    
    
    # videoloop_stop is a simple switcher between ON and OFF modes
    videoloop_stop = [False]
    
    root = tk.Tk()
    root.geometry("1920x1080+0+0")
    
    button1 = tk.Button(
        root, text="start", bg="#fff", font=("", 50),
        command=lambda: button1_clicked(videoloop_stop))
    button1.place(x=1000, y=100, width=400, height=250)
    
    button2 = tk.Button(
        root, text="stop", bg="#fff", font=("", 50),
        command=lambda: button2_clicked(videoloop_stop))
    button2.place(x=1000, y=360, width=400, height=250)
    
    root.mainloop()
    
    import sys
    import cv2
    import threading
    import tkinter as tk
    import tkinter.ttk as ttk
    from queue import Queue
    from PIL import Image
    from PIL import ImageTk
    
    
    class App(tk.Frame):
        def __init__(self, parent, title):
            tk.Frame.__init__(self, parent)
            self.is_running = False
            self.thread = None
            self.queue = Queue()
            self.photo = ImageTk.PhotoImage(Image.new("RGB", (800, 600), "white"))
            parent.wm_withdraw()
            parent.wm_title(title)
            self.create_ui()
            self.grid(sticky=tk.NSEW)
            self.bind('<<MessageGenerated>>', self.on_next_frame)
            parent.wm_protocol("WM_DELETE_WINDOW", self.on_destroy)
            parent.grid_rowconfigure(0, weight = 1)
            parent.grid_columnconfigure(0, weight = 1)
            parent.wm_deiconify()
    
        def create_ui(self):
            self.button_frame = ttk.Frame(self)
            self.stop_button = ttk.Button(self.button_frame, text="Stop", command=self.stop)
            self.stop_button.pack(side=tk.RIGHT)
            self.start_button = ttk.Button(self.button_frame, text="Start", command=self.start)
            self.start_button.pack(side=tk.RIGHT)
            self.view = ttk.Label(self, image=self.photo)
            self.view.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
            self.button_frame.pack(side=tk.BOTTOM, fill=tk.X, expand=True)
    
        def on_destroy(self):
            self.stop()
            self.after(20)
            if self.thread is not None:
                self.thread.join(0.2)
            self.winfo_toplevel().destroy()
    
        def start(self):
            self.is_running = True
            self.thread = threading.Thread(target=self.videoLoop, args=())
            self.thread.daemon = True
            self.thread.start()
    
        def stop(self):
            self.is_running = False
    
        def videoLoop(self, mirror=False):
            No=0
            cap = cv2.VideoCapture(No)
            cap.set(cv2.CAP_PROP_FRAME_WIDTH, 800)
            cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 600)
    
            while self.is_running:
                ret, to_draw = cap.read()
                if mirror is True:
                    to_draw = to_draw[:,::-1]
                image = cv2.cvtColor(to_draw, cv2.COLOR_BGR2RGB)
                self.queue.put(image)
                self.event_generate('<<MessageGenerated>>')
    
        def on_next_frame(self, eventargs):
            if not self.queue.empty():
                image = self.queue.get()
                image = Image.fromarray(image)
                self.photo = ImageTk.PhotoImage(image)
                self.view.configure(image=self.photo)
    
    
    def main(args):
        root = tk.Tk()
        app = App(root, "OpenCV Image Viewer")
        root.mainloop()
    
    if __name__ == '__main__':
        sys.exit(main(sys.argv))