##// END OF EJS Templates
Update wx frontend.
Gael Varoquaux -
Show More
@@ -0,0 +1,207 b''
1 # encoding: utf-8 -*- test-case-name:
2 # FIXME: Need to add tests.
3 # ipython1.frontend.cocoa.tests.test_cocoa_frontend -*-
4
5 """Classes to provide a Wx frontend to the
6 ipython1.kernel.engineservice.EngineService.
7
8 """
9
10 __docformat__ = "restructuredtext en"
11
12 #-------------------------------------------------------------------------------
13 # Copyright (C) 2008 The IPython Development Team
14 #
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
17 #-------------------------------------------------------------------------------
18
19 #-------------------------------------------------------------------------------
20 # Imports
21 #-------------------------------------------------------------------------------
22
23
24 import wx
25 from console_widget import ConsoleWidget
26 import re
27
28 import IPython
29 from IPython.kernel.engineservice import EngineService
30 from IPython.frontend.frontendbase import FrontEndBase
31
32 #-------------------------------------------------------------------------------
33 # Classes to implement the Wx frontend
34 #-------------------------------------------------------------------------------
35
36
37
38
39 class IPythonWxController(FrontEndBase, ConsoleWidget):
40
41 output_prompt = \
42 '\n\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02%i\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
43
44 # Are we entering multi line input?
45 multi_line_input = False
46
47 # The added tab stop to the string. It may, for instance, come from
48 # copy and pasting something with tabs.
49 tab_stop = 0
50 # FIXME: We still have to deal with this.
51
52 #--------------------------------------------------------------------------
53 # Public API
54 #--------------------------------------------------------------------------
55
56 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
57 size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
58 *args, **kwds):
59 """ Create Shell instance.
60 """
61 ConsoleWidget.__init__(self, parent, id, pos, size, style)
62 FrontEndBase.__init__(self, engine=EngineService(),
63 )
64
65 # FIXME: Something is wrong with the history, I instanciate it
66 # with an empty cache, but this is not the way to do.
67 self.lines = {}
68
69 # Start the IPython engine
70 self.engine.startService()
71
72 # Capture Character keys
73 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
74
75 #FIXME: print banner.
76 banner = """IPython1 %s -- An enhanced Interactive Python.""" \
77 % IPython.__version__
78
79
80 def appWillTerminate_(self, notification):
81 """appWillTerminate"""
82
83 self.engine.stopService()
84
85
86 def complete(self, token):
87 """Complete token in engine's user_ns
88
89 Parameters
90 ----------
91 token : string
92
93 Result
94 ------
95 Deferred result of
96 IPython.kernel.engineservice.IEngineBase.complete
97 """
98
99 return self.engine.complete(token)
100
101
102 def render_result(self, result):
103 if 'stdout' in result and result['stdout']:
104 self.write('\n' + result['stdout'])
105 if 'display' in result and result['display']:
106 self.write("%s%s\n" % (
107 self.output_prompt % result['number'],
108 result['display']['pprint']
109 ) )
110
111
112 def render_error(self, failure):
113 self.insert_text('\n\n'+str(failure)+'\n\n')
114 return failure
115
116
117 #--------------------------------------------------------------------------
118 # Private API
119 #--------------------------------------------------------------------------
120
121
122 def _on_key_down(self, event, skip=True):
123 """ Capture the character events, let the parent
124 widget handle them, and put our logic afterward.
125 """
126 current_line_number = self.GetCurrentLine()
127 # Capture enter
128 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
129 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
130 self._on_enter()
131 # Up history
132 elif event.KeyCode == wx.WXK_UP and (
133 ( current_line_number == self.current_prompt_line and
134 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
135 or event.ControlDown() ):
136 new_buffer = self.get_history_previous(
137 self.get_current_edit_buffer())
138 if new_buffer is not None:
139 self.replace_current_edit_buffer(new_buffer)
140 # Down history
141 elif event.KeyCode == wx.WXK_DOWN and (
142 ( current_line_number == self.LineCount -1 and
143 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
144 or event.ControlDown() ):
145 new_buffer = self.get_history_next()
146 if new_buffer is not None:
147 self.replace_current_edit_buffer(new_buffer)
148 else:
149 ConsoleWidget._on_key_down(self, event, skip=True)
150
151
152 def _on_enter(self):
153 """ Called when the return key is pressed in a line editing
154 buffer.
155 """
156 current_buffer = self.get_current_edit_buffer()
157 current_buffer = current_buffer.replace('\r\n', '\n')
158 current_buffer = current_buffer.replace('\t', 4*' ')
159 cleaned_buffer = '\n'.join(l.rstrip()
160 for l in current_buffer.split('\n'))
161 if ( not self.multi_line_input
162 or re.findall(r"\n[\t ]*$", cleaned_buffer)):
163 if self.is_complete(cleaned_buffer):
164 self._add_history(None, cleaned_buffer.rstrip())
165 result = self.engine.shell.execute(cleaned_buffer)
166 self.render_result(result)
167 self.new_prompt(self.prompt % (result['number'] + 1))
168 self.multi_line_input = False
169 else:
170 if self.multi_line_input:
171 self.write('\n' + self._get_indent_string(current_buffer))
172 else:
173 self.multi_line_input = True
174 self.write('\n\t')
175 else:
176 self.write('\n'+self._get_indent_string(current_buffer))
177
178
179 def _get_indent_string(self, string):
180 string = string.split('\n')[-1]
181 indent_chars = len(string) - len(string.lstrip())
182 indent_string = '\t'*(indent_chars // 4) + \
183 ' '*(indent_chars % 4)
184
185 return indent_string
186
187
188
189 if __name__ == '__main__':
190 class MainWindow(wx.Frame):
191 def __init__(self, parent, id, title):
192 wx.Frame.__init__(self, parent, id, title, size=(300,250))
193 self._sizer = wx.BoxSizer(wx.VERTICAL)
194 self.shell = IPythonWxController(self)
195 self._sizer.Add(self.shell, 1, wx.EXPAND)
196 self.SetSizer(self._sizer)
197 self.SetAutoLayout(1)
198 self.Show(True)
199
200 app = wx.PySimpleApp()
201 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
202 frame.shell.SetFocus()
203 frame.SetSize((660, 460))
204 self = frame.shell
205
206 app.MainLoop()
207
@@ -1,370 +1,388 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
26
25 import re
27 import re
26
28
27 # FIXME: Need to provide an API for non user-generated display on the
29 # FIXME: Need to provide an API for non user-generated display on the
28 # screen: this should not be editable by the user.
30 # screen: this should not be editable by the user.
29
31
32 if wx.Platform == '__WXMSW__':
33 _DEFAULT_SIZE = 80
34 else:
35 _DEFAULT_SIZE = 10
36
37 _DEFAULT_STYLE = {
38 'stdout' : 'fore:#0000FF',
39 'stderr' : 'fore:#007f00',
40 'trace' : 'fore:#FF0000',
41
42 'default' : 'size:%d' % _DEFAULT_SIZE,
43 'bracegood' : 'fore:#FFFFFF,back:#0000FF,bold',
44 'bracebad' : 'fore:#000000,back:#FF0000,bold',
45
46 # properties for the various Python lexer styles
47 'comment' : 'fore:#007F00',
48 'number' : 'fore:#007F7F',
49 'string' : 'fore:#7F007F,italic',
50 'char' : 'fore:#7F007F,italic',
51 'keyword' : 'fore:#00007F,bold',
52 'triple' : 'fore:#7F0000',
53 'tripledouble': 'fore:#7F0000',
54 'class' : 'fore:#0000FF,bold,underline',
55 'def' : 'fore:#007F7F,bold',
56 'operator' : 'bold',
57
58 }
59
60 # new style numbers
61 _STDOUT_STYLE = 15
62 _STDERR_STYLE = 16
63 _TRACE_STYLE = 17
64
65
30 #-------------------------------------------------------------------------------
66 #-------------------------------------------------------------------------------
31 # The console widget class
67 # The console widget class
32 #-------------------------------------------------------------------------------
68 #-------------------------------------------------------------------------------
33 class ConsoleWidget(stc.StyledTextCtrl):
69 class ConsoleWidget(editwindow.EditWindow):
34 """ Specialized styled text control view for console-like workflow.
70 """ Specialized styled text control view for console-like workflow.
35
71
36 This widget is mainly interested in dealing with the prompt and
72 This widget is mainly interested in dealing with the prompt and
37 keeping the cursor inside the editing line.
73 keeping the cursor inside the editing line.
38 """
74 """
39
75
76 style = _DEFAULT_STYLE.copy()
77
40 # Translation table from ANSI escape sequences to color. Override
78 # Translation table from ANSI escape sequences to color. Override
41 # this to specify your colors.
79 # this to specify your colors.
42 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
80 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
43 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
81 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
44 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
82 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
45 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
83 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
46 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
84 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
47 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
85 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
48 '1;34': [12, 'LIGHT BLUE'], '1;35':
86 '1;34': [12, 'LIGHT BLUE'], '1;35':
49 [13, 'MEDIUM VIOLET RED'],
87 [13, 'MEDIUM VIOLET RED'],
50 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
88 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
51
89
52 # The color of the carret (call _apply_style() after setting)
90 # The color of the carret (call _apply_style() after setting)
53 carret_color = 'BLACK'
91 carret_color = 'BLACK'
54
92
55
93
56 #--------------------------------------------------------------------------
94 #--------------------------------------------------------------------------
57 # Public API
95 # Public API
58 #--------------------------------------------------------------------------
96 #--------------------------------------------------------------------------
59
97
98 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
99 size=wx.DefaultSize, style=0,
100 autocomplete_mode='IPYTHON'):
101 """ Autocomplete_mode: Can be 'IPYTHON' or 'STC'
102 'IPYTHON' show autocompletion the ipython way
103 'STC" show it scintilla text control way
104 """
105 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
106 self.configure_scintilla()
107
108 # FIXME: we need to retrieve this from the interpreter.
109 self.prompt = \
110 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02%i\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
111 self.new_prompt(self.prompt % 1)
112
113 self.autocomplete_mode = autocomplete_mode
114
115 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
116
117
118 def configure_scintilla(self):
119 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
120 # the widget
121 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
122 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
123 # Also allow Ctrl Shift "=" for poor non US keyboard users.
124 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
125 stc.STC_CMD_ZOOMIN)
126
127 self.CmdKeyAssign(stc.STC_KEY_PRIOR, stc.STC_SCMOD_SHIFT,
128 stc.STC_CMD_PAGEUP)
129
130 self.CmdKeyAssign(stc.STC_KEY_NEXT, stc.STC_SCMOD_SHIFT,
131 stc.STC_CMD_PAGEDOWN)
132
133 # Keys: we need to clear some of the keys the that don't play
134 # well with a console.
135 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
136 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
137 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
138
139
140 self.SetEOLMode(stc.STC_EOL_CRLF)
141 self.SetWrapMode(stc.STC_WRAP_CHAR)
142 self.SetWrapMode(stc.STC_WRAP_WORD)
143 self.SetBufferedDraw(True)
144 self.SetUseAntiAliasing(True)
145 self.SetLayoutCache(stc.STC_CACHE_PAGE)
146 self.SetUndoCollection(False)
147 self.SetUseTabs(True)
148 self.SetIndent(4)
149 self.SetTabWidth(4)
150
151 self.EnsureCaretVisible()
152
153 self.SetMargins(3, 3) #text is moved away from border with 3px
154 # Suppressing Scintilla margins
155 self.SetMarginWidth(0, 0)
156 self.SetMarginWidth(1, 0)
157 self.SetMarginWidth(2, 0)
158
159 self._apply_style()
160
161 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
162
163 #self.SetEdgeMode(stc.STC_EDGE_LINE)
164 #self.SetEdgeColumn(80)
165
166 # styles
167 p = self.style
168 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
169 self.StyleClearAll()
170 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
171 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
172 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
173
174 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
175 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
176 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
177 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
178 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
179 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
180 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
181 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
182 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
183 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
184 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
185 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
186 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
187
188
60 def write(self, text):
189 def write(self, text):
61 """ Write given text to buffer, while translating the ansi escape
190 """ Write given text to buffer, while translating the ansi escape
62 sequences.
191 sequences.
63 """
192 """
64 segments = self.color_pat.split(text)
193 segments = self.color_pat.split(text)
65 segment = segments.pop(0)
194 segment = segments.pop(0)
66 self.StartStyling(self.GetLength(), 0xFF)
195 self.StartStyling(self.GetLength(), 0xFF)
67 self.AppendText(segment)
196 self.AppendText(segment)
68
197
69 if segments:
198 if segments:
70 ansi_tags = self.color_pat.findall(text)
199 ansi_tags = self.color_pat.findall(text)
71
200
72 for tag in ansi_tags:
201 for tag in ansi_tags:
73 i = segments.index(tag)
202 i = segments.index(tag)
74 self.StartStyling(self.GetLength(), 0xFF)
203 self.StartStyling(self.GetLength(), 0xFF)
75 self.AppendText(segments[i+1])
204 self.AppendText(segments[i+1])
76
205
77 if tag != '0':
206 if tag != '0':
78 self.SetStyling(len(segments[i+1]),
207 self.SetStyling(len(segments[i+1]),
79 self.ANSI_STYLES[tag][0])
208 self.ANSI_STYLES[tag][0])
80
209
81 segments.pop(i)
210 segments.pop(i)
82
211
83 self.GotoPos(self.GetLength())
212 self.GotoPos(self.GetLength())
84
213
85
214
86 def new_prompt(self, prompt):
215 def new_prompt(self, prompt):
87 """ Prints a prompt at start of line, and move the start of the
216 """ Prints a prompt at start of line, and move the start of the
88 current block there.
217 current block there.
89
218
90 The prompt can be give with ascii escape sequences.
219 The prompt can be give with ascii escape sequences.
91 """
220 """
92 self.write(prompt)
221 self.write(prompt)
93 # now we update our cursor giving end of prompt
222 # now we update our cursor giving end of prompt
94 self.current_prompt_pos = self.GetLength()
223 self.current_prompt_pos = self.GetLength()
95 self.current_prompt_line = self.GetCurrentLine()
224 self.current_prompt_line = self.GetCurrentLine()
96
225
97 autoindent = self.indent * ' '
98 autoindent = autoindent.replace(' ','\t')
99 self.write(autoindent)
100
101
226
102 def replace_current_edit_buffer(self, text):
227 def replace_current_edit_buffer(self, text):
103 """ Replace currently entered command line with given text.
228 """ Replace currently entered command line with given text.
104 """
229 """
105 self.SetSelection(self.current_prompt_pos, self.GetLength())
230 self.SetSelection(self.current_prompt_pos, self.GetLength())
106 self.ReplaceSelection(text)
231 self.ReplaceSelection(text)
107 self.GotoPos(self.GetLength())
232 self.GotoPos(self.GetLength())
108
233
109
234
110 def get_current_edit_buffer(self):
235 def get_current_edit_buffer(self):
111 """ Returns the text in current edit buffer.
236 """ Returns the text in current edit buffer.
112 """
237 """
113 return self.GetTextRange(self.current_prompt_pos,
238 return self.GetTextRange(self.current_prompt_pos,
114 self.GetLength())
239 self.GetLength())
115
240
116
241
117 #--------------------------------------------------------------------------
242 #--------------------------------------------------------------------------
118 # Private API
243 # Private API
119 #--------------------------------------------------------------------------
244 #--------------------------------------------------------------------------
120
245
121 def __init__(self, parent, pos=wx.DefaultPosition, ID=-1,
122 size=wx.DefaultSize, style=0,
123 autocomplete_mode='IPYTHON'):
124 """ Autocomplete_mode: Can be 'IPYTHON' or 'STC'
125 'IPYTHON' show autocompletion the ipython way
126 'STC" show it scintilla text control way
127 """
128 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
129
130 #------ Scintilla configuration -----------------------------------
131
132 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
133 # the widget
134 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
135 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
136 # Also allow Ctrl Shift "=" for poor non US keyboard users.
137 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
138 stc.STC_CMD_ZOOMIN)
139
140 self.SetEOLMode(stc.STC_EOL_CRLF)
141 self.SetWrapMode(stc.STC_WRAP_CHAR)
142 self.SetWrapMode(stc.STC_WRAP_WORD)
143 self.SetBufferedDraw(True)
144 self.SetUseAntiAliasing(True)
145 self.SetLayoutCache(stc.STC_CACHE_PAGE)
146 self.SetUndoCollection(False)
147 self.SetUseTabs(True)
148 self.SetIndent(4)
149 self.SetTabWidth(4)
150
151 self.EnsureCaretVisible()
152
153 self.SetMargins(3, 3) #text is moved away from border with 3px
154 # Suppressing Scintilla margins
155 self.SetMarginWidth(0, 0)
156 self.SetMarginWidth(1, 0)
157 self.SetMarginWidth(2, 0)
158
159 self._apply_style()
160
161 self.indent = 0
162 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
163
164 # FIXME: we need to retrieve this from the interpreter.
165 self.prompt = \
166 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x026\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
167 self.new_prompt(self.prompt)
168
169 self.autocomplete_mode = autocomplete_mode
170
171 self.Bind(wx.EVT_KEY_DOWN, self._onKeypress)
172
173
174 def _apply_style(self):
246 def _apply_style(self):
175 """ Applies the colors for the different text elements and the
247 """ Applies the colors for the different text elements and the
176 carret.
248 carret.
177 """
249 """
178 # FIXME: We need to do something for the fonts, but this is
179 # clearly not the right option.
180 #we define platform specific fonts
181 # if wx.Platform == '__WXMSW__':
182 # faces = { 'times': 'Times New Roman',
183 # 'mono' : 'Courier New',
184 # 'helv' : 'Arial',
185 # 'other': 'Comic Sans MS',
186 # 'size' : 10,
187 # 'size2': 8,
188 # }
189 # elif wx.Platform == '__WXMAC__':
190 # faces = { 'times': 'Times New Roman',
191 # 'mono' : 'Monaco',
192 # 'helv' : 'Arial',
193 # 'other': 'Comic Sans MS',
194 # 'size' : 10,
195 # 'size2': 8,
196 # }
197 # else:
198 # faces = { 'times': 'Times',
199 # 'mono' : 'Courier',
200 # 'helv' : 'Helvetica',
201 # 'other': 'new century schoolbook',
202 # 'size' : 10,
203 # 'size2': 8,
204 # }
205 # self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
206 # "fore:%s,back:%s,size:%d,face:%s"
207 # % (self.ANSI_STYLES['0;30'][1],
208 # self.background_color,
209 # faces['size'], faces['mono']))
210
211 self.SetCaretForeground(self.carret_color)
250 self.SetCaretForeground(self.carret_color)
212
251
213 self.StyleClearAll()
252 self.StyleClearAll()
214 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
253 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
215 "fore:#FF0000,back:#0000FF,bold")
254 "fore:#FF0000,back:#0000FF,bold")
216 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
255 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
217 "fore:#000000,back:#FF0000,bold")
256 "fore:#000000,back:#FF0000,bold")
218
257
219 for style in self.ANSI_STYLES.values():
258 for style in self.ANSI_STYLES.values():
220 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
259 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
221
260
222
261
223 def removeFromTo(self, from_pos, to_pos):
262 def removeFromTo(self, from_pos, to_pos):
224 if from_pos < to_pos:
263 if from_pos < to_pos:
225 self.SetSelection(from_pos, to_pos)
264 self.SetSelection(from_pos, to_pos)
226 self.DeleteBack()
265 self.DeleteBack()
227
266
228
267
229 def selectFromTo(self, from_pos, to_pos):
268 def selectFromTo(self, from_pos, to_pos):
230 self.SetSelectionStart(from_pos)
269 self.SetSelectionStart(from_pos)
231 self.SetSelectionEnd(to_pos)
270 self.SetSelectionEnd(to_pos)
232
271
233
272
234 def writeCompletion(self, possibilities):
273 def writeCompletion(self, possibilities):
235 if self.autocomplete_mode == 'IPYTHON':
274 if self.autocomplete_mode == 'IPYTHON':
236 max_len = len(max(possibilities, key=len))
275 max_len = len(max(possibilities, key=len))
237 max_symbol = ' '*max_len
276 max_symbol = ' '*max_len
238
277
239 #now we check how much symbol we can put on a line...
278 #now we check how much symbol we can put on a line...
240 test_buffer = max_symbol + ' '*4
279 test_buffer = max_symbol + ' '*4
241
280
242 allowed_symbols = 80/len(test_buffer)
281 allowed_symbols = 80/len(test_buffer)
243 if allowed_symbols == 0:
282 if allowed_symbols == 0:
244 allowed_symbols = 1
283 allowed_symbols = 1
245
284
246 pos = 1
285 pos = 1
247 buf = ''
286 buf = ''
248 for symbol in possibilities:
287 for symbol in possibilities:
249 #buf += symbol+'\n'#*spaces)
288 #buf += symbol+'\n'#*spaces)
250 if pos < allowed_symbols:
289 if pos < allowed_symbols:
251 spaces = max_len - len(symbol) + 4
290 spaces = max_len - len(symbol) + 4
252 buf += symbol+' '*spaces
291 buf += symbol+' '*spaces
253 pos += 1
292 pos += 1
254 else:
293 else:
255 buf += symbol+'\n'
294 buf += symbol+'\n'
256 pos = 1
295 pos = 1
257 self.write(buf)
296 self.write(buf)
258 else:
297 else:
259 possibilities.sort() # Python sorts are case sensitive
298 possibilities.sort() # Python sorts are case sensitive
260 self.AutoCompSetIgnoreCase(False)
299 self.AutoCompSetIgnoreCase(False)
261 self.AutoCompSetAutoHide(False)
300 self.AutoCompSetAutoHide(False)
262 #let compute the length ot last word
301 #let compute the length ot last word
263 splitter = [' ', '(', '[', '{']
302 splitter = [' ', '(', '[', '{']
264 last_word = self.get_current_edit_buffer()
303 last_word = self.get_current_edit_buffer()
265 for breaker in splitter:
304 for breaker in splitter:
266 last_word = last_word.split(breaker)[-1]
305 last_word = last_word.split(breaker)[-1]
267 self.AutoCompShow(len(last_word), " ".join(possibilities))
306 self.AutoCompShow(len(last_word), " ".join(possibilities))
268
307
308
309 def scroll_to_bottom(self):
310 maxrange = self.GetScrollRange(wx.VERTICAL)
311 self.ScrollLines(maxrange)
312
269
313
270 def _onKeypress(self, event, skip=True):
314 def _on_key_down(self, event, skip=True):
271 """ Key press callback used for correcting behavior for
315 """ Key press callback used for correcting behavior for
272 console-like interfaces: the cursor is constraint to be after
316 console-like interfaces: the cursor is constraint to be after
273 the last prompt.
317 the last prompt.
274
318
275 Return True if event as been catched.
319 Return True if event as been catched.
276 """
320 """
277 catched = False
321 catched = False
322 if event.KeyCode == ord('L') and event.ControlDown() :
323 skip = False
324 catched = True
325 self.scroll_to_bottom()
326
278 if self.AutoCompActive():
327 if self.AutoCompActive():
279 event.Skip()
328 event.Skip()
280 else:
329 else:
281 if event.GetKeyCode() == wx.WXK_HOME:
330 if event.KeyCode == wx.WXK_HOME:
282 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
331 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
283 self.GotoPos(self.current_prompt_pos)
332 self.GotoPos(self.current_prompt_pos)
284 catched = True
333 catched = True
285
334
286 elif event.Modifiers == wx.MOD_SHIFT:
335 elif event.Modifiers in (wx.MOD_SHIFT, wx.MOD_WIN) :
287 self.selectFromTo(self.current_prompt_pos,
336 self.selectFromTo(self.current_prompt_pos,
288 self.GetCurrentPos())
337 self.GetCurrentPos())
289 catched = True
338 catched = True
290
339
291 elif event.GetKeyCode() == wx.WXK_UP:
340 elif event.KeyCode == wx.WXK_UP:
292 if self.GetCurrentLine() > self.current_prompt_line:
341 if self.GetCurrentLine() > self.current_prompt_line:
293 if self.GetCurrentLine() == self.current_prompt_line + 1 \
342 if self.GetCurrentLine() == self.current_prompt_line + 1 \
294 and self.GetColumn(self.GetCurrentPos()) < \
343 and self.GetColumn(self.GetCurrentPos()) < \
295 self.GetColumn(self.current_prompt_pos):
344 self.GetColumn(self.current_prompt_pos):
296 self.GotoPos(self.current_prompt_pos)
345 self.GotoPos(self.current_prompt_pos)
297 else:
346 else:
298 event.Skip()
347 event.Skip()
299 catched = True
348 catched = True
300
349
301 elif event.GetKeyCode() in (wx.WXK_LEFT, wx.WXK_BACK):
350 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
302 if self.GetCurrentPos() > self.current_prompt_pos:
351 if self.GetCurrentPos() > self.current_prompt_pos:
303 event.Skip()
352 event.Skip()
304 catched = True
353 catched = True
305
354
306 if skip and not catched:
355 if skip and not catched:
307 event.Skip()
356 event.Skip()
308
357
309 if event.GetKeyCode() not in (wx.WXK_PAGEUP, wx.WXK_PAGEDOWN)\
358 if event.KeyCode not in (wx.WXK_PAGEUP, wx.WXK_PAGEDOWN)\
310 and event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN,
359 and event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN,
311 wx.MOD_SHIFT):
360 wx.MOD_SHIFT):
312 # If cursor is outside the editing region, put it back.
361 # If cursor is outside the editing region, put it back.
313 if self.GetCurrentPos() < self.current_prompt_pos:
362 if self.GetCurrentPos() < self.current_prompt_pos:
314 self.GotoPos(self.current_prompt_pos)
363 self.GotoPos(self.current_prompt_pos)
315
364
316 return catched
365 return catched
317
366
318
367
319 def OnUpdateUI(self, evt):
320 # check for matching braces
321 braceAtCaret = -1
322 braceOpposite = -1
323 charBefore = None
324 caretPos = self.GetCurrentPos()
325
326 if caretPos > 0:
327 charBefore = self.GetCharAt(caretPos - 1)
328 styleBefore = self.GetStyleAt(caretPos - 1)
329
330 # check before
331 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
332 braceAtCaret = caretPos - 1
333
334 # check after
335 if braceAtCaret < 0:
336 charAfter = self.GetCharAt(caretPos)
337 styleAfter = self.GetStyleAt(caretPos)
338
339 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
340 braceAtCaret = caretPos
341
342 if braceAtCaret >= 0:
343 braceOpposite = self.BraceMatch(braceAtCaret)
344
345 if braceAtCaret != -1 and braceOpposite == -1:
346 self.BraceBadLight(braceAtCaret)
347 else:
348 self.BraceHighlight(braceAtCaret, braceOpposite)
349
350
368
351 if __name__ == '__main__':
369 if __name__ == '__main__':
352 # Some simple code to test the console widget.
370 # Some simple code to test the console widget.
353 class MainWindow(wx.Frame):
371 class MainWindow(wx.Frame):
354 def __init__(self, parent, id, title):
372 def __init__(self, parent, id, title):
355 wx.Frame.__init__(self, parent, id, title, size=(300,250))
373 wx.Frame.__init__(self, parent, id, title, size=(300,250))
356 self._sizer = wx.BoxSizer(wx.VERTICAL)
374 self._sizer = wx.BoxSizer(wx.VERTICAL)
357 self.console_widget = ConsoleWidget(self)
375 self.console_widget = ConsoleWidget(self)
358 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
376 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
359 self.SetSizer(self._sizer)
377 self.SetSizer(self._sizer)
360 self.SetAutoLayout(1)
378 self.SetAutoLayout(1)
361 self.Show(True)
379 self.Show(True)
362
380
363 app = wx.PySimpleApp()
381 app = wx.PySimpleApp()
364 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
382 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
365 w.SetSize((780, 460))
383 w.SetSize((780, 460))
366 w.Show()
384 w.Show()
367
385
368 app.MainLoop()
386 app.MainLoop()
369
387
370
388
General Comments 0
You need to be logged in to leave comments. Login now