changes by Barry, e.g. font lock & email addresses
[python/dscho.git] / Demo / tkinter / guido / rmt.py
blob25b84e220311bb502cf5821df251e431828a1afd
1 #! /usr/local/bin/python
3 # A Python program implementing rmt, an application for remotely
4 # controlling other Tk applications.
5 # Cf. Ousterhout, Tcl and the Tk Toolkit, Figs. 27.5-8, pp. 273-276.
7 # Note that because of forward references in the original, we
8 # sometimes delay bindings until after the corresponding procedure is
9 # defined. We also introduce names for some unnamed code blocks in
10 # the original because of restrictions on lambda forms in Python.
12 # XXX This should be written in a more Python-like style!!!
14 from Tkinter import *
16 # 1. Create basic application structure: menu bar on top of
17 # text widget, scrollbar on right.
19 root = Tk()
20 tk = root.tk
21 mBar = Frame(root, {'relief': 'raised', 'bd': 2,
22 Pack: {'side': 'top', 'fill': 'x'}})
23 f = Frame(root)
24 f.pack({'expand': 1, 'fill': 'both'})
25 s = Scrollbar(f, {'relief': 'flat',
26 Pack: {'side': 'right', 'fill': 'y'}})
27 t = Text(f, {'relief': 'raised', 'bd': 2, 'yscrollcommand': (s, 'set'),
28 'setgrid': 1,
29 Pack: {'side': 'left', 'fill': 'both', 'expand': 1}})
31 t.tag_config('bold', {'font': '-Adobe-Courier-Bold-R-Normal-*-120-*'})
32 s['command'] = (t, 'yview')
33 root.title('Tk Remote Controller')
34 root.iconname('Tk Remote')
36 # 2. Create menu button and menus.
38 file = Menubutton(mBar, {'text': 'File', 'underline': 0,
39 Pack: {'side': 'left'}})
40 file_m = Menu(file)
41 file['menu'] = file_m
42 file_m_apps = Menu(file_m)
43 file_m.add('cascade', {'label': 'Select Application', 'underline': 0,
44 'menu': file_m_apps})
45 file_m.add('command', {'label': 'Quit', 'underline': 0, 'command': 'exit'})
47 # 3. Create bindings for text widget to allow commands to be
48 # entered and information to be selected. New characters
49 # can only be added at the end of the text (can't ever move
50 # insertion point).
52 def single1(e):
53 x = e.x
54 y = e.y
55 t.setvar('tk_priv(selectMode)', 'char')
56 t.mark_set('anchor', At(x, y))
57 # Should focus W
58 t.bind('<1>', single1)
60 def double1(e):
61 x = e.x
62 y = e.y
63 t.setvar('tk_priv(selectMode)', 'word')
64 t.tk_textSelectTo(At(x, y))
65 t.bind('<Double-1>', double1)
67 def triple1(e):
68 x = e.x
69 y = e.y
70 t.setvar('tk_priv(selectMode)', 'line')
71 t.tk_textSelectTo(At(x, y))
72 t.bind('<Triple-1>', triple1)
74 def returnkey(e):
75 t.insert(AtInsert(), '\n')
76 invoke()
77 t.bind('<Return>', returnkey)
79 def controlv(e):
80 t.insert(AtInsert(), t.selection_get())
81 t.yview_pickplace(AtInsert())
82 if t.index(AtInsert())[-2:] == '.0':
83 invoke()
84 t.bind('<Control-v>', controlv)
86 # 4. Procedure to backspace over one character, as long as
87 # the character isn't part of the prompt.
89 def backspace(e):
90 if t.index('promptEnd') != t.index('insert - 1 char'):
91 t.delete('insert - 1 char', AtInsert())
92 t.yview_pickplace(AtInsert())
93 t.bind('<BackSpace>', backspace)
94 t.bind('<Control-h>', backspace)
95 t.bind('<Delete>', backspace)
98 # 5. Procedure that's invoked when return is typed: if
99 # there's not yet a complete command (e.g. braces are open)
100 # then do nothing. Otherwise, execute command (locally or
101 # remotely), output the result or error message, and issue
102 # a new prompt.
104 def invoke():
105 cmd = t.get('promptEnd + 1 char', AtInsert())
106 if t.getboolean(tk.call('info', 'complete', cmd)): # XXX
107 if app == root.winfo_name():
108 msg = tk.call('eval', cmd) # XXX
109 else:
110 msg = t.send(app, cmd)
111 if msg:
112 t.insert(AtInsert(), msg + '\n')
113 prompt()
114 t.yview_pickplace(AtInsert())
116 def prompt():
117 t.insert(AtInsert(), app + ': ')
118 t.mark_set('promptEnd', 'insert - 1 char')
119 t.tag_add('bold', 'insert linestart', 'promptEnd')
121 # 6. Procedure to select a new application. Also changes
122 # the prompt on the current command line to reflect the new
123 # name.
125 def newApp(appName):
126 global app
127 app = appName
128 t.delete('promptEnd linestart', 'promptEnd')
129 t.insert('promptEnd', appName + ':')
130 t.tag_add('bold', 'promptEnd linestart', 'promptEnd')
132 def fillAppsMenu():
133 file_m_apps.add('command')
134 file_m_apps.delete(0, 'last')
135 names = root.winfo_interps()
136 names = map(None, names) # convert tuple to list
137 names.sort()
138 for name in names:
139 try:
140 root.send(name, 'winfo name .')
141 except TclError:
142 # Inoperative window -- ignore it
143 pass
144 else:
145 file_m_apps.add('command', {'label': name,
146 'command':
147 lambda name=name:
148 newApp(name)})
150 file_m_apps['postcommand'] = fillAppsMenu
151 mBar.tk_menuBar(file)
153 # 7. Miscellaneous initialization.
155 app = root.winfo_name()
156 prompt()
157 t.focus()
159 root.mainloop()