HOME Python download 書き込む

12. Tk.Text の使い方


1. 初めに

Tk.Text はテキストを表示したり、ユーザーがテキストを入力するのに使います。 HTML と同じように、表示を飾りつけしたり、コマンドへリンクを張ったりすることができます。 Tkinter demo にも例がありますのでそちらも見てください。 また、詳しい解説は The Tkinter Text Widget あるいは TkRef: 17. The Text widget を見てください。

2. Tk.Text の使い方

2.1. Tk.Text の生成

Tk.Text はほかの widget と同じように親 widget と option を指定して生成します。 主な option には以下があります。詳しくは、
を見てください。

オプション 説明
bg or background テキストの背景色
bd or borderwidth 縁の幅、デフォルトは 2 pixels.
cursor カーソルの形状を指定する。
font フォントの指定
fg or foreground 文字とビットマップの色
height 表示する行数。
padx テキストから縁への左右のマージン
pady テキストから縁への上下のマージン
relief 縁の形状。デフォルトは Tk.SUNKEN
state 編集できるかどうか指定する。
  • Tk.NORMAL : 編集できる。(デフォルト)
  • Tk.DISABLED : 編集できない。
tabs タブ位置の指定。
width 表示する文字数
wrap 長い行の折り返し方法の指定。
  • Tk.CHAR: 文字単位で折り返す。
  • Tk.WORD: 単語単位で折り返す。
  • Tk.NONE: 折り返ししない。

2.1. Tk.Text での文字位置の表し方

2.1.2. 文字位置の指定法

Tk.Text の文字の位置を示す方法には以下に示す方法があります。
行、列を指定する方法
"line.column" のように、行と列をドットで区切って明示的に指定します。 例えば、"1.0" は文書の最初を、 "2.3" は 2 行目の 3 文字目の後ろを指します。 また、"line.end" とすると行末を表します。 例えば、 "5.end" は 5 行目の末尾を表します。
あらかじめ定義されたインデックス
あらかじめ定義されたインデックスには以下の5つがあります。
マーク名
マーク名もインデックスとして使えます。2.1.2. を参照してください。
tag.first, tag.last
それぞれ、タグが指定された文字列の最初の文字の前の位置と最後の文字の後ろの位置を指します。
@x.y
widget の左上からの座標 (x, y) に一番近い位置の文字の前の位置を返します。
埋め込まれたオブジェクトへのポインター
埋め込まれたオブジェクトもインデックスとして利用できます。埋め込みオブジェクトの表示方法(例えば中央寄せ) を指定するのに使います。
Tk.Text のテキストの位置を表す方法について詳しいことは The Tkinter Text Widget あるいは TkRef: 17.1. Indices in text widgets を参照してください。

2.1.2. マーク

テキストの位置を記憶させるのにマークをつけることができます。マークは、そのマークがついているテキストとともに 移動します。

2.1.3. インデックスの操作

インデックスに以下の文字列を加えることによりインデックスを操作できます。 以下の説明では、もともとのインデックスを INDEX0 と表します。

2.3. タグ

テキストの表示の仕方を調節したり、テキストを関数と結びつけるのにタグを用います。

2.4. Tk.Text のメソッド

Tk.Text の主なメソッドを以下に示します。 詳しくは、 TkRef, 17.7. Methods on Text widgets を見てください。
.compare(index1, op, index2)
index1 と index2 を比べます。op は真偽値を返すオペレータを表す文字列です。
例えば、.compare("2.0", "<=", Tk.END) はテキストが2行以上あるとき真を返します。
.delete(index1, index2=None)
index1 から index2 までの文字列を削除します。
.get(index1, index2=None)
index1 から index2 までの文字列を取得します。
.index (idx)
idx のインデックスを 'line.column' の形で返します。
.insert(index, text, tags=None)
index に text を挿入します。タグを指定することができます。
.search ( pattern, index, option, ... )
index から pattern を検索します。
.see(index)
もし、index が表示されていなければ、スクロールして表示されるようにする。
.window_create (index, option, ... )
index に widget を挿入します。

2.5. ScrolledText

初めから縦スクロールバーのついている ScrolledText が便利でよく使います。 ScrolledText は縦スクロールバーがついているほかは Tk.Text と同じです。 ScrolledText モジュールをインポートして使います。

Tk.Text にスクロールバーをつける方法は Tkinter demo にある。twind.py を見てください。

