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