3
import code, string, sys, traceback
9
from twisted.spread.ui import gtkutil
10
from twisted.python import util
11
rcfile = util.sibpath(__file__, 'gtkrc')
15
def isCursorOnFirstLine(entry):
16
firstnewline = string.find(entry.get_chars(0,-1), '\n')
17
if entry.get_point() <= firstnewline or firstnewline == -1:
20
def isCursorOnLastLine(entry):
21
if entry.get_point() >= string.rfind(string.rstrip(entry.get_chars(0,-1)), '\n'):
25
class InputText(gtk.GtkText):
29
def __init__(self, toplevel=None):
30
gtk.GtkText.__init__(self)
31
self['name'] = 'Input'
32
self.set_editable(gtk.TRUE)
33
self.connect("key_press_event", self.processKey)
34
#self.set_word_wrap(gtk.TRUE)
40
self.toplevel = toplevel
44
self.histpos = self.histpos - 1
45
self.delete_text(0, -1)
46
self.insert_defaults(self.history[self.histpos])
49
def historyDown(self):
50
if self.histpos < len(self.history) - 1:
51
self.histpos = self.histpos + 1
52
self.delete_text(0, -1)
53
self.insert_defaults(self.history[self.histpos])
54
elif self.histpos == len(self.history) - 1:
55
self.histpos = self.histpos + 1
56
self.delete_text(0, -1)
58
def processKey(self, entry, event):
59
# TODO: make key bindings easier to customize.
62
# ASSUMPTION: Assume Meta == mod4
63
isMeta = event.state & gtk.GDK.MOD4_MASK
64
if event.keyval == gtk.GDK.Return:
65
isShift = event.state & gtk.GDK.SHIFT_MASK
68
self.insert_defaults('\n')
71
text = self.get_chars(0,-1)
75
# It's a local-command, don't evaluate it as
79
# This will tell us it's a complete expression.
80
c = code.compile_command(text)
81
except SyntaxError, e:
83
self.set_positionLineOffset(e.lineno, e.offset)
84
print "offset", e.offset
85
errmsg = {'traceback': [],
86
'exception': [str(e) + '\n']}
87
self.toplevel.output.console([('exception', errmsg)])
88
except OverflowError, e:
89
e = traceback.format_exception_only(OverflowError, e)
90
errmsg = {'traceback': [],
92
self.toplevel.output.console([('exception', errmsg)])
98
self.sendMessage(entry)
101
elif ((event.keyval == gtk.GDK.Up and isCursorOnFirstLine(self))
102
or (isMeta and event.string == 'p')):
105
elif ((event.keyval == gtk.GDK.Down and isCursorOnLastLine(self))
106
or (isMeta and event.string == 'n')):
111
self.emit_stop_by_name("key_press_event")
115
self.delete_text(0, -1)
116
self.linemode = False
118
def set_positionLineOffset(self, line, offset):
119
text = self.get_chars(0, -1)
121
for l in xrange(line - 1):
122
pos = string.index(text, '\n', pos) + 1
123
pos = pos + offset - 1
124
self.set_position(pos)
126
def sendMessage(self, unused_data=None):
127
text = self.get_chars(0,-1)
129
self.blockcount = self.blockcount + 1
130
fmt = ">>> # begin %s\n%%s\n#end %s\n" % (
131
self.blockcount, self.blockcount)
134
self.history.append(text)
135
self.histpos = len(self.history)
136
self.toplevel.output.console([['command',fmt % text]])
137
self.toplevel.codeInput(text)
140
def readHistoryFile(self, filename=None):
142
filename = self.historyfile
144
f = open(filename, 'r', 1)
145
self.history.extend(f.readlines())
147
self.histpos = len(self.history)
149
def writeHistoryFile(self, filename=None):
151
filename = self.historyfile
153
f = open(filename, 'a', 1)
154
f.writelines(self.history)
158
class Interaction(gtk.GtkWindow):
159
titleText = "Abstract Python Console"
162
gtk.GtkWindow.__init__(self, gtk.WINDOW_TOPLEVEL)
163
self.set_title(self.titleText)
164
self.set_default_size(300, 300)
165
self.set_name("Manhole")
168
pane = gtk.GtkVPaned()
170
self.output = OutputConsole(toplevel=self)
171
pane.pack1(gtkutil.scrollify(self.output), gtk.TRUE, gtk.FALSE)
173
self.input = InputText(toplevel=self)
174
pane.pack2(gtkutil.scrollify(self.input), gtk.FALSE, gtk.TRUE)
175
vbox.pack_start(pane, 1,1,0)
178
self.input.grab_focus()
180
def codeInput(self, text):
181
raise NotImplementedError("Bleh.")
184
class LocalInteraction(Interaction):
185
titleText = "Local Python Console"
187
Interaction.__init__(self)
190
self.filename = "<gtk console>"
192
def codeInput(self, text):
193
from twisted.manhole.service import runInConsole
194
val = runInConsole(text, self.output.console,
195
self.globalNS, self.localNS, self.filename)
197
self.localNS["_"] = val
198
self.output.console([("result", repr(val) + "\n")])
200
class OutputConsole(gtk.GtkText):
203
def __init__(self, toplevel=None):
204
gtk.GtkText.__init__(self)
205
self['name'] = "Console"
206
gtkutil.defocusify(self)
207
#self.set_word_wrap(gtk.TRUE)
210
self.toplevel = toplevel
212
def console(self, message):
213
self.set_point(self.get_length())
216
style = self.get_style()
219
for element in message:
220
if element[0] == 'exception':
221
s = traceback.format_list(element[1]['traceback'])
222
s.extend(element[1]['exception'])
223
s = string.join(s, '')
227
if element[0] != previous_kind:
228
style = style_cache.get(element[0], None)
231
'widget \"Manhole.*.Console\" '
232
'style \"Console_%s\"\n'
235
style_cache[element[0]] = style = self.get_style()
236
# XXX: You'd think we'd use style.bg instead of 'None'
237
# here, but that doesn't seem to match the color of
239
self.insert(style.font, style.fg[gtk.STATE_NORMAL],
241
previous_kind = element[0]
242
l = self.get_length()
243
diff = self.maxBufSz - l
246
self.delete_text(0,diff)
249
a = self.get_vadjustment()
250
a.set_value(a.upper - a.page_size)