Tkinter 嵌套主循环

Posted

技术标签:

【中文标题】Tkinter 嵌套主循环【英文标题】:Tkinter nested mainloop 【发布时间】:2013-06-19 13:51:33 【问题描述】:

我正在用 tkinter/python 编写一个视频播放器,所以基本上我有一个可以播放视频的 GUI。现在,我想实现一个停止按钮,这意味着我将有一个用于 GUI 的mainloop(),另一个嵌套的mainloop() 用于播放/停止视频并返回到 GUI 启动窗口。 Here,据说:

事件循环可以嵌套;可以从事件处理程序中调用 mainloop。

但是,我不明白如何实现这种嵌套。有人可以给我一个这样的脚本的简单例子吗?

编辑

这是我的代码的工作版本,因为我似乎在做一些异国情调的事情。当然,我是新手,所以我可能对嵌套主循环的必要性有误。

#!/usr/bin/python

import numpy as np
from multiprocessing import Process, Queue
import cv2
import cv2.cv as cv
from PIL import Image, ImageTk
import Tkinter as tk

def image_capture(queue):
    vidFile = cv2.VideoCapture(0)
    while True:
        flag, frame=vidFile.read()
        frame = cv2.cvtColor(frame,cv2.cv.CV_BGR2RGB)
        queue.put(frame)
        cv2.waitKey(10)

def update_all(root, imagelabel, queue, process, var):
    if var.get()==True:
        im = queue.get()
        a = Image.fromarray(im)
        b = ImageTk.PhotoImage(image=a)
        imagelabel.configure(image=b)
        imagelabel._image_cache = b  # avoid garbage collection
        root.update()
        root.after(0, func=lambda: update_all(root, imagelabel, queue, process, var))
    else:
        print var.get()
        root.quit()

def playvideo(root, imagelabel, queue, var):
    print 'beginning'
    p = Process(target=image_capture, args=(task,))
    p.start()
    update_all(root, imagelabel, queue, p, var)
    print 'entering nested mainloop'
    root.mainloop()
    p.terminate()
    if var.get()==False:
        im = ImageTk.PhotoImage(file='logo.png')
        imagelabel.configure(image=im)
        imagelabel._image_cache = im  # avoid garbage collection
        root.update()
    var.set(True)
    print 'finishing'

if __name__ == '__main__':
    #initialize multiprocessing
    task = Queue()
    #GUI of root window
    root = tk.Tk()
    #the image container
    image_label = tk.Label(master=root)
    image_label.grid(column=0, row=0, columnspan=2, rowspan=1)
    #fill label with image until video is loaded
    bg_im = ImageTk.PhotoImage(file='logo.png')
    image_label['image'] = bg_im
    #frame for buttons
    button_frame = tk.Frame(root)
    button_frame.grid(column=0, row=1, columnspan=1)
    #load video button and a switch to wait for the videopath to be chosen
    load_button = tk.Button(master=button_frame, text='Load video',command=lambda: playvideo(root, image_label, task, switch))
    load_button.grid(column=0, row=0, sticky='ew')
    #Stop button
    switch = tk.BooleanVar(master=root, value=True, name='switch')
    stop_button = tk.Button(master=button_frame, text='Stop',command=lambda: switch.set(False))
    stop_button.grid(column=0, row=1, sticky='ew')
    #quit button
    quit_button = tk.Button(master=button_frame, text='Quit',command=root.destroy)
    quit_button.grid(column=0, row=2, sticky='ew')
    root.mainloop()

【问题讨论】:

虽然有可能,但很少需要调用它。这样做的理由,而你试图做的事情可能不会像你想象的那样奏效。 实际上,它确实像我希望的那样工作......当然,我不确定这是不是最好的方法。 这里听起来有些可疑。为什么播放视频会阻塞主事件循环? ...如果它阻塞了主循环,为什么它阻塞对主循环的嵌套调用? 播放视频不会阻塞 mainevent 循环。但我发现能够按我喜欢的次数播放/停止视频的唯一方法是做一个嵌套的主循环。我正在编辑我的问题以向您展示我的代码(据我所知,这是有效的)。 【参考方案1】:

这应该是 Tkinter 中嵌套主循环的示例:

import Tkinter

def main():
    print 'main'
    t.mainloop()
    print 'end main'

t = Tkinter.Tk()
b = Tkinter.Button(t, command = main)
b.pack()
t.mainloop()

每当您点击按钮时,都会执行一个新的主循环。

main
main
main
main
main
# now close the window
end main
end main
end main
end main
end main

【讨论】:

以上是关于Tkinter 嵌套主循环的主要内容,如果未能解决你的问题,请参考以下文章

Python D-Bus 和 Tkinter 主循环集成

Tkinter/Matplotlib 后端冲突导致无限主循环

为啥我的主循环在 tkinter 中不起作用?

Tkinter:在主循环中调用事件

在 Tkinter 主循环期间录制 OpenCV 视频

Tkinter:如何使用线程来防止主事件循环“冻结”