Guido van Rossum | f06ee5f | 1996-11-27 19:52:01 +0000 | [diff] [blame] | 1 | #! /usr/bin/env python |
Guido van Rossum | 1846882 | 1994-06-20 07:49:28 +0000 | [diff] [blame] | 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 | |
Guido van Rossum | 74f2565 | 1994-07-13 13:08:01 +0000 | [diff] [blame] | 12 | # XXX This should be written in a more Python-like style!!! |
| 13 | |
Benjamin Peterson | d6d63f5 | 2009-01-04 18:53:28 +0000 | [diff] [blame] | 14 | from tkinter import * |
Fred Drake | 4dd0bf9 | 1996-07-23 17:47:21 +0000 | [diff] [blame] | 15 | import sys |
Guido van Rossum | 1846882 | 1994-06-20 07:49:28 +0000 | [diff] [blame] | 16 | |
| 17 | # 1. Create basic application structure: menu bar on top of |
| 18 | # text widget, scrollbar on right. |
| 19 | |
| 20 | root = Tk() |
| 21 | tk = root.tk |
Fred Drake | 4dd0bf9 | 1996-07-23 17:47:21 +0000 | [diff] [blame] | 22 | mBar = Frame(root, relief=RAISED, borderwidth=2) |
| 23 | mBar.pack(fill=X) |
Guido van Rossum | 1846882 | 1994-06-20 07:49:28 +0000 | [diff] [blame] | 24 | |
Fred Drake | 4dd0bf9 | 1996-07-23 17:47:21 +0000 | [diff] [blame] | 25 | f = Frame(root) |
| 26 | f.pack(expand=1, fill=BOTH) |
| 27 | s = Scrollbar(f, relief=FLAT) |
| 28 | s.pack(side=RIGHT, fill=Y) |
| 29 | t = Text(f, relief=RAISED, borderwidth=2, yscrollcommand=s.set, setgrid=1) |
| 30 | t.pack(side=LEFT, fill=BOTH, expand=1) |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 31 | t.tag_config('bold', font='-Adobe-Courier-Bold-R-Normal-*-120-*') |
Fred Drake | 4dd0bf9 | 1996-07-23 17:47:21 +0000 | [diff] [blame] | 32 | s['command'] = t.yview |
| 33 | |
Guido van Rossum | 1846882 | 1994-06-20 07:49:28 +0000 | [diff] [blame] | 34 | root.title('Tk Remote Controller') |
| 35 | root.iconname('Tk Remote') |
| 36 | |
| 37 | # 2. Create menu button and menus. |
| 38 | |
Fred Drake | 4dd0bf9 | 1996-07-23 17:47:21 +0000 | [diff] [blame] | 39 | file = Menubutton(mBar, text='File', underline=0) |
| 40 | file.pack(side=LEFT) |
Guido van Rossum | 1846882 | 1994-06-20 07:49:28 +0000 | [diff] [blame] | 41 | file_m = Menu(file) |
| 42 | file['menu'] = file_m |
Fred Drake | 4dd0bf9 | 1996-07-23 17:47:21 +0000 | [diff] [blame] | 43 | file_m_apps = Menu(file_m, tearoff=0) |
| 44 | file_m.add_cascade(label='Select Application', underline=0, |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 45 | menu=file_m_apps) |
Fred Drake | 4dd0bf9 | 1996-07-23 17:47:21 +0000 | [diff] [blame] | 46 | file_m.add_command(label='Quit', underline=0, command=sys.exit) |
Guido van Rossum | 1846882 | 1994-06-20 07:49:28 +0000 | [diff] [blame] | 47 | |
| 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 | |
| 53 | def single1(e): |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 54 | x = e.x |
| 55 | y = e.y |
| 56 | t.setvar('tk_priv(selectMode)', 'char') |
| 57 | t.mark_set('anchor', At(x, y)) |
| 58 | # Should focus W |
Guido van Rossum | 1846882 | 1994-06-20 07:49:28 +0000 | [diff] [blame] | 59 | t.bind('<1>', single1) |
| 60 | |
| 61 | def double1(e): |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 62 | x = e.x |
| 63 | y = e.y |
| 64 | t.setvar('tk_priv(selectMode)', 'word') |
| 65 | t.tk_textSelectTo(At(x, y)) |
Guido van Rossum | 1846882 | 1994-06-20 07:49:28 +0000 | [diff] [blame] | 66 | t.bind('<Double-1>', double1) |
| 67 | |
| 68 | def triple1(e): |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 69 | x = e.x |
| 70 | y = e.y |
| 71 | t.setvar('tk_priv(selectMode)', 'line') |
| 72 | t.tk_textSelectTo(At(x, y)) |
Guido van Rossum | 1846882 | 1994-06-20 07:49:28 +0000 | [diff] [blame] | 73 | t.bind('<Triple-1>', triple1) |
| 74 | |
| 75 | def returnkey(e): |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 76 | t.insert(AtInsert(), '\n') |
| 77 | invoke() |
Guido van Rossum | 1846882 | 1994-06-20 07:49:28 +0000 | [diff] [blame] | 78 | t.bind('<Return>', returnkey) |
| 79 | |
| 80 | def controlv(e): |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 81 | t.insert(AtInsert(), t.selection_get()) |
| 82 | t.yview_pickplace(AtInsert()) |
| 83 | if t.index(AtInsert())[-2:] == '.0': |
| 84 | invoke() |
Guido van Rossum | 1846882 | 1994-06-20 07:49:28 +0000 | [diff] [blame] | 85 | t.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 | |
| 90 | def backspace(e): |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 91 | if t.index('promptEnd') != t.index('insert - 1 char'): |
| 92 | t.delete('insert - 1 char', AtInsert()) |
| 93 | t.yview_pickplace(AtInsert()) |
Guido van Rossum | 1846882 | 1994-06-20 07:49:28 +0000 | [diff] [blame] | 94 | t.bind('<BackSpace>', backspace) |
| 95 | t.bind('<Control-h>', backspace) |
| 96 | t.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 | |
| 105 | def invoke(): |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 106 | 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 |
| 110 | else: |
| 111 | msg = t.send(app, cmd) |
| 112 | if msg: |
| 113 | t.insert(AtInsert(), msg + '\n') |
| 114 | prompt() |
| 115 | t.yview_pickplace(AtInsert()) |
Guido van Rossum | 1846882 | 1994-06-20 07:49:28 +0000 | [diff] [blame] | 116 | |
| 117 | def prompt(): |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 118 | t.insert(AtInsert(), app + ': ') |
| 119 | t.mark_set('promptEnd', 'insert - 1 char') |
| 120 | t.tag_add('bold', 'insert linestart', 'promptEnd') |
Guido van Rossum | 1846882 | 1994-06-20 07:49:28 +0000 | [diff] [blame] | 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 | |
| 126 | def newApp(appName): |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 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') |
Guido van Rossum | 1846882 | 1994-06-20 07:49:28 +0000 | [diff] [blame] | 132 | |
Guido van Rossum | 1846882 | 1994-06-20 07:49:28 +0000 | [diff] [blame] | 133 | def fillAppsMenu(): |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 134 | file_m_apps.add('command') |
| 135 | file_m_apps.delete(0, 'last') |
| 136 | names = root.winfo_interps() |
Collin Winter | 6f2df4d | 2007-07-17 20:59:35 +0000 | [diff] [blame] | 137 | names = list(names) # convert tuple to list |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 138 | names.sort() |
| 139 | for name in names: |
| 140 | try: |
| 141 | root.send(name, 'winfo name .') |
| 142 | except TclError: |
| 143 | # Inoperative window -- ignore it |
| 144 | pass |
| 145 | else: |
| 146 | file_m_apps.add_command( |
| 147 | label=name, |
| 148 | command=lambda name=name: newApp(name)) |
Guido van Rossum | 1846882 | 1994-06-20 07:49:28 +0000 | [diff] [blame] | 149 | |
| 150 | file_m_apps['postcommand'] = fillAppsMenu |
| 151 | mBar.tk_menuBar(file) |
| 152 | |
| 153 | # 7. Miscellaneous initialization. |
| 154 | |
Guido van Rossum | df09691 | 1994-06-20 09:08:51 +0000 | [diff] [blame] | 155 | app = root.winfo_name() |
Guido van Rossum | 1846882 | 1994-06-20 07:49:28 +0000 | [diff] [blame] | 156 | prompt() |
Guido van Rossum | df09691 | 1994-06-20 09:08:51 +0000 | [diff] [blame] | 157 | t.focus() |
Guido van Rossum | 1846882 | 1994-06-20 07:49:28 +0000 | [diff] [blame] | 158 | |
| 159 | root.mainloop() |