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!!!
16 # 1. Create basic application structure: menu bar on top of
17 # text widget, scrollbar on right.
21 mBar
= Frame(root
, {'relief': 'raised', 'bd': 2,
22 Pack
: {'side': 'top', 'fill': 'x'}})
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'),
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'}})
42 file_m_apps
= Menu(file_m
)
43 file_m
.add('cascade', {'label': 'Select Application', 'underline': 0,
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
55 t
.setvar('tk_priv(selectMode)', 'char')
56 t
.mark_set('anchor', At(x
, y
))
58 t
.bind('<1>', single1
)
63 t
.setvar('tk_priv(selectMode)', 'word')
64 t
.tk_textSelectTo(At(x
, y
))
65 t
.bind('<Double-1>', double1
)
70 t
.setvar('tk_priv(selectMode)', 'line')
71 t
.tk_textSelectTo(At(x
, y
))
72 t
.bind('<Triple-1>', triple1
)
75 t
.insert(AtInsert(), '\n')
77 t
.bind('<Return>', returnkey
)
80 t
.insert(AtInsert(), t
.selection_get())
81 t
.yview_pickplace(AtInsert())
82 if t
.index(AtInsert())[-2:] == '.0':
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.
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
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
110 msg
= t
.send(app
, cmd
)
112 t
.insert(AtInsert(), msg
+ '\n')
114 t
.yview_pickplace(AtInsert())
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
128 t
.delete('promptEnd linestart', 'promptEnd')
129 t
.insert('promptEnd', appName
+ ':')
130 t
.tag_add('bold', 'promptEnd linestart', 'promptEnd')
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
140 root
.send(name
, 'winfo name .')
142 # Inoperative window -- ignore it
145 file_m_apps
.add('command', {'label': name
,
150 file_m_apps
['postcommand'] = fillAppsMenu
151 mBar
.tk_menuBar(file)
153 # 7. Miscellaneous initialization.
155 app
= root
.winfo_name()