/Demo/tkinter/guido/rmt.py

http://unladen-swallow.googlecode.com/ · Python · 159 lines · 101 code · 29 blank · 29 comment · 10 complexity · 1f6d300909b102b969b8cb22563fd3b2 MD5 · raw file

  1. #! /usr/bin/env python
  2. # A Python program implementing rmt, an application for remotely
  3. # controlling other Tk applications.
  4. # Cf. Ousterhout, Tcl and the Tk Toolkit, Figs. 27.5-8, pp. 273-276.
  5. # Note that because of forward references in the original, we
  6. # sometimes delay bindings until after the corresponding procedure is
  7. # defined. We also introduce names for some unnamed code blocks in
  8. # the original because of restrictions on lambda forms in Python.
  9. # XXX This should be written in a more Python-like style!!!
  10. from Tkinter import *
  11. import sys
  12. # 1. Create basic application structure: menu bar on top of
  13. # text widget, scrollbar on right.
  14. root = Tk()
  15. tk = root.tk
  16. mBar = Frame(root, relief=RAISED, borderwidth=2)
  17. mBar.pack(fill=X)
  18. f = Frame(root)
  19. f.pack(expand=1, fill=BOTH)
  20. s = Scrollbar(f, relief=FLAT)
  21. s.pack(side=RIGHT, fill=Y)
  22. t = Text(f, relief=RAISED, borderwidth=2, yscrollcommand=s.set, setgrid=1)
  23. t.pack(side=LEFT, fill=BOTH, expand=1)
  24. t.tag_config('bold', font='-Adobe-Courier-Bold-R-Normal-*-120-*')
  25. s['command'] = t.yview
  26. root.title('Tk Remote Controller')
  27. root.iconname('Tk Remote')
  28. # 2. Create menu button and menus.
  29. file = Menubutton(mBar, text='File', underline=0)
  30. file.pack(side=LEFT)
  31. file_m = Menu(file)
  32. file['menu'] = file_m
  33. file_m_apps = Menu(file_m, tearoff=0)
  34. file_m.add_cascade(label='Select Application', underline=0,
  35. menu=file_m_apps)
  36. file_m.add_command(label='Quit', underline=0, command=sys.exit)
  37. # 3. Create bindings for text widget to allow commands to be
  38. # entered and information to be selected. New characters
  39. # can only be added at the end of the text (can't ever move
  40. # insertion point).
  41. def single1(e):
  42. x = e.x
  43. y = e.y
  44. t.setvar('tk_priv(selectMode)', 'char')
  45. t.mark_set('anchor', At(x, y))
  46. # Should focus W
  47. t.bind('<1>', single1)
  48. def double1(e):
  49. x = e.x
  50. y = e.y
  51. t.setvar('tk_priv(selectMode)', 'word')
  52. t.tk_textSelectTo(At(x, y))
  53. t.bind('<Double-1>', double1)
  54. def triple1(e):
  55. x = e.x
  56. y = e.y
  57. t.setvar('tk_priv(selectMode)', 'line')
  58. t.tk_textSelectTo(At(x, y))
  59. t.bind('<Triple-1>', triple1)
  60. def returnkey(e):
  61. t.insert(AtInsert(), '\n')
  62. invoke()
  63. t.bind('<Return>', returnkey)
  64. def controlv(e):
  65. t.insert(AtInsert(), t.selection_get())
  66. t.yview_pickplace(AtInsert())
  67. if t.index(AtInsert())[-2:] == '.0':
  68. invoke()
  69. t.bind('<Control-v>', controlv)
  70. # 4. Procedure to backspace over one character, as long as
  71. # the character isn't part of the prompt.
  72. def backspace(e):
  73. if t.index('promptEnd') != t.index('insert - 1 char'):
  74. t.delete('insert - 1 char', AtInsert())
  75. t.yview_pickplace(AtInsert())
  76. t.bind('<BackSpace>', backspace)
  77. t.bind('<Control-h>', backspace)
  78. t.bind('<Delete>', backspace)
  79. # 5. Procedure that's invoked when return is typed: if
  80. # there's not yet a complete command (e.g. braces are open)
  81. # then do nothing. Otherwise, execute command (locally or
  82. # remotely), output the result or error message, and issue
  83. # a new prompt.
  84. def invoke():
  85. cmd = t.get('promptEnd + 1 char', AtInsert())
  86. if t.getboolean(tk.call('info', 'complete', cmd)): # XXX
  87. if app == root.winfo_name():
  88. msg = tk.call('eval', cmd) # XXX
  89. else:
  90. msg = t.send(app, cmd)
  91. if msg:
  92. t.insert(AtInsert(), msg + '\n')
  93. prompt()
  94. t.yview_pickplace(AtInsert())
  95. def prompt():
  96. t.insert(AtInsert(), app + ': ')
  97. t.mark_set('promptEnd', 'insert - 1 char')
  98. t.tag_add('bold', 'insert linestart', 'promptEnd')
  99. # 6. Procedure to select a new application. Also changes
  100. # the prompt on the current command line to reflect the new
  101. # name.
  102. def newApp(appName):
  103. global app
  104. app = appName
  105. t.delete('promptEnd linestart', 'promptEnd')
  106. t.insert('promptEnd', appName + ':')
  107. t.tag_add('bold', 'promptEnd linestart', 'promptEnd')
  108. def fillAppsMenu():
  109. file_m_apps.add('command')
  110. file_m_apps.delete(0, 'last')
  111. names = root.winfo_interps()
  112. names = list(names)
  113. names.sort()
  114. for name in names:
  115. try:
  116. root.send(name, 'winfo name .')
  117. except TclError:
  118. # Inoperative window -- ignore it
  119. pass
  120. else:
  121. file_m_apps.add_command(
  122. label=name,
  123. command=lambda name=name: newApp(name))
  124. file_m_apps['postcommand'] = fillAppsMenu
  125. mBar.tk_menuBar(file)
  126. # 7. Miscellaneous initialization.
  127. app = root.winfo_name()
  128. prompt()
  129. t.focus()
  130. root.mainloop()