在没有GUI冻结的情况下,在主循环旁边运行Tkinter相关代码

时间:2013-09-04 05:45:35

标签: python multithreading image user-interface tkinter

我正在编写一个简单的图像查看器,让用户可以非常快速地翻阅数万张图像,一次大约100张。图像是磁盘上的文件。

为了使观看者能够正常工作,它必须在用户当前的图像之前连续预加载图像(否则观看者会显得迟钝)。

我用来在Tkinter标签网格中显示图像的基本配方如下(已经过测试并且有效):

def load_image(fn):
    image = Image.open(fn)
    print "Before photoimage"
    img = ImageTk.PhotoImage(image)
    print "After photoimage"

label.config(image=load_image("some_image.png")

我需要ImageTk.PhotoImage实例来在标签上显示图像。我已经实现了两种不同的方法,每种方法都有相关的问题。

第一种方法: 启动一个预先加载图像的单独线程:

def load_ahead():
    for fn in images:
        cache[fn] = load_image()
threading.Thread(target=load_ahead).start()
top.mainloop()

这在我的Linux机器上运行得很好。但是,在另一台机器上(恰好运行Windows,并使用pyinstaller编译),似乎发生了死锁。打印“之前的Photoimage”,然后程序冻结,这表明加载程序线程在创建ImageTk.PhotoImage对象时卡住了。最好在主(Tkinter主循环)线程中发生ImageTk.PhotoImage对象的创建? PhotoImage的创建在计算上是否昂贵,或者与从磁盘实际加载图像相比可以忽略不计?

第二种方法: 为了避免在Tkiner的主循环线程中创建PhotoImage对象的这种可能要求,我使用了Tk.after:

def load_some_images():
    #load only 10 images. function must return quickly to prevent freezing GUI
    for i in xrange(10): 
        fn = get_next_image()
        cache[fn] = load_image(fn)
    top.after_idle(load_some_images)

top.after_idle(load_some_images)

这个问题是,appart从创建额外的开销(即图像加载过程必须分解成非常小的块,因为它与GUI竞争)它会在调用期间定期冻结GUI ,它似乎消耗了执行过程中发生的任何键盘事件。

第三种方法 有没有办法可以检测到待处理的用户事件?我怎样才能完成这样的事情?

def load_some_images():
    while True:
        try: top.pending_gui_events.get_nowait()
        except: break
        #user is still idle! continuing caching of images
        fn = get_next_image()
        cache[fn] = load_image(fn)
    top.after_idle(load_some_images)

top.after(5,load_some_images)

编辑:我尝试使用top.tk.call('after','info')来检查挂起的键盘事件。这并不总是可靠的,并且界面仍然缓慢/无响应。

提前感谢任何想法

1 个答案:

答案 0 :(得分:2)

我建议创建load_one_image函数而不是load_some_images函数。它不太可能干扰事件循环。

另外,根据经验,通过after_idle调用的函数不应该使用after_idle自行重新安排。原因是after_idle将阻塞,直到空闲事件队列耗尽。如果在处理队列时继续向队列添加内容,则永远不会完全耗尽。这可能就是为什么你的GUI似乎偶尔会用你的第二种方法挂起的原因。

尝试after(5, ...)而不是after_idle(...)。如果你的系统可以在不到5ms的时间内创建一个图像,你可以在大约半秒内处理100个图像,这可能足够快,可以提供一个非常活泼的界面。您可以调整延迟,看看它如何影响应用程序的整体感觉。