##// END OF EJS Templates
Make synchronous printing work better.
Gael Varoquaux -
Show More
@@ -1,428 +1,435 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 A Wx widget to act as a console and input commands.
3 A Wx widget to act as a console and input commands.
4
4
5 This widget deals with prompts and provides an edit buffer
5 This widget deals with prompts and provides an edit buffer
6 restricted to after the last prompt.
6 restricted to after the last prompt.
7 """
7 """
8
8
9 __docformat__ = "restructuredtext en"
9 __docformat__ = "restructuredtext en"
10
10
11 #-------------------------------------------------------------------------------
11 #-------------------------------------------------------------------------------
12 # Copyright (C) 2008 The IPython Development Team
12 # Copyright (C) 2008 The IPython Development Team
13 #
13 #
14 # Distributed under the terms of the BSD License. The full license is
14 # Distributed under the terms of the BSD License. The full license is
15 # in the file COPYING, distributed as part of this software.
15 # in the file COPYING, distributed as part of this software.
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17
17
18 #-------------------------------------------------------------------------------
18 #-------------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-------------------------------------------------------------------------------
20 #-------------------------------------------------------------------------------
21
21
22 import wx
22 import wx
23 import wx.stc as stc
23 import wx.stc as stc
24
24
25 from wx.py import editwindow
25 from wx.py import editwindow
26 import time
26 import sys
27 import sys
27 LINESEP = '\n'
28 LINESEP = '\n'
28 if sys.platform == 'win32':
29 if sys.platform == 'win32':
29 LINESEP = '\n\r'
30 LINESEP = '\n\r'
30
31
31 import re
32 import re
32
33
33 # FIXME: Need to provide an API for non user-generated display on the
34 # FIXME: Need to provide an API for non user-generated display on the
34 # screen: this should not be editable by the user.
35 # screen: this should not be editable by the user.
35
36
36 _DEFAULT_SIZE = 10
37 _DEFAULT_SIZE = 10
37 if sys.platform == 'darwin':
38 if sys.platform == 'darwin':
38 _DEFAULT_STYLE = 12
39 _DEFAULT_STYLE = 12
39
40
40 _DEFAULT_STYLE = {
41 _DEFAULT_STYLE = {
41 'stdout' : 'fore:#0000FF',
42 'stdout' : 'fore:#0000FF',
42 'stderr' : 'fore:#007f00',
43 'stderr' : 'fore:#007f00',
43 'trace' : 'fore:#FF0000',
44 'trace' : 'fore:#FF0000',
44
45
45 'default' : 'size:%d' % _DEFAULT_SIZE,
46 'default' : 'size:%d' % _DEFAULT_SIZE,
46 'bracegood' : 'fore:#00AA00,back:#000000,bold',
47 'bracegood' : 'fore:#00AA00,back:#000000,bold',
47 'bracebad' : 'fore:#FF0000,back:#000000,bold',
48 'bracebad' : 'fore:#FF0000,back:#000000,bold',
48
49
49 # properties for the various Python lexer styles
50 # properties for the various Python lexer styles
50 'comment' : 'fore:#007F00',
51 'comment' : 'fore:#007F00',
51 'number' : 'fore:#007F7F',
52 'number' : 'fore:#007F7F',
52 'string' : 'fore:#7F007F,italic',
53 'string' : 'fore:#7F007F,italic',
53 'char' : 'fore:#7F007F,italic',
54 'char' : 'fore:#7F007F,italic',
54 'keyword' : 'fore:#00007F,bold',
55 'keyword' : 'fore:#00007F,bold',
55 'triple' : 'fore:#7F0000',
56 'triple' : 'fore:#7F0000',
56 'tripledouble' : 'fore:#7F0000',
57 'tripledouble' : 'fore:#7F0000',
57 'class' : 'fore:#0000FF,bold,underline',
58 'class' : 'fore:#0000FF,bold,underline',
58 'def' : 'fore:#007F7F,bold',
59 'def' : 'fore:#007F7F,bold',
59 'operator' : 'bold'
60 'operator' : 'bold'
60 }
61 }
61
62
62 # new style numbers
63 # new style numbers
63 _STDOUT_STYLE = 15
64 _STDOUT_STYLE = 15
64 _STDERR_STYLE = 16
65 _STDERR_STYLE = 16
65 _TRACE_STYLE = 17
66 _TRACE_STYLE = 17
66
67
67
68
68 # system colors
69 # system colors
69 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
70 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
70
71
71 #-------------------------------------------------------------------------------
72 #-------------------------------------------------------------------------------
72 # The console widget class
73 # The console widget class
73 #-------------------------------------------------------------------------------
74 #-------------------------------------------------------------------------------
74 class ConsoleWidget(editwindow.EditWindow):
75 class ConsoleWidget(editwindow.EditWindow):
75 """ Specialized styled text control view for console-like workflow.
76 """ Specialized styled text control view for console-like workflow.
76
77
77 This widget is mainly interested in dealing with the prompt and
78 This widget is mainly interested in dealing with the prompt and
78 keeping the cursor inside the editing line.
79 keeping the cursor inside the editing line.
79 """
80 """
80
81
81 # This is where the title captured from the ANSI escape sequences are
82 # This is where the title captured from the ANSI escape sequences are
82 # stored.
83 # stored.
83 title = 'Console'
84 title = 'Console'
84
85
85 # The buffer being edited.
86 # The buffer being edited.
86 def _set_input_buffer(self, string):
87 def _set_input_buffer(self, string):
87 self.SetSelection(self.current_prompt_pos, self.GetLength())
88 self.SetSelection(self.current_prompt_pos, self.GetLength())
88 self.ReplaceSelection(string)
89 self.ReplaceSelection(string)
89 self.GotoPos(self.GetLength())
90 self.GotoPos(self.GetLength())
90
91
91 def _get_input_buffer(self):
92 def _get_input_buffer(self):
92 """ Returns the text in current edit buffer.
93 """ Returns the text in current edit buffer.
93 """
94 """
94 input_buffer = self.GetTextRange(self.current_prompt_pos,
95 input_buffer = self.GetTextRange(self.current_prompt_pos,
95 self.GetLength())
96 self.GetLength())
96 input_buffer = input_buffer.replace(LINESEP, '\n')
97 input_buffer = input_buffer.replace(LINESEP, '\n')
97 return input_buffer
98 return input_buffer
98
99
99 input_buffer = property(_get_input_buffer, _set_input_buffer)
100 input_buffer = property(_get_input_buffer, _set_input_buffer)
100
101
101 style = _DEFAULT_STYLE.copy()
102 style = _DEFAULT_STYLE.copy()
102
103
103 # Translation table from ANSI escape sequences to color. Override
104 # Translation table from ANSI escape sequences to color. Override
104 # this to specify your colors.
105 # this to specify your colors.
105 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
106 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
106 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
107 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
107 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
108 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
108 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
109 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
109 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
110 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
110 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
111 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
111 '1;34': [12, 'LIGHT BLUE'], '1;35':
112 '1;34': [12, 'LIGHT BLUE'], '1;35':
112 [13, 'MEDIUM VIOLET RED'],
113 [13, 'MEDIUM VIOLET RED'],
113 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
114 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
114
115
115 # The color of the carret (call _apply_style() after setting)
116 # The color of the carret (call _apply_style() after setting)
116 carret_color = 'BLACK'
117 carret_color = 'BLACK'
117
118
119 # Store the last time a refresh was done
120 _last_refresh_time = 0
121
118 #--------------------------------------------------------------------------
122 #--------------------------------------------------------------------------
119 # Public API
123 # Public API
120 #--------------------------------------------------------------------------
124 #--------------------------------------------------------------------------
121
125
122 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
126 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
123 size=wx.DefaultSize, style=0, ):
127 size=wx.DefaultSize, style=0, ):
124 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
128 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
125 self._configure_scintilla()
129 self._configure_scintilla()
126
130
127 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
131 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
128 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
132 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
129
133
130
134
131 def write(self, text, refresh=True):
135 def write(self, text, refresh=True):
132 """ Write given text to buffer, while translating the ansi escape
136 """ Write given text to buffer, while translating the ansi escape
133 sequences.
137 sequences.
134 """
138 """
135 # XXX: do not put print statements to sys.stdout/sys.stderr in
139 # XXX: do not put print statements to sys.stdout/sys.stderr in
136 # this method, the print statements will call this method, as
140 # this method, the print statements will call this method, as
137 # you will end up with an infinit loop
141 # you will end up with an infinit loop
138 title = self.title_pat.split(text)
142 title = self.title_pat.split(text)
139 if len(title)>1:
143 if len(title)>1:
140 self.title = title[-2]
144 self.title = title[-2]
141
145
142 text = self.title_pat.sub('', text)
146 text = self.title_pat.sub('', text)
143 segments = self.color_pat.split(text)
147 segments = self.color_pat.split(text)
144 segment = segments.pop(0)
148 segment = segments.pop(0)
145 self.GotoPos(self.GetLength())
149 self.GotoPos(self.GetLength())
146 self.StartStyling(self.GetLength(), 0xFF)
150 self.StartStyling(self.GetLength(), 0xFF)
147 try:
151 try:
148 self.AppendText(segment)
152 self.AppendText(segment)
149 except UnicodeDecodeError:
153 except UnicodeDecodeError:
150 # XXX: Do I really want to skip the exception?
154 # XXX: Do I really want to skip the exception?
151 pass
155 pass
152
156
153 if segments:
157 if segments:
154 for ansi_tag, text in zip(segments[::2], segments[1::2]):
158 for ansi_tag, text in zip(segments[::2], segments[1::2]):
155 self.StartStyling(self.GetLength(), 0xFF)
159 self.StartStyling(self.GetLength(), 0xFF)
156 try:
160 try:
157 self.AppendText(text)
161 self.AppendText(text)
158 except UnicodeDecodeError:
162 except UnicodeDecodeError:
159 # XXX: Do I really want to skip the exception?
163 # XXX: Do I really want to skip the exception?
160 pass
164 pass
161
165
162 if ansi_tag not in self.ANSI_STYLES:
166 if ansi_tag not in self.ANSI_STYLES:
163 style = 0
167 style = 0
164 else:
168 else:
165 style = self.ANSI_STYLES[ansi_tag][0]
169 style = self.ANSI_STYLES[ansi_tag][0]
166
170
167 self.SetStyling(len(text), style)
171 self.SetStyling(len(text), style)
168
172
169 self.GotoPos(self.GetLength())
173 self.GotoPos(self.GetLength())
170 if refresh:
174 if refresh:
171 # Maybe this is faster than wx.Yield()
175 # Maybe this is faster than wx.Yield()
172 self.ProcessEvent(wx.PaintEvent())
176 self.ProcessEvent(wx.PaintEvent())
173 #wx.Yield()
177 current_time = time.time()
178 if current_time - self._last_refresh_time > 0.03:
179 wx.Yield()
180 self._last_refresh_time = current_time
174
181
175
182
176 def new_prompt(self, prompt):
183 def new_prompt(self, prompt):
177 """ Prints a prompt at start of line, and move the start of the
184 """ Prints a prompt at start of line, and move the start of the
178 current block there.
185 current block there.
179
186
180 The prompt can be given with ascii escape sequences.
187 The prompt can be given with ascii escape sequences.
181 """
188 """
182 self.write(prompt, refresh=False)
189 self.write(prompt, refresh=False)
183 # now we update our cursor giving end of prompt
190 # now we update our cursor giving end of prompt
184 self.current_prompt_pos = self.GetLength()
191 self.current_prompt_pos = self.GetLength()
185 self.current_prompt_line = self.GetCurrentLine()
192 self.current_prompt_line = self.GetCurrentLine()
186 wx.Yield()
193 wx.Yield()
187 self.EnsureCaretVisible()
194 self.EnsureCaretVisible()
188
195
189
196
190 def scroll_to_bottom(self):
197 def scroll_to_bottom(self):
191 maxrange = self.GetScrollRange(wx.VERTICAL)
198 maxrange = self.GetScrollRange(wx.VERTICAL)
192 self.ScrollLines(maxrange)
199 self.ScrollLines(maxrange)
193
200
194
201
195 def pop_completion(self, possibilities, offset=0):
202 def pop_completion(self, possibilities, offset=0):
196 """ Pops up an autocompletion menu. Offset is the offset
203 """ Pops up an autocompletion menu. Offset is the offset
197 in characters of the position at which the menu should
204 in characters of the position at which the menu should
198 appear, relativ to the cursor.
205 appear, relativ to the cursor.
199 """
206 """
200 self.AutoCompSetIgnoreCase(False)
207 self.AutoCompSetIgnoreCase(False)
201 self.AutoCompSetAutoHide(False)
208 self.AutoCompSetAutoHide(False)
202 self.AutoCompSetMaxHeight(len(possibilities))
209 self.AutoCompSetMaxHeight(len(possibilities))
203 self.AutoCompShow(offset, " ".join(possibilities))
210 self.AutoCompShow(offset, " ".join(possibilities))
204
211
205
212
206 def get_line_width(self):
213 def get_line_width(self):
207 """ Return the width of the line in characters.
214 """ Return the width of the line in characters.
208 """
215 """
209 return self.GetSize()[0]/self.GetCharWidth()
216 return self.GetSize()[0]/self.GetCharWidth()
210
217
211 #--------------------------------------------------------------------------
218 #--------------------------------------------------------------------------
212 # EditWindow API
219 # EditWindow API
213 #--------------------------------------------------------------------------
220 #--------------------------------------------------------------------------
214
221
215 def OnUpdateUI(self, event):
222 def OnUpdateUI(self, event):
216 """ Override the OnUpdateUI of the EditWindow class, to prevent
223 """ Override the OnUpdateUI of the EditWindow class, to prevent
217 syntax highlighting both for faster redraw, and for more
224 syntax highlighting both for faster redraw, and for more
218 consistent look and feel.
225 consistent look and feel.
219 """
226 """
220
227
221 #--------------------------------------------------------------------------
228 #--------------------------------------------------------------------------
222 # Private API
229 # Private API
223 #--------------------------------------------------------------------------
230 #--------------------------------------------------------------------------
224
231
225 def _apply_style(self):
232 def _apply_style(self):
226 """ Applies the colors for the different text elements and the
233 """ Applies the colors for the different text elements and the
227 carret.
234 carret.
228 """
235 """
229 self.SetCaretForeground(self.carret_color)
236 self.SetCaretForeground(self.carret_color)
230
237
231 #self.StyleClearAll()
238 #self.StyleClearAll()
232 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
239 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
233 "fore:#FF0000,back:#0000FF,bold")
240 "fore:#FF0000,back:#0000FF,bold")
234 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
241 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
235 "fore:#000000,back:#FF0000,bold")
242 "fore:#000000,back:#FF0000,bold")
236
243
237 for style in self.ANSI_STYLES.values():
244 for style in self.ANSI_STYLES.values():
238 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
245 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
239
246
240
247
241 def _configure_scintilla(self):
248 def _configure_scintilla(self):
242 self.SetEOLMode(stc.STC_EOL_LF)
249 self.SetEOLMode(stc.STC_EOL_LF)
243
250
244 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
251 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
245 # the widget
252 # the widget
246 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
253 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
247 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
254 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
248 # Also allow Ctrl Shift "=" for poor non US keyboard users.
255 # Also allow Ctrl Shift "=" for poor non US keyboard users.
249 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
256 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
250 stc.STC_CMD_ZOOMIN)
257 stc.STC_CMD_ZOOMIN)
251
258
252 # Keys: we need to clear some of the keys the that don't play
259 # Keys: we need to clear some of the keys the that don't play
253 # well with a console.
260 # well with a console.
254 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
261 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
255 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
262 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
256 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
263 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
257 self.CmdKeyClear(ord('A'), stc.STC_SCMOD_CTRL)
264 self.CmdKeyClear(ord('A'), stc.STC_SCMOD_CTRL)
258
265
259 self.SetEOLMode(stc.STC_EOL_CRLF)
266 self.SetEOLMode(stc.STC_EOL_CRLF)
260 self.SetWrapMode(stc.STC_WRAP_CHAR)
267 self.SetWrapMode(stc.STC_WRAP_CHAR)
261 self.SetWrapMode(stc.STC_WRAP_WORD)
268 self.SetWrapMode(stc.STC_WRAP_WORD)
262 self.SetBufferedDraw(True)
269 self.SetBufferedDraw(True)
263 self.SetUseAntiAliasing(True)
270 self.SetUseAntiAliasing(True)
264 self.SetLayoutCache(stc.STC_CACHE_PAGE)
271 self.SetLayoutCache(stc.STC_CACHE_PAGE)
265 self.SetUndoCollection(False)
272 self.SetUndoCollection(False)
266 self.SetUseTabs(True)
273 self.SetUseTabs(True)
267 self.SetIndent(4)
274 self.SetIndent(4)
268 self.SetTabWidth(4)
275 self.SetTabWidth(4)
269
276
270 # we don't want scintilla's autocompletion to choose
277 # we don't want scintilla's autocompletion to choose
271 # automaticaly out of a single choice list, as we pop it up
278 # automaticaly out of a single choice list, as we pop it up
272 # automaticaly
279 # automaticaly
273 self.AutoCompSetChooseSingle(False)
280 self.AutoCompSetChooseSingle(False)
274 self.AutoCompSetMaxHeight(10)
281 self.AutoCompSetMaxHeight(10)
275 # XXX: this doesn't seem to have an effect.
282 # XXX: this doesn't seem to have an effect.
276 self.AutoCompSetFillUps('\n')
283 self.AutoCompSetFillUps('\n')
277
284
278 self.SetMargins(3, 3) #text is moved away from border with 3px
285 self.SetMargins(3, 3) #text is moved away from border with 3px
279 # Suppressing Scintilla margins
286 # Suppressing Scintilla margins
280 self.SetMarginWidth(0, 0)
287 self.SetMarginWidth(0, 0)
281 self.SetMarginWidth(1, 0)
288 self.SetMarginWidth(1, 0)
282 self.SetMarginWidth(2, 0)
289 self.SetMarginWidth(2, 0)
283
290
284 self._apply_style()
291 self._apply_style()
285
292
286 # Xterm escape sequences
293 # Xterm escape sequences
287 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
294 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
288 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
295 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
289
296
290 #self.SetEdgeMode(stc.STC_EDGE_LINE)
297 #self.SetEdgeMode(stc.STC_EDGE_LINE)
291 #self.SetEdgeColumn(80)
298 #self.SetEdgeColumn(80)
292
299
293 # styles
300 # styles
294 p = self.style
301 p = self.style
295 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
302 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
296 self.StyleClearAll()
303 self.StyleClearAll()
297 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
304 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
298 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
305 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
299 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
306 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
300
307
301 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
308 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
302 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
309 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
303 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
310 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
304 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
311 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
305 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
312 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
306 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
313 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
307 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
314 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
308 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
315 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
309 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
316 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
310 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
317 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
311 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
318 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
312 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
319 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
313 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
320 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
314 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
321 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
315
322
316 def _on_key_down(self, event, skip=True):
323 def _on_key_down(self, event, skip=True):
317 """ Key press callback used for correcting behavior for
324 """ Key press callback used for correcting behavior for
318 console-like interfaces: the cursor is constraint to be after
325 console-like interfaces: the cursor is constraint to be after
319 the last prompt.
326 the last prompt.
320
327
321 Return True if event as been catched.
328 Return True if event as been catched.
322 """
329 """
323 catched = True
330 catched = True
324 # Intercept some specific keys.
331 # Intercept some specific keys.
325 if event.KeyCode == ord('L') and event.ControlDown() :
332 if event.KeyCode == ord('L') and event.ControlDown() :
326 self.scroll_to_bottom()
333 self.scroll_to_bottom()
327 elif event.KeyCode == ord('K') and event.ControlDown() :
334 elif event.KeyCode == ord('K') and event.ControlDown() :
328 self.input_buffer = ''
335 self.input_buffer = ''
329 elif event.KeyCode == ord('A') and event.ControlDown() :
336 elif event.KeyCode == ord('A') and event.ControlDown() :
330 self.GotoPos(self.GetLength())
337 self.GotoPos(self.GetLength())
331 self.SetSelectionStart(self.current_prompt_pos)
338 self.SetSelectionStart(self.current_prompt_pos)
332 self.SetSelectionEnd(self.GetCurrentPos())
339 self.SetSelectionEnd(self.GetCurrentPos())
333 catched = True
340 catched = True
334 elif event.KeyCode == ord('E') and event.ControlDown() :
341 elif event.KeyCode == ord('E') and event.ControlDown() :
335 self.GotoPos(self.GetLength())
342 self.GotoPos(self.GetLength())
336 catched = True
343 catched = True
337 elif event.KeyCode == wx.WXK_PAGEUP:
344 elif event.KeyCode == wx.WXK_PAGEUP:
338 self.ScrollPages(-1)
345 self.ScrollPages(-1)
339 elif event.KeyCode == wx.WXK_PAGEDOWN:
346 elif event.KeyCode == wx.WXK_PAGEDOWN:
340 self.ScrollPages(1)
347 self.ScrollPages(1)
341 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
348 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
342 self.ScrollLines(-1)
349 self.ScrollLines(-1)
343 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
350 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
344 self.ScrollLines(1)
351 self.ScrollLines(1)
345 else:
352 else:
346 catched = False
353 catched = False
347
354
348 if self.AutoCompActive():
355 if self.AutoCompActive():
349 event.Skip()
356 event.Skip()
350 else:
357 else:
351 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
358 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
352 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
359 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
353 catched = True
360 catched = True
354 self.CallTipCancel()
361 self.CallTipCancel()
355 self.write('\n', refresh=False)
362 self.write('\n', refresh=False)
356 # Under windows scintilla seems to be doing funny stuff to the
363 # Under windows scintilla seems to be doing funny stuff to the
357 # line returns here, but the getter for input_buffer filters
364 # line returns here, but the getter for input_buffer filters
358 # this out.
365 # this out.
359 if sys.platform == 'win32':
366 if sys.platform == 'win32':
360 self.input_buffer = self.input_buffer
367 self.input_buffer = self.input_buffer
361 self._on_enter()
368 self._on_enter()
362
369
363 elif event.KeyCode == wx.WXK_HOME:
370 elif event.KeyCode == wx.WXK_HOME:
364 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
371 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
365 self.GotoPos(self.current_prompt_pos)
372 self.GotoPos(self.current_prompt_pos)
366 catched = True
373 catched = True
367
374
368 elif event.Modifiers == wx.MOD_SHIFT:
375 elif event.Modifiers == wx.MOD_SHIFT:
369 # FIXME: This behavior is not ideal: if the selection
376 # FIXME: This behavior is not ideal: if the selection
370 # is already started, it will jump.
377 # is already started, it will jump.
371 self.SetSelectionStart(self.current_prompt_pos)
378 self.SetSelectionStart(self.current_prompt_pos)
372 self.SetSelectionEnd(self.GetCurrentPos())
379 self.SetSelectionEnd(self.GetCurrentPos())
373 catched = True
380 catched = True
374
381
375 elif event.KeyCode == wx.WXK_UP:
382 elif event.KeyCode == wx.WXK_UP:
376 if self.GetCurrentLine() > self.current_prompt_line:
383 if self.GetCurrentLine() > self.current_prompt_line:
377 if self.GetCurrentLine() == self.current_prompt_line + 1 \
384 if self.GetCurrentLine() == self.current_prompt_line + 1 \
378 and self.GetColumn(self.GetCurrentPos()) < \
385 and self.GetColumn(self.GetCurrentPos()) < \
379 self.GetColumn(self.current_prompt_pos):
386 self.GetColumn(self.current_prompt_pos):
380 self.GotoPos(self.current_prompt_pos)
387 self.GotoPos(self.current_prompt_pos)
381 else:
388 else:
382 event.Skip()
389 event.Skip()
383 catched = True
390 catched = True
384
391
385 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
392 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
386 if self.GetCurrentPos() > self.current_prompt_pos:
393 if self.GetCurrentPos() > self.current_prompt_pos:
387 event.Skip()
394 event.Skip()
388 catched = True
395 catched = True
389
396
390 if skip and not catched:
397 if skip and not catched:
391 # Put the cursor back in the edit region
398 # Put the cursor back in the edit region
392 if self.GetCurrentPos() < self.current_prompt_pos:
399 if self.GetCurrentPos() < self.current_prompt_pos:
393 self.GotoPos(self.current_prompt_pos)
400 self.GotoPos(self.current_prompt_pos)
394 else:
401 else:
395 event.Skip()
402 event.Skip()
396
403
397 return catched
404 return catched
398
405
399
406
400 def _on_key_up(self, event, skip=True):
407 def _on_key_up(self, event, skip=True):
401 """ If cursor is outside the editing region, put it back.
408 """ If cursor is outside the editing region, put it back.
402 """
409 """
403 event.Skip()
410 event.Skip()
404 if self.GetCurrentPos() < self.current_prompt_pos:
411 if self.GetCurrentPos() < self.current_prompt_pos:
405 self.GotoPos(self.current_prompt_pos)
412 self.GotoPos(self.current_prompt_pos)
406
413
407
414
408
415
409 if __name__ == '__main__':
416 if __name__ == '__main__':
410 # Some simple code to test the console widget.
417 # Some simple code to test the console widget.
411 class MainWindow(wx.Frame):
418 class MainWindow(wx.Frame):
412 def __init__(self, parent, id, title):
419 def __init__(self, parent, id, title):
413 wx.Frame.__init__(self, parent, id, title, size=(300,250))
420 wx.Frame.__init__(self, parent, id, title, size=(300,250))
414 self._sizer = wx.BoxSizer(wx.VERTICAL)
421 self._sizer = wx.BoxSizer(wx.VERTICAL)
415 self.console_widget = ConsoleWidget(self)
422 self.console_widget = ConsoleWidget(self)
416 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
423 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
417 self.SetSizer(self._sizer)
424 self.SetSizer(self._sizer)
418 self.SetAutoLayout(1)
425 self.SetAutoLayout(1)
419 self.Show(True)
426 self.Show(True)
420
427
421 app = wx.PySimpleApp()
428 app = wx.PySimpleApp()
422 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
429 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
423 w.SetSize((780, 460))
430 w.SetSize((780, 460))
424 w.Show()
431 w.Show()
425
432
426 app.MainLoop()
433 app.MainLoop()
427
434
428
435
General Comments 0
You need to be logged in to leave comments. Login now