Mercurial > code > home > repos > light9
annotate lib/ipython_view.py @ 2405:69ca2b2fc133
overcomplicated attempt at persisting the pane layout in the rdf graph
this was hard because we have to somehow wait for the graph to load before config'ing the panes
author | drewp@bigasterisk.com |
---|---|
date | Fri, 17 May 2024 16:58:26 -0700 |
parents | 3c523c71da29 |
children |
rev | line source |
---|---|
709 | 1 # this version is adapted from http://wiki.ipython.org/Old_Embedding/GTK |
2 | |
3 """ | |
4 Backend to the console plugin. | |
5 | |
6 @author: Eitan Isaacson | |
7 @organization: IBM Corporation | |
8 @copyright: Copyright (c) 2007 IBM Corporation | |
9 @license: BSD | |
10 | |
11 All rights reserved. This program and the accompanying materials are made | |
12 available under the terms of the BSD which accompanies this distribution, and | |
13 is available at U{http://www.opensource.org/licenses/bsd-license.php} | |
14 """ | |
15 # this file is a modified version of source code from the Accerciser project | |
16 # http://live.gnome.org/accerciser | |
17 | |
1056
547d65ea9902
port curvecalc to gtk3. mostly worked, but there are severe bugs with redraws
Drew Perttula <drewp@bigasterisk.com>
parents:
709
diff
changeset
|
18 from gi.repository import Gtk |
547d65ea9902
port curvecalc to gtk3. mostly worked, but there are severe bugs with redraws
Drew Perttula <drewp@bigasterisk.com>
parents:
709
diff
changeset
|
19 from gi.repository import Gdk |
709 | 20 import re |
21 import sys | |
22 import os | |
1056
547d65ea9902
port curvecalc to gtk3. mostly worked, but there are severe bugs with redraws
Drew Perttula <drewp@bigasterisk.com>
parents:
709
diff
changeset
|
23 from gi.repository import Pango |
1866
3c523c71da29
pyflakes cleanups and some refactors
Drew Perttula <drewp@bigasterisk.com>
parents:
1056
diff
changeset
|
24 from io import StringIO |
3c523c71da29
pyflakes cleanups and some refactors
Drew Perttula <drewp@bigasterisk.com>
parents:
1056
diff
changeset
|
25 from functools import reduce |
709 | 26 |
27 try: | |
1866
3c523c71da29
pyflakes cleanups and some refactors
Drew Perttula <drewp@bigasterisk.com>
parents:
1056
diff
changeset
|
28 import IPython |
3c523c71da29
pyflakes cleanups and some refactors
Drew Perttula <drewp@bigasterisk.com>
parents:
1056
diff
changeset
|
29 except Exception as e: |
709 | 30 raise "Error importing IPython (%s)" % str(e) |
31 | |
32 ansi_colors = {'0;30': 'Black', | |
33 '0;31': 'Red', | |
34 '0;32': 'Green', | |
35 '0;33': 'Brown', | |
36 '0;34': 'Blue', | |
37 '0;35': 'Purple', | |
38 '0;36': 'Cyan', | |
39 '0;37': 'LightGray', | |
40 '1;30': 'DarkGray', | |
41 '1;31': 'DarkRed', | |
42 '1;32': 'SeaGreen', | |
43 '1;33': 'Yellow', | |
44 '1;34': 'LightBlue', | |
45 '1;35': 'MediumPurple', | |
46 '1;36': 'LightCyan', | |
47 '1;37': 'White'} | |
48 | |
49 class IterableIPShell: | |
50 def __init__(self,argv=None,user_ns=None,user_global_ns=None, | |
51 cin=None, cout=None,cerr=None, input_func=None): | |
52 if input_func: | |
53 IPython.iplib.raw_input_original = input_func | |
54 if cin: | |
55 IPython.Shell.Term.cin = cin | |
56 if cout: | |
57 IPython.Shell.Term.cout = cout | |
58 if cerr: | |
59 IPython.Shell.Term.cerr = cerr | |
60 | |
61 if argv is None: | |
62 argv=[] | |
63 | |
64 # This is to get rid of the blockage that occurs during | |
65 # IPython.Shell.InteractiveShell.user_setup() | |
66 IPython.iplib.raw_input = lambda x: None | |
67 | |
68 self.term = IPython.genutils.IOTerm(cin=cin, cout=cout, cerr=cerr) | |
69 os.environ['TERM'] = 'dumb' | |
70 excepthook = sys.excepthook | |
71 self.IP = IPython.Shell.make_IPython(argv,user_ns=user_ns, | |
72 user_global_ns=user_global_ns, | |
73 embedded=True, | |
74 shell_class=IPython.Shell.InteractiveShell) | |
75 self.IP.system = lambda cmd: self.shell(self.IP.var_expand(cmd), | |
76 header='IPython system call: ', | |
77 verbose=self.IP.rc.system_verbose) | |
78 sys.excepthook = excepthook | |
79 self.iter_more = 0 | |
80 self.history_level = 0 | |
81 self.complete_sep = re.compile('[\s\{\}\[\]\(\)]') | |
82 | |
83 def execute(self): | |
84 self.history_level = 0 | |
85 orig_stdout = sys.stdout | |
86 sys.stdout = IPython.Shell.Term.cout | |
87 try: | |
88 line = self.IP.raw_input(None, self.iter_more) | |
89 if self.IP.autoindent: | |
90 self.IP.readline_startup_hook(None) | |
91 except KeyboardInterrupt: | |
92 self.IP.write('\nKeyboardInterrupt\n') | |
93 self.IP.resetbuffer() | |
94 # keep cache in sync with the prompt counter: | |
95 self.IP.outputcache.prompt_count -= 1 | |
96 | |
97 if self.IP.autoindent: | |
98 self.IP.indent_current_nsp = 0 | |
99 self.iter_more = 0 | |
100 except: | |
101 self.IP.showtraceback() | |
102 else: | |
103 self.iter_more = self.IP.push(line) | |
104 if (self.IP.SyntaxTB.last_syntax_error and | |
105 self.IP.rc.autoedit_syntax): | |
106 self.IP.edit_syntax_error() | |
107 if self.iter_more: | |
108 self.prompt = str(self.IP.outputcache.prompt2).strip() | |
109 if self.IP.autoindent: | |
110 self.IP.readline_startup_hook(self.IP.pre_readline) | |
111 else: | |
112 self.prompt = str(self.IP.outputcache.prompt1).strip() | |
113 sys.stdout = orig_stdout | |
114 | |
115 def historyBack(self): | |
116 self.history_level -= 1 | |
117 return self._getHistory() | |
118 | |
119 def historyForward(self): | |
120 self.history_level += 1 | |
121 return self._getHistory() | |
122 | |
123 def _getHistory(self): | |
124 try: | |
125 rv = self.IP.user_ns['In'][self.history_level].strip('\n') | |
126 except IndexError: | |
127 self.history_level = 0 | |
128 rv = '' | |
129 return rv | |
130 | |
131 def updateNamespace(self, ns_dict): | |
132 self.IP.user_ns.update(ns_dict) | |
133 | |
134 def complete(self, line): | |
135 split_line = self.complete_sep.split(line) | |
136 possibilities = self.IP.complete(split_line[-1]) | |
137 if possibilities: | |
138 common_prefix = reduce(self._commonPrefix, possibilities) | |
139 completed = line[:-len(split_line[-1])]+common_prefix | |
140 else: | |
141 completed = line | |
142 return completed, possibilities | |
143 | |
144 def _commonPrefix(self, str1, str2): | |
145 for i in range(len(str1)): | |
146 if not str2.startswith(str1[:i+1]): | |
147 return str1[:i] | |
148 return str1 | |
149 | |
150 def shell(self, cmd,verbose=0,debug=0,header=''): | |
151 stat = 0 | |
1866
3c523c71da29
pyflakes cleanups and some refactors
Drew Perttula <drewp@bigasterisk.com>
parents:
1056
diff
changeset
|
152 if verbose or debug: print(header+cmd) |
709 | 153 # flush stdout so we don't mangle python's buffering |
154 if not debug: | |
155 input, output = os.popen4(cmd) | |
1866
3c523c71da29
pyflakes cleanups and some refactors
Drew Perttula <drewp@bigasterisk.com>
parents:
1056
diff
changeset
|
156 print(output.read()) |
709 | 157 output.close() |
158 input.close() | |
159 | |
1056
547d65ea9902
port curvecalc to gtk3. mostly worked, but there are severe bugs with redraws
Drew Perttula <drewp@bigasterisk.com>
parents:
709
diff
changeset
|
160 class ConsoleView(Gtk.TextView): |
709 | 161 def __init__(self): |
1056
547d65ea9902
port curvecalc to gtk3. mostly worked, but there are severe bugs with redraws
Drew Perttula <drewp@bigasterisk.com>
parents:
709
diff
changeset
|
162 Gtk.TextView.__init__(self) |
547d65ea9902
port curvecalc to gtk3. mostly worked, but there are severe bugs with redraws
Drew Perttula <drewp@bigasterisk.com>
parents:
709
diff
changeset
|
163 self.modify_font(Pango.FontDescription('Mono')) |
709 | 164 self.set_cursor_visible(True) |
165 self.text_buffer = self.get_buffer() | |
166 self.mark = self.text_buffer.create_mark('scroll_mark', | |
167 self.text_buffer.get_end_iter(), | |
168 False) | |
169 for code in ansi_colors: | |
170 self.text_buffer.create_tag(code, | |
171 foreground=ansi_colors[code], | |
172 weight=700) | |
173 self.text_buffer.create_tag('0') | |
174 self.text_buffer.create_tag('notouch', editable=False) | |
175 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?') | |
176 self.line_start = \ | |
177 self.text_buffer.create_mark('line_start', | |
178 self.text_buffer.get_end_iter(), True | |
179 ) | |
180 self.connect('key-press-event', self._onKeypress) | |
181 self.last_cursor_pos = 0 | |
182 | |
183 def write(self, text, editable=False): | |
184 segments = self.color_pat.split(text) | |
185 segment = segments.pop(0) | |
186 start_mark = self.text_buffer.create_mark(None, | |
187 self.text_buffer.get_end_iter(), | |
188 True) | |
189 self.text_buffer.insert(self.text_buffer.get_end_iter(), segment) | |
190 | |
191 if segments: | |
192 ansi_tags = self.color_pat.findall(text) | |
193 for tag in ansi_tags: | |
194 i = segments.index(tag) | |
195 self.text_buffer.insert_with_tags_by_name(self.text_buffer.get_end_iter(), | |
196 segments[i+1], tag) | |
197 segments.pop(i) | |
198 if not editable: | |
199 self.text_buffer.apply_tag_by_name('notouch', | |
200 self.text_buffer.get_iter_at_mark(start_mark), | |
201 self.text_buffer.get_end_iter()) | |
202 self.text_buffer.delete_mark(start_mark) | |
203 self.scroll_mark_onscreen(self.mark) | |
204 | |
205 def showPrompt(self, prompt): | |
206 self.write(prompt) | |
207 self.text_buffer.move_mark(self.line_start,self.text_buffer.get_end_iter()) | |
208 | |
209 def changeLine(self, text): | |
210 iter = self.text_buffer.get_iter_at_mark(self.line_start) | |
211 iter.forward_to_line_end() | |
212 self.text_buffer.delete(self.text_buffer.get_iter_at_mark(self.line_start), iter) | |
213 self.write(text, True) | |
214 | |
215 def getCurrentLine(self): | |
216 rv = self.text_buffer.get_slice(self.text_buffer.get_iter_at_mark(self.line_start), | |
217 self.text_buffer.get_end_iter(), False) | |
218 return rv | |
219 | |
220 def showReturned(self, text): | |
221 iter = self.text_buffer.get_iter_at_mark(self.line_start) | |
222 iter.forward_to_line_end() | |
223 self.text_buffer.apply_tag_by_name('notouch', | |
224 self.text_buffer.get_iter_at_mark(self.line_start), | |
225 iter) | |
226 self.write('\n'+text) | |
227 if text: | |
228 self.write('\n') | |
229 self.showPrompt(self.prompt) | |
230 self.text_buffer.move_mark(self.line_start,self.text_buffer.get_end_iter()) | |
231 self.text_buffer.place_cursor(self.text_buffer.get_end_iter()) | |
232 | |
233 def _onKeypress(self, obj, event): | |
234 if not event.string: | |
235 return | |
236 insert_mark = self.text_buffer.get_insert() | |
237 insert_iter = self.text_buffer.get_iter_at_mark(insert_mark) | |
238 selection_mark = self.text_buffer.get_selection_bound() | |
239 selection_iter = self.text_buffer.get_iter_at_mark(selection_mark) | |
240 start_iter = self.text_buffer.get_iter_at_mark(self.line_start) | |
241 if start_iter.compare(insert_iter) <= 0 and \ | |
242 start_iter.compare(selection_iter) <= 0: | |
243 return | |
244 elif start_iter.compare(insert_iter) > 0 and \ | |
245 start_iter.compare(selection_iter) > 0: | |
246 self.text_buffer.place_cursor(start_iter) | |
247 elif insert_iter.compare(selection_iter) < 0: | |
248 self.text_buffer.move_mark(insert_mark, start_iter) | |
249 elif insert_iter.compare(selection_iter) > 0: | |
250 self.text_buffer.move_mark(selection_mark, start_iter) | |
251 | |
252 | |
253 class IPythonView(ConsoleView, IterableIPShell): | |
254 def __init__(self, **kw): | |
255 ConsoleView.__init__(self) | |
256 self.cout = StringIO() | |
257 IterableIPShell.__init__(self, cout=self.cout,cerr=self.cout, | |
258 input_func=self.raw_input, **kw) | |
259 self.connect('key_press_event', self.keyPress) | |
260 self.execute() | |
261 self.cout.truncate(0) | |
262 self.showPrompt(self.prompt) | |
263 self.interrupt = False | |
264 | |
265 def raw_input(self, prompt=''): | |
266 if self.interrupt: | |
267 self.interrupt = False | |
268 raise KeyboardInterrupt | |
269 return self.getCurrentLine() | |
270 | |
271 def keyPress(self, widget, event): | |
1056
547d65ea9902
port curvecalc to gtk3. mostly worked, but there are severe bugs with redraws
Drew Perttula <drewp@bigasterisk.com>
parents:
709
diff
changeset
|
272 if event.state & Gdk.ModifierType.CONTROL_MASK and event.keyval == 99: |
709 | 273 self.interrupt = True |
274 self._processLine() | |
275 return True | |
1056
547d65ea9902
port curvecalc to gtk3. mostly worked, but there are severe bugs with redraws
Drew Perttula <drewp@bigasterisk.com>
parents:
709
diff
changeset
|
276 elif event.keyval == Gtk.keysyms.Return: |
709 | 277 self._processLine() |
278 return True | |
1056
547d65ea9902
port curvecalc to gtk3. mostly worked, but there are severe bugs with redraws
Drew Perttula <drewp@bigasterisk.com>
parents:
709
diff
changeset
|
279 elif event.keyval == Gtk.keysyms.Up: |
709 | 280 self.changeLine(self.historyBack()) |
281 return True | |
1056
547d65ea9902
port curvecalc to gtk3. mostly worked, but there are severe bugs with redraws
Drew Perttula <drewp@bigasterisk.com>
parents:
709
diff
changeset
|
282 elif event.keyval == Gtk.keysyms.Down: |
709 | 283 self.changeLine(self.historyForward()) |
284 return True | |
285 # todo: Home needs to advance past the ipython prompt | |
1056
547d65ea9902
port curvecalc to gtk3. mostly worked, but there are severe bugs with redraws
Drew Perttula <drewp@bigasterisk.com>
parents:
709
diff
changeset
|
286 elif event.keyval == Gtk.keysyms.Tab: |
709 | 287 if not self.getCurrentLine().strip(): |
288 return False | |
289 completed, possibilities = self.complete(self.getCurrentLine()) | |
290 if len(possibilities) > 1: | |
291 slice = self.getCurrentLine() | |
292 self.write('\n') | |
293 for symbol in possibilities: | |
294 self.write(symbol+'\n') | |
295 self.showPrompt(self.prompt) | |
296 self.changeLine(completed or slice) | |
297 return True | |
298 | |
299 def _processLine(self): | |
300 self.history_pos = 0 | |
301 self.execute() | |
302 rv = self.cout.getvalue() | |
303 if rv: rv = rv.strip('\n') | |
304 self.showReturned(rv) | |
305 self.cout.truncate(0) |