HOME | 11. Canvas を使って納涼しよう | Python | A-1. Widget に画像を張り込む | download | 書き込む |
オプション | 説明 |
---|---|
bg or background | テキストの背景色 |
bd or borderwidth | 縁の幅、デフォルトは 2 pixels. |
cursor | カーソルの形状を指定する。 |
font | フォントの指定 |
fg or foreground | 文字とビットマップの色 |
height | 表示する行数。 |
padx | テキストから縁への左右のマージン |
pady | テキストから縁への上下のマージン |
relief | 縁の形状。デフォルトは Tk.SUNKEN |
state | 編集できるかどうか指定する。
|
tabs | タブ位置の指定。 |
width | 表示する文字数 |
wrap | 長い行の折り返し方法の指定。
|
2.1.2. マーク
テキストの位置を記憶させるのにマークをつけることができます。マークは、そのマークがついているテキストとともに
移動します。
2.1.3. インデックスの操作
インデックスに以下の文字列を加えることによりインデックスを操作できます。
以下の説明では、もともとのインデックスを INDEX0 と表します。
"chars" は単に "c" と書くこともできます。
2.3. タグ
テキストの表示の仕方を調節したり、テキストを関数と結びつけるのにタグを用います。
すでに存在しているテキストにタグをつけるときは
.tag_add(tag_name, index1, index2=None)
メソッドを使います。
タグは複数つけることができます。複数つけるときはタプルを使います。
2.4. Tk.Text のメソッド
Tk.Text の主なメソッドを以下に示します。
詳しくは、 TkRef, 17.7. Methods on Text widgets
を見てください。
Tk.Text にスクロールバーをつける方法は Tkinter demo にある。twind.py を見てください。
[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 を参照してください。
ここでは、実際の編集を行う個々のメソッドについて見ていきます。
また、現在編集しているファイルの名前 self.file_name を None にし、 Window のタイトルを u'無題' にします。
さらに、 現在編集しているファイルの名前 self.file_name に fname を代入し、 Window のタイトルを fname にします。
その後、.mark_set メソッドであらかじめ定義されているマーク Tk.INSERT ( 挿入位置を表す)を文章の最初にセットし、 .see メソッドを使って、そこが表示されるようにします。
Tk.Text は Tk.Widget を埋め込むことができるので、ユーザーは実際に試しながら、説明を読み進めることができます。 ラベルだけでタイマーを作ろう の Tk.Text 版 (jexplain.py) で作ってみました。 テキスト中に埋め込まれた widget をクリックすると動き出し、また、'コードを見る' をダブルクリックすると 該当するソースコードを見ることができます。ちなみに、説明用に書き換えたのは timer.py だけであり、 rhello.py, thello.py はもともとのスクリプトをそのまま使っています。つまり、手間をかけずに見栄えのする 取扱説明書が書けるということです。 図2に jexplain.py の画面を、[code 2] にそのコードを示します。
[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()テキストを表示するプログラムなので冗長なのはお許しください。 要点を以下に示します。
3番目の引数のタグ名は、文字列の属性を示すタグの名前を示します。タプルでくくって複数指定することも可能です。 タグの表示設定は、tag_config メソッドを使います。このメソッドの最初の引数はタグ名、残りはタグの 表示設定のためのキーワードパラメターです。[code 2] の 43--49 行目に例があります。
タグとコマンドを結び付けるには tag_bind メソッドを使います。このメソッドは 引数を3つとり、それぞれ、タグ名、イベントの種類、呼び出す関数(またはメソッド) へのポインターです。使用例は [code 2] の 50--52 行目にあります。
さて、<Enter> が起こると、self.on_enter (165--168 行目)が呼び出されます。 このメソッドは、まず、tag_names メソッドを使って、カーソルがある位置のタグ名を取得し、タグ名のタプルの 最後の要素を self.link に代入します。そして、tag_config メソッドを使って、 タグが self.link の文字色を '#FF0099' に変えます。
そのほかに、Tk.Text はソフトの取扱説明書を書くときにも便利です。 表示を飾りつけできるだけでなく、埋め込んだ Widget を動かすこともでき、 ユーザーの理解を助けます。埋め込む Widget は、多くの場合、 もともとのアプリケーションのものがそのまま使えるので、説明書を書く手間も省けます
生の Tk.Text を使うことはほとんどなく、縦スクロークのついている ScrolledText を使うことが多いでしょう。
HOME | 11. Canvas を使って納涼しよう | Python | A-1. Widget に画像を張り込む | download | 書き込む |