PageRenderTime 119ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/virtaal/virtaal/plugins/_python_console.py

https://github.com/AndreasEisele/wikitrans-pootle
Python | 368 lines | 354 code | 11 blank | 3 comment | 0 complexity | 3304ecb59b008c922a4847487647ea63 MD5 | raw file
  1. #!/usr/bin/env python
  2. import gobject
  3. import gtk
  4. import pango
  5. import re
  6. import sys
  7. import traceback
  8. from gtk import gdk
  9. from virtaal.controllers import BasePlugin
  10. class PythonConsole(gtk.ScrolledWindow):
  11. def __init__(self, namespace = {}, destroy_cb = None):
  12. gtk.ScrolledWindow.__init__(self)
  13. self.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC);
  14. self.set_shadow_type(gtk.SHADOW_IN)
  15. self.view = gtk.TextView()
  16. self.view.modify_font(pango.FontDescription('Monospace'))
  17. self.view.set_editable(True)
  18. self.view.set_wrap_mode(gtk.WRAP_WORD_CHAR)
  19. self.add(self.view)
  20. self.view.show()
  21. buffer = self.view.get_buffer()
  22. self.normal = buffer.create_tag("normal")
  23. self.error = buffer.create_tag("error")
  24. self.error.set_property("foreground", "red")
  25. self.command = buffer.create_tag("command")
  26. self.command.set_property("foreground", "blue")
  27. self.__spaces_pattern = re.compile(r'^\s+')
  28. self.namespace = namespace
  29. self.destroy_cb = destroy_cb
  30. self.block_command = False
  31. # Init first line
  32. buffer.create_mark("input-line", buffer.get_end_iter(), True)
  33. buffer.insert(buffer.get_end_iter(), ">>> ")
  34. buffer.create_mark("input", buffer.get_end_iter(), True)
  35. # Init history
  36. self.history = ['']
  37. self.history_pos = 0
  38. self.current_command = ''
  39. self.namespace['__history__'] = self.history
  40. # Set up hooks for standard output.
  41. self.stdout = gtkoutfile(self, sys.stdout.fileno(), self.normal)
  42. self.stderr = gtkoutfile(self, sys.stderr.fileno(), self.error)
  43. # Signals
  44. self.view.connect("key-press-event", self.__key_press_event_cb)
  45. buffer.connect("mark-set", self.__mark_set_cb)
  46. def __key_press_event_cb(self, view, event):
  47. if event.keyval == gtk.keysyms.d and \
  48. event.state == gtk.gdk.CONTROL_MASK:
  49. self.destroy()
  50. elif event.keyval == gtk.keysyms.Return and \
  51. event.state == gtk.gdk.CONTROL_MASK:
  52. # Get the command
  53. buffer = view.get_buffer()
  54. inp_mark = buffer.get_mark("input")
  55. inp = buffer.get_iter_at_mark(inp_mark)
  56. cur = buffer.get_end_iter()
  57. line = buffer.get_text(inp, cur)
  58. self.current_command = self.current_command + line + "\n"
  59. self.history_add(line)
  60. # Prepare the new line
  61. cur = buffer.get_end_iter()
  62. buffer.insert(cur, "\n... ")
  63. cur = buffer.get_end_iter()
  64. buffer.move_mark(inp_mark, cur)
  65. # Keep indentation of precendent line
  66. spaces = re.match(self.__spaces_pattern, line)
  67. if spaces is not None:
  68. buffer.insert(cur, line[spaces.start() : spaces.end()])
  69. cur = buffer.get_end_iter()
  70. buffer.place_cursor(cur)
  71. gobject.idle_add(self.scroll_to_end)
  72. return True
  73. elif event.keyval == gtk.keysyms.Return:
  74. # Get the marks
  75. buffer = view.get_buffer()
  76. lin_mark = buffer.get_mark("input-line")
  77. inp_mark = buffer.get_mark("input")
  78. # Get the command line
  79. inp = buffer.get_iter_at_mark(inp_mark)
  80. cur = buffer.get_end_iter()
  81. line = buffer.get_text(inp, cur)
  82. self.current_command = self.current_command + line + "\n"
  83. self.history_add(line)
  84. # Make the line blue
  85. lin = buffer.get_iter_at_mark(lin_mark)
  86. buffer.apply_tag(self.command, lin, cur)
  87. buffer.insert(cur, "\n")
  88. cur_strip = self.current_command.rstrip()
  89. if cur_strip.endswith(":") \
  90. or (self.current_command[-2:] != "\n\n" and self.block_command):
  91. # Unfinished block command
  92. self.block_command = True
  93. com_mark = "... "
  94. elif cur_strip.endswith("\\"):
  95. com_mark = "... "
  96. else:
  97. # Eval the command
  98. self.__run(self.current_command)
  99. self.current_command = ''
  100. self.block_command = False
  101. com_mark = ">>> "
  102. # Prepare the new line
  103. cur = buffer.get_end_iter()
  104. buffer.move_mark(lin_mark, cur)
  105. buffer.insert(cur, com_mark)
  106. cur = buffer.get_end_iter()
  107. buffer.move_mark(inp_mark, cur)
  108. buffer.place_cursor(cur)
  109. gobject.idle_add(self.scroll_to_end)
  110. return True
  111. elif event.keyval == gtk.keysyms.KP_Down or \
  112. event.keyval == gtk.keysyms.Down:
  113. # Next entry from history
  114. view.emit_stop_by_name("key_press_event")
  115. self.history_down()
  116. gobject.idle_add(self.scroll_to_end)
  117. return True
  118. elif event.keyval == gtk.keysyms.KP_Up or \
  119. event.keyval == gtk.keysyms.Up:
  120. # Previous entry from history
  121. view.emit_stop_by_name("key_press_event")
  122. self.history_up()
  123. gobject.idle_add(self.scroll_to_end)
  124. return True
  125. elif event.keyval == gtk.keysyms.KP_Left or \
  126. event.keyval == gtk.keysyms.Left or \
  127. event.keyval == gtk.keysyms.BackSpace:
  128. buffer = view.get_buffer()
  129. inp = buffer.get_iter_at_mark(buffer.get_mark("input"))
  130. cur = buffer.get_iter_at_mark(buffer.get_insert())
  131. return inp.compare(cur) == 0
  132. elif event.keyval == gtk.keysyms.Home:
  133. # Go to the begin of the command instead of the begin of
  134. # the line
  135. buffer = view.get_buffer()
  136. inp = buffer.get_iter_at_mark(buffer.get_mark("input"))
  137. if event.state == gtk.gdk.SHIFT_MASK:
  138. buffer.move_mark_by_name("insert", inp)
  139. else:
  140. buffer.place_cursor(inp)
  141. return True
  142. def __mark_set_cb(self, buffer, iter, name):
  143. input = buffer.get_iter_at_mark(buffer.get_mark("input"))
  144. pos = buffer.get_iter_at_mark(buffer.get_insert())
  145. self.view.set_editable(pos.compare(input) != -1)
  146. def get_command_line(self):
  147. buffer = self.view.get_buffer()
  148. inp = buffer.get_iter_at_mark(buffer.get_mark("input"))
  149. cur = buffer.get_end_iter()
  150. return buffer.get_text(inp, cur)
  151. def set_command_line(self, command):
  152. buffer = self.view.get_buffer()
  153. mark = buffer.get_mark("input")
  154. inp = buffer.get_iter_at_mark(mark)
  155. cur = buffer.get_end_iter()
  156. buffer.delete(inp, cur)
  157. buffer.insert(inp, command)
  158. buffer.select_range(buffer.get_iter_at_mark(mark),
  159. buffer.get_end_iter())
  160. self.view.grab_focus()
  161. def history_add(self, line):
  162. if line.strip() != '':
  163. self.history_pos = len(self.history)
  164. self.history[self.history_pos - 1] = line
  165. self.history.append('')
  166. def history_up(self):
  167. if self.history_pos > 0:
  168. self.history[self.history_pos] = self.get_command_line()
  169. self.history_pos = self.history_pos - 1
  170. self.set_command_line(self.history[self.history_pos])
  171. def history_down(self):
  172. if self.history_pos < len(self.history) - 1:
  173. self.history[self.history_pos] = self.get_command_line()
  174. self.history_pos = self.history_pos + 1
  175. self.set_command_line(self.history[self.history_pos])
  176. def scroll_to_end(self):
  177. iter = self.view.get_buffer().get_end_iter()
  178. self.view.scroll_to_iter(iter, 0.0)
  179. return False
  180. def write(self, text, tag = None):
  181. buffer = self.view.get_buffer()
  182. if tag is None:
  183. buffer.insert(buffer.get_end_iter(), text)
  184. else:
  185. buffer.insert_with_tags(buffer.get_end_iter(), text, tag)
  186. gobject.idle_add(self.scroll_to_end)
  187. def eval(self, command, display_command = False):
  188. buffer = self.view.get_buffer()
  189. lin = buffer.get_mark("input-line")
  190. buffer.delete(buffer.get_iter_at_mark(lin),
  191. buffer.get_end_iter())
  192. if isinstance(command, list) or isinstance(command, tuple):
  193. for c in command:
  194. if display_command:
  195. self.write(">>> " + c + "\n", self.command)
  196. self.__run(c)
  197. else:
  198. if display_command:
  199. self.write(">>> " + c + "\n", self.command)
  200. self.__run(command)
  201. cur = buffer.get_end_iter()
  202. buffer.move_mark_by_name("input-line", cur)
  203. buffer.insert(cur, ">>> ")
  204. cur = buffer.get_end_iter()
  205. buffer.move_mark_by_name("input", cur)
  206. self.view.scroll_to_iter(buffer.get_end_iter(), 0.0)
  207. def __run(self, command):
  208. sys.stdout, self.stdout = self.stdout, sys.stdout
  209. sys.stderr, self.stderr = self.stderr, sys.stderr
  210. try:
  211. try:
  212. r = eval(command, self.namespace, self.namespace)
  213. if r is not None:
  214. print `r`
  215. except SyntaxError:
  216. exec command in self.namespace
  217. except:
  218. if hasattr(sys, 'last_type') and sys.last_type == SystemExit:
  219. self.destroy()
  220. else:
  221. traceback.print_exc()
  222. sys.stdout, self.stdout = self.stdout, sys.stdout
  223. sys.stderr, self.stderr = self.stderr, sys.stderr
  224. def destroy(self):
  225. if self.destroy_cb is not None:
  226. self.destroy_cb()
  227. class gtkoutfile:
  228. """A fake output file object. It sends output to a TK test widget,
  229. and if asked for a file number, returns one set on instance creation"""
  230. def __init__(self, console, fn, tag):
  231. self.fn = fn
  232. self.console = console
  233. self.tag = tag
  234. def close(self): pass
  235. def flush(self): pass
  236. def fileno(self): return self.fn
  237. def isatty(self): return 0
  238. def read(self, a): return ''
  239. def readline(self): return ''
  240. def readlines(self): return []
  241. def write(self, s): self.console.write(s, self.tag)
  242. def writelines(self, l): self.console.write(l, self.tag)
  243. def seek(self, a): raise IOError, (29, 'Illegal seek')
  244. def tell(self): raise IOError, (29, 'Illegal seek')
  245. truncate = tell
  246. class Plugin(BasePlugin):
  247. description = _("Run-time access to Virtaal's internals (for developers).")
  248. display_name = _('Python Console')
  249. version = 0.1
  250. # INITIALIZERS #
  251. def __init__(self, internal_name, main_controller):
  252. self.internal_name = internal_name
  253. self.main_controller = main_controller
  254. self._init_plugin()
  255. def _init_plugin(self):
  256. self.window = None
  257. self._setup_key_bindings()
  258. self._setup_menu_item()
  259. def _setup_key_bindings(self):
  260. """Setup Gtk+ key bindings (accelerators)."""
  261. gtk.accel_map_add_entry("<Virtaal>/View/Python Console", gtk.keysyms.y, gdk.CONTROL_MASK)
  262. self.accel_group = gtk.AccelGroup()
  263. self.accel_group.connect_by_path("<Virtaal>/View/Python Console", self._on_menuitem_activated)
  264. self.main_controller.view.add_accel_group(self.accel_group)
  265. def _setup_menu_item(self):
  266. self.menu = self.main_controller.view.gui.get_widget('menu_view')
  267. self.menuitem = gtk.MenuItem(label=_('P_ython Console'))
  268. self.menuitem.show()
  269. self.menu.append(self.menuitem)
  270. gtk.accel_map_add_entry("<Virtaal>/View/Python Console", gtk.keysyms.F9, 0)
  271. accel_group = self.menu.get_accel_group()
  272. if accel_group is None:
  273. accel_group = self.accel_group
  274. self.menu.set_accel_group(self.accel_group)
  275. self.menuitem.set_accel_path("<Virtaal>/View/Python Console")
  276. self.menu.set_accel_group(accel_group)
  277. self.menuitem.connect('activate', self._on_menuitem_activated)
  278. # METHODS #
  279. def show_console(self, *args):
  280. if not self.window:
  281. ns = {
  282. '__builtins__' : __builtins__,
  283. 'mc': self.main_controller,
  284. 'sc': self.main_controller.store_controller,
  285. 'uc': self.main_controller.unit_controller,
  286. 'mv': self.main_controller.view,
  287. 'sv': self.main_controller.store_controller.view,
  288. 'uv': self.main_controller.unit_controller.view,
  289. }
  290. console = PythonConsole(namespace = ns, destroy_cb = self._on_console_destroyed)
  291. console.set_size_request(600, 400)
  292. self.window = gtk.Window()
  293. self.window.set_title('Virtaal Python Console')
  294. self.window.set_transient_for(self.main_controller.view.main_window)
  295. self.window.add(console)
  296. self.window.connect('destroy', self._on_console_destroyed)
  297. self.window.show_all()
  298. self.window.grab_focus()
  299. # EVENT HANDLERS #
  300. def _on_console_destroyed(self, *args):
  301. self.window = None
  302. def _on_menuitem_activated(self, *args):
  303. self.show_console()