naritoブログ

【お知らせ】
新ブログができました。今後そちらで更新し、このサイトは更新されません(ウェブサイト自体は残しておきます)
このブログの内容に関してコメントしたい場合は、新ブログのフリースペースに書き込んでください

このブログの内容を新ブログに移行中です。このブログで見つからない記事は、新ブログにありま

Tkinterで、Textウィジェットにon_change、on_scrollイベントを作る

約425日前 2017年10月12日16:03
プログラミング関連
Python Tkinter
TkinterのTextウィジェットには、他のGUIライブラリによくあるon_change やon_scrollのようなイベントはありません。(ないよね?)
なので、これらが欲しい場合は自前で書く必要があります。

スタックオーバーフローには、そのためのナイスな解決策がありました。
https://stackoverflow.com/questions/16369470/tkinter-adding-line-number-to-text-widget/16375233#16375233

これを基に作成したのが、以下のコードです。

import tkinter as tk


class CustomText(tk.Text):
"""Textの、イベントを拡張したウィジェット."""

def __init__(self, master, **kwargs):
super().__init__(master, **kwargs)
self.tk.eval('''
proc widget_proxy {widget widget_command args} {
set result [uplevel [linsert $args 0 $widget_command]]
if {([lrange $args 0 1] == {xview moveto}) ||
([lrange $args 0 1] == {xview scroll}) ||
([lrange $args 0 1] == {yview moveto}) ||
([lrange $args 0 1] == {yview scroll})} {
event generate $widget <<Scroll>> -when tail
}
if {([lindex $args 0] in {insert replace delete})} {
event generate $widget <<Change>> -when tail
}
# return the result from the real widget command
return $result
}
''')
self.tk.eval('''
rename {widget} _{widget}
interp alias {{}} ::{widget} {{}} widget_proxy {widget} _{widget}
'''.format(widget=str(self)))


root = tk.Tk()
root.title('Editor Test')

text_widget = CustomText(root)
text_widget.grid(column=0, row=0, sticky=(tk.N, tk.S, tk.E, tk.W))

# on_changeと、on_scrollイベントの設定。printするだけ
text_widget.bind('<<Scroll>>', lambda event: print('scroll'))
text_widget.bind('<<Change>>', lambda event: print('change'))

root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)
root.mainloop()




tkinterにはTcl/Tkレベルの機能の呼び出しや、Tcl/Tkコードを評価する仕組みがあります。その一つがtk.evalです。
非常に面白い例だと、例えば以下のようなことができます。

import tkinter as tk


class MyTk(tk.Tk):

def __init__(self):
super().__init__()
self.eval("""
wm title . "Test"
grid [ttk::button .b -text "Hello World"]
""")


root = MyTk()
root.mainloop()




ちゃんと表示されました!



self.eval内にはTcl/Tkコードを書いています。タイトルの設定と、ttk.Buttonを作成しgridレイアウトで配置しています。
殆どの場合、Tkinterを使う上でTcl/Tkコードを書く必要はないのですが、今回のような例もあります。


tk.evalを使うのに抵抗があるのならば、以下のようなやり方もできます。
https://stackoverflow.com/questions/40617515/python-tkinter-text-modified-callback