5

私は現在、neovimのサンプル UI を実装しており、プラットフォームの人気/シンプルさから Tkinter/python を使用することにしました。私が抱えている問題は、ウィンドウの高さが特定のしきい値を超えると、tkinter が UI の更新を「スタック」するように見えることです。

これが問題を示すビデオです。

右側のウィンドウは neovim を実行するターミナル エミュレーターで、左側のウィンドウはそれに接続された Tkinter UI プログラムです。アイデアは、tkinter UI が寸法を含む neovim 端末画面をミラーリングする必要があるということです。このビデオでは、ターミナル ウィンドウからフォーカスを外さないため、Tk が処理しなければならない唯一のイベントは、neovim (画面の更新を記述する仮想 'nvim' イベント) への接続から発生します。

ビデオの最初の部分は、ウィンドウの高さが小さい場合はすべてがうまく機能することを示していますが、高さを上げると更新が遅れ始めます。

Tkinter プログラムのコードは次のとおりですneovim API は非常に新しく、まだ大規模な開発が行われていますが (一部の読者にはコードが意味をなさない可能性があります)、私が解決しようとしている問題は、(Tk テキスト ウィジェットを使用して) ターミナル エミュレーターを実装することに近いと思います。フォーマットされたテキストのバーストが効率的に更新されます。

私はGUIプログラミングに非常に不慣れです。Tkinter はこのタスクに賢明な選択ですか? はいの場合、誰かが私が間違っていることのヒントを教えてくれますか?

何が起こっているのかを少し説明するには: Neovim API はスレッドセーフでありvim.next_event()、イベントが受信されるまでメソッドはブロックされます (ビジー待機なしで、下で libuv イベントループを使用します)。

呼び出しが戻ると、vim.next_event()を使用して Tkinter スレッドに通知し、実際のイベント処理を行います (画面の更新を最適化するためにとgenerate_eventの間のイベントもバッファリングします)。redraw:startredraw:stop

そのため、実際には 2 つのイベント ループが並行して実行されており、バックグラウンド イベント ループがスレッド セーフな方法で Tkinter イベント ループにフィードしています (このgenerate_eventメソッドは、他のスレッドから呼び出すことができる数少ないメソッドの 1 つです)。

4

2 に答える 2

1

実際、それが Tkinter であることを再確認します。私がこれを行う方法は、イベントを取得したときに端末に書き込むだけです。

しかし、よく見てみると、これはあなたの問題かもしれません:

    t = Thread(target=get_nvim_events, args=(self.nvim_events,
                                             self.vim,
                                             self.root,))

スレッドは、イベント ループではうまく機能しません。Tkinter には既にイベント ループがあります。neovim api がコールバックを使用するようにセットアップされているかどうかはわかりませんが、それは通常、変更を伝達する方法です。

あなたは GUI プログラミングに慣れていないと言うので、イベント ループの概念に慣れていないと仮定します。基本的に、次のようなコードがあるとします。

while True:
    if something_to_do:
        do_it_now()

明らかにそれはビジー ループであり、CPU を焼き尽くします。そのため、通常、イベント ループは OS でコールバックをブロックまたはセットアップします。これにより、CPU を解放し、何か興味深いことが発生すると、OS は次のように言います。 、」または「誰かがキーを押しました」または「ねえ、今あなたを起こすように私に言いました!」

したがって、GUI 開発者としてのあなたの仕事は、そのイベント ループに接続することです。あなたは何かがいつ起こっても気にしません- あなたはそれに反応したいだけです. Tkinter では、「非イベント コールバック」を参照.afterしてください。良い選択は次の方法です。.after_idle

システムがアイドル状態のときに呼び出されるコールバックを登録します。コールバックが呼び出され、メインループで処理するイベントがなくなります。コールバックは、after_idle への呼び出しごとに 1 回だけ呼び出されます。

これは、キーの押下やマウスのクリックをブロックせず、Tkinter が他の処理 (描画、コールバックの呼び出しなど) を終了した後にのみ実行されることを意味します。

スレッドとメインループに問題が発生している可能性があると思います (おそらく GIL のおかげです)。私は周りを見回しましたが、すぐに明らかなことは何も見ませんでしたが、あなたがやりたいことは次のようなものです:

def do_something(arg):
    # do something with `arg` here


def event_happened(event_args): #whatever args the event generates
    root.after_idle(lambda: do_something(event_args))

vim.bind("did_something", event_happened)

もちろん、イベント ループを完全にバイパスして、イベントに必要な処理をさせることもできます。

于 2014-06-18T19:23:53.620 に答える