创建一个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",
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))