1

私は、ユーザーが一度に約 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 インスタンスが必要です。私は 2 つの異なるアプローチを実装しましたが、それぞれに関連する問題があります。

最初のアプローチ: 画像をプリロードする別のスレッドを起動します。

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

これは、私の Linux マシンでは非常にうまく機能します。ただし、別のマシン (たまたま Windows を実行していて、pyinstaller でコンパイルされている) では、デッドロックが発生しているようです。「Before Photoimage」が出力された後、プログラムがフリーズします。これは、ローダー スレッドが ImageTk.PhotoImage オブジェクトの作成でスタックしたことを示しています。メイン (Tkinter メインループの) スレッド内で ImageTk.PhotoImage オブジェクトを作成する必要がありますか? PhotoImage の作成は計算コストが高いですか、それとも実際にディスクからイメージをロードする場合と比較して無視できますか?

2 番目のアプローチ: PhotoImage オブジェクトが Tkiner のメインループ スレッド内から作成されるというこの可能性のある要件を回避するために、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 をフリーズさせることです。実行中に発生したキーボードイベントを消費しているようです。

3 番目のアプローチ 保留中のユーザー イベントを検出する方法はありますか? どうすればこのようなことを達成できますか?

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') を使用して、保留中のキーボード イベントを確認しようとしました。これは常に確実であるとは限らず、インターフェイスは依然として遅く、応答しません。

アイデアをお寄せいただきありがとうございます

4

1 に答える 1