waitKey() 函数不适用于 tkinter
waitKey() function not working with tkinter
我用 tkinter 设计了一个 window 并希望在那里显示视频流,我还希望能够使用“p”键暂停视频。我创建了一个函数,可以将网络摄像头拍摄的照片放入 window 上的标签中。然后我把这个函数放到while循环里反复调用,在window里做一个连续的视频。我在循环中使用 waitKey() 函数来读取按键。 waitKey() 还设置视频的帧速率或速度。我设法获取了视频 运行,但该程序对按键没有反应。另一方面,我可以在更改 waitKey() 的参数时更改帧速率,因此该函数似乎可以工作,但它不会读取按键,按下按键时也没有任何反应。也没有错误消息。如果有人能告诉我如何在此循环中使用 waitKey() 以便我可以通过按键控制视频流,或者建议一种不同的方法,我将不胜感激。这是我的代码,其中大部分是设计 window 但带有 waitKey() 函数的循环在最后:
import tkinter as tk
from tkinter import *
from tkinter import ttk
from tkinter.ttk import *
from PIL import ImageTk, Image
from cv2 import cv2
mainwin = Tk() #create main window
appWinWidth = int(0.6 * mainwin.winfo_screenwidth()) #set main window size (depending on the screen)
appWinHeight = int(0.5 * mainwin.winfo_screenheight())
screen_width = mainwin.winfo_screenwidth() #get screen dimensions
screen_height = mainwin.winfo_screenheight()
appWinX = int((screen_width - appWinWidth)/2) #set coordinates of the main window so that it
appWinY = int((screen_height - appWinHeight)/2) #would be located in the middle of the screen
#create and place the frames
frame1 = tk.Frame(mainwin, background = "white", width = int(appWinWidth/2), height = appWinHeight)
frame2 = tk.Frame(mainwin, background = "black", width = int(appWinWidth/2), height = appWinHeight)
frame1.grid(row = 0, column = 0, sticky = "nsew")
frame2.grid(row = 0, column = 1, sticky = "nsew")
mainwin.grid_columnconfigure(0, weight = 1)
mainwin.grid_columnconfigure(1, weight = 1)
mainwin.grid_rowconfigure(0, weight = 1)
#set the geometry of the main window
mainwin.geometry("{}x{}+{}+{}".format(appWinWidth, appWinHeight, appWinX, appWinY))
mainwin.resizable(width = 0, height = 0)
#create labels in frames
frame2.grid_propagate(0) #labels and other widgets in frame don't change the size of the frame
frame2.grid()
labelF2 = Label(frame2)
labelF2.grid()
labelF2.place(relx=0.5, rely=0.5, anchor="center")
frame1.grid_propagate(0)
labelF1 = Label(frame1, background = "white")
labelF1.grid()
mainwin.update()
#get camera feed and frame size
cap = cv2.VideoCapture(0)
capFrameWidth = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
capFrameHeight = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
widthRelation = capFrameWidth/frame2.winfo_width()
heigthRelation = capFrameHeight/frame2.winfo_height()
#define the size of the video frame so that the video would fit into the frame of the main window
if widthRelation > 1 and widthRelation > heigthRelation:
fittedSize = (int(capFrameWidth/widthRelation), int(capFrameHeight/widthRelation))
elif heigthRelation > 1 and heigthRelation > widthRelation:
fittedSize = (int(capFrameWidth/heigthRelation), int(capFrameHeight/heigthRelation))
else:
fittedSize = (capFrameWidth, capFrameHeight)
#funtion for getting a video frame, resizing it for the main window and placing it into the frame 2
def video_to_mainwin():
chk, frame = cap.read()
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
imgV = cv2.resize(cv2image, fittedSize)
img = Image.fromarray(imgV)
imgtk = ImageTk.PhotoImage(image=img)
labelF2.imgtk = imgtk
labelF2.configure(image=imgtk)
#run the video function continuously to create a stream, and be ready to react to key presses
while True:
key = cv2.waitKey(1)
if key == ord('p'): #if 'p' is pressed, pause the stream and write 'Paused' to frame 1 on the main window
video_to_mainwin()
labelF1.config(text = 'Paused')
mainwin.update()
key2 = cv2.waitKey(0) #remain paused until 'c' is pressed
if key2 == ord('c'):
labelF1.config(text = '')
else: #if no key is pressed, then just show the video stream
video_to_mainwin()
mainwin.update()
如果您在 tkinter
中显示 cv2
图像,则不需要 cv2.waitKey()
。
你可以使用 tkinter
来完成这个
mainwin.bind("p", function_name)
当您按 p
. 时, 变为 运行 function_name()
当您使用 bind()
时,您不需要 while True
来检查按下的键,因为 tkinter
mainloop 会为您完成。
您只需要 root.after(milliseconds, function_name)
到 运行 函数,它会每隔几毫秒在 window 中更新图像。
工作代码
#from tkinter import * # PEP8: `import *` is not preferred
#from tkinter.ttk import * # PEP8: `import *` is not preferred
import tkinter as tk
from tkinter import ttk
from PIL import ImageTk, Image
from cv2 import cv2
# --- constants --- (PEP8: UPPER_CASE_NAMES)
#FPS = 25
# --- classes --- (PEP8: CamelCaseNames)
# ... empty ...
# --- functions --- (PEP8: lower_case_names)
def video_to_mainwin():
"""Getting a video frame, resizing it for the main window and placing it into the frame 2."""
if not paused:
chk, frame = cap.read()
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
imgV = cv2.resize(cv2image, fitted_size)
img = Image.fromarray(imgV)
imgtk = ImageTk.PhotoImage(image=img)
labelF2.imgtk = imgtk
labelF2.configure(image=imgtk)
# run it again after `1000/FPS` milliseconds
#mainwin.after(int(1000/FPS), video_to_mainwin)
mainwin.after(int(1000/cap_fps), video_to_mainwin)
def set_pause(event):
global paused
paused = True
labelF1.config(text = 'Paused')
def unset_pause(event):
global paused
paused = False
labelF1.config(text = '')
# --- main ---- (PEP8: lower_case_names)
paused = False # default value at start
# - cap -
cap = cv2.VideoCapture(0)
cap_frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
cap_frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
cap_fps = int(cap.get(cv2.CAP_PROP_FPS))
# - tk -
mainwin = tk.Tk() # create main window
app_win_width = int(0.6 * mainwin.winfo_screenwidth()) # set main window size (depending on the screen)
app_win_height = int(0.5 * mainwin.winfo_screenheight())
screen_width = mainwin.winfo_screenwidth() # get screen dimensions
screen_height = mainwin.winfo_screenheight()
app_win_x = int((screen_width - app_win_width)/2) # set coordinates of the main window so that it
app_win_y = int((screen_height - app_win_height)/2) # would be located in the middle of the screen
#create and place the frames
frame1 = tk.Frame(mainwin, background="white", width=int(app_win_width/2), height=app_win_height)
frame2 = tk.Frame(mainwin, background="black", width=int(app_win_width/2), height=app_win_height)
frame1.grid(row=0, column=0, sticky="nsew") # PEP8: without spaces around `=`
frame2.grid(row=0, column=1, sticky="nsew") # PEP8: without spaces around `=`
mainwin.grid_columnconfigure(0, weight=1) # PEP8: without spaces around `=`
mainwin.grid_columnconfigure(1, weight=1) # PEP8: without spaces around `=`
mainwin.grid_rowconfigure(0, weight=1)
#set the geometry of the main window
mainwin.geometry("{}x{}+{}+{}".format(app_win_width, app_win_height, app_win_x, app_win_y))
mainwin.resizable(width=0, height=0)
# create labels in frames
frame2.grid_propagate(0) # labels and other widgets in frame don't change the size of the frame
frame2.grid()
labelF2 = tk.Label(frame2)
labelF2.place(relx=0.5, rely=0.5, anchor="center")
frame1.grid_propagate(0)
labelF1 = tk.Label(frame1, background = "white")
labelF1.grid()
mainwin.update() # to create window and correctly calculate sizes
# get camera feed and frame size
width_relation = cap_frame_width/frame2.winfo_width()
heigth_relation = cap_frame_height/frame2.winfo_height()
# define the size of the video frame so that the video would fit into the frame of the main window
if width_relation > 1 and width_relation > heigth_relation:
fitted_size = (int(cap_frame_width/width_relation), int(cap_frame_height/width_relation))
elif heigth_relation > 1 and heigth_relation > width_relation:
fitted_size = (int(cap_frame_width/heigth_relation), int(cap_frame_height/heigth_relation))
else:
fitted_size = (cap_frame_width, cap_frame_height)
# assing functions to buttons
mainwin.bind("p", set_pause)
mainwin.bind("c", unset_pause)
# star video
video_to_mainwin()
# start tkinter mainloop (event loop)
mainwin.mainloop()
# - end -
cap.release()
PEP 8 -- Style Guide for Python Code
我在 Github 上有几个 cv2
和 tkinter
的例子。
例如:python-cv2-streams-viewer 在分隔 thread
中使用 cv2
从网络摄像头或文件(甚至从互联网上的文件)读取。它还使用 tk.Frame
创建显示来自 cv2
的流的小部件。它多次使用此小部件以同时显示多个流。
我用 tkinter 设计了一个 window 并希望在那里显示视频流,我还希望能够使用“p”键暂停视频。我创建了一个函数,可以将网络摄像头拍摄的照片放入 window 上的标签中。然后我把这个函数放到while循环里反复调用,在window里做一个连续的视频。我在循环中使用 waitKey() 函数来读取按键。 waitKey() 还设置视频的帧速率或速度。我设法获取了视频 运行,但该程序对按键没有反应。另一方面,我可以在更改 waitKey() 的参数时更改帧速率,因此该函数似乎可以工作,但它不会读取按键,按下按键时也没有任何反应。也没有错误消息。如果有人能告诉我如何在此循环中使用 waitKey() 以便我可以通过按键控制视频流,或者建议一种不同的方法,我将不胜感激。这是我的代码,其中大部分是设计 window 但带有 waitKey() 函数的循环在最后:
import tkinter as tk
from tkinter import *
from tkinter import ttk
from tkinter.ttk import *
from PIL import ImageTk, Image
from cv2 import cv2
mainwin = Tk() #create main window
appWinWidth = int(0.6 * mainwin.winfo_screenwidth()) #set main window size (depending on the screen)
appWinHeight = int(0.5 * mainwin.winfo_screenheight())
screen_width = mainwin.winfo_screenwidth() #get screen dimensions
screen_height = mainwin.winfo_screenheight()
appWinX = int((screen_width - appWinWidth)/2) #set coordinates of the main window so that it
appWinY = int((screen_height - appWinHeight)/2) #would be located in the middle of the screen
#create and place the frames
frame1 = tk.Frame(mainwin, background = "white", width = int(appWinWidth/2), height = appWinHeight)
frame2 = tk.Frame(mainwin, background = "black", width = int(appWinWidth/2), height = appWinHeight)
frame1.grid(row = 0, column = 0, sticky = "nsew")
frame2.grid(row = 0, column = 1, sticky = "nsew")
mainwin.grid_columnconfigure(0, weight = 1)
mainwin.grid_columnconfigure(1, weight = 1)
mainwin.grid_rowconfigure(0, weight = 1)
#set the geometry of the main window
mainwin.geometry("{}x{}+{}+{}".format(appWinWidth, appWinHeight, appWinX, appWinY))
mainwin.resizable(width = 0, height = 0)
#create labels in frames
frame2.grid_propagate(0) #labels and other widgets in frame don't change the size of the frame
frame2.grid()
labelF2 = Label(frame2)
labelF2.grid()
labelF2.place(relx=0.5, rely=0.5, anchor="center")
frame1.grid_propagate(0)
labelF1 = Label(frame1, background = "white")
labelF1.grid()
mainwin.update()
#get camera feed and frame size
cap = cv2.VideoCapture(0)
capFrameWidth = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
capFrameHeight = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
widthRelation = capFrameWidth/frame2.winfo_width()
heigthRelation = capFrameHeight/frame2.winfo_height()
#define the size of the video frame so that the video would fit into the frame of the main window
if widthRelation > 1 and widthRelation > heigthRelation:
fittedSize = (int(capFrameWidth/widthRelation), int(capFrameHeight/widthRelation))
elif heigthRelation > 1 and heigthRelation > widthRelation:
fittedSize = (int(capFrameWidth/heigthRelation), int(capFrameHeight/heigthRelation))
else:
fittedSize = (capFrameWidth, capFrameHeight)
#funtion for getting a video frame, resizing it for the main window and placing it into the frame 2
def video_to_mainwin():
chk, frame = cap.read()
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
imgV = cv2.resize(cv2image, fittedSize)
img = Image.fromarray(imgV)
imgtk = ImageTk.PhotoImage(image=img)
labelF2.imgtk = imgtk
labelF2.configure(image=imgtk)
#run the video function continuously to create a stream, and be ready to react to key presses
while True:
key = cv2.waitKey(1)
if key == ord('p'): #if 'p' is pressed, pause the stream and write 'Paused' to frame 1 on the main window
video_to_mainwin()
labelF1.config(text = 'Paused')
mainwin.update()
key2 = cv2.waitKey(0) #remain paused until 'c' is pressed
if key2 == ord('c'):
labelF1.config(text = '')
else: #if no key is pressed, then just show the video stream
video_to_mainwin()
mainwin.update()
如果您在 tkinter
中显示 cv2
图像,则不需要 cv2.waitKey()
。
你可以使用 tkinter
来完成这个
mainwin.bind("p", function_name)
当您按 p
. 时, 变为 运行 function_name()
当您使用 bind()
时,您不需要 while True
来检查按下的键,因为 tkinter
mainloop 会为您完成。
您只需要 root.after(milliseconds, function_name)
到 运行 函数,它会每隔几毫秒在 window 中更新图像。
工作代码
#from tkinter import * # PEP8: `import *` is not preferred
#from tkinter.ttk import * # PEP8: `import *` is not preferred
import tkinter as tk
from tkinter import ttk
from PIL import ImageTk, Image
from cv2 import cv2
# --- constants --- (PEP8: UPPER_CASE_NAMES)
#FPS = 25
# --- classes --- (PEP8: CamelCaseNames)
# ... empty ...
# --- functions --- (PEP8: lower_case_names)
def video_to_mainwin():
"""Getting a video frame, resizing it for the main window and placing it into the frame 2."""
if not paused:
chk, frame = cap.read()
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
imgV = cv2.resize(cv2image, fitted_size)
img = Image.fromarray(imgV)
imgtk = ImageTk.PhotoImage(image=img)
labelF2.imgtk = imgtk
labelF2.configure(image=imgtk)
# run it again after `1000/FPS` milliseconds
#mainwin.after(int(1000/FPS), video_to_mainwin)
mainwin.after(int(1000/cap_fps), video_to_mainwin)
def set_pause(event):
global paused
paused = True
labelF1.config(text = 'Paused')
def unset_pause(event):
global paused
paused = False
labelF1.config(text = '')
# --- main ---- (PEP8: lower_case_names)
paused = False # default value at start
# - cap -
cap = cv2.VideoCapture(0)
cap_frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
cap_frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
cap_fps = int(cap.get(cv2.CAP_PROP_FPS))
# - tk -
mainwin = tk.Tk() # create main window
app_win_width = int(0.6 * mainwin.winfo_screenwidth()) # set main window size (depending on the screen)
app_win_height = int(0.5 * mainwin.winfo_screenheight())
screen_width = mainwin.winfo_screenwidth() # get screen dimensions
screen_height = mainwin.winfo_screenheight()
app_win_x = int((screen_width - app_win_width)/2) # set coordinates of the main window so that it
app_win_y = int((screen_height - app_win_height)/2) # would be located in the middle of the screen
#create and place the frames
frame1 = tk.Frame(mainwin, background="white", width=int(app_win_width/2), height=app_win_height)
frame2 = tk.Frame(mainwin, background="black", width=int(app_win_width/2), height=app_win_height)
frame1.grid(row=0, column=0, sticky="nsew") # PEP8: without spaces around `=`
frame2.grid(row=0, column=1, sticky="nsew") # PEP8: without spaces around `=`
mainwin.grid_columnconfigure(0, weight=1) # PEP8: without spaces around `=`
mainwin.grid_columnconfigure(1, weight=1) # PEP8: without spaces around `=`
mainwin.grid_rowconfigure(0, weight=1)
#set the geometry of the main window
mainwin.geometry("{}x{}+{}+{}".format(app_win_width, app_win_height, app_win_x, app_win_y))
mainwin.resizable(width=0, height=0)
# create labels in frames
frame2.grid_propagate(0) # labels and other widgets in frame don't change the size of the frame
frame2.grid()
labelF2 = tk.Label(frame2)
labelF2.place(relx=0.5, rely=0.5, anchor="center")
frame1.grid_propagate(0)
labelF1 = tk.Label(frame1, background = "white")
labelF1.grid()
mainwin.update() # to create window and correctly calculate sizes
# get camera feed and frame size
width_relation = cap_frame_width/frame2.winfo_width()
heigth_relation = cap_frame_height/frame2.winfo_height()
# define the size of the video frame so that the video would fit into the frame of the main window
if width_relation > 1 and width_relation > heigth_relation:
fitted_size = (int(cap_frame_width/width_relation), int(cap_frame_height/width_relation))
elif heigth_relation > 1 and heigth_relation > width_relation:
fitted_size = (int(cap_frame_width/heigth_relation), int(cap_frame_height/heigth_relation))
else:
fitted_size = (cap_frame_width, cap_frame_height)
# assing functions to buttons
mainwin.bind("p", set_pause)
mainwin.bind("c", unset_pause)
# star video
video_to_mainwin()
# start tkinter mainloop (event loop)
mainwin.mainloop()
# - end -
cap.release()
PEP 8 -- Style Guide for Python Code
我在 Github 上有几个 cv2
和 tkinter
的例子。
例如:python-cv2-streams-viewer 在分隔 thread
中使用 cv2
从网络摄像头或文件(甚至从互联网上的文件)读取。它还使用 tk.Frame
创建显示来自 cv2
的流的小部件。它多次使用此小部件以同时显示多个流。