3
# Interactive Python-GTK Console
4
# Copyright (C), 1998 James Henstridge <james@daa.com.au>
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) 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.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
20
# This module implements an interactive python session in a GTK window. To
21
# start the session, use the gtk_console command. Its specification is:
22
# gtk_console(namespace, title, copyright)
23
# where namespace is a dictionary representing the namespace of the session,
24
# title is the title on the window and
25
# copyright is any additional copyright info to print.
27
# As well as the starting attributes in namespace, the session will also
28
# have access to the list __history__, which is the command history.
30
import sys, string, traceback
31
import pango, gtk, gtk.keysyms
35
if not hasattr(sys, 'ps1'): sys.ps1 = '>>> '
36
if not hasattr(sys, 'ps2'): sys.ps2 = '... '
38
# some functions to help recognise breaks between commands
40
'''Returns s with any quoted strings removed (leaving quote marks)'''
46
if in_quote and (ch != quote or prev == '\\'):
47
if ch == '\\' and prev == '\\':
62
def bracketsBalanced(s):
63
'''Returns true iff the brackets in s are balanced'''
65
brackets = {'(':')', '[':']', '{':'}'}
70
if len(stack) != 0 and brackets[stack[-1]] == ch:
74
return len(stack) == 0
77
'''A fake output file object. It sends output to a TK test widget,
78
and if asked for a file number, returns one set on instance creation'''
79
def __init__(self, buffer, fileno, tag):
80
self.__fileno = fileno
81
self.__buffer = buffer
85
def fileno(self): return self.__fileno
86
def isatty(self): return 0
87
def read(self, a): return ''
88
def readline(self): return ''
89
def readlines(self): return []
90
def write(self, string):
91
#stdout.write(str(self.__w.get_point()) + '\n')
92
iter = self.__buffer.get_end_iter()
93
self.__buffer.insert_with_tags_by_name(iter, string, self.__tag)
94
def writelines(self, lines):
95
iter = self.__buffer.get_end_iter()
97
self.__buffer.insert_with_tags_by_name(iter, lines, self.__tag)
98
def seek(self, a): raise IOError, (29, 'Illegal seek')
99
def tell(self): raise IOError, (29, 'Illegal seek')
102
class Console(gtk.VBox):
103
def __init__(self, namespace={}, quit_cb=None):
104
gtk.VBox.__init__(self, spacing=2)
105
self.set_border_width(2)
107
self.quit_cb = quit_cb
109
swin = gtk.ScrolledWindow()
110
swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
111
self.pack_start(swin)
114
self.vadj = swin.get_vadjustment()
116
self.textview = gtk.TextView()
117
self.textview.set_editable(gtk.FALSE)
118
self.textview.set_cursor_visible(gtk.FALSE)
119
self.textview.set_wrap_mode(gtk.WRAP_WORD)
120
self.buffer = self.textview.get_buffer()
121
swin.add(self.textview)
124
# tags to use when inserting text ...
125
tag = self.buffer.create_tag('normal')
126
tag = self.buffer.create_tag('command')
127
tag.set_property('weight', pango.WEIGHT_BOLD)
128
tag = self.buffer.create_tag('error')
129
tag.set_property('style', pango.STYLE_ITALIC)
131
self.inputbox = gtk.HBox(spacing=2)
132
self.pack_end(self.inputbox, expand=gtk.FALSE)
135
self.prompt = gtk.Label(sys.ps1)
136
self.prompt.set_padding(2, 0)
137
self.inputbox.pack_start(self.prompt, expand=gtk.FALSE)
140
self.closer = gtk.Button("Close")
141
self.closer.connect("clicked", self.quit)
142
self.inputbox.pack_end(self.closer, expand=gtk.FALSE)
145
self.line = gtk.Entry()
146
self.line.connect("key_press_event", self.key_function)
147
self.inputbox.pack_start(self.line, padding=2)
150
self.namespace = namespace
155
# set up hooks for standard output.
156
self.stdout = gtkoutfile(self.buffer, sys.stdout.fileno(), 'normal')
157
self.stderr = gtkoutfile(self.buffer, sys.stderr.fileno(), 'error')
159
# set up command history
162
self.namespace['__history__'] = self.history
165
message = 'Python %s\n\n' % (sys.version,)
166
iter = self.buffer.get_end_iter()
167
self.buffer.insert_with_tags_by_name(iter, message, 'command')
168
self.line.grab_focus()
170
def quit(self, *args):
173
if self.quit_cb: self.quit_cb()
175
def key_function(self, entry, event):
176
if event.keyval == gtk.keysyms.Return:
177
self.line.emit_stop_by_name("key_press_event")
179
if event.keyval == gtk.keysyms.Tab:
180
self.line.emit_stop_by_name("key_press_event")
181
self.line.append_text('\t')
182
gtk.idle_add(self.focus_text)
183
elif event.keyval in (gtk.keysyms.KP_Up, gtk.keysyms.Up):
184
self.line.emit_stop_by_name("key_press_event")
186
gtk.idle_add(self.focus_text)
187
elif event.keyval in (gtk.keysyms.KP_Down, gtk.keysyms.Down):
188
self.line.emit_stop_by_name("key_press_event")
190
gtk.idle_add(self.focus_text)
191
elif event.keyval in (gtk.keysyms.D, gtk.keysyms.d) and \
192
event.state & gtk.gdk.CONTROL_MASK:
193
self.line.emit_stop_by_name("key_press_event")
196
def focus_text(self):
197
self.line.grab_focus()
198
return gtk.FALSE # don't requeue this handler
199
def scroll_bottom(self):
200
self.vadj.set_value(self.vadj.upper - self.vadj.page_size)
209
l = self.line.get_text()
210
if len(l) > 0 and l[0] == '\n': l = l[1:]
211
if len(l) > 0 and l[-1] == '\n': l = l[:-1]
212
self.history[self.histpos] = l
213
self.histpos = self.histpos - 1
214
self.line.set_text(self.history[self.histpos])
216
def historyDown(self):
217
if self.histpos < len(self.history) - 1:
218
l = self.line.get_text()
219
if len(l) > 0 and l[0] == '\n': l = l[1:]
220
if len(l) > 0 and l[-1] == '\n': l = l[:-1]
221
self.history[self.histpos] = l
222
self.histpos = self.histpos + 1
223
self.line.set_text(self.history[self.histpos])
224
self.line.set_position(-1)
227
l = self.line.get_text() + '\n'
228
if len(l) > 1 and l[0] == '\n': l = l[1:]
229
self.histpos = len(self.history) - 1
230
if len(l) > 0 and l[-1] == '\n':
231
self.history[self.histpos] = l[:-1]
233
self.history[self.histpos] = l
234
self.line.set_text('')
235
iter = self.buffer.get_end_iter()
236
self.buffer.insert_with_tags_by_name(iter, self.prompt.get_text() + l,
243
self.histpos = self.histpos + 1
244
self.history.append('')
245
self.cmd = self.cmd + l
246
self.cmd2 = self.cmd2 + remQuotStr(l)
248
if not bracketsBalanced(self.cmd2) or l[-1] == ':' or \
249
l[-1] == '\\' or l[0] in ' \11':
250
self.prompt.set_text(sys.ps2)
251
self.prompt.queue_draw()
258
sys.stdout, self.stdout = self.stdout, sys.stdout
259
sys.stderr, self.stderr = self.stderr, sys.stderr
262
r = eval(cmd, self.namespace, self.namespace)
263
self.namespace['_'] = r
267
exec cmd in self.namespace
269
if hasattr(sys, 'last_type') and \
270
sys.last_type == SystemExit:
273
traceback.print_exc()
274
self.prompt.set_text(sys.ps1)
275
self.prompt.queue_draw()
276
gtk.idle_add(self.scroll_bottom)
277
sys.stdout, self.stdout = self.stdout, sys.stdout
278
sys.stderr, self.stderr = self.stderr, sys.stderr
280
def gtk_console(ns, title='Python', menu=None):
282
win.set_default_size(475, 300)
283
win.connect("destroy", gtk.mainquit)
284
win.connect("delete_event", gtk.mainquit)
286
cons = Console(namespace=ns, quit_cb=gtk.mainquit)
291
box.pack_start(menu, expand=gtk.FALSE)
301
if __name__ == '__main__':
302
if len(sys.argv) < 2 or sys.argv[1] != '-gimp':
303
gtk_console({'__builtins__': __builtins__, '__name__': '__main__',