3. Tk.Text による簡易日本語エディタ

Tkinter でエディタそのものを作ることは使用例を示す以外には無いと思います。 しかし、アプリケーション中で、ユーザーにテキストを入力してもらうことはしばしば必要になると思います。 そのとき、コピー、ペーストなどの 簡単な編集機能がついていると便利です。図1、[code 1] に ScrolledText を使った簡易エディタ (jmemo.py) を示します。


図1:jmemo.py で生成する Window

[code 1] (jmemo.py)

 01:     #! /usr/bin/env python
 02:     # -*- coding: shift_jis -*-
 03:     
 04:     """
 05:     jmemo.py 
 06:     
 07:     It works on Win and Linux.
 08:     July 06, 2005
 09:     """
 10:     
 11:     import Tkinter as Tk
 12:     import ScrolledText as S
 13:     import tkFileDialog as D
 14:     import os
 15:     
 16:     
 17:     ## classes -----------------------------------------------------------------------------------
 18:     
 19:     class Frame(Tk.Frame):
 20:         """ The main class of this program. """
 21:         
 22:         def __init__(self, master=None):
 23:             Tk.Frame.__init__(self, master)
 24:             self.master.title(os.name=='posix' and 'untitled' or u'無題')
 25:             self.file_name=None
 26:     
 27:             ### Menu
 28:             menu_bar = Tk.Menu(self, tearoff=0)
 29:             # File
 30:             menu_file = Tk.Menu(menu_bar, tearoff=0)
 31:             menu_bar.add_cascade(label=u"ファイル(F)", menu=menu_file,  underline=5)
 32:             menu_file.add_command(label=u"新規作成(N)", command=self.new_memo, underline=5, accelerator = 'Ctrl-N')
 33:             menu_file.add_command(label=u"開く(O)", command=self.open_memo, underline=3, accelerator = 'Ctrl-O')
 34:             menu_file.add_command(label=u"保存(S)", command=self.save_memo, underline=3, accelerator = 'Ctrl-S')
 35:             menu_file.add_command(label=u"名前をつけて保存(A)", command=self.saveas_memo, underline=9)
 36:             menu_file.add_separator()
 37:             menu_file.add_command(label=u"終了(Q)", command=self.exit, underline=3 , accelerator = 'Ctrl-Q')
 38:     
 39:             # Edit
 40:             menu_edit = Tk.Menu(menu_bar, tearoff=0)
 41:             menu_bar.add_cascade(label=u'編集(E)', menu=menu_edit, underline=3)
 42:             menu_edit.add_command(label=u'全てを選択(A)', command=self.select_all, underline=6, accelerator = 'Ctrl-A')
 43:             menu_edit.add_command(label=u'切り取り(X)', command=self.cut, underline=5,
 44:                                   accelerator = os.name=='posix' and 'Ctrl-W' or 'Ctrl-X')
 45:             menu_edit.add_command(label=u'コピー(C)', command=self.copy, underline=4,
 46:                                   accelerator = os.name=='posix' and 'Alt-W' or 'Ctrl-C')
 47:             menu_edit.add_command(label=u'ペースト(V)', command=self.paste, underline=5, 
 48:                                   accelerator = os.name=='posix' and 'Ctrl-Y' or 'Ctrl-V')
 49:             menu_edit.add_command(label=u'カーソルのある行を削除', command=self.delete_line, accelerator = 'Shift-Del')
 50:                                   
 51:     
 52:     
 53:     
 54:             # short-cuts
 55:             self.master.bind('<Control-KeyPress-o>', self.open_memo)
 56:             self.master.bind('<Control-KeyPress-s>', self.save_memo)
 57:             self.master.bind('<Control-KeyPress-q>', self.exit)
 58:             self.master.bind('<Control-KeyPress-a>', self.select_all)
 59:             self.master.bind('<Shift-KeyPress-Delete>', self.delete_line)
 60:             self.master.bind('<Double-Button-1>', self.delete_line)
 61:             if os.name == 'posix':
 62:                 self.master.bind('<Alt-KeyPress-w>', self.copy)
 63:     
 64:             # add menu bar
 65:             try:
 66:                 self.master.config(menu=menu_bar)     # this required to show the menu bar
 67:             except AttributeError:
 68:                 self.master.Tk.call(master, "config", "-menu", menu_bar)
 69:     
 70:     
 71:             self.txt = S.ScrolledText(self, font=('Helvetica', '10'))
 72:             self.txt.pack(fill=Tk.BOTH, expand=1)
 73:             self.txt.focus_set()
 74:     
 75:     
 76:         def new_memo(self, event=None):
 77:             self.file_name=None
 78:             self.master.title(os.name=='posix' and 'untitled' or u'無題')
 79:             self.txt.delete('1.0', Tk.END)
 80:     
 81:     
 82:         def open_memo(self, event=None):
 83:             fname = D.askopenfilename(filetypes =[('text files', '*.txt'), ('all files', '*.*')])
 84:                                        
 85:             if fname:
 86:                 self.txt.delete('1.0', Tk.END)
 87:                 f=file(fname)
 88:                 self.txt.insert(Tk.END, f.read().decode('shift_jis'))  ## decode is required to show data
 89:                 f.close()
 90:                 self.file_name = fname
 91:                 self.master.title(fname)
 92:     
 93:     
 94:         def save(self, f):
 95:             f.write(self.txt.get('1.0', Tk.END).encode('shift_jis')) ## encode is required to write data
 96:             f.close()
 97:                 
 98:         def save_memo(self, event=None):
 99:             if self.file_name:
