我正在编写一个简单的图像查看器,它让用户可以非常快速地翻阅数万张图像,一次大约 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 对象时卡住了。ImageTk.PhotoImage 对象的创建必须发生在主(Tkinter 主循环)线程中吗?PhotoImage 的创建在计算上是昂贵的,还是与实际从磁盘加载图像相比可以忽略不计?
第二种方法: 为了避免从 Tkiner 的 mainloop 线程中创建 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)
这样做的问题是,除了创建额外的开销(即图像加载过程必须分解成非常小的块,因为它与 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') 检查待处理的键盘事件。这并不总是可靠的,并且界面仍然缓慢/无响应。
提前感谢您的任何想法