blob: 2ac2408ce2507010add133ac870174124c8c007c [file] [log] [blame]
Guido van Rossum18468821994-06-20 07:49:28 +00001#! /ufs/guido/bin/sgi/tkpython
2
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.
6
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.
11
12from Tkinter import *
13
14# 1. Create basic application structure: menu bar on top of
15# text widget, scrollbar on right.
16
17root = Tk()
18tk = root.tk
19mBar = Frame(root, {'relief': 'raised', 'bd': 2,
20 Pack: {'side': 'top', 'fill': 'x'}})
21f = Frame(root)
22f.pack({'expand': 1, 'fill': 'both'})
23s = Scrollbar(f, {'relief': 'flat',
24 Pack: {'side': 'right', 'fill': 'y'}})
25t = Text(f, {'relief': 'raised', 'bd': 2, 'yscrollcommand': (s, 'set'),
26 'setgrid': 1,
27 Pack: {'side': 'left', 'fill': 'both', 'expand': 1}})
28
29t.tag_config('bold', {'font': '-Adobe-Courier-Bold-R-Normal-*-120-*'})
30s['command'] = (t, 'yview')
31root.title('Tk Remote Controller')
32root.iconname('Tk Remote')
33
34# 2. Create menu button and menus.
35
36file = Menubutton(mBar, {'text': 'File', 'underline': 0,
37 Pack: {'side': 'left'}})
38file_m = Menu(file)
39file['menu'] = file_m
40file_m_apps = Menu(file_m)
41file_m.add('cascade', {'label': 'Select Application', 'underline': 0,
42 'menu': file_m_apps})
43file_m.add('command', {'label': 'Quit', 'underline': 0, 'command': 'exit'})
44
45# 3. Create bindings for text widget to allow commands to be
46# entered and information to be selected. New characters
47# can only be added at the end of the text (can't ever move
48# insertion point).
49
50def single1(e):
51 x = e.x
52 y = e.y
53 tk.setvar('tk_priv(selectMode)', 'char')
54 t.mark_set('anchor', At(x, y))
55 # Should focus W
56t.bind('<1>', single1)
57
58def double1(e):
59 x = e.x
60 y = e.y
61 tk.setvar('tk_priv(selectMode)', 'word')
62 tk.call('tk_textSelectTo', t, At(x, y))
63t.bind('<Double-1>', double1)
64
65def triple1(e):
66 x = e.x
67 y = e.y
68 tk.setvar('tk_priv(selectMode)', 'line')
69 tk.call('tk_textSelectTo', t, At(x, y))
70t.bind('<Triple-1>', triple1)
71
72def returnkey(e):
73 t.insert('insert', '\n')
74 invoke()
75t.bind('<Return>', returnkey)
76
77def controlv(e):
78 t.insert('insert', tk.call('selection', 'get'))
79 t.yview_pickplace('insert')
80 if t.index('insert')[-2:] == '.0':
81 invoke()
82t.bind('<Control-v>', controlv)
83
84# 4. Procedure to backspace over one character, as long as
85# the character isn't part of the prompt.
86
87def backspace(e):
88 if t.index('promptEnd') != t.index('insert - 1 char'):
89 t.delete('insert - 1 char', 'insert')
90 t.yview_pickplace('insert')
91t.bind('<BackSpace>', backspace)
92t.bind('<Control-h>', backspace)
93t.bind('<Delete>', backspace)
94
95
96# 5. Procedure that's invoked when return is typed: if
97# there's not yet a complete command (e.g. braces are open)
98# then do nothing. Otherwise, execute command (locally or
99# remotely), output the result or error message, and issue
100# a new prompt.
101
102def invoke():
103 cmd = t.get('promptEnd + 1 char', 'insert')
104 if tk.getboolean(tk.call('info', 'complete', cmd)):
105 if app == tk.call('winfo', 'name', '.'):
106 msg = tk.call('eval', cmd)
107 else:
108 msg = tk.call('send', app, cmd)
109 if msg:
110 t.insert('insert', msg + '\n')
111 prompt()
112 t.yview_pickplace('insert')
113
114def prompt():
115 t.insert('insert', app + ': ')
116 t.mark_set('promptEnd', 'insert - 1 char')
117 t.tag_add('bold', 'insert linestart', 'promptEnd')
118
119# 6. Procedure to select a new application. Also changes
120# the prompt on the current command line to reflect the new
121# name.
122
123def newApp(appName):
124 global app
125 app = appName
126 t.delete('promptEnd linestart', 'promptEnd')
127 t.insert('promptEnd', appName + ':')
128 t.tag_add('bold', 'promptEnd linestart', 'promptEnd')
129
130newApp_tcl = `id(newApp)`
131tk.createcommand(newApp_tcl, newApp)
132
133def fillAppsMenu():
134 file_m_apps.add('command')
135 file_m_apps.delete(0, 'last')
136 names = tk.splitlist(tk.call('winfo', 'interps'))
137 names = map(None, names) # convert tuple to list
138 names.sort()
139 for name in names:
140 file_m_apps.add('command', {'label': name,
141 'command': (newApp_tcl, name)})
142
143file_m_apps['postcommand'] = fillAppsMenu
144mBar.tk_menuBar(file)
145
146# 7. Miscellaneous initialization.
147
148app = tk.call('winfo', 'name', '.')
149prompt()
150tk.call('focus', t)
151
152root.mainloop()