100:                 self.save(file(self.file_name, 'w'))
101:             else:
102:                 self.saveas_memo()
103:     
104:         def saveas_memo(self):
105:             fname = D.asksaveasfilename(filetypes =[('text files', '*.txt')])
106:             if fname:
107:                 self.save(file(fname, 'w'))
108:                 self.file_name=fname
109:                 self.master.title(fname)
110:     
111:     
112:         def select_all(self, event=None):
113:             self.txt.tag_add(Tk.SEL, '1.0', Tk.END+'-1c')
114:             self.txt.mark_set(Tk.INSERT, '1.0')
115:             self.txt.see(Tk.INSERT)
116:     
117:         def cut(self, event=None):
118:             if self.txt.tag_ranges(Tk.SEL):
119:                 self.copy()
120:                 self.txt.delete(Tk.SEL_FIRST, Tk.SEL_LAST)
121:     
122:         def copy(self, event=None): 
123:             if self.txt.tag_ranges(Tk.SEL): 
124:                 text = self.txt.get(Tk.SEL_FIRST, Tk.SEL_LAST)  
125:                 self.clipboard_clear()              
126:                 self.clipboard_append(text)
127:     
128:         def paste(self, event=None):
129:             text = self.selection_get(selection='CLIPBOARD')
130:             if text:
131:                 self.txt.insert(Tk.INSERT, text)
132:                 self.txt.tag_remove(Tk.SEL, '1.0', Tk.END) 
133:                 self.txt.see(Tk.INSERT)
134:     
135:         def delete_line(self, event=None):
136:             self.txt.delete(Tk.INSERT + " linestart", Tk.INSERT + " lineend")
137:             
138:         def exit(self, event=None):
139:             self.master.destroy()
140:     
141:     ##------------------------------------------------ 
142:     
143:     if __name__ == '__main__':
144:         f = Frame()
145:         f.pack()
146:         f.mainloop()
__init__ はほとんどメニューの作成に費やされています。メニューについては、 Menu を使ったシンプル 画像 Viewer を参照してください。 ここでは、実際の編集を行う個々のメソッドについて見ていきます。

3.1. new_memo(self, event=None)

.delete メソッドを使って、テキスト全体を削除します。 .delete は1つか2つの引数をとり、最初の引数は削除する範囲の最初のインデックス。 2つ目の引数は最後のインデックスです。 Tk.Text では最初のインデックスは '1.0' で表します。

また、現在編集しているファイルの名前 self.file_nameNone にし、 Window のタイトルを u'無題' にします。

3.2. open_memo(self, event=None)

askopenfilename を使って、ファイル名 (fname) を取得し、もしファイル名があれば、 現在編集している内容を削除し、fname の内容を読み込んで表示します。 日本語を表示するには、Unicode 文字列の decode メソッドを使う必要があります。

さらに、 現在編集しているファイルの名前 self.file_namefname を代入し、 Window のタイトルを fname にします。

3.3. save(self, f)

書き込み用に開いているファイルハンドル f に表示されている内容を書き込みます。 書き込むとき Unicode 文字列の encode メソッドを使う必要があります。

3.4. save_memo(self, event=None)

