[英]How to modify this class with a "destroy" attribute/function?
您好,这是 github 上的一个模块,用于在 tkinter上播放视频。 我发现它满足了我的需求,但不幸的是它缺少“破坏”属性(例如 video.destroy())。 当我这样做时,它返回“AttributeError:'tkvideo2' object 没有属性'destroy'”。 我试着询问模块海报,但他似乎已经放弃了这个项目。 有人可以帮忙吗?
代码:
try:
import Tkinter as tk # for Python2 (although it has already reached EOL)
except ImportError:
import tkinter as tk # for Python3
import threading
from time import perf_counter, sleep
import imageio
from PIL import Image, ImageTk
class tkvideo():
"""
Main class of tkVideo. Handles loading and playing
the video inside the selected label.
:keyword path:
Path of video file
:keyword label:
Name of label that will house the player
:param loop:
If equal to 0, the video only plays once,
if not it plays in an infinite loop (default 0)
:param size:
Changes the video's dimensions (2-tuple,
default is 640x360)
:param hz:
Sets the video's frame rate (float,
default 0 is unchecked)
"""
def __init__(self, path, label, loop = 0, size = (640,360), hz = 0):
self.path = path
self.label = label
self.loop = loop
self.size = size
self.hz = hz
def load(self, path, label, loop, hz):
"""
Loads the video's frames recursively onto the selected label widget's image parameter.
Loop parameter controls whether the function will run in an infinite loop
or once.
"""
frame_data = imageio.get_reader(path)
if hz > 0:
frame_duration = float(1 / hz)
else:
frame_duration = float(0)
if loop == 1:
while True:
before = perf_counter()
for image in frame_data.iter_data():
frame_image = ImageTk.PhotoImage(Image.fromarray(image).resize(self.size))
label.config(image=frame_image)
label.image = frame_image
diff = frame_duration + before
after = perf_counter()
diff = diff - after
if diff > 0:
sleep(diff)
before = perf_counter()
else:
before = perf_counter()
for image in frame_data.iter_data():
frame_image = ImageTk.PhotoImage(Image.fromarray(image).resize(self.size))
label.config(image=frame_image)
label.image = frame_image
diff = frame_duration + before
after = perf_counter()
diff = diff - after
if diff > 0:
sleep(diff)
before = perf_counter()
def play(self):
"""
Creates and starts a thread as a daemon that plays the video by rapidly going through
the video's frames.
"""
thread = threading.Thread(target=self.load, args=(self.path, self.label, self.loop, self.hz))
thread.daemon = 1
thread.start()
编辑:
Traceback (most recent call last):
File "lib\threading.py", line 973, in _bootstrap_inner
self.run()
File "lib\threading.py", line 910, in run
self._target(*self._args, **self._kwargs)
File "tkvideo2.py", line 60, in load
label.config(image=frame_image)
File "lib\tkinter\__init__.py", line 1646, in configure
return self._configure('configure', cnf, kw)
File "lib\tkinter\__init__.py", line 1636, in _configure
self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
_tkinter.TclError: invalid command name ".!label2"
tkvideo
不是真正的小部件,但 class 仅从视频中读取帧并将它们放入小部件Label
中,您将其用作tkvideo()
中的参数 - 所以您必须销毁此Label
tkvideo
。
但首先你应该停止读取帧的线程。 在tkvideo
你可以使用变量self.running = True
并且在销毁 label 之前你应该设置self.running = False
并且线程应该在每个循环中检查这个值以停止循环和停止线程。
完整的工作示例
import tkinter as tk # for Python3
import threading
from time import perf_counter, sleep
import imageio
from PIL import Image, ImageTk
class tkvideo():
def __init__(self, path, label, loop=False, size=(640,360), hz=0):
self.path = path
self.label = label
self.loop = loop
self.size = size
self.hz = hz
self.running = True # <-- variable to control loop
def load(self, path, label, loop, hz):
"""
Loads the video's frames recursively onto the selected label widget's image parameter.
Loop parameter controls whether the function will run in an infinite loop
or once.
"""
frame_data = imageio.get_reader(path)
if hz > 0:
frame_duration = float(1 / hz)
else:
frame_duration = float(0)
if loop:
while True:
before = perf_counter()
for image in frame_data.iter_data():
frame_image = ImageTk.PhotoImage(Image.fromarray(image).resize(self.size))
if not self.running: # <-- variable to control loop
return # exit function and stop thread
label.config(image=frame_image)
label.image = frame_image
diff = frame_duration + before
after = perf_counter()
diff = diff - after
if diff > 0:
sleep(diff)
before = perf_counter()
else:
before = perf_counter()
for image in frame_data.iter_data():
frame_image = ImageTk.PhotoImage(Image.fromarray(image).resize(self.size))
if not self.running: # <-- variable to control loop
return # exit function and stop thread
label.config(image=frame_image)
label.image = frame_image
diff = frame_duration + before
after = perf_counter()
diff = diff - after
if diff > 0:
sleep(diff)
before = perf_counter()
def play(self):
"""
Creates and starts a thread as a daemon that plays the video by rapidly going through
the video's frames.
"""
thread = threading.Thread(target=self.load, args=(self.path, self.label, self.loop, self.hz))
thread.daemon = 1
thread.start()
# --- main ---
import tkinter as tk
import time
def stop():
video.running = False # <-- variable to control loop
time.sleep(0.1) # thread may need time to stop
label.destroy()
root = tk.Tk()
label = tk.Label(root)
label.pack()
video = tkvideo('video.avi', label)
video.play()
button = tk.Button(root, text="STOP", command=stop)
button.pack()
root.mainloop()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.