home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / lib / rhythmbox / plugins / python-console / pythonconsole.py next >
Encoding:
Python Source  |  2009-04-07  |  13.4 KB  |  428 lines

  1. # -*- coding: utf-8 -*-
  2. #
  3. # pythonconsole.py
  4. #
  5. # Copyright (C) 2006 - Steve Fr√©cinaux
  6. #
  7. # This program is free software; you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation; either version 2, or (at your option)
  10. # any later version.
  11. #
  12. # The Rhythmbox authors hereby grant permission for non-GPL compatible
  13. # GStreamer plugins to be used and distributed together with GStreamer
  14. # and Rhythmbox. This permission is above and beyond the permissions granted
  15. # by the GPL license by which Rhythmbox is covered. If you modify this code
  16. # you may extend this exception to your version of the code, but you are not
  17. # obligated to do so. If you do not wish to do so, delete this exception
  18. # statement from your version.
  19. # This program is distributed in the hope that it will be useful,
  20. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  22. # GNU General Public License for more details.
  23. #
  24. # You should have received a copy of the GNU General Public License
  25. # along with this program; if not, write to the Free Software
  26. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
  27.  
  28. # Parts from "Interactive Python-GTK Console" (stolen from epiphany's console.py)
  29. #     Copyright (C), 1998 James Henstridge <james@daa.com.au>
  30. #     Copyright (C), 2005 Adam Hooper <adamh@densi.com>
  31. # Bits from gedit Python Console Plugin
  32. #     Copyrignt (C), 2005 Rapha√´l Slinckx
  33.  
  34. import string
  35. import sys
  36. import re
  37. import traceback
  38. import gobject
  39. import gtk
  40. import pango
  41. import gconf
  42. import rhythmdb, rb
  43.  
  44. try:
  45.     import rpdb2
  46.     have_rpdb2 = True
  47. except:
  48.     have_rpdb2 = False
  49.  
  50. ui_str = """
  51. <ui>
  52.   <menubar name="MenuBar">
  53.     <menu name="ToolsMenu" action="Tools">
  54.       <placeholder name="ToolsOps_5">
  55.         <menuitem name="PythonConsole" action="PythonConsole"/>
  56.         <menuitem name="PythonDebugger" action="PythonDebugger"/>
  57.       </placeholder>
  58.     </menu>
  59.   </menubar>
  60. </ui>
  61. """
  62.  
  63. class PythonConsolePlugin(rb.Plugin):
  64.     def __init__(self):
  65.         rb.Plugin.__init__(self)
  66.         self.window = None
  67.             
  68.     def activate(self, shell):
  69.         data = dict()
  70.         manager = shell.get_player().get_property('ui-manager')
  71.         
  72.         data['action_group'] = gtk.ActionGroup('PythonConsolePluginActions')
  73.  
  74.         action = gtk.Action('PythonConsole', _('_Python Console'),
  75.                             _("Show Rhythmbox's python console"),
  76.                             'gnome-mime-text-x-python')
  77.         action.connect('activate', self.show_console, shell)
  78.         data['action_group'].add_action(action)
  79.  
  80.         action = gtk.Action('PythonDebugger', _('Python Debugger'),
  81.                     _("Enable remote python debugging with rpdb2"),
  82.                     None)
  83.         if have_rpdb2:
  84.             action.connect('activate', self.enable_debugging, shell)
  85.         else:
  86.             action.set_visible(False)
  87.         data['action_group'].add_action(action)
  88.                 
  89.         manager.insert_action_group(data['action_group'], 0)
  90.         data['ui_id'] = manager.add_ui_from_string(ui_str)
  91.         manager.ensure_update()
  92.         
  93.         shell.set_data('PythonConsolePluginInfo', data)
  94.     
  95.     def deactivate(self, shell):
  96.         data = shell.get_data('PythonConsolePluginInfo')
  97.  
  98.         manager = shell.get_player().get_property('ui-manager')
  99.         manager.remove_ui(data['ui_id'])
  100.         manager.remove_action_group(data['action_group'])
  101.         manager.ensure_update()
  102.  
  103.         shell.set_data('PythonConsolePluginInfo', None)
  104.         
  105.         if self.window is not None:
  106.             self.window.destroy()
  107.         
  108.     def show_console(self, action, shell):
  109.         if not self.window:
  110.             ns = {'__builtins__' : __builtins__, 
  111.                   'rb' : rb,
  112.                   'rhythmdb' : rhythmdb,
  113.                   'shell' : shell}
  114.             console = PythonConsole(namespace = ns, 
  115.                                     destroy_cb = self.destroy_console)
  116.             console.set_size_request(600, 400)
  117.             console.eval('print "' + \
  118.                          _('You can access the main window ' \
  119.                          'through the \'shell\' variable :') +  
  120.                          '\\n%s" % shell', False)
  121.     
  122.             self.window = gtk.Window()
  123.             self.window.set_title('Rhythmbox Python Console')
  124.             self.window.add(console)
  125.             self.window.connect('destroy', self.destroy_console)
  126.             self.window.show_all()
  127.         else:
  128.             self.window.show_all()
  129.         self.window.grab_focus()
  130.  
  131.     def enable_debugging(self, action, shell):
  132.         msg = _("After you press OK, Rhythmbox will wait until you connect to it with winpdb or rpdb2. If you have not set a debugger password in GConf, it will use the default password ('rhythmbox').")
  133.         dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_OK_CANCEL, msg)
  134.         if dialog.run() == gtk.RESPONSE_OK:
  135.             gconfclient = gconf.client_get_default()
  136.             password = gconfclient.get_string('/apps/rhythmbox/plugins/pythonconsole/rpdb2_password') or "rhythmbox"
  137.             def start_debugger(password):
  138.                 rpdb2.start_embedded_debugger(password)
  139.                 return False
  140.  
  141.             gobject.idle_add(start_debugger, password)
  142.         dialog.destroy()
  143.     
  144.     def destroy_console(self, *args):
  145.         self.window.destroy()
  146.         self.window = None
  147.  
  148. class PythonConsole(gtk.ScrolledWindow):
  149.     def __init__(self, namespace = {}, destroy_cb = None):
  150.         gtk.ScrolledWindow.__init__(self)
  151.  
  152.         self.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC);
  153.         self.set_shadow_type(gtk.SHADOW_IN)
  154.         self.view = gtk.TextView()
  155.         self.view.modify_font(pango.FontDescription('Monospace'))
  156.         self.view.set_editable(True)
  157.         self.view.set_wrap_mode(gtk.WRAP_WORD_CHAR)
  158.         self.add(self.view)
  159.         self.view.show()
  160.  
  161.         buffer = self.view.get_buffer()
  162.         self.normal = buffer.create_tag("normal")
  163.         self.error  = buffer.create_tag("error")
  164.         self.error.set_property("foreground", "red")
  165.         self.command = buffer.create_tag("command")
  166.         self.command.set_property("foreground", "blue")
  167.  
  168.         self.__spaces_pattern = re.compile(r'^\s+')        
  169.         self.namespace = namespace
  170.  
  171.         self.destroy_cb = destroy_cb
  172.  
  173.         self.block_command = False
  174.  
  175.         # Init first line
  176.         buffer.create_mark("input-line", buffer.get_end_iter(), True)
  177.         buffer.insert(buffer.get_end_iter(), ">>> ")
  178.         buffer.create_mark("input", buffer.get_end_iter(), True)
  179.  
  180.         # Init history
  181.         self.history = ['']
  182.         self.history_pos = 0
  183.         self.current_command = ''
  184.         self.namespace['__history__'] = self.history
  185.  
  186.         # Set up hooks for standard output.
  187.         self.stdout = gtkoutfile(self, sys.stdout.fileno(), self.normal)
  188.         self.stderr = gtkoutfile(self, sys.stderr.fileno(), self.error)
  189.         
  190.         # Signals
  191.         self.view.connect("key-press-event", self.__key_press_event_cb)
  192.         buffer.connect("mark-set", self.__mark_set_cb)
  193.         
  194.          
  195.     def __key_press_event_cb(self, view, event):
  196.         if event.keyval == gtk.keysyms.d and \
  197.            event.state == gtk.gdk.CONTROL_MASK:
  198.             self.destroy()
  199.         
  200.         elif event.keyval == gtk.keysyms.Return and \
  201.              event.state == gtk.gdk.CONTROL_MASK:
  202.             # Get the command
  203.             buffer = view.get_buffer()
  204.             inp_mark = buffer.get_mark("input")
  205.             inp = buffer.get_iter_at_mark(inp_mark)
  206.             cur = buffer.get_end_iter()
  207.             line = buffer.get_text(inp, cur)
  208.             self.current_command = self.current_command + line + "\n"
  209.             self.history_add(line)
  210.  
  211.             # Prepare the new line
  212.             cur = buffer.get_end_iter()
  213.             buffer.insert(cur, "\n... ")
  214.             cur = buffer.get_end_iter()
  215.             buffer.move_mark(inp_mark, cur)
  216.             
  217.             # Keep indentation of precendent line
  218.             spaces = re.match(self.__spaces_pattern, line)
  219.             if spaces is not None:
  220.                 buffer.insert(cur, line[spaces.start() : spaces.end()])
  221.                 cur = buffer.get_end_iter()
  222.                 
  223.             buffer.place_cursor(cur)
  224.             gobject.idle_add(self.scroll_to_end)
  225.             return True
  226.         
  227.         elif event.keyval == gtk.keysyms.Return:
  228.             # Get the marks
  229.             buffer = view.get_buffer()
  230.             lin_mark = buffer.get_mark("input-line")
  231.             inp_mark = buffer.get_mark("input")
  232.  
  233.             # Get the command line
  234.             inp = buffer.get_iter_at_mark(inp_mark)
  235.             cur = buffer.get_end_iter()
  236.             line = buffer.get_text(inp, cur)
  237.             self.current_command = self.current_command + line + "\n"
  238.             self.history_add(line)
  239.  
  240.             # Make the line blue
  241.             lin = buffer.get_iter_at_mark(lin_mark)
  242.             buffer.apply_tag(self.command, lin, cur)
  243.             buffer.insert(cur, "\n")
  244.             
  245.             cur_strip = self.current_command.rstrip()
  246.  
  247.             if cur_strip.endswith(":") \
  248.             or (self.current_command[-2:] != "\n\n" and self.block_command):
  249.                 # Unfinished block command
  250.                 self.block_command = True
  251.                 com_mark = "... "
  252.             elif cur_strip.endswith("\\"):
  253.                 com_mark = "... "
  254.             else:
  255.                 # Eval the command
  256.                 self.__run(self.current_command)
  257.                 self.current_command = ''
  258.                 self.block_command = False
  259.                 com_mark = ">>> "
  260.  
  261.             # Prepare the new line
  262.             cur = buffer.get_end_iter()
  263.             buffer.move_mark(lin_mark, cur)
  264.             buffer.insert(cur, com_mark)
  265.             cur = buffer.get_end_iter()
  266.             buffer.move_mark(inp_mark, cur)
  267.             buffer.place_cursor(cur)
  268.             gobject.idle_add(self.scroll_to_end)
  269.             return True
  270.  
  271.         elif event.keyval == gtk.keysyms.KP_Down or \
  272.              event.keyval == gtk.keysyms.Down:
  273.             # Next entry from history
  274.             view.emit_stop_by_name("key_press_event")
  275.             self.history_down()
  276.             gobject.idle_add(self.scroll_to_end)
  277.             return True
  278.  
  279.         elif event.keyval == gtk.keysyms.KP_Up or \
  280.              event.keyval == gtk.keysyms.Up:
  281.             # Previous entry from history
  282.             view.emit_stop_by_name("key_press_event")
  283.             self.history_up()
  284.             gobject.idle_add(self.scroll_to_end)
  285.             return True
  286.  
  287.         elif event.keyval == gtk.keysyms.KP_Left or \
  288.              event.keyval == gtk.keysyms.Left or \
  289.              event.keyval == gtk.keysyms.BackSpace:
  290.             buffer = view.get_buffer()
  291.             inp = buffer.get_iter_at_mark(buffer.get_mark("input"))
  292.             cur = buffer.get_iter_at_mark(buffer.get_insert())
  293.             return inp.compare(cur) == 0
  294.  
  295.         elif event.keyval == gtk.keysyms.Home:
  296.             # Go to the begin of the command instead of the begin of
  297.             # the line
  298.             buffer = view.get_buffer()
  299.             inp = buffer.get_iter_at_mark(buffer.get_mark("input"))
  300.             if event.state == gtk.gdk.SHIFT_MASK:
  301.                 buffer.move_mark_by_name("insert", inp)
  302.             else:
  303.                 buffer.place_cursor(inp)
  304.             return True
  305.         
  306.     def __mark_set_cb(self, buffer, iter, name):
  307.         input = buffer.get_iter_at_mark(buffer.get_mark("input"))
  308.         pos   = buffer.get_iter_at_mark(buffer.get_insert())
  309.         self.view.set_editable(pos.compare(input) != -1)
  310.  
  311.     def get_command_line(self):
  312.         buffer = self.view.get_buffer()
  313.         inp = buffer.get_iter_at_mark(buffer.get_mark("input"))
  314.         cur = buffer.get_end_iter()
  315.         return buffer.get_text(inp, cur)
  316.     
  317.     def set_command_line(self, command):
  318.         buffer = self.view.get_buffer()
  319.         mark = buffer.get_mark("input")
  320.         inp = buffer.get_iter_at_mark(mark)
  321.         cur = buffer.get_end_iter()
  322.         buffer.delete(inp, cur)
  323.         buffer.insert(inp, command)
  324.         buffer.select_range(buffer.get_iter_at_mark(mark),
  325.                             buffer.get_end_iter())
  326.         self.view.grab_focus()
  327.     
  328.     def history_add(self, line):
  329.         if line.strip() != '':
  330.             self.history_pos = len(self.history)
  331.             self.history[self.history_pos - 1] = line
  332.             self.history.append('')
  333.     
  334.     def history_up(self):
  335.         if self.history_pos > 0:
  336.             self.history[self.history_pos] = self.get_command_line()
  337.             self.history_pos = self.history_pos - 1
  338.             self.set_command_line(self.history[self.history_pos])
  339.             
  340.     def history_down(self):
  341.         if self.history_pos < len(self.history) - 1:
  342.             self.history[self.history_pos] = self.get_command_line()
  343.             self.history_pos = self.history_pos + 1
  344.             self.set_command_line(self.history[self.history_pos])
  345.     
  346.     def scroll_to_end(self):
  347.         iter = self.view.get_buffer().get_end_iter()
  348.         self.view.scroll_to_iter(iter, 0.0)
  349.         return False
  350.  
  351.     def write(self, text, tag = None):
  352.         buffer = self.view.get_buffer()
  353.         if tag is None:
  354.             buffer.insert(buffer.get_end_iter(), text)
  355.         else:
  356.             buffer.insert_with_tags(buffer.get_end_iter(), text, tag)
  357.         gobject.idle_add(self.scroll_to_end)
  358.      
  359.      def eval(self, command, display_command = False):
  360.         buffer = self.view.get_buffer()
  361.         lin = buffer.get_mark("input-line")
  362.         buffer.delete(buffer.get_iter_at_mark(lin),
  363.                       buffer.get_end_iter())
  364.  
  365.         if isinstance(command, list) or isinstance(command, tuple):
  366.              for c in command:
  367.                  if display_command:
  368.                      self.write(">>> " + c + "\n", self.command)
  369.                  self.__run(c)
  370.         else:
  371.              if display_command:
  372.                  self.write(">>> " + c + "\n", self.command)
  373.             self.__run(command) 
  374.  
  375.         cur = buffer.get_end_iter()
  376.         buffer.move_mark_by_name("input-line", cur)
  377.         buffer.insert(cur, ">>> ")
  378.         cur = buffer.get_end_iter()
  379.         buffer.move_mark_by_name("input", cur)
  380.         self.view.scroll_to_iter(buffer.get_end_iter(), 0.0)
  381.     
  382.      def __run(self, command):
  383.         sys.stdout, self.stdout = self.stdout, sys.stdout
  384.         sys.stderr, self.stderr = self.stderr, sys.stderr
  385.         
  386.         try:
  387.             try:
  388.                 r = eval(command, self.namespace, self.namespace)
  389.                 if r is not None:
  390.                     print `r`
  391.             except SyntaxError:
  392.                 exec command in self.namespace
  393.         except:
  394.             if hasattr(sys, 'last_type') and sys.last_type == SystemExit:
  395.                 self.destroy()
  396.             else:
  397.                 traceback.print_exc()
  398.  
  399.         sys.stdout, self.stdout = self.stdout, sys.stdout
  400.         sys.stderr, self.stderr = self.stderr, sys.stderr
  401.  
  402.     def destroy(self):
  403.         if self.destroy_cb is not None:
  404.             self.destroy_cb()
  405.         
  406. class gtkoutfile:
  407.     """A fake output file object.  It sends output to a TK test widget,
  408.     and if asked for a file number, returns one set on instance creation"""
  409.     def __init__(self, console, fn, tag):
  410.         self.fn = fn
  411.         self.console = console
  412.         self.tag = tag
  413.     def close(self):         pass
  414.     def flush(self):         pass
  415.     def fileno(self):        return self.fn
  416.     def isatty(self):        return 0
  417.     def read(self, a):       return ''
  418.     def readline(self):      return ''
  419.     def readlines(self):     return []
  420.     def write(self, s):      self.console.write(s, self.tag)
  421.     def writelines(self, l): self.console.write(l, self.tag)
  422.     def seek(self, a):       raise IOError, (29, 'Illegal seek')
  423.     def tell(self):          raise IOError, (29, 'Illegal seek')
  424.     truncate = tell
  425.  
  426. # ex:noet:ts=8:
  427.