上書き保存をするメソッドです。もし、self.file_name があれば、それに保存します。 なければ、self.saveas_memo メソッドを使って名前をつけて保存します。

3.5. saveas_memo(self)

asksaveasfilename を使ってファイル名 (fname) を取得し、 もし、fname があれば self.save を使って表示されている 内容を保存し、fnameself.file_name に代入し、Window のタイトルを fname にします。

3.6. select_all(self, event=None)

Tk.SEL というタグ(このタグがつくと選択されていることを表す)を .tag_add メソッドを使って、 表示されているテキスト全体につけます。このメソッドは3つの引数をとり、最初の引数は加えるタグの名前、 2番目の引数はタグをつけるテキストの最初のインデックス、3番目の引数(省略可能)はタグをつける最後の インデックスです。この例で、Tk.END + '-1c' となっているのは、 Tk.END から1文字前に戻るという意味です。 Tk.END は現在の末尾の次の文字(すなわちまだ存在していない)のインデックスですから、 現在の末尾のインデックスは Tk.END から1文字戻る必要があります。

その後、.mark_set メソッドであらかじめ定義されているマーク Tk.INSERT ( 挿入位置を表す)を文章の最初にセットし、 .see メソッドを使って、そこが表示されるようにします。

3.7. cut(self, event=None)

.tag_ranges メソッドを使って Tk.SEL の範囲を調べます。 (つまり、選択されている部分があるかどうか調べます。) もし、選択されている部分があれば、self.copy でその部分をコピーし、.delete メソッドでその部分を削除します。

3.8. copy(self, event=None)

選択されている部分があれば、その部分をクリップボードにコピーします。

3.9. paste(self, event=None)

.selection_get メソッドを使って、クリップボードの内容を text に代入します。 もし text があれば、それを Tk.INSERT に挿入します。 その後、Tk.SEL タグを削除し、 Tk.INSERT が表示されるようにします。

3.10. delete_line(self, event=None)

挿入カーソルがある行を削除します。インデックス操作の例を挙げるために付け加えました。

4. Tk.Text を用いた取り扱い説明書

Python/Tkinter でアプリケーションを作った場合、取扱説明書を Tk.Text を使って書くと手軽にわかりやすい 説明書が書けます。

Tk.Text は Tk.Widget を埋め込むことができるので、ユーザーは実際に試しながら、説明を読み進めることができます。 ラベルだけでタイマーを作ろう の Tk.Text 版 (jexplain.py) で作ってみました。 テキスト中に埋め込まれた widget をクリックすると動き出し、また、'コードを見る' をダブルクリックすると 該当するソースコードを見ることができます。ちなみに、説明用に書き換えたのは timer.py だけであり、 rhello.py, thello.py はもともとのスクリプトをそのまま使っています。つまり、手間をかけずに見栄えのする 取扱説明書が書けるということです。 図2に jexplain.py の画面を、[code 2] にそのコードを示します。


図2:jexplain.py で生成する Window

