blob: 6e9a8288faf8260262ae326fea2e6593131903c7 [file] [log] [blame]
Guido van Rossumf06ee5f1996-11-27 19:52:01 +00001#! /usr/bin/env python
Guido van Rossum18468821994-06-20 07:49:28 +00002
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
Guido van Rossum74f25651994-07-13 13:08:01 +000012# XXX This should be written in a more Python-like style!!!
13
Guido van Rossum18468821994-06-20 07:49:28 +000014from Tkinter import *
Fred Drake4dd0bf91996-07-23 17:47:21 +000015import sys
Guido van Rossum18468821994-06-20 07:49:28 +000016
17# 1. Create basic application structure: menu bar on top of
18# text widget, scrollbar on right.
19
20root = Tk()
21tk = root.tk
Fred Drake4dd0bf91996-07-23 17:47:21 +000022mBar = Frame(root, relief=RAISED, borderwidth=2)
23mBar.pack(fill=X)
Guido van Rossum18468821994-06-20 07:49:28 +000024
Fred Drake4dd0bf91996-07-23 17:47:21 +000025f = Frame(root)
26f.pack(expand=1, fill=BOTH)
27s = Scrollbar(f, relief=FLAT)
28s.pack(side=RIGHT, fill=Y)
29t = Text(f, relief=RAISED, borderwidth=2, yscrollcommand=s.set, setgrid=1)
30t.pack(side=LEFT, fill=BOTH, expand=1)
31t.tag_config('bold', font='-Adobe-Courier-Bold-R-Normal-*-120-*')
32s['command'] = t.yview
33
Guido van Rossum18468821994-06-20 07:49:28 +000034root.title('Tk Remote Controller')
35root.iconname('Tk Remote')
36
37# 2. Create menu button and menus.
38
Fred Drake4dd0bf91996-07-23 17:47:21 +000039file = Menubutton(mBar, text='File', underline=0)
40file.pack(side=LEFT)
Guido van Rossum18468821994-06-20 07:49:28 +000041file_m = Menu(file)
42file['menu'] = file_m
Fred Drake4dd0bf91996-07-23 17:47:21 +000043file_m_apps = Menu(file_m, tearoff=0)
44file_m.add_cascade(label='Select Application', underline=0,
45 menu=file_m_apps)
46file_m.add_command(label='Quit', underline=0, command=sys.exit)
Guido van Rossum18468821994-06-20 07:49:28 +000047
48# 3. Create bindings for text widget to allow commands to be
49# entered and information to be selected. New characters
50# can only be added at the end of the text (can't ever move
51# insertion point).
52
53def single1(e):
54 x = e.x
55 y = e.y
Guido van Rossumdf096911994-06-20 09:08:51 +000056 t.setvar('tk_priv(selectMode)', 'char')
Guido van Rossum18468821994-06-20 07:49:28 +000057 t.mark_set('anchor', At(x, y))
58 # Should focus W
59t.bind('<1>', single1)
60
61def double1(e):
62 x = e.x
63 y = e.y
Guido van Rossumdf096911994-06-20 09:08:51 +000064 t.setvar('tk_priv(selectMode)', 'word')
65 t.tk_textSelectTo(At(x, y))
Guido van Rossum18468821994-06-20 07:49:28 +000066t.bind('<Double-1>', double1)
67
68def triple1(e):
69 x = e.x
70 y = e.y
Guido van Rossumdf096911994-06-20 09:08:51 +000071 t.setvar('tk_priv(selectMode)', 'line')
72 t.tk_textSelectTo(At(x, y))
Guido van Rossum18468821994-06-20 07:49:28 +000073t.bind('<Triple-1>', triple1)
74
75def returnkey(e):
Guido van Rossumdf096911994-06-20 09:08:51 +000076 t.insert(AtInsert(), '\n')
Guido van Rossum18468821994-06-20 07:49:28 +000077 invoke()
78t.bind('<Return>', returnkey)
79
80def controlv(e):
Guido van Rossumdf096911994-06-20 09:08:51 +000081 t.insert(AtInsert(), t.selection_get())
82 t.yview_pickplace(AtInsert())
83 if t.index(AtInsert())[-2:] == '.0':
Guido van Rossum18468821994-06-20 07:49:28 +000084 invoke()
85t.bind('<Control-v>', controlv)
86
87# 4. Procedure to backspace over one character, as long as
88# the character isn't part of the prompt.
89
90def backspace(e):
91 if t.index('promptEnd') != t.index('insert - 1 char'):
Guido van Rossumdf096911994-06-20 09:08:51 +000092 t.delete('insert - 1 char', AtInsert())
93 t.yview_pickplace(AtInsert())
Guido van Rossum18468821994-06-20 07:49:28 +000094t.bind('<BackSpace>', backspace)
95t.bind('<Control-h>', backspace)
96t.bind('<Delete>', backspace)
97
98
99# 5. Procedure that's invoked when return is typed: if
100# there's not yet a complete command (e.g. braces are open)
101# then do nothing. Otherwise, execute command (locally or
102# remotely), output the result or error message, and issue
103# a new prompt.
104
105def invoke():
Guido van Rossumdf096911994-06-20 09:08:51 +0000106 cmd = t.get('promptEnd + 1 char', AtInsert())
107 if t.getboolean(tk.call('info', 'complete', cmd)): # XXX
108 if app == root.winfo_name():
109 msg = tk.call('eval', cmd) # XXX
Guido van Rossum18468821994-06-20 07:49:28 +0000110 else:
Guido van Rossumdf096911994-06-20 09:08:51 +0000111 msg = t.send(app, cmd)
Guido van Rossum18468821994-06-20 07:49:28 +0000112 if msg:
Guido van Rossumdf096911994-06-20 09:08:51 +0000113 t.insert(AtInsert(), msg + '\n')
Guido van Rossum18468821994-06-20 07:49:28 +0000114 prompt()
Guido van Rossumdf096911994-06-20 09:08:51 +0000115 t.yview_pickplace(AtInsert())
Guido van Rossum18468821994-06-20 07:49:28 +0000116
117def prompt():
Guido van Rossumdf096911994-06-20 09:08:51 +0000118 t.insert(AtInsert(), app + ': ')
Guido van Rossum18468821994-06-20 07:49:28 +0000119 t.mark_set('promptEnd', 'insert - 1 char')
120 t.tag_add('bold', 'insert linestart', 'promptEnd')
121
122# 6. Procedure to select a new application. Also changes
123# the prompt on the current command line to reflect the new
124# name.
125
126def newApp(appName):
127 global app
128 app = appName
129 t.delete('promptEnd linestart', 'promptEnd')
130 t.insert('promptEnd', appName + ':')
131 t.tag_add('bold', 'promptEnd linestart', 'promptEnd')
132
Guido van Rossum18468821994-06-20 07:49:28 +0000133def fillAppsMenu():
134 file_m_apps.add('command')
135 file_m_apps.delete(0, 'last')
Guido van Rossumdf096911994-06-20 09:08:51 +0000136 names = root.winfo_interps()
Guido van Rossum18468821994-06-20 07:49:28 +0000137 names = map(None, names) # convert tuple to list
138 names.sort()
139 for name in names:
Guido van Rossumdf096911994-06-20 09:08:51 +0000140 try:
141 root.send(name, 'winfo name .')
142 except TclError:
143 # Inoperative window -- ignore it
144 pass
145 else:
Fred Drake4dd0bf91996-07-23 17:47:21 +0000146 file_m_apps.add_command(
147 label=name,
148 command=lambda name=name: newApp(name))
Guido van Rossum18468821994-06-20 07:49:28 +0000149
150file_m_apps['postcommand'] = fillAppsMenu
151mBar.tk_menuBar(file)
152
153# 7. Miscellaneous initialization.
154
Guido van Rossumdf096911994-06-20 09:08:51 +0000155app = root.winfo_name()
Guido van Rossum18468821994-06-20 07:49:28 +0000156prompt()
Guido van Rossumdf096911994-06-20 09:08:51 +0000157t.focus()
Guido van Rossum18468821994-06-20 07:49:28 +0000158
159root.mainloop()