root/gaphor/tags/gaphor-0.12.5/gaphor/misc/console.py

Revision 1860, 10.9 kB (checked in by arj..@yirdis.nl, 1 year ago)
  • Added gaphor.tests.TestCase (application aware test case).
  • Updated console (indentation + pygtk 2.10)
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 #!/usr/bin/env python
2
3 #  GTK Interactive Console
4 #  (C) 2003, Jon Anderson
5 #  See www.python.org/2.2/license.html for
6 #  license details.
7 #
8 #import pygtk
9 #pygtk.require('2.0')
10 import gtk
11 import gtk.gdk
12 import code
13 import sys
14 import pango
15
16 import __builtin__
17 import __main__
18
19 banner = """Gaphor Interactive Python Console
20 %s
21 """ % sys.version
22
23 class Completer:
24     """
25     Taken from rlcompleter, with readline references stripped, and a
26     local dictionary to use.
27     """
28
29     def __init__(self, locals):
30         self.locals = locals
31
32     def complete(self, text, state):
33         """
34         Return the next possible completion for 'text'.
35         This is called successively with state == 0, 1, 2, ... until it
36         returns None.  The completion should begin with 'text'.
37         """
38         if state == 0:
39             if "." in text:
40                 self.matches = self.attr_matches(text)
41             else:
42                 self.matches = self.global_matches(text)
43         try:
44             return self.matches[state]
45         except IndexError:
46             return None
47
48     def global_matches(self, text):
49         """
50         Compute matches when text is a simple name.
51
52         Return a list of all keywords, built-in functions and names
53         currently defines in __main__ that match.
54         """
55         import keyword
56         matches = []
57         n = len(text)
58         for list in [keyword.kwlist,__builtin__.__dict__.keys(),__main__.__dict__.keys(), self.locals.keys()]:
59             for word in list:
60                 if word[:n] == text and word != "__builtins__":
61                     matches.append(word)
62         return matches
63
64     def attr_matches(self, text):
65         """
66         Compute matches when text contains a dot.
67
68         Assuming the text is of the form NAME.NAME....[NAME], and is
69         evaluatable in the globals of __main__, it will be evaluated
70         and its attributes (as revealed by dir()) are used as possible
71         completions.  (For class instances, class members are are also
72         considered.)
73
74         WARNING: this can still invoke arbitrary C code, if an object
75         with a __getattr__ hook is evaluated.
76         """
77         import re
78         m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text)
79         if not m:
80             return
81         expr, attr = m.group(1, 3)
82         object = eval(expr, __main__.__dict__, self.locals)
83         words = dir(object)
84         if hasattr(object,'__class__'):
85             words.append('__class__')
86             words = words + get_class_members(object.__class__)
87         matches = []
88         n = len(attr)
89         for word in words:
90             if word[:n] == attr and word != "__builtins__":
91                 matches.append("%s.%s" % (expr, word))
92         return matches
93
94 def get_class_members(klass):
95     ret = dir(klass)
96     if hasattr(klass,'__bases__'):
97         for base in klass.__bases__:
98             ret = ret + get_class_members(base)
99     return ret
100
101
102
103 class OutputStream:
104     """
105     A Multiplexing output stream.
106     It can replace another stream, and tee output to the original stream and too
107     a GTK textview.
108     """
109
110     def __init__(self,view,old_out,style):
111         self.view = view
112         self.buffer = view.get_buffer()
113         self.mark = self.buffer.create_mark("End",self.buffer.get_end_iter(), False )
114         self.out = old_out
115         self.style = style
116         self.tee = 1
117
118     def write(self,text):
119         if self.tee:
120             self.out.write(text)
121
122         end = self.buffer.get_end_iter()
123
124         if not self.view  == None:
125             self.view.scroll_to_mark(self.mark, 0, True, 1, 1)
126
127         self.buffer.insert_with_tags(end,text,self.style)
128
129
130 class GTKInterpreterConsole(gtk.ScrolledWindow):
131     """
132     An InteractiveConsole for GTK. It's an actual widget,
133     so it can be dropped in just about anywhere.
134     """
135
136     __gtype_name__ = 'GTKInterpreterConsole'
137
138     def __init__(self):
139         gtk.ScrolledWindow.__init__(self)
140         self.set_policy (gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
141
142         self.text = gtk.TextView()
143         self.text.set_wrap_mode(True)
144
145         self.interpreter = code.InteractiveInterpreter()
146
147         self.completer = Completer(self.interpreter.locals)
148         self.buffer = []
149         self.history = []
150         self.banner = banner
151         self.ps1 = ">>> "
152         self.ps2 = "... "
153
154         self.text.add_events( gtk.gdk.KEY_PRESS_MASK )
155         self.text.connect( "key_press_event", self.key_pressed )
156
157         self.current_history = -1
158
159         self.mark = self.text.get_buffer().create_mark("End",self.text.get_buffer().get_end_iter(), False )
160
161         #setup colors
162         self.style_banner = gtk.TextTag("banner")
163         self.style_banner.set_property( "foreground", "saddle brown" )
164
165         self.style_ps1 = gtk.TextTag("ps1")
166         self.style_ps1.set_property( "foreground", "DarkOrchid4" )
167         self.style_ps1.set_property( "editable", False )
168         self.style_ps1.set_property("font", "courier" )
169
170         self.style_ps2 = gtk.TextTag("ps2")
171         self.style_ps2.set_property( "foreground", "DarkOliveGreen" )
172         self.style_ps2.set_property( "editable", False  )
173         self.style_ps2.set_property("font", "courier" )
174
175         self.style_out = gtk.TextTag("stdout")
176         self.style_out.set_property( "foreground", "midnight blue" )
177         self.style_err = gtk.TextTag("stderr")
178         self.style_err.set_property( "style", pango.STYLE_ITALIC )
179         self.style_err.set_property( "foreground", "red" )
180
181         self.text.get_buffer().get_tag_table().add(self.style_banner)
182         self.text.get_buffer().get_tag_table().add(self.style_ps1)
183         self.text.get_buffer().get_tag_table().add(self.style_ps2)
184         self.text.get_buffer().get_tag_table().add(self.style_out)
185         self.text.get_buffer().get_tag_table().add(self.style_err)
186
187         self.stdout = None #OutputStream(self.text,sys.stdout,self.style_out)
188         self.stderr = None #OutputStream(self.text,sys.stderr,self.style_err)
189
190         #sys.stderr = self.stderr
191         #sys.stdout = self.stdout
192
193         self.current_prompt = None
194
195         self.write_line(self.banner, self.style_banner)
196         self.prompt_ps1()
197
198         self.add(self.text)
199         self.text.show()
200
201     def reset_history(self):
202         self.history = []
203
204     def reset_buffer(self):
205         self.buffer = []
206
207     def prompt_ps1(self):
208         self.current_prompt = self.prompt_ps1
209         self.write_line(self.ps1,self.style_ps1)
210
211     def prompt_ps2(self):
212         self.current_prompt = self.prompt_ps2
213         self.write_line(self.ps2,self.style_ps2)
214
215     def write_line(self,text,style=None):
216         start,end = self.text.get_buffer().get_bounds()
217         if style==None:
218             self.text.get_buffer().insert(end,text)
219         else:
220             self.text.get_buffer().insert_with_tags(end,text,style)
221
222         self.text.scroll_to_mark(self.mark, 0, True, 1, 1)
223
224     def push(self, line):
225         self.buffer.append(line)
226         if len(line) > 0:
227             self.history.append(line)
228
229         source = "\n".join(self.buffer)
230
231         more = self.interpreter.runsource(source, "<<console>>")
232
233         if not more:
234             self.reset_buffer()
235
236         return more
237
238     def key_pressed(self,widget,event):
239         if event.keyval == gtk.gdk.keyval_from_name('Return'):
240           return self.execute_line()
241
242         if event.keyval == gtk.gdk.keyval_from_name('Up'):
243             self.current_history = self.current_history - 1
244             if self.current_history < - len(self.history):
245                 self.current_history = - len(self.history)
246             return self.show_history()
247         elif event.keyval == gtk.gdk.keyval_from_name('Down'):
248             self.current_history = self.current_history + 1
249             if self.current_history > 0:
250                 self.current_history = 0
251             return self.show_history()
252         elif event.keyval == gtk.gdk.keyval_from_name('Home'):
253             l = self.text.get_buffer().get_line_count() - 1
254             start = self.text.get_buffer().get_iter_at_line_offset(l,4)
255             self.text.get_buffer().place_cursor(start)
256             return True
257         elif event.keyval == gtk.gdk.keyval_from_name('space') and event.state & gtk.gdk.CONTROL_MASK:
258             return self.complete_line()
259         return False
260
261     def show_history(self):
262         if self.current_history == 0:
263             return True
264         else:
265             self.replace_line( self.history[self.current_history] )
266             return True
267
268     def current_line(self):
269         start,end = self.current_line_bounds()
270         return self.text.get_buffer().get_text(start,end, True)
271
272     def current_line_bounds(self):
273         txt_buffer = self.text.get_buffer()
274         l = txt_buffer.get_line_count() - 1
275
276         start = txt_buffer.get_iter_at_line(l)
277         if start.get_chars_in_line() >= 4:
278             start.forward_chars(4)
279         end = txt_buffer.get_end_iter()
280         return start,end
281
282     def replace_line(self,txt):
283         start,end = self.current_line_bounds()
284         self.text.get_buffer().delete(start,end)
285         self.write_line(txt)
286
287     def execute_line(self):
288         line = self.current_line()
289
290         self.write_line("\n")
291
292         more = self.push(line)
293
294         self.text.get_buffer().place_cursor(self.text.get_buffer().get_end_iter())
295
296         if more:
297             self.prompt_ps2()
298         else:
299             self.prompt_ps1()
300
301         self.current_history = 0
302
303         return True
304
305     def complete_line(self):
306         line = self.current_line()
307         tokens = line.split()
308         token = tokens[-1]
309
310         completions = []
311         p = self.completer.complete(token,len(completions))
312         while p != None:
313             completions.append(p)
314             p = self.completer.complete(token, len(completions))
315
316         if len(completions) != 1:
317             self.write_line("\n")
318             self.write_line("\n".join(completions), self.style_ps1)
319             self.write_line("\n")
320             self.current_prompt()
321             self.write_line(line)
322         else:
323             i = line.rfind(token)
324             line = line[0:i] + completions[0]
325             self.replace_line(line)
326
327         return True
328
329     def do_realize(self):
330         gtk.ScrolledWindow.do_realize(self)
331         self.stdout = OutputStream(self.text,sys.stdout,self.style_out)
332         self.stderr = OutputStream(self.text,sys.stderr,self.style_err)
333
334         sys.stdout = self.stdout
335         sys.stderr = self.stderr
336
337     def do_unrealize(self):
338         sys.stdout = self.stdout.out
339         sys.stderr = self.stderr.out
340         gtk.ScrolledWindow.do_unrealize(self)
341
342 def main():
343     w = gtk.Window()
344     console = GTKInterpreterConsole()
345     console.set_size_request(640,480)
346     w.add(console)
347     def destroy(arg=None):
348         gtk.main_quit()
349    
350     def key_event(widget,event):
351         if gtk.gdk.keyval_name(event.keyval) == 'd' and event.state & gtk.gdk.CONTROL_MASK:
352             destroy()
353         return False
354
355     w.connect("destroy", destroy)
356
357     w.add_events( gtk.gdk.KEY_PRESS_MASK )
358     w.connect( 'key_press_event', key_event)
359     w.show_all()
360
361     gtk.main()
362
363 if __name__ == '__main__':
364     main()
365
366 # vim:sw=4:et:ai
Note: See TracBrowser for help on using the browser.