[code 2] (jexplain.py)

 01:     #! /usr/bin/env python
 02:     # -*- coding: shift_jis -*-
 03:     
 04:     """
 05:     jexplain.py 
 06:     
 07:     July 11, 2005
 08:     """
 09:     
 10:     import Tkinter as Tk
 11:     import rhello as RH
 12:     import thello as TH
 13:     import timer2 as Ti
 14:     from ScrolledText import ScrolledText
 15:     
 16:     ## functions ------------------------------------------------------------
 17:     def read_contents(fname):
 18:         """ read contens of `fname' """
 19:         f = file(fname)
 20:         str = f.read()
 21:         f.close()
 22:         return str
 23:     
 24:     def left_slide(str):  
 25:         ls0 = str.split('+')
 26:         ls1 = ls0[0].split('x')
 27:         return ('500x600+%d+%s' % (int(ls1[0]) + int(ls0[1]), ls0[2]))
 28:     
 29:     ## classes ---------------------------------------------------------------
 30:     
 31:     class Frame(Tk.Frame):
 32:         """ The main class of this program. """
 33:         
 34:         def __init__(self, master=None):
 35:             Tk.Frame.__init__(self, master)
 36:             self.visited=dict()
 37:             self.code_window = None
 38:             self.master.geometry('500x600+20+20')
 39:             self.st = ScrolledText(self, padx=20, pady=25, cursor='arrow', wrap=Tk.WORD, font=('Helvetica', '12'))
 40:             self.st.pack(fill=Tk.BOTH, expand=1)
 41:     
 42:             ### tag configuration
 43:             self.st.tag_config('h1', font=('Helvetica', '24'), justify=Tk.CENTER)
 44:             self.st.tag_config('h2', font=('Helvetica', '18'))
 45:             self.st.tag_config('link', font=('Helvetica', '12'), foreground='blue', underline=1)
 46:             self.st.tag_config('em', font=('Helvetica', '12', 'bold'), foreground="red")
 47:             self.st.tag_config('cite', font=('Helvetica', '12'), foreground="navy")
 48:             self.st.tag_config('var', font=('Helvetica', '12', 'bold'), foreground="darkgreen")
 49:             self.st.tag_config('center', justify=Tk.CENTER)
 50:             self.st.tag_bind('link', "<Double-Button-1>", self.on_clicked)
 51:             self.st.tag_bind('link', "<Enter>", self.on_enter, '+')
 52:             self.st.tag_bind('link', "<Leave>", self.on_leave, '+')
 53:     
 54:     
 55:             ### title
 56:             self.st.insert(Tk.END, u'Label を動かして遊んでみよう\n', 'h1' )
 57:             hr = Tk.Frame(self.st, relief=Tk.RIDGE, height=0, width=460)
 58:             self.st.window_create(Tk.END,  window=hr, align=Tk.CENTER)
 59:             self.st.tag_add('center', hr)
 60:             self.st.insert(Tk.END, u'\n')
 61:     
 62:             ### section 1
 63:             self.st.insert(Tk.END, u'1. 初めに\n\n', 'h2' )
 64:             self.st.insert(Tk.END,    
 65:                  u"Tk のデモのラベルの項には、 ”ラベルは動かせないからつまらない”\n")
 66:             self.st.insert(Tk.END,    
 67:                  u"(\"Labels are pretty boring because you can't do anything with them.\")\n", 'cite')
 68:             self.st.insert(Tk.END,    
 69:                  u"と書かれていますが、マウスのクリック、キープレスなどのイベントと結びつけると"
 70:                  u"いろいろと動かすことだできます。\n"
 71:                  u"ここでは、クリックするとランダムに文字色が変わる")
 72:             self.st.insert(Tk.END,  u'Hello world', 'var')  
 73:             self.st.insert(Tk.END,  u'、クリックすると文字の温度が上がっていく ')  
 74:             self.st.insert(Tk.END,  u'Hello world', 'var')  
 75:             self.st.insert(Tk.END,  u'、クリックすると動き出す ')  
 76:             self.st.insert(Tk.END,  u'Timer', 'var')  
 77:             self.st.insert(Tk.END,  u' を例にとって 説明 しようと思います。 \n\n')  
 78:                  
 79:             ## section 2
 80:             self.st.insert(Tk.END, u'2. 色がランダムに変わるラベル\n\n', 'h2' )
 81:             self.st.insert(Tk.END, u'とりあえず、刺激に反応するラベルを作って見ましょう。\n'
 82:                               u'図1は一見何の変哲もない Hello world ですが、 クリック すると 背景色が変わります。\n(')
 83:             self.st.insert(Tk.END, u'コードを見る', ('link', 'rhello.py') )
 84:             self.st.insert(Tk.END,  ")\n\n")                          
 85:             hello1 = RH.Label(self.st, relief=Tk.RIDGE)
 86:             self.st.window_create(Tk.END,  window=hello1, align=Tk.CENTER, padx=20, pady=20)
 87:             self.st.tag_add('center', hello1)
 88:             self.st.insert(Tk.END, u'\n図1: クリックすると背景色が変わる hello world\n', 'center')
 89:             self.st.insert(Tk.END,    u'実際にクリックしてみてください。\n背景色が変わります。\n\n'
 90:                                  , ('center','em'))
 91:     
 92:             ## section 3
 93:             self.st.insert(Tk.END, u'3. 反応が継続するラベル\n\n', 'h2' )
 94:             self.st.insert(Tk.END, u'次に動作が継続するラベルを作ってみましょう。\n'
 95:                               u'図2クリックすると Hello world の文字が現れ、 文字の 温度が 徐々に上昇していきます。\n('
 96:                               )
 97:             self.st.insert(Tk.END, u'コードを見る', ('link', 'thello.py') )
 98:             self.st.insert(Tk.END,  ")\n\n")
 99:             hello2 = TH.Frame(self.st, relief=Tk.RIDGE)
