| 
									
										
										
										
											1996-11-27 19:52:01 +00:00
										 |  |  | #! /usr/bin/env python | 
					
						
							| 
									
										
										
										
											1994-06-20 07:49:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | # A Python program implementing rmt, an application for remotely | 
					
						
							|  |  |  | # controlling other Tk applications. | 
					
						
							|  |  |  | # Cf. Ousterhout, Tcl and the Tk Toolkit, Figs. 27.5-8, pp. 273-276. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Note that because of forward references in the original, we | 
					
						
							|  |  |  | # sometimes delay bindings until after the corresponding procedure is | 
					
						
							|  |  |  | # defined.  We also introduce names for some unnamed code blocks in | 
					
						
							|  |  |  | # the original because of restrictions on lambda forms in Python. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1994-07-13 13:08:01 +00:00
										 |  |  | # XXX This should be written in a more Python-like style!!! | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1994-06-20 07:49:28 +00:00
										 |  |  | from Tkinter import * | 
					
						
							| 
									
										
										
										
											1996-07-23 17:47:21 +00:00
										 |  |  | import sys | 
					
						
							| 
									
										
										
										
											1994-06-20 07:49:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | # 1. Create basic application structure: menu bar on top of | 
					
						
							|  |  |  | # text widget, scrollbar on right. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | root = Tk() | 
					
						
							|  |  |  | tk = root.tk | 
					
						
							| 
									
										
										
										
											1996-07-23 17:47:21 +00:00
										 |  |  | mBar = Frame(root, relief=RAISED, borderwidth=2) | 
					
						
							|  |  |  | mBar.pack(fill=X) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1994-06-20 07:49:28 +00:00
										 |  |  | f = Frame(root) | 
					
						
							| 
									
										
										
										
											1996-07-23 17:47:21 +00:00
										 |  |  | f.pack(expand=1, fill=BOTH) | 
					
						
							|  |  |  | s = Scrollbar(f, relief=FLAT) | 
					
						
							|  |  |  | s.pack(side=RIGHT, fill=Y) | 
					
						
							|  |  |  | t = Text(f, relief=RAISED, borderwidth=2, yscrollcommand=s.set, setgrid=1) | 
					
						
							|  |  |  | t.pack(side=LEFT, fill=BOTH, expand=1) | 
					
						
							|  |  |  | t.tag_config('bold', font='-Adobe-Courier-Bold-R-Normal-*-120-*')  | 
					
						
							|  |  |  | s['command'] = t.yview | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1994-06-20 07:49:28 +00:00
										 |  |  | root.title('Tk Remote Controller') | 
					
						
							|  |  |  | root.iconname('Tk Remote') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # 2. Create menu button and menus. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1996-07-23 17:47:21 +00:00
										 |  |  | file = Menubutton(mBar, text='File', underline=0) | 
					
						
							|  |  |  | file.pack(side=LEFT) | 
					
						
							| 
									
										
										
										
											1994-06-20 07:49:28 +00:00
										 |  |  | file_m = Menu(file) | 
					
						
							|  |  |  | file['menu'] = file_m | 
					
						
							| 
									
										
										
										
											1996-07-23 17:47:21 +00:00
										 |  |  | file_m_apps = Menu(file_m, tearoff=0) | 
					
						
							|  |  |  | file_m.add_cascade(label='Select Application', underline=0, | 
					
						
							|  |  |  | 		   menu=file_m_apps) | 
					
						
							|  |  |  | file_m.add_command(label='Quit', underline=0, command=sys.exit) | 
					
						
							| 
									
										
										
										
											1994-06-20 07:49:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | # 3. Create bindings for text widget to allow commands to be | 
					
						
							|  |  |  | # entered and information to be selected.  New characters | 
					
						
							|  |  |  | # can only be added at the end of the text (can't ever move | 
					
						
							|  |  |  | # insertion point). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def single1(e): | 
					
						
							|  |  |  | 	x = e.x | 
					
						
							|  |  |  | 	y = e.y | 
					
						
							| 
									
										
										
										
											1994-06-20 09:08:51 +00:00
										 |  |  | 	t.setvar('tk_priv(selectMode)', 'char') | 
					
						
							| 
									
										
										
										
											1994-06-20 07:49:28 +00:00
										 |  |  | 	t.mark_set('anchor', At(x, y)) | 
					
						
							|  |  |  | 	# Should focus W | 
					
						
							|  |  |  | t.bind('<1>', single1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def double1(e): | 
					
						
							|  |  |  | 	x = e.x | 
					
						
							|  |  |  | 	y = e.y | 
					
						
							| 
									
										
										
										
											1994-06-20 09:08:51 +00:00
										 |  |  | 	t.setvar('tk_priv(selectMode)', 'word') | 
					
						
							|  |  |  | 	t.tk_textSelectTo(At(x, y)) | 
					
						
							| 
									
										
										
										
											1994-06-20 07:49:28 +00:00
										 |  |  | t.bind('<Double-1>', double1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def triple1(e): | 
					
						
							|  |  |  | 	x = e.x | 
					
						
							|  |  |  | 	y = e.y | 
					
						
							| 
									
										
										
										
											1994-06-20 09:08:51 +00:00
										 |  |  | 	t.setvar('tk_priv(selectMode)', 'line') | 
					
						
							|  |  |  | 	t.tk_textSelectTo(At(x, y)) | 
					
						
							| 
									
										
										
										
											1994-06-20 07:49:28 +00:00
										 |  |  | t.bind('<Triple-1>', triple1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def returnkey(e): | 
					
						
							| 
									
										
										
										
											1994-06-20 09:08:51 +00:00
										 |  |  | 	t.insert(AtInsert(), '\n') | 
					
						
							| 
									
										
										
										
											1994-06-20 07:49:28 +00:00
										 |  |  | 	invoke() | 
					
						
							|  |  |  | t.bind('<Return>', returnkey) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def controlv(e): | 
					
						
							| 
									
										
										
										
											1994-06-20 09:08:51 +00:00
										 |  |  | 	t.insert(AtInsert(), t.selection_get()) | 
					
						
							|  |  |  | 	t.yview_pickplace(AtInsert()) | 
					
						
							|  |  |  | 	if t.index(AtInsert())[-2:] == '.0': | 
					
						
							| 
									
										
										
										
											1994-06-20 07:49:28 +00:00
										 |  |  | 		invoke() | 
					
						
							|  |  |  | t.bind('<Control-v>', controlv) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # 4. Procedure to backspace over one character, as long as | 
					
						
							|  |  |  | # the character isn't part of the prompt. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def backspace(e): | 
					
						
							|  |  |  | 	if t.index('promptEnd') != t.index('insert - 1 char'): | 
					
						
							| 
									
										
										
										
											1994-06-20 09:08:51 +00:00
										 |  |  | 		t.delete('insert - 1 char', AtInsert()) | 
					
						
							|  |  |  | 		t.yview_pickplace(AtInsert()) | 
					
						
							| 
									
										
										
										
											1994-06-20 07:49:28 +00:00
										 |  |  | t.bind('<BackSpace>', backspace) | 
					
						
							|  |  |  | t.bind('<Control-h>', backspace) | 
					
						
							|  |  |  | t.bind('<Delete>', backspace) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # 5. Procedure that's invoked when return is typed:  if | 
					
						
							|  |  |  | # there's not yet a complete command (e.g. braces are open) | 
					
						
							|  |  |  | # then do nothing.  Otherwise, execute command (locally or | 
					
						
							|  |  |  | # remotely), output the result or error message, and issue | 
					
						
							|  |  |  | # a new prompt. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def invoke(): | 
					
						
							| 
									
										
										
										
											1994-06-20 09:08:51 +00:00
										 |  |  | 	cmd = t.get('promptEnd + 1 char', AtInsert()) | 
					
						
							|  |  |  | 	if t.getboolean(tk.call('info', 'complete', cmd)): # XXX | 
					
						
							|  |  |  | 		if app == root.winfo_name(): | 
					
						
							|  |  |  | 			msg = tk.call('eval', cmd) # XXX | 
					
						
							| 
									
										
										
										
											1994-06-20 07:49:28 +00:00
										 |  |  | 		else: | 
					
						
							| 
									
										
										
										
											1994-06-20 09:08:51 +00:00
										 |  |  | 			msg = t.send(app, cmd) | 
					
						
							| 
									
										
										
										
											1994-06-20 07:49:28 +00:00
										 |  |  | 		if msg: | 
					
						
							| 
									
										
										
										
											1994-06-20 09:08:51 +00:00
										 |  |  | 			t.insert(AtInsert(), msg + '\n') | 
					
						
							| 
									
										
										
										
											1994-06-20 07:49:28 +00:00
										 |  |  | 		prompt() | 
					
						
							| 
									
										
										
										
											1994-06-20 09:08:51 +00:00
										 |  |  | 	t.yview_pickplace(AtInsert()) | 
					
						
							| 
									
										
										
										
											1994-06-20 07:49:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def prompt(): | 
					
						
							| 
									
										
										
										
											1994-06-20 09:08:51 +00:00
										 |  |  | 	t.insert(AtInsert(), app + ': ') | 
					
						
							| 
									
										
										
										
											1994-06-20 07:49:28 +00:00
										 |  |  | 	t.mark_set('promptEnd', 'insert - 1 char') | 
					
						
							|  |  |  | 	t.tag_add('bold', 'insert linestart', 'promptEnd') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # 6. Procedure to select a new application.  Also changes | 
					
						
							|  |  |  | # the prompt on the current command line to reflect the new | 
					
						
							|  |  |  | # name. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def newApp(appName): | 
					
						
							|  |  |  | 	global app | 
					
						
							|  |  |  | 	app = appName | 
					
						
							|  |  |  | 	t.delete('promptEnd linestart', 'promptEnd') | 
					
						
							|  |  |  | 	t.insert('promptEnd', appName + ':') | 
					
						
							|  |  |  | 	t.tag_add('bold', 'promptEnd linestart', 'promptEnd') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def fillAppsMenu(): | 
					
						
							|  |  |  | 	file_m_apps.add('command') | 
					
						
							|  |  |  | 	file_m_apps.delete(0, 'last') | 
					
						
							| 
									
										
										
										
											1994-06-20 09:08:51 +00:00
										 |  |  | 	names = root.winfo_interps() | 
					
						
							| 
									
										
										
										
											1994-06-20 07:49:28 +00:00
										 |  |  | 	names = map(None, names) # convert tuple to list | 
					
						
							|  |  |  | 	names.sort() | 
					
						
							|  |  |  | 	for name in names: | 
					
						
							| 
									
										
										
										
											1994-06-20 09:08:51 +00:00
										 |  |  | 		try: | 
					
						
							|  |  |  | 			root.send(name, 'winfo name .') | 
					
						
							|  |  |  | 		except TclError: | 
					
						
							|  |  |  | 			# Inoperative window -- ignore it | 
					
						
							|  |  |  | 			pass | 
					
						
							|  |  |  | 		else: | 
					
						
							| 
									
										
										
										
											1996-07-23 17:47:21 +00:00
										 |  |  | 			file_m_apps.add_command( | 
					
						
							|  |  |  | 			    label=name, | 
					
						
							|  |  |  | 			    command=lambda name=name: newApp(name)) | 
					
						
							| 
									
										
										
										
											1994-06-20 07:49:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | file_m_apps['postcommand'] = fillAppsMenu | 
					
						
							|  |  |  | mBar.tk_menuBar(file) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # 7. Miscellaneous initialization. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1994-06-20 09:08:51 +00:00
										 |  |  | app = root.winfo_name() | 
					
						
							| 
									
										
										
										
											1994-06-20 07:49:28 +00:00
										 |  |  | prompt() | 
					
						
							| 
									
										
										
										
											1994-06-20 09:08:51 +00:00
										 |  |  | t.focus() | 
					
						
							| 
									
										
										
										
											1994-06-20 07:49:28 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | root.mainloop() |