Trash: Old Embedding GTK - ShuaiYAN/ipython GitHub Wiki
an updated gtk ipython view which works is available in accercisers source: http://git.gnome.org/browse/accerciser/tree/plugins/ipython_view.py
The Accerciser project has some great code that lets you run an IPython console inside a gtk.TextArea
http://live.gnome.org/Accerciser
The file ipython_view.py seems to be independent in their code, and is licensed under the BSD license. We include a modified version of the file below.
This has been tested and seems to work in Linux (Ubuntu 6.10 and Fedora Core 6) and Windows (XP with Python 2.4.2, IPython 0.7.3, gtk+-devel 2.10.7, PyGTK 2.10.3). It supports the up/down arrows to view history, and tab completion for commands/variables etc.
Suggestions for improvement very much appreciated! If you find bugs in the ipython_view.py file, please report them to GNOME bugzilla, http://bugzilla.gnome.org/browse.cgi?product=accerciser
- The HOME key doesn't return you to the right place when typing a command.
- On Windows, if you type a valid magic function, for example %magic then it causes your program to hang.
- On Windows, with IPython 0.8.1, colours don't seem to work and tab-completion is not available.
Here is a simple example showing how the IPython console can be embedded in a short Python/PyGTK script to give a basic scrolling terminal window.
Here is the code:
import gtk from ipython_view import * import pango import platform if platform.system()=="Windows": FONT = "Lucida Console 9" else: FONT = "Luxi Mono 10" W = gtk.Window() W.set_size_request(750,550) W.set_resizable(True) S = gtk.ScrolledWindow() S.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC) V = IPythonView() V.modify_font(pango.FontDescription(FONT)) V.set_wrap_mode(gtk.WRAP_CHAR) V.show() S.add(V) S.show() W.add(S) W.show() W.connect('delete_event',lambda x,y:False) W.connect('destroy',lambda x:gtk.main_quit()) gtk.main()
If you have variables you want to expose in the shell, use the following syntax. Also look at the files in the Accerciser source code for how to synchronise other aspects of your GUI with the actions inthe IPython prompt.
V.updateNamespace({'myvar': myvar})
Here is a modified version of the ipython_view.py from the Accerciser subversion repository.
Changes relative to the copy in Subversion:
- throw an exception on failed import, instead of remaining silent.
- added if argv is None: argv = [] to fix case of Python being called with commandline args not intended for ipython
""" Backend to the console plugin. @author: Eitan Isaacson @organization: IBM Corporation @copyright: Copyright (c) 2007 IBM Corporation @license: BSD All rights reserved. This program and the accompanying materials are made available under the terms of the BSD which accompanies this distribution, and is available at U{http://www.opensource.org/licenses/bsd-license.php} """ # this file is a modified version of source code from the Accerciser project # http://live.gnome.org/accerciser import gtk import re import sys import os import pango from StringIO import StringIO try: import IPython except Exception,e: raise "Error importing IPython (%s)" % str(e) ansi_colors = {'0;30': 'Black', '0;31': 'Red', '0;32': 'Green', '0;33': 'Brown', '0;34': 'Blue', '0;35': 'Purple', '0;36': 'Cyan', '0;37': 'LightGray', '1;30': 'DarkGray', '1;31': 'DarkRed', '1;32': 'SeaGreen', '1;33': 'Yellow', '1;34': 'LightBlue', '1;35': 'MediumPurple', '1;36': 'LightCyan', '1;37': 'White'} class IterableIPShell: def __init__(self,argv=None,user_ns=None,user_global_ns=None, cin=None, cout=None,cerr=None, input_func=None): if input_func: IPython.iplib.raw_input_original = input_func if cin: IPython.Shell.Term.cin = cin if cout: IPython.Shell.Term.cout = cout if cerr: IPython.Shell.Term.cerr = cerr if argv is None: argv=[] # This is to get rid of the blockage that occurs during # IPython.Shell.InteractiveShell.user_setup() IPython.iplib.raw_input = lambda x: None self.term = IPython.genutils.IOTerm(cin=cin, cout=cout, cerr=cerr) os.environ['TERM'] = 'dumb' excepthook = sys.excepthook self.IP = IPython.Shell.make_IPython(argv,user_ns=user_ns, user_global_ns=user_global_ns, embedded=True, shell_class=IPython.Shell.InteractiveShell) self.IP.system = lambda cmd: self.shell(self.IP.var_expand(cmd), header='IPython system call: ', verbose=self.IP.rc.system_verbose) sys.excepthook = excepthook self.iter_more = 0 self.history_level = 0 self.complete_sep = re.compile('[\s\{\}\[\]\(\)]') def execute(self): self.history_level = 0 orig_stdout = sys.stdout sys.stdout = IPython.Shell.Term.cout try: line = self.IP.raw_input(None, self.iter_more) if self.IP.autoindent: self.IP.readline_startup_hook(None) except KeyboardInterrupt: self.IP.write('\nKeyboardInterrupt\n') self.IP.resetbuffer() # keep cache in sync with the prompt counter: self.IP.outputcache.prompt_count -= 1 if self.IP.autoindent: self.IP.indent_current_nsp = 0 self.iter_more = 0 except: self.IP.showtraceback() else: self.iter_more = self.IP.push(line) if (self.IP.SyntaxTB.last_syntax_error and self.IP.rc.autoedit_syntax): self.IP.edit_syntax_error() if self.iter_more: self.prompt = str(self.IP.outputcache.prompt2).strip() if self.IP.autoindent: self.IP.readline_startup_hook(self.IP.pre_readline) else: self.prompt = str(self.IP.outputcache.prompt1).strip() sys.stdout = orig_stdout def historyBack(self): self.history_level -= 1 return self._getHistory() def historyForward(self): self.history_level += 1 return self._getHistory() def _getHistory(self): try: rv = self.IP.user_ns['In'][self.history_level].strip('\n') except IndexError: self.history_level = 0 rv = '' return rv def updateNamespace(self, ns_dict): self.IP.user_ns.update(ns_dict) def complete(self, line): split_line = self.complete_sep.split(line) possibilities = self.IP.complete(split_line[-1]) if possibilities: common_prefix = reduce(self._commonPrefix, possibilities) completed = line[:-len(split_line[-1])]+common_prefix else: completed = line return completed, possibilities def _commonPrefix(self, str1, str2): for i in range(len(str1)): if not str2.startswith(str1[:i+1]): return str1[:i] return str1 def shell(self, cmd,verbose=0,debug=0,header=''): stat = 0 if verbose or debug: print header+cmd # flush stdout so we don't mangle python's buffering if not debug: input, output = os.popen4(cmd) print output.read() output.close() input.close() class ConsoleView(gtk.TextView): def __init__(self): gtk.TextView.__init__(self) self.modify_font(pango.FontDescription('Mono')) self.set_cursor_visible(True) self.text_buffer = self.get_buffer() self.mark = self.text_buffer.create_mark('scroll_mark', self.text_buffer.get_end_iter(), False) for code in ansi_colors: self.text_buffer.create_tag(code, foreground=ansi_colors[code], weight=700) self.text_buffer.create_tag('0') self.text_buffer.create_tag('notouch', editable=False) self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?') self.line_start = \ self.text_buffer.create_mark('line_start', self.text_buffer.get_end_iter(), True ) self.connect('key-press-event', self._onKeypress) self.last_cursor_pos = 0 def write(self, text, editable=False): segments = self.color_pat.split(text) segment = segments.pop(0) start_mark = self.text_buffer.create_mark(None, self.text_buffer.get_end_iter(), True) self.text_buffer.insert(self.text_buffer.get_end_iter(), segment) if segments: ansi_tags = self.color_pat.findall(text) for tag in ansi_tags: i = segments.index(tag) self.text_buffer.insert_with_tags_by_name(self.text_buffer.get_end_iter(), segments[i+1], tag) segments.pop(i) if not editable: self.text_buffer.apply_tag_by_name('notouch', self.text_buffer.get_iter_at_mark(start_mark), self.text_buffer.get_end_iter()) self.text_buffer.delete_mark(start_mark) self.scroll_mark_onscreen(self.mark) def showPrompt(self, prompt): self.write(prompt) self.text_buffer.move_mark(self.line_start,self.text_buffer.get_end_iter()) def changeLine(self, text): iter = self.text_buffer.get_iter_at_mark(self.line_start) iter.forward_to_line_end() self.text_buffer.delete(self.text_buffer.get_iter_at_mark(self.line_start), iter) self.write(text, True) def getCurrentLine(self): rv = self.text_buffer.get_slice(self.text_buffer.get_iter_at_mark(self.line_start), self.text_buffer.get_end_iter(), False) return rv def showReturned(self, text): iter = self.text_buffer.get_iter_at_mark(self.line_start) iter.forward_to_line_end() self.text_buffer.apply_tag_by_name('notouch', self.text_buffer.get_iter_at_mark(self.line_start), iter) self.write('\n'+text) if text: self.write('\n') self.showPrompt(self.prompt) self.text_buffer.move_mark(self.line_start,self.text_buffer.get_end_iter()) self.text_buffer.place_cursor(self.text_buffer.get_end_iter()) def _onKeypress(self, obj, event): if not event.string: return insert_mark = self.text_buffer.get_insert() insert_iter = self.text_buffer.get_iter_at_mark(insert_mark) selection_mark = self.text_buffer.get_selection_bound() selection_iter = self.text_buffer.get_iter_at_mark(selection_mark) start_iter = self.text_buffer.get_iter_at_mark(self.line_start) if start_iter.compare(insert_iter) <= 0 and \ start_iter.compare(selection_iter) <= 0: return elif start_iter.compare(insert_iter) > 0 and \ start_iter.compare(selection_iter) > 0: self.text_buffer.place_cursor(start_iter) elif insert_iter.compare(selection_iter) < 0: self.text_buffer.move_mark(insert_mark, start_iter) elif insert_iter.compare(selection_iter) > 0: self.text_buffer.move_mark(selection_mark, start_iter) class IPythonView(ConsoleView, IterableIPShell): def __init__(self): ConsoleView.__init__(self) self.cout = StringIO() IterableIPShell.__init__(self, cout=self.cout,cerr=self.cout, input_func=self.raw_input) self.connect('key_press_event', self.keyPress) self.execute() self.cout.truncate(0) self.showPrompt(self.prompt) self.interrupt = False def raw_input(self, prompt=''): if self.interrupt: self.interrupt = False raise KeyboardInterrupt return self.getCurrentLine() def keyPress(self, widget, event): if event.state & gtk.gdk.CONTROL_MASK and event.keyval == 99: self.interrupt = True self._processLine() return True elif event.keyval == gtk.keysyms.Return: self._processLine() return True elif event.keyval == gtk.keysyms.Up: self.changeLine(self.historyBack()) return True elif event.keyval == gtk.keysyms.Down: self.changeLine(self.historyForward()) return True elif event.keyval == gtk.keysyms.Tab: if not self.getCurrentLine().strip(): return False completed, possibilities = self.complete(self.getCurrentLine()) if len(possibilities) > 1: slice = self.getCurrentLine() self.write('\n') for symbol in possibilities: self.write(symbol+'\n') self.showPrompt(self.prompt) self.changeLine(completed or slice) return True def _processLine(self): self.history_pos = 0 self.execute() rv = self.cout.getvalue() if rv: rv = rv.strip('\n') self.showReturned(rv) self.cout.truncate(0)