100:             self.st.window_create(Tk.END,  window=hello2, align=Tk.CENTER, padx=20, pady=20)
101:             self.st.tag_add('center', hello2)
102:             self.st.insert(Tk.END, u'\n図2: クリックすると文字色の温度が上がる hello world\n', 'center')
103:             self.st.insert(Tk.END,    u'実際にクリックしてみてください。\n文字色が変わります。\n'
104:                                  u'右クリックで元に戻ります。\n\n', ('center','em'))
105:     
106:             
107:             ### section 4
108:             self.st.insert(Tk.END, u'4. Label だけでタイマーを作ろう\n\n', 'h2' )
109:     
110:             self.st.insert(Tk.END,    
111:                      u"実用的な例として、 図3に示すようなアラームクロックをラベルだけで作ってみましょう。"
112:                      u"このアラームはマウスの左ボタンで Start/Stop し、右ボタンで Reset します。\n(" )
113:             self.st.insert(Tk.END, u'コードを見る', ('link', 'timer2.py') )                 
114:             self.st.insert(Tk.END,  ")\n\n")
115:             app1 = Ti.Frame(self.st, 3, relief=Tk.RIDGE)
116:             self.st.window_create(Tk.END,  window=app1, align=Tk.CENTER, padx=20, pady=20)
117:             self.st.tag_add('center', app1)
118:             self.st.insert(Tk.END,  "\n")
119:             self.st.insert(Tk.END, u'\n図3: Label だけでできたアラームクロック\n', 'center')
120:             self.st.insert(Tk.END,    u'実際にクリックしてみてください。\n動いたり止まったりします。\n\n', ('center','em'))
121:             self.st.insert(Tk.END,    u'この時計は残り 20 秒をきると、 図4を クリックした ときに 見えるように、'
122:                                  u' 時計アイコンの背景が黄色くなります。\n')
123:             app2 = Ti.Frame(self.st, 0.2, relief=Tk.RIDGE)
124:             self.st.window_create(Tk.END,  window=app2, align=Tk.CENTER, padx=20, pady=20)
125:             self.st.tag_add('center', app2)
126:             self.st.insert(Tk.END, u'\n図4: 残り 20 秒を切ったところ。\n', 'center')
127:             self.st.insert(Tk.END, u'実際にクリックしてみてください。\n動いたり止まったりします。\n\n', ('center','em'))
128:     
129:             self.st.insert(Tk.END,    u'さらに、残り時間がなくなると、図5をクリックしたときに見えるように、'
130:                                  u'時計アイコンの背景が赤、黄と点滅します。\n')
131:             app3 = Ti.Frame(self.st, 0.0, relief=Tk.RIDGE)
132:             self.st.window_create(Tk.END,  window=app3, align=Tk.CENTER, padx=20, pady=20)
133:             self.st.tag_add('center', app3)
134:             self.st.insert(Tk.END, u'\n図5: 残時間がなくなったところ。\n', 'center')
135:             self.st.insert(Tk.END, u'実際にクリックしてみてください。\n動いたり止まったりします。\n\n', ('center','em'))
136:     
137:             ### section 5
138:             self.st.insert(Tk.END, u'5. 終わりに\n\n', 'h2' )
139:             self.st.insert(Tk.END, u'Tk.Label だけでも動かせるアプリを作れることがお分かりいただけたと思います。\n'
140:                               u'また、Tk.Text を使うと インターラクティブな 説明が できることが お分かりいただけたと思います。\n\n'
141:             )
142:     
143:             self.st.configure(state=Tk.DISABLED)
144:     
145:         ##----
146:         def on_leave(self, event):
147:             self.st.tag_config(self.link, foreground=self.link in self.visited and 'deepskyblue' or 'blue')
148:             
149:             
150:         def on_clicked(self, event):
151:             if self.code_window and self.code_window.winfo_exists():
152:                 self.code.delete('1.0', Tk.END)
153:             else:
154:                 self.code_window = Tk.Toplevel()
155:                 self.code_window.geometry(left_slide(self.master.winfo_geometry()))
156:                 self.code = ScrolledText(self.code_window, wrap=Tk.WORD)
157:                 self.code.pack(fill=Tk.BOTH, expand=1)
158:                 
159:             self.code_window.title(self.link)
160:             self.code.insert(Tk.END, read_contents(self.link))
161:             self.code_window.focus_set()
162:             self.visited[self.link]=True
163:     
164:             
165:         def on_enter(self, event):
166:             tags = self.st.tag_names(Tk.CURRENT)
167:             self.link = tags[-1]
168:             self.st.tag_config(self.link, foreground="#FF0099")
169:     
170:     
171:             
172:     ##------------------------------------------------ 
173:     
174:     if __name__ == '__main__':
175:         f = Frame()
176:         f.pack(fill=Tk.BOTH, expand=1)
177:         f.mainloop()
テキストを表示するプログラムなので冗長なのはお許しください。 要点を以下に示します。

4.1. タグを使ってテキストの属性を定義する。

Tk.Text にテキストを挿入するには insert メソッドを使います。このメソッドは3つの引数をとり、 それぞれ、挿入する場所、挿入する文字列、その文字列のタグです。 文字列は、日本語を使うときは先頭に Unicode を表す u をつけます。

3番目の引数のタグ名は、文字列の属性を示すタグの名前を示します。タプルでくくって複数指定することも可能です。 タグの表示設定は、tag_config メソッドを使います。このメソッドの最初の引数はタグ名、残りはタグの 表示設定のためのキーワードパラメターです。[code 2] の 43--49 行目に例があります。

タグとコマンドを結び付けるには tag_bind メソッドを使います。このメソッドは 引数を3つとり、それぞれ、タグ名、イベントの種類、呼び出す関数(またはメソッド) へのポインターです。使用例は [code 2] の 50--52 行目にあります。

4.2. Widget を貼り込む

Tk.Text に widget を貼り込むのは以下のようにします。[code 2] の 85--87, 99--101 行を参考にしてください。
  1. まず、Tk.Text を親 widget として widget を作成する。
  2. window_create メソッドを使って widget を張り込みます。第一引数は貼り込む場所、それ以降はキーワードパラメター です。貼り込む widget はキーワード 'window' で指定します。
  3. tag_add メソッドを使って、貼り込んだ widget の属性を指定します。第一引数がタグ名、第二引数が widget を表す変数です。[code 2] の例では、'center' タグを指定して、widget が中央に来るようにしています。

4.3. コマンドにリンクを張る

[code 2] では 'link' タグとイベントをバインドさせてコマンドを呼び出しています。 'link' タグは単独では使わず、ダブルクリックしたとき表示するコード名を2番目のタグとして指定しておきます。 ([code 2]: 83, 97, 113 行目)
マウスカーソルがやってきたとき
マウスカーソルがやってくることを表すイベント名は '<Enter>' です。イベント名について 詳しくは TkRef: 24. Events: responding to stimuliEvents and Bindings を見てください。

さて、<Enter> が起こると、self.on_enter (165--168 行目)が呼び出されます。 このメソッドは、まず、tag_names メソッドを使って、カーソルがある位置のタグ名を取得し、タグ名のタプルの 最後の要素を self.link に代入します。そして、tag_config メソッドを使って、 タグが self.link の文字色を '#FF0099' に変えます。

マウスがそのまま去ったとき
マウスカーソルが去ることを表すイベント名は '<Leave>' です。 さて、<Leave> が起こると、self.on_leave (146--147 行目)が呼び出されます。 このメソッドは、まず、self.link が表示されたことがあれば文字色を水色に、なければ青色に変えます。
マウス左ボタンがダブルクリックされたとき
self.on_click (150--162 行目) を呼び出します。このメソッドは、別窓を開いてソースコードを表示します。 また、self.visited[self.link]True を代入し、一度ソースが表示されたことを 記憶します。

5. 終わりに

Tk.Text を使うとエディタを簡単に作ることができます。 エディタそのものを作ることはないと思いますが、アプリケーションに付随したエディタを作るとき便利です。

そのほかに、Tk.Text はソフトの取扱説明書を書くときにも便利です。 表示を飾りつけできるだけでなく、埋め込んだ Widget を動かすこともでき、 ユーザーの理解を助けます。埋め込む Widget は、多くの場合、 もともとのアプリケーションのものがそのまま使えるので、説明書を書く手間も省けます

生の Tk.Text を使うことはほとんどなく、縦スクロークのついている ScrolledText を使うことが多いでしょう。