汎用的・抽象的な回答
あえて申し上げれば「どんな処理にでも向いたような方式」はないように思えます。
t_obaraさん、magichanさんが質問コメントを寄せておられますがその真意はおそらく質問者さんがやろうとしていることの詳細がわからないと回答できないとおっしゃっているわけではなく、「どのような類の処理なのか」を言えばよいだけと思います。
自分はご質問のコードからGUIの状態変更=>単純な2Dアニメーション的なものを実現するのが目的と想定してコメントしてみます。
ボタンを押している間だけ処理を実行するというイベントフォーマットは
イベントフォーマットというよりイベントシーケンスといった方がよいかと思いますが、そのようなものはないと思います。
tkinter自体にそのようなイベントシーケンスがないなら、なんらかの方法で自前で定義することになるでしょう。そのようなことを多用したいとき「多少なりともシンプル」にと考えたくなりますね。実現方法にはいろいろな工夫があり得ると思いますが例を2つほど挙げてみます。
(なお、質問者さんのコードは別のスレッドで「時間間隔を置かずに闇雲にGUIを変更し続ける」というものですがアニメーションかどうかに限らず「CPUを無暗に浪費するビジーループ的な処理」はお勧めできませんので「ある一定のインターバルでイベントハンドラーを起動する」という考え方を前提にします。)
案1 単に共通関数にする
<Button-1>と<ButtonRelease-1>を望むウィジェットに設定し、押されている間特定の関数を指定インターバルごとに起動させるような共通関数を定義する方法です。
python
1import tkinter as tk
2
3
4def bind_button1_down(widget, handler, interval=10):
5 assert interval > 0
6 e = None
7
8 def on_button1_down(ev):
9 nonlocal e
10 e = ev
11 on_tick()
12
13 def on_button1_released(ev):
14 nonlocal e
15 e = None
16
17 def on_tick():
18 if e:
19 handler(e)
20 widget.after(interval, on_tick)
21
22 widget.bind('<Button-1>', on_button1_down)
23 widget.bind('<ButtonRelease-1>', on_button1_released)
24
25
26root = tk.Tk()
27root.geometry("500x500")
28
29button = tk.Button(root, text="press me")
30bind_button1_down(button, lambda ev: label.place(x=0, y=(label.winfo_y() + 1) % 450), interval=1)
31button.pack()
32
33label = tk.Label(root, text="***")
34label.place(x=0, y=0)
35root.mainloop()
案2 カスタムイベントが発生するクラスを定義
別にクラスでなくても関数でもいいのですが・・・案1だとイベント設定がtkinterの他のイベントと雰囲気が違うので「あたかもそういうイベントが最初からあるかのように書ける(だけ)」というものです。
python
1import tkinter as tk
2
3
4class TkEx(tk.Tk):
5 def __init__(self, *args, **kwargs):
6 super().__init__(*args, **kwargs)
7 self.bind_all('<Button-1>', self.on_button1_pressed)
8 self.bind_all('<ButtonRelease-1>', self.on_button1_released)
9 self._button1down_event = None
10 self._button1down_interval = 10
11
12 def on_button1_pressed(self, ev):
13 self._button1down_event = ev
14 self.on_tick()
15
16 def on_button1_released(self, ev):
17 self._button1down_event = None
18
19 def on_tick(self):
20 if self._button1down_event:
21 self._button1down_event.widget.event_generate('<<ButtonDown-1>>', when='tail')
22 self.after(self._button1down_interval, self.on_tick)
23
24 def set_button1_donw_interval(self, interval):
25 self._button1down_interval = interval
26
27
28root = TkEx()
29root.geometry('500x500')
30root.set_button1_donw_interval(1)
31
32button = tk.Button(root, text="press me")
33button.bind('<<ButtonDown-1>>', lambda ev: label.place(x=0, y=(label.winfo_y() + 1) % 450))
34button.pack()
35
36label = tk.Label(root, text="***")
37label.place(x=0, y=0)
38root.mainloop()
上の例は非常に単純化したものでボタン1に対してしか機能がありませんし、インターバルをボタンごとに変更するためにはもうちょっと工夫せねばなりません。そんなことを考えるにつけ、わざわざこんないびつな仕様のクラスを定義するよりは案1の共通関数ぐらいで充分な気がしました。コード例を書いておいてなんですが・・・上の例は筋が悪い感じがするのです。
追記:
magichanさんから有益なコメントをいただいています。回答コメントを見ていただけると例1/例2のいずれもいろいろ配慮すべき点があることがわかると思います。回答に書いたコードは「あくまでたたき台と思っていただきたい」点を強調しておきます。
元の回答でターゲット個別へのインターバル指定云々と書きましたが、それは一つの考慮点にしかすぎずもっといろいろ考えるべき点がありそうです。(例えば上のコード例ではボタンリリース時に状態を変え、リリース後に発火したafterハンドラーでコールバックを起動しない配慮をしていますが、after_cancelとした方がスマートに感じます。)