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