##// END OF EJS Templates
Full patch for better user color tweaking + patch for 'enter' synchro bug
Laurent Dufrechou -
Show More
@@ -1,251 +1,250 b''
1 """
1 """
2 Frontend class that uses IPython0 to prefilter the inputs.
2 Frontend class that uses IPython0 to prefilter the inputs.
3
3
4 Using the IPython0 mechanism gives us access to the magics.
4 Using the IPython0 mechanism gives us access to the magics.
5
5
6 This is a transitory class, used here to do the transition between
6 This is a transitory class, used here to do the transition between
7 ipython0 and ipython1. This class is meant to be short-lived as more
7 ipython0 and ipython1. This class is meant to be short-lived as more
8 functionnality is abstracted out of ipython0 in reusable functions and
8 functionnality is abstracted out of ipython0 in reusable functions and
9 is added on the interpreter. This class can be a used to guide this
9 is added on the interpreter. This class can be a used to guide this
10 refactoring.
10 refactoring.
11 """
11 """
12 __docformat__ = "restructuredtext en"
12 __docformat__ = "restructuredtext en"
13
13
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15 # Copyright (C) 2008 The IPython Development Team
15 # Copyright (C) 2008 The IPython Development Team
16 #
16 #
17 # Distributed under the terms of the BSD License. The full license is in
17 # Distributed under the terms of the BSD License. The full license is in
18 # the file COPYING, distributed as part of this software.
18 # the file COPYING, distributed as part of this software.
19 #-------------------------------------------------------------------------------
19 #-------------------------------------------------------------------------------
20
20
21 #-------------------------------------------------------------------------------
21 #-------------------------------------------------------------------------------
22 # Imports
22 # Imports
23 #-------------------------------------------------------------------------------
23 #-------------------------------------------------------------------------------
24 import sys
24 import sys
25
25
26 from linefrontendbase import LineFrontEndBase, common_prefix
26 from linefrontendbase import LineFrontEndBase, common_prefix
27 from frontendbase import FrontEndBase
27 from frontendbase import FrontEndBase
28
28
29 from IPython.ipmaker import make_IPython
29 from IPython.ipmaker import make_IPython
30 from IPython.ipapi import IPApi
30 from IPython.ipapi import IPApi
31 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
31 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
32
32
33 from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap
33 from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap
34
34
35 from IPython.genutils import Term
35 from IPython.genutils import Term
36 import pydoc
36 import pydoc
37 import os
37 import os
38 import sys
38 import sys
39
39
40
40
41 def mk_system_call(system_call_function, command):
41 def mk_system_call(system_call_function, command):
42 """ given a os.system replacement, and a leading string command,
42 """ given a os.system replacement, and a leading string command,
43 returns a function that will execute the command with the given
43 returns a function that will execute the command with the given
44 argument string.
44 argument string.
45 """
45 """
46 def my_system_call(args):
46 def my_system_call(args):
47 system_call_function("%s %s" % (command, args))
47 system_call_function("%s %s" % (command, args))
48
48
49 my_system_call.__doc__ = "Calls %s" % command
49 my_system_call.__doc__ = "Calls %s" % command
50 return my_system_call
50 return my_system_call
51
51
52 #-------------------------------------------------------------------------------
52 #-------------------------------------------------------------------------------
53 # Frontend class using ipython0 to do the prefiltering.
53 # Frontend class using ipython0 to do the prefiltering.
54 #-------------------------------------------------------------------------------
54 #-------------------------------------------------------------------------------
55 class PrefilterFrontEnd(LineFrontEndBase):
55 class PrefilterFrontEnd(LineFrontEndBase):
56 """ Class that uses ipython0 to do prefilter the input, do the
56 """ Class that uses ipython0 to do prefilter the input, do the
57 completion and the magics.
57 completion and the magics.
58
58
59 The core trick is to use an ipython0 instance to prefilter the
59 The core trick is to use an ipython0 instance to prefilter the
60 input, and share the namespace between the interpreter instance used
60 input, and share the namespace between the interpreter instance used
61 to execute the statements and the ipython0 used for code
61 to execute the statements and the ipython0 used for code
62 completion...
62 completion...
63 """
63 """
64
64
65 debug = False
65 debug = False
66
66
67 def __init__(self, ipython0=None, *args, **kwargs):
67 def __init__(self, ipython0=None, *args, **kwargs):
68 """ Parameters:
68 """ Parameters:
69 -----------
69 -----------
70
70
71 ipython0: an optional ipython0 instance to use for command
71 ipython0: an optional ipython0 instance to use for command
72 prefiltering and completion.
72 prefiltering and completion.
73 """
73 """
74 LineFrontEndBase.__init__(self, *args, **kwargs)
74 LineFrontEndBase.__init__(self, *args, **kwargs)
75 self.shell.output_trap = RedirectorOutputTrap(
75 self.shell.output_trap = RedirectorOutputTrap(
76 out_callback=self.write,
76 out_callback=self.write,
77 err_callback=self.write,
77 err_callback=self.write,
78 )
78 )
79 self.shell.traceback_trap = SyncTracebackTrap(
79 self.shell.traceback_trap = SyncTracebackTrap(
80 formatters=self.shell.traceback_trap.formatters,
80 formatters=self.shell.traceback_trap.formatters,
81 )
81 )
82
82
83 # Start the ipython0 instance:
83 # Start the ipython0 instance:
84 self.save_output_hooks()
84 self.save_output_hooks()
85 if ipython0 is None:
85 if ipython0 is None:
86 # Instanciate an IPython0 interpreter to be able to use the
86 # Instanciate an IPython0 interpreter to be able to use the
87 # prefiltering.
87 # prefiltering.
88 # XXX: argv=[] is a bit bold.
88 # XXX: argv=[] is a bit bold.
89 ipython0 = make_IPython(argv=[],
89 ipython0 = make_IPython(argv=[],
90 user_ns=self.shell.user_ns,
90 user_ns=self.shell.user_ns,
91 user_global_ns=self.shell.user_global_ns)
91 user_global_ns=self.shell.user_global_ns)
92 self.ipython0 = ipython0
92 self.ipython0 = ipython0
93 # Set the pager:
93 # Set the pager:
94 self.ipython0.set_hook('show_in_pager',
94 self.ipython0.set_hook('show_in_pager',
95 lambda s, string: self.write("\n" + string))
95 lambda s, string: self.write("\n" + string))
96 self.ipython0.write = self.write
96 self.ipython0.write = self.write
97 self._ip = _ip = IPApi(self.ipython0)
97 self._ip = _ip = IPApi(self.ipython0)
98 # Make sure the raw system call doesn't get called, as we don't
98 # Make sure the raw system call doesn't get called, as we don't
99 # have a stdin accessible.
99 # have a stdin accessible.
100 self._ip.system = self.system_call
100 self._ip.system = self.system_call
101 # XXX: Muck around with magics so that they work better
101 # XXX: Muck around with magics so that they work better
102 # in our environment
102 # in our environment
103 self.ipython0.magic_ls = mk_system_call(self.system_call,
103 self.ipython0.magic_ls = mk_system_call(self.system_call,
104 'ls -CF')
104 'ls -CF')
105 # And now clean up the mess created by ipython0
105 # And now clean up the mess created by ipython0
106 self.release_output()
106 self.release_output()
107
107
108
108
109 if not 'banner' in kwargs and self.banner is None:
109 if not 'banner' in kwargs and self.banner is None:
110 self.banner = self.ipython0.BANNER + """
110 self.banner = self.ipython0.BANNER
111 This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code."""
112
111
113 self.start()
112 self.start()
114
113
115 #--------------------------------------------------------------------------
114 #--------------------------------------------------------------------------
116 # FrontEndBase interface
115 # FrontEndBase interface
117 #--------------------------------------------------------------------------
116 #--------------------------------------------------------------------------
118
117
119 def show_traceback(self):
118 def show_traceback(self):
120 """ Use ipython0 to capture the last traceback and display it.
119 """ Use ipython0 to capture the last traceback and display it.
121 """
120 """
122 # Don't do the capture; the except_hook has already done some
121 # Don't do the capture; the except_hook has already done some
123 # modifications to the IO streams, if we store them, we'll be
122 # modifications to the IO streams, if we store them, we'll be
124 # storing the wrong ones.
123 # storing the wrong ones.
125 #self.capture_output()
124 #self.capture_output()
126 self.ipython0.showtraceback(tb_offset=-1)
125 self.ipython0.showtraceback(tb_offset=-1)
127 self.release_output()
126 self.release_output()
128
127
129
128
130 def execute(self, python_string, raw_string=None):
129 def execute(self, python_string, raw_string=None):
131 if self.debug:
130 if self.debug:
132 print 'Executing Python code:', repr(python_string)
131 print 'Executing Python code:', repr(python_string)
133 self.capture_output()
132 self.capture_output()
134 LineFrontEndBase.execute(self, python_string,
133 LineFrontEndBase.execute(self, python_string,
135 raw_string=raw_string)
134 raw_string=raw_string)
136 self.release_output()
135 self.release_output()
137
136
138
137
139 def save_output_hooks(self):
138 def save_output_hooks(self):
140 """ Store all the output hooks we can think of, to be able to
139 """ Store all the output hooks we can think of, to be able to
141 restore them.
140 restore them.
142
141
143 We need to do this early, as starting the ipython0 instance will
142 We need to do this early, as starting the ipython0 instance will
144 screw ouput hooks.
143 screw ouput hooks.
145 """
144 """
146 self.__old_cout_write = Term.cout.write
145 self.__old_cout_write = Term.cout.write
147 self.__old_cerr_write = Term.cerr.write
146 self.__old_cerr_write = Term.cerr.write
148 self.__old_stdout = sys.stdout
147 self.__old_stdout = sys.stdout
149 self.__old_stderr= sys.stderr
148 self.__old_stderr= sys.stderr
150 self.__old_help_output = pydoc.help.output
149 self.__old_help_output = pydoc.help.output
151 self.__old_display_hook = sys.displayhook
150 self.__old_display_hook = sys.displayhook
152
151
153
152
154 def capture_output(self):
153 def capture_output(self):
155 """ Capture all the output mechanisms we can think of.
154 """ Capture all the output mechanisms we can think of.
156 """
155 """
157 self.save_output_hooks()
156 self.save_output_hooks()
158 Term.cout.write = self.write
157 Term.cout.write = self.write
159 Term.cerr.write = self.write
158 Term.cerr.write = self.write
160 sys.stdout = Term.cout
159 sys.stdout = Term.cout
161 sys.stderr = Term.cerr
160 sys.stderr = Term.cerr
162 pydoc.help.output = self.shell.output_trap.out
161 pydoc.help.output = self.shell.output_trap.out
163
162
164
163
165 def release_output(self):
164 def release_output(self):
166 """ Release all the different captures we have made.
165 """ Release all the different captures we have made.
167 """
166 """
168 Term.cout.write = self.__old_cout_write
167 Term.cout.write = self.__old_cout_write
169 Term.cerr.write = self.__old_cerr_write
168 Term.cerr.write = self.__old_cerr_write
170 sys.stdout = self.__old_stdout
169 sys.stdout = self.__old_stdout
171 sys.stderr = self.__old_stderr
170 sys.stderr = self.__old_stderr
172 pydoc.help.output = self.__old_help_output
171 pydoc.help.output = self.__old_help_output
173 sys.displayhook = self.__old_display_hook
172 sys.displayhook = self.__old_display_hook
174
173
175
174
176 def complete(self, line):
175 def complete(self, line):
177 # FIXME: This should be factored out in the linefrontendbase
176 # FIXME: This should be factored out in the linefrontendbase
178 # method.
177 # method.
179 word = line.split('\n')[-1].split(' ')[-1]
178 word = line.split('\n')[-1].split(' ')[-1]
180 completions = self.ipython0.complete(word)
179 completions = self.ipython0.complete(word)
181 # FIXME: The proper sort should be done in the complete method.
180 # FIXME: The proper sort should be done in the complete method.
182 key = lambda x: x.replace('_', '')
181 key = lambda x: x.replace('_', '')
183 completions.sort(key=key)
182 completions.sort(key=key)
184 if completions:
183 if completions:
185 prefix = common_prefix(completions)
184 prefix = common_prefix(completions)
186 line = line[:-len(word)] + prefix
185 line = line[:-len(word)] + prefix
187 return line, completions
186 return line, completions
188
187
189
188
190 #--------------------------------------------------------------------------
189 #--------------------------------------------------------------------------
191 # LineFrontEndBase interface
190 # LineFrontEndBase interface
192 #--------------------------------------------------------------------------
191 #--------------------------------------------------------------------------
193
192
194 def prefilter_input(self, input_string):
193 def prefilter_input(self, input_string):
195 """ Using IPython0 to prefilter the commands to turn them
194 """ Using IPython0 to prefilter the commands to turn them
196 in executable statements that are valid Python strings.
195 in executable statements that are valid Python strings.
197 """
196 """
198 input_string = LineFrontEndBase.prefilter_input(self, input_string)
197 input_string = LineFrontEndBase.prefilter_input(self, input_string)
199 filtered_lines = []
198 filtered_lines = []
200 # The IPython0 prefilters sometime produce output. We need to
199 # The IPython0 prefilters sometime produce output. We need to
201 # capture it.
200 # capture it.
202 self.capture_output()
201 self.capture_output()
203 self.last_result = dict(number=self.prompt_number)
202 self.last_result = dict(number=self.prompt_number)
204
203
205 ## try:
204 ## try:
206 ## for line in input_string.split('\n'):
205 ## for line in input_string.split('\n'):
207 ## filtered_lines.append(
206 ## filtered_lines.append(
208 ## self.ipython0.prefilter(line, False).rstrip())
207 ## self.ipython0.prefilter(line, False).rstrip())
209 ## except:
208 ## except:
210 ## # XXX: probably not the right thing to do.
209 ## # XXX: probably not the right thing to do.
211 ## self.ipython0.showsyntaxerror()
210 ## self.ipython0.showsyntaxerror()
212 ## self.after_execute()
211 ## self.after_execute()
213 ## finally:
212 ## finally:
214 ## self.release_output()
213 ## self.release_output()
215
214
216
215
217 try:
216 try:
218 try:
217 try:
219 for line in input_string.split('\n'):
218 for line in input_string.split('\n'):
220 filtered_lines.append(
219 filtered_lines.append(
221 self.ipython0.prefilter(line, False).rstrip())
220 self.ipython0.prefilter(line, False).rstrip())
222 except:
221 except:
223 # XXX: probably not the right thing to do.
222 # XXX: probably not the right thing to do.
224 self.ipython0.showsyntaxerror()
223 self.ipython0.showsyntaxerror()
225 self.after_execute()
224 self.after_execute()
226 finally:
225 finally:
227 self.release_output()
226 self.release_output()
228
227
229
228
230
229
231 # Clean up the trailing whitespace, to avoid indentation errors
230 # Clean up the trailing whitespace, to avoid indentation errors
232 filtered_string = '\n'.join(filtered_lines)
231 filtered_string = '\n'.join(filtered_lines)
233 return filtered_string
232 return filtered_string
234
233
235
234
236 #--------------------------------------------------------------------------
235 #--------------------------------------------------------------------------
237 # PrefilterFrontEnd interface
236 # PrefilterFrontEnd interface
238 #--------------------------------------------------------------------------
237 #--------------------------------------------------------------------------
239
238
240 def system_call(self, command_string):
239 def system_call(self, command_string):
241 """ Allows for frontend to define their own system call, to be
240 """ Allows for frontend to define their own system call, to be
242 able capture output and redirect input.
241 able capture output and redirect input.
243 """
242 """
244 return os.system(command_string)
243 return os.system(command_string)
245
244
246
245
247 def do_exit(self):
246 def do_exit(self):
248 """ Exit the shell, cleanup and save the history.
247 """ Exit the shell, cleanup and save the history.
249 """
248 """
250 self.ipython0.atexit_operations()
249 self.ipython0.atexit_operations()
251
250
@@ -1,436 +1,512 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 time
27 import sys
27 import sys
28 LINESEP = '\n'
28 LINESEP = '\n'
29 if sys.platform == 'win32':
29 if sys.platform == 'win32':
30 LINESEP = '\n\r'
30 LINESEP = '\n\r'
31
31
32 import re
32 import re
33
33
34 # 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
35 # screen: this should not be editable by the user.
35 # screen: this should not be editable by the user.
36 #-------------------------------------------------------------------------------
37 # Constants
38 #-------------------------------------------------------------------------------
39 _COMPLETE_BUFFER_MARKER = 31
40 _ERROR_MARKER = 30
41 _INPUT_MARKER = 29
36
42
37 _DEFAULT_SIZE = 10
43 _DEFAULT_SIZE = 10
38 if sys.platform == 'darwin':
44 if sys.platform == 'darwin':
39 _DEFAULT_SIZE = 12
45 _DEFAULT_SIZE = 12
40
46
41 _DEFAULT_STYLE = {
47 _DEFAULT_STYLE = {
42 'stdout' : 'fore:#0000FF',
48 'stdout' : 'fore:#0000FF',
43 'stderr' : 'fore:#007f00',
49 'stderr' : 'fore:#007f00',
44 'trace' : 'fore:#FF0000',
50 'trace' : 'fore:#FF0000',
45
51
46 'default' : 'size:%d' % _DEFAULT_SIZE,
52 'default' : 'size:%d' % _DEFAULT_SIZE,
47 'bracegood' : 'fore:#00AA00,back:#000000,bold',
53 'bracegood' : 'fore:#00AA00,back:#000000,bold',
48 'bracebad' : 'fore:#FF0000,back:#000000,bold',
54 'bracebad' : 'fore:#FF0000,back:#000000,bold',
49
55
50 # properties for the various Python lexer styles
56 # properties for the various Python lexer styles
51 'comment' : 'fore:#007F00',
57 'comment' : 'fore:#007F00',
52 'number' : 'fore:#007F7F',
58 'number' : 'fore:#007F7F',
53 'string' : 'fore:#7F007F,italic',
59 'string' : 'fore:#7F007F,italic',
54 'char' : 'fore:#7F007F,italic',
60 'char' : 'fore:#7F007F,italic',
55 'keyword' : 'fore:#00007F,bold',
61 'keyword' : 'fore:#00007F,bold',
56 'triple' : 'fore:#7F0000',
62 'triple' : 'fore:#7F0000',
57 'tripledouble' : 'fore:#7F0000',
63 'tripledouble' : 'fore:#7F0000',
58 'class' : 'fore:#0000FF,bold,underline',
64 'class' : 'fore:#0000FF,bold,underline',
59 'def' : 'fore:#007F7F,bold',
65 'def' : 'fore:#007F7F,bold',
60 'operator' : 'bold'
66 'operator' : 'bold'
61 }
67 }
62
68
63 # new style numbers
69 # new style numbers
64 _STDOUT_STYLE = 15
70 _STDOUT_STYLE = 15
65 _STDERR_STYLE = 16
71 _STDERR_STYLE = 16
66 _TRACE_STYLE = 17
72 _TRACE_STYLE = 17
67
73
68
74
69 # system colors
75 # system colors
70 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
76 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
71
77
72 #-------------------------------------------------------------------------------
78 #-------------------------------------------------------------------------------
73 # The console widget class
79 # The console widget class
74 #-------------------------------------------------------------------------------
80 #-------------------------------------------------------------------------------
75 class ConsoleWidget(editwindow.EditWindow):
81 class ConsoleWidget(editwindow.EditWindow):
76 """ Specialized styled text control view for console-like workflow.
82 """ Specialized styled text control view for console-like workflow.
77
83
78 This widget is mainly interested in dealing with the prompt and
84 This widget is mainly interested in dealing with the prompt and
79 keeping the cursor inside the editing line.
85 keeping the cursor inside the editing line.
80 """
86 """
81
87
82 # This is where the title captured from the ANSI escape sequences are
88 # This is where the title captured from the ANSI escape sequences are
83 # stored.
89 # stored.
84 title = 'Console'
90 title = 'Console'
85
91
86 # The buffer being edited.
92 # The buffer being edited.
87 def _set_input_buffer(self, string):
93 def _set_input_buffer(self, string):
88 self.SetSelection(self.current_prompt_pos, self.GetLength())
94 self.SetSelection(self.current_prompt_pos, self.GetLength())
89 self.ReplaceSelection(string)
95 self.ReplaceSelection(string)
90 self.GotoPos(self.GetLength())
96 self.GotoPos(self.GetLength())
91
97
92 def _get_input_buffer(self):
98 def _get_input_buffer(self):
93 """ Returns the text in current edit buffer.
99 """ Returns the text in current edit buffer.
94 """
100 """
95 input_buffer = self.GetTextRange(self.current_prompt_pos,
101 input_buffer = self.GetTextRange(self.current_prompt_pos,
96 self.GetLength())
102 self.GetLength())
97 input_buffer = input_buffer.replace(LINESEP, '\n')
103 input_buffer = input_buffer.replace(LINESEP, '\n')
98 return input_buffer
104 return input_buffer
99
105
100 input_buffer = property(_get_input_buffer, _set_input_buffer)
106 input_buffer = property(_get_input_buffer, _set_input_buffer)
101
107
102 style = _DEFAULT_STYLE.copy()
108 style = _DEFAULT_STYLE.copy()
103
109
104 # Translation table from ANSI escape sequences to color. Override
110 # Translation table from ANSI escape sequences to color. Override
105 # this to specify your colors.
111 # this to specify your colors.
106 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
112 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
107 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
113 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
108 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
114 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
109 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
115 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
110 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
116 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
111 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
117 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
112 '1;34': [12, 'LIGHT BLUE'], '1;35':
118 '1;34': [12, 'LIGHT BLUE'], '1;35': [13, 'MEDIUM VIOLET RED'],
113 [13, 'MEDIUM VIOLET RED'],
114 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
119 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
115
120
116 # The color of the carret (call _apply_style() after setting)
121 # The color of the carret (call _apply_style() after setting)
117 carret_color = 'BLACK'
122 carret_color = 'BLACK'
118
123
124 _COMPLETE_BUFFER_BG = '#FAFAF1' # Nice green
125 _INPUT_BUFFER_BG = '#FDFFD3' # Nice yellow
126 _ERROR_BG = '#FFF1F1' # Nice red
127
128 background_color = 'WHITE'
129
130 #we define platform specific fonts
131 if wx.Platform == '__WXMSW__':
132 faces = { 'times': 'Times New Roman',
133 'mono' : 'Courier New',
134 'helv' : 'Arial',
135 'other': 'Comic Sans MS',
136 'size' : 10,
137 'size2': 8,
138 }
139 elif wx.Platform == '__WXMAC__':
140 faces = { 'times': 'Times New Roman',
141 'mono' : 'Monaco',
142 'helv' : 'Arial',
143 'other': 'Comic Sans MS',
144 'size' : 10,
145 'size2': 8,
146 }
147 else:
148 faces = { 'times': 'Times',
149 'mono' : 'Courier',
150 'helv' : 'Helvetica',
151 'other': 'new century schoolbook',
152 'size' : 10,
153 'size2': 8,
154 }
155
119 # Store the last time a refresh was done
156 # Store the last time a refresh was done
120 _last_refresh_time = 0
157 _last_refresh_time = 0
121
158
122 #--------------------------------------------------------------------------
159 #--------------------------------------------------------------------------
123 # Public API
160 # Public API
124 #--------------------------------------------------------------------------
161 #--------------------------------------------------------------------------
125
162
126 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
163 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
127 size=wx.DefaultSize, style=wx.WANTS_CHARS, ):
164 size=wx.DefaultSize, style=wx.WANTS_CHARS, ):
128 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
165 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
129 self._configure_scintilla()
166 self._configure_scintilla()
167 self.enter_catched = False #this var track if 'enter' key as ever been processed
168 #thus it will only be reallowed until key goes up
169 self.current_prompt_pos = 0
130
170
131 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
171 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
132 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
172 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
133
173
134
174
135 def write(self, text, refresh=True):
175 def write(self, text, refresh=True):
136 """ Write given text to buffer, while translating the ansi escape
176 """ Write given text to buffer, while translating the ansi escape
137 sequences.
177 sequences.
138 """
178 """
139 # XXX: do not put print statements to sys.stdout/sys.stderr in
179 # XXX: do not put print statements to sys.stdout/sys.stderr in
140 # this method, the print statements will call this method, as
180 # this method, the print statements will call this method, as
141 # you will end up with an infinit loop
181 # you will end up with an infinit loop
142 title = self.title_pat.split(text)
182 title = self.title_pat.split(text)
143 if len(title)>1:
183 if len(title)>1:
144 self.title = title[-2]
184 self.title = title[-2]
145
185
146 text = self.title_pat.sub('', text)
186 text = self.title_pat.sub('', text)
147 segments = self.color_pat.split(text)
187 segments = self.color_pat.split(text)
148 segment = segments.pop(0)
188 segment = segments.pop(0)
149 self.GotoPos(self.GetLength())
189 self.GotoPos(self.GetLength())
150 self.StartStyling(self.GetLength(), 0xFF)
190 self.StartStyling(self.GetLength(), 0xFF)
151 try:
191 try:
152 self.AppendText(segment)
192 self.AppendText(segment)
153 except UnicodeDecodeError:
193 except UnicodeDecodeError:
154 # XXX: Do I really want to skip the exception?
194 # XXX: Do I really want to skip the exception?
155 pass
195 pass
156
196
157 if segments:
197 if segments:
158 for ansi_tag, text in zip(segments[::2], segments[1::2]):
198 for ansi_tag, text in zip(segments[::2], segments[1::2]):
159 self.StartStyling(self.GetLength(), 0xFF)
199 self.StartStyling(self.GetLength(), 0xFF)
160 try:
200 try:
161 self.AppendText(text)
201 self.AppendText(text)
162 except UnicodeDecodeError:
202 except UnicodeDecodeError:
163 # XXX: Do I really want to skip the exception?
203 # XXX: Do I really want to skip the exception?
164 pass
204 pass
165
205
166 if ansi_tag not in self.ANSI_STYLES:
206 if ansi_tag not in self.ANSI_STYLES:
167 style = 0
207 style = 0
168 else:
208 else:
169 style = self.ANSI_STYLES[ansi_tag][0]
209 style = self.ANSI_STYLES[ansi_tag][0]
170
210
171 self.SetStyling(len(text), style)
211 self.SetStyling(len(text), style)
172
212
173 self.GotoPos(self.GetLength())
213 self.GotoPos(self.GetLength())
174 if refresh:
214 if refresh:
175 current_time = time.time()
215 current_time = time.time()
176 if current_time - self._last_refresh_time > 0.03:
216 if current_time - self._last_refresh_time > 0.03:
177 if sys.platform == 'win32':
217 if sys.platform == 'win32':
178 wx.SafeYield()
218 wx.SafeYield()
179 else:
219 else:
180 wx.Yield()
220 wx.Yield()
181 # self.ProcessEvent(wx.PaintEvent())
221 # self.ProcessEvent(wx.PaintEvent())
182 self._last_refresh_time = current_time
222 self._last_refresh_time = current_time
183
223
184
224
185 def new_prompt(self, prompt):
225 def new_prompt(self, prompt):
186 """ Prints a prompt at start of line, and move the start of the
226 """ Prints a prompt at start of line, and move the start of the
187 current block there.
227 current block there.
188
228
189 The prompt can be given with ascii escape sequences.
229 The prompt can be given with ascii escape sequences.
190 """
230 """
191 self.write(prompt, refresh=False)
231 self.write(prompt, refresh=False)
192 # now we update our cursor giving end of prompt
232 # now we update our cursor giving end of prompt
193 self.current_prompt_pos = self.GetLength()
233 self.current_prompt_pos = self.GetLength()
194 self.current_prompt_line = self.GetCurrentLine()
234 self.current_prompt_line = self.GetCurrentLine()
195 self.EnsureCaretVisible()
235 self.EnsureCaretVisible()
196
236
197
237
198 def scroll_to_bottom(self):
238 def scroll_to_bottom(self):
199 maxrange = self.GetScrollRange(wx.VERTICAL)
239 maxrange = self.GetScrollRange(wx.VERTICAL)
200 self.ScrollLines(maxrange)
240 self.ScrollLines(maxrange)
201
241
202
242
203 def pop_completion(self, possibilities, offset=0):
243 def pop_completion(self, possibilities, offset=0):
204 """ Pops up an autocompletion menu. Offset is the offset
244 """ Pops up an autocompletion menu. Offset is the offset
205 in characters of the position at which the menu should
245 in characters of the position at which the menu should
206 appear, relativ to the cursor.
246 appear, relativ to the cursor.
207 """
247 """
208 self.AutoCompSetIgnoreCase(False)
248 self.AutoCompSetIgnoreCase(False)
209 self.AutoCompSetAutoHide(False)
249 self.AutoCompSetAutoHide(False)
210 self.AutoCompSetMaxHeight(len(possibilities))
250 self.AutoCompSetMaxHeight(len(possibilities))
211 self.AutoCompShow(offset, " ".join(possibilities))
251 self.AutoCompShow(offset, " ".join(possibilities))
212
252
213
253
214 def get_line_width(self):
254 def get_line_width(self):
215 """ Return the width of the line in characters.
255 """ Return the width of the line in characters.
216 """
256 """
217 return self.GetSize()[0]/self.GetCharWidth()
257 return self.GetSize()[0]/self.GetCharWidth()
218
258
219 #--------------------------------------------------------------------------
259 #--------------------------------------------------------------------------
220 # EditWindow API
260 # EditWindow API
221 #--------------------------------------------------------------------------
261 #--------------------------------------------------------------------------
222
262
223 def OnUpdateUI(self, event):
263 def OnUpdateUI(self, event):
224 """ Override the OnUpdateUI of the EditWindow class, to prevent
264 """ Override the OnUpdateUI of the EditWindow class, to prevent
225 syntax highlighting both for faster redraw, and for more
265 syntax highlighting both for faster redraw, and for more
226 consistent look and feel.
266 consistent look and feel.
227 """
267 """
228
268
229 #--------------------------------------------------------------------------
269 #--------------------------------------------------------------------------
230 # Private API
270 # Styling API
231 #--------------------------------------------------------------------------
271 #--------------------------------------------------------------------------
232
272
233 def _apply_style(self):
273 def set_new_style(self):
234 """ Applies the colors for the different text elements and the
274 """ call this method with new style and ansi_style to change colors of the console """
235 carret.
275 self._configure_scintilla()
236 """
237 self.SetCaretForeground(self.carret_color)
238
239 #self.StyleClearAll()
240 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
241 "fore:#FF0000,back:#0000FF,bold")
242 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
243 "fore:#000000,back:#FF0000,bold")
244
245 for style in self.ANSI_STYLES.values():
246 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
247
276
277 #--------------------------------------------------------------------------
278 # Private API
279 #--------------------------------------------------------------------------
248
280
249 def _configure_scintilla(self):
281 def _configure_scintilla(self):
282 # Marker for complete buffer.
283 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
284 background = self._COMPLETE_BUFFER_BG)
285 # Marker for current input buffer.
286 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
287 background = self._INPUT_BUFFER_BG)
288 # Marker for tracebacks.
289 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
290 background = self._ERROR_BG)
291
250 self.SetEOLMode(stc.STC_EOL_LF)
292 self.SetEOLMode(stc.STC_EOL_LF)
251
293
252 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
294 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
253 # the widget
295 # the widget
254 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
296 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
255 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
297 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
256 # Also allow Ctrl Shift "=" for poor non US keyboard users.
298 # Also allow Ctrl Shift "=" for poor non US keyboard users.
257 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
299 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
258 stc.STC_CMD_ZOOMIN)
300 stc.STC_CMD_ZOOMIN)
259
301
260 # Keys: we need to clear some of the keys the that don't play
302 # Keys: we need to clear some of the keys the that don't play
261 # well with a console.
303 # well with a console.
262 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
304 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
263 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
305 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
264 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
306 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
265 self.CmdKeyClear(ord('A'), stc.STC_SCMOD_CTRL)
307 self.CmdKeyClear(ord('A'), stc.STC_SCMOD_CTRL)
266
308
267 self.SetEOLMode(stc.STC_EOL_CRLF)
309 self.SetEOLMode(stc.STC_EOL_CRLF)
268 self.SetWrapMode(stc.STC_WRAP_CHAR)
310 self.SetWrapMode(stc.STC_WRAP_CHAR)
269 self.SetWrapMode(stc.STC_WRAP_WORD)
311 self.SetWrapMode(stc.STC_WRAP_WORD)
270 self.SetBufferedDraw(True)
312 self.SetBufferedDraw(True)
271 self.SetUseAntiAliasing(True)
313 self.SetUseAntiAliasing(True)
272 self.SetLayoutCache(stc.STC_CACHE_PAGE)
314 self.SetLayoutCache(stc.STC_CACHE_PAGE)
273 self.SetUndoCollection(False)
315 self.SetUndoCollection(False)
274 self.SetUseTabs(True)
316 self.SetUseTabs(True)
275 self.SetIndent(4)
317 self.SetIndent(4)
276 self.SetTabWidth(4)
318 self.SetTabWidth(4)
277
319
278 # we don't want scintilla's autocompletion to choose
320 # we don't want scintilla's autocompletion to choose
279 # automaticaly out of a single choice list, as we pop it up
321 # automaticaly out of a single choice list, as we pop it up
280 # automaticaly
322 # automaticaly
281 self.AutoCompSetChooseSingle(False)
323 self.AutoCompSetChooseSingle(False)
282 self.AutoCompSetMaxHeight(10)
324 self.AutoCompSetMaxHeight(10)
283 # XXX: this doesn't seem to have an effect.
325 # XXX: this doesn't seem to have an effect.
284 self.AutoCompSetFillUps('\n')
326 self.AutoCompSetFillUps('\n')
285
327
286 self.SetMargins(3, 3) #text is moved away from border with 3px
328 self.SetMargins(3, 3) #text is moved away from border with 3px
287 # Suppressing Scintilla margins
329 # Suppressing Scintilla margins
288 self.SetMarginWidth(0, 0)
330 self.SetMarginWidth(0, 0)
289 self.SetMarginWidth(1, 0)
331 self.SetMarginWidth(1, 0)
290 self.SetMarginWidth(2, 0)
332 self.SetMarginWidth(2, 0)
291
333
292 self._apply_style()
334 #self._apply_style()
335 self.SetCaretForeground(self.carret_color)
293
336
294 # Xterm escape sequences
337 # Xterm escape sequences
295 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
338 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
296 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
339 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
297
340
298 #self.SetEdgeMode(stc.STC_EDGE_LINE)
341 #self.SetEdgeMode(stc.STC_EDGE_LINE)
299 #self.SetEdgeColumn(80)
342 #self.SetEdgeColumn(80)
300
343
344
301 # styles
345 # styles
302 p = self.style
346 p = self.style
347
348 if 'default' in p:
303 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
349 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
350 else:
351 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "fore:%s,back:%s,size:%d,face:%s"
352 % (self.ANSI_STYLES['0;30'][1], self.background_color,
353 self.faces['size'], self.faces['mono']))
354
355 #all styles = default one
304 self.StyleClearAll()
356 self.StyleClearAll()
357
358 # XXX: two lines below are usefull if not using the lexer
359 #for style in self.ANSI_STYLES.values():
360 # self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
361
362 if 'stdout' in p:
305 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
363 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
364 if 'stderr' in p:
306 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
365 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
366 if 'trace' in p:
307 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
367 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
308
368 if 'bracegood' in p:
309 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
369 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
370 if 'bracebad' in p:
310 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
371 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
372 if 'comment' in p:
311 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
373 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
374 if 'number' in p:
312 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
375 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
376 if 'string' in p:
313 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
377 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
378 if 'char' in p:
314 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
379 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
380 if 'keyword' in p:
315 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
381 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
382 if 'keyword' in p:
316 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
383 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
384 if 'triple' in p:
317 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
385 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
386 if 'tripledouble' in p:
318 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
387 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
388 if 'class' in p:
319 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
389 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
390 if 'def' in p:
320 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
391 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
392 if 'operator' in p:
321 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
393 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
394 if 'comment' in p:
322 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
395 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
323
396
324 def _on_key_down(self, event, skip=True):
397 def _on_key_down(self, event, skip=True):
325 """ Key press callback used for correcting behavior for
398 """ Key press callback used for correcting behavior for
326 console-like interfaces: the cursor is constraint to be after
399 console-like interfaces: the cursor is constraint to be after
327 the last prompt.
400 the last prompt.
328
401
329 Return True if event as been catched.
402 Return True if event as been catched.
330 """
403 """
331 catched = True
404 catched = True
332 # Intercept some specific keys.
405 # Intercept some specific keys.
333 if event.KeyCode == ord('L') and event.ControlDown() :
406 if event.KeyCode == ord('L') and event.ControlDown() :
334 self.scroll_to_bottom()
407 self.scroll_to_bottom()
335 elif event.KeyCode == ord('K') and event.ControlDown() :
408 elif event.KeyCode == ord('K') and event.ControlDown() :
336 self.input_buffer = ''
409 self.input_buffer = ''
337 elif event.KeyCode == ord('A') and event.ControlDown() :
410 elif event.KeyCode == ord('A') and event.ControlDown() :
338 self.GotoPos(self.GetLength())
411 self.GotoPos(self.GetLength())
339 self.SetSelectionStart(self.current_prompt_pos)
412 self.SetSelectionStart(self.current_prompt_pos)
340 self.SetSelectionEnd(self.GetCurrentPos())
413 self.SetSelectionEnd(self.GetCurrentPos())
341 catched = True
414 catched = True
342 elif event.KeyCode == ord('E') and event.ControlDown() :
415 elif event.KeyCode == ord('E') and event.ControlDown() :
343 self.GotoPos(self.GetLength())
416 self.GotoPos(self.GetLength())
344 catched = True
417 catched = True
345 elif event.KeyCode == wx.WXK_PAGEUP:
418 elif event.KeyCode == wx.WXK_PAGEUP:
346 self.ScrollPages(-1)
419 self.ScrollPages(-1)
347 elif event.KeyCode == wx.WXK_PAGEDOWN:
420 elif event.KeyCode == wx.WXK_PAGEDOWN:
348 self.ScrollPages(1)
421 self.ScrollPages(1)
349 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
422 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
350 self.ScrollLines(-1)
423 self.ScrollLines(-1)
351 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
424 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
352 self.ScrollLines(1)
425 self.ScrollLines(1)
353 else:
426 else:
354 catched = False
427 catched = False
355
428
356 if self.AutoCompActive():
429 if self.AutoCompActive():
357 event.Skip()
430 event.Skip()
358 else:
431 else:
359 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
432 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
360 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
433 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
361 catched = True
434 catched = True
435 if(not self.enter_catched):
362 self.CallTipCancel()
436 self.CallTipCancel()
363 self.write('\n', refresh=False)
437 self.write('\n', refresh=False)
364 # Under windows scintilla seems to be doing funny stuff to the
438 # Under windows scintilla seems to be doing funny stuff to the
365 # line returns here, but the getter for input_buffer filters
439 # line returns here, but the getter for input_buffer filters
366 # this out.
440 # this out.
367 if sys.platform == 'win32':
441 if sys.platform == 'win32':
368 self.input_buffer = self.input_buffer
442 self.input_buffer = self.input_buffer
369 self._on_enter()
443 self._on_enter()
444 self.enter_catched = True
370
445
371 elif event.KeyCode == wx.WXK_HOME:
446 elif event.KeyCode == wx.WXK_HOME:
372 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
447 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
373 self.GotoPos(self.current_prompt_pos)
448 self.GotoPos(self.current_prompt_pos)
374 catched = True
449 catched = True
375
450
376 elif event.Modifiers == wx.MOD_SHIFT:
451 elif event.Modifiers == wx.MOD_SHIFT:
377 # FIXME: This behavior is not ideal: if the selection
452 # FIXME: This behavior is not ideal: if the selection
378 # is already started, it will jump.
453 # is already started, it will jump.
379 self.SetSelectionStart(self.current_prompt_pos)
454 self.SetSelectionStart(self.current_prompt_pos)
380 self.SetSelectionEnd(self.GetCurrentPos())
455 self.SetSelectionEnd(self.GetCurrentPos())
381 catched = True
456 catched = True
382
457
383 elif event.KeyCode == wx.WXK_UP:
458 elif event.KeyCode == wx.WXK_UP:
384 if self.GetCurrentLine() > self.current_prompt_line:
459 if self.GetCurrentLine() > self.current_prompt_line:
385 if self.GetCurrentLine() == self.current_prompt_line + 1 \
460 if self.GetCurrentLine() == self.current_prompt_line + 1 \
386 and self.GetColumn(self.GetCurrentPos()) < \
461 and self.GetColumn(self.GetCurrentPos()) < \
387 self.GetColumn(self.current_prompt_pos):
462 self.GetColumn(self.current_prompt_pos):
388 self.GotoPos(self.current_prompt_pos)
463 self.GotoPos(self.current_prompt_pos)
389 else:
464 else:
390 event.Skip()
465 event.Skip()
391 catched = True
466 catched = True
392
467
393 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
468 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
394 if self.GetCurrentPos() > self.current_prompt_pos:
469 if self.GetCurrentPos() > self.current_prompt_pos:
395 event.Skip()
470 event.Skip()
396 catched = True
471 catched = True
397
472
398 if skip and not catched:
473 if skip and not catched:
399 # Put the cursor back in the edit region
474 # Put the cursor back in the edit region
400 if self.GetCurrentPos() < self.current_prompt_pos:
475 if self.GetCurrentPos() < self.current_prompt_pos:
401 self.GotoPos(self.current_prompt_pos)
476 self.GotoPos(self.current_prompt_pos)
402 else:
477 else:
403 event.Skip()
478 event.Skip()
404
479
405 return catched
480 return catched
406
481
407
482
408 def _on_key_up(self, event, skip=True):
483 def _on_key_up(self, event, skip=True):
409 """ If cursor is outside the editing region, put it back.
484 """ If cursor is outside the editing region, put it back.
410 """
485 """
411 event.Skip()
486 event.Skip()
412 if self.GetCurrentPos() < self.current_prompt_pos:
487 if self.GetCurrentPos() < self.current_prompt_pos:
413 self.GotoPos(self.current_prompt_pos)
488 self.GotoPos(self.current_prompt_pos)
414
489
490 self.enter_catched = False #we re-allow enter event processing
415
491
416
492
417 if __name__ == '__main__':
493 if __name__ == '__main__':
418 # Some simple code to test the console widget.
494 # Some simple code to test the console widget.
419 class MainWindow(wx.Frame):
495 class MainWindow(wx.Frame):
420 def __init__(self, parent, id, title):
496 def __init__(self, parent, id, title):
421 wx.Frame.__init__(self, parent, id, title, size=(300,250))
497 wx.Frame.__init__(self, parent, id, title, size=(300,250))
422 self._sizer = wx.BoxSizer(wx.VERTICAL)
498 self._sizer = wx.BoxSizer(wx.VERTICAL)
423 self.console_widget = ConsoleWidget(self)
499 self.console_widget = ConsoleWidget(self)
424 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
500 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
425 self.SetSizer(self._sizer)
501 self.SetSizer(self._sizer)
426 self.SetAutoLayout(1)
502 self.SetAutoLayout(1)
427 self.Show(True)
503 self.Show(True)
428
504
429 app = wx.PySimpleApp()
505 app = wx.PySimpleApp()
430 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
506 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
431 w.SetSize((780, 460))
507 w.SetSize((780, 460))
432 w.Show()
508 w.Show()
433
509
434 app.MainLoop()
510 app.MainLoop()
435
511
436
512
@@ -1,119 +1,175 b''
1 """
1 """
2 Entry point for a simple application giving a graphical frontend to
2 Entry point for a simple application giving a graphical frontend to
3 ipython.
3 ipython.
4 """
4 """
5
5
6 try:
6 try:
7 import wx
7 import wx
8 except ImportError, e:
8 except ImportError, e:
9 e.message = """%s
9 e.message = """%s
10 ________________________________________________________________________________
10 ________________________________________________________________________________
11 You need wxPython to run this application.
11 You need wxPython to run this application.
12 """ % e.message
12 """ % e.message
13 e.args = (e.message, ) + e.args[1:]
13 e.args = (e.message, ) + e.args[1:]
14 raise e
14 raise e
15
15
16 import wx.stc as stc
17
16 from wx_frontend import WxController
18 from wx_frontend import WxController
17 import __builtin__
19 import __builtin__
18
20
19
21
20 class IPythonXController(WxController):
22 class IPythonXController(WxController):
21 """ Sub class of WxController that adds some application-specific
23 """ Sub class of WxController that adds some application-specific
22 bindings.
24 bindings.
23 """
25 """
24
26
25 debug = False
27 debug = False
26
28
27 def __init__(self, *args, **kwargs):
29 def __init__(self, *args, **kwargs):
30
31 if kwargs['colorset'] == 'black':
32 self.prompt_in1 = \
33 '\n\x01\x1b[0;30m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;30m\x02]: \x01\x1b[0m\x02'
34
35 self.prompt_out = \
36 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
37
28 WxController.__init__(self, *args, **kwargs)
38 WxController.__init__(self, *args, **kwargs)
29 self.ipython0.ask_exit = self.do_exit
39 self.ipython0.ask_exit = self.do_exit
40
41 if kwargs['colorset'] == 'black':
42
43 self.carret_color = 'WHITE'
44 self.background_color = 'BLACK'
45
46 self.SetEdgeMode(stc.STC_EDGE_LINE)
47 self.SetEdgeColumn(88)
48
49 self.style = {
50 #'stdout' : '',#fore:#0000FF',
51 #'stderr' : '',#fore:#007f00',
52 #'trace' : '',#fore:#FF0000',
53
54 #'bracegood' : 'fore:#0000FF,back:#0000FF,bold',
55 #'bracebad' : 'fore:#FF0000,back:#0000FF,bold',
56 'default' : "fore:%s,back:%s,size:%d,face:%s,bold"
57 % ("#EEEEEE", self.background_color,
58 self.faces['size'], self.faces['mono']),
59
60 # properties for the various Python lexer styles
61 'comment' : 'fore:#BBBBBB,italic',
62 'number' : 'fore:#FF9692',
63 'string' : 'fore:#ed9d13,italic',
64 'char' : 'fore:#FFFFFF,italic',
65 'keyword' : 'fore:#6AB825,bold',
66 'triple' : 'fore:#FF7BDD',
67 'tripledouble' : 'fore:#FF7BDD',
68 'class' : 'fore:#FF00FF,bold,underline',
69 'def' : 'fore:#FFFF00,bold',
70 'operator' : 'bold'
71 }
72
73 #we define the background of old inputs
74 self._COMPLETE_BUFFER_BG = '#000000' # RRGGBB: Black
75 #we define the background of current input
76 self._INPUT_BUFFER_BG = '#444444' # RRGGBB: Light black
77 #we define the background when an error is reported
78 self._ERROR_BG = '#800000' #'#d22323' #'#AE0021' # RRGGBB: Black
79
80 self.set_new_style()
81
30 # Scroll to top
82 # Scroll to top
31 maxrange = self.GetScrollRange(wx.VERTICAL)
83 maxrange = self.GetScrollRange(wx.VERTICAL)
32 self.ScrollLines(-maxrange)
84 self.ScrollLines(-maxrange)
33
85
34
86
35 def _on_key_down(self, event, skip=True):
87 def _on_key_down(self, event, skip=True):
36 # Intercept Ctrl-D to quit
88 # Intercept Ctrl-D to quit
37 if event.KeyCode == ord('D') and event.ControlDown() and \
89 if event.KeyCode == ord('D') and event.ControlDown() and \
38 self.input_buffer == '' and \
90 self.input_buffer == '' and \
39 self._input_state == 'readline':
91 self._input_state == 'readline':
40 wx.CallAfter(self.ask_exit)
92 wx.CallAfter(self.ask_exit)
41 else:
93 else:
42 WxController._on_key_down(self, event, skip=skip)
94 WxController._on_key_down(self, event, skip=skip)
43
95
44
96
45 def ask_exit(self):
97 def ask_exit(self):
46 """ Ask the user whether to exit.
98 """ Ask the user whether to exit.
47 """
99 """
48 self._input_state = 'subprocess'
100 self._input_state = 'subprocess'
49 self.write('\n', refresh=False)
101 self.write('\n', refresh=False)
50 self.capture_output()
102 self.capture_output()
51 self.ipython0.shell.exit()
103 self.ipython0.shell.exit()
52 self.release_output()
104 self.release_output()
53 if not self.ipython0.exit_now:
105 if not self.ipython0.exit_now:
54 wx.CallAfter(self.new_prompt,
106 wx.CallAfter(self.new_prompt,
55 self.input_prompt_template.substitute(
107 self.input_prompt_template.substitute(
56 number=self.last_result['number'] + 1))
108 number=self.last_result['number'] + 1))
57 else:
109 else:
58 wx.CallAfter(wx.GetApp().Exit)
110 wx.CallAfter(wx.GetApp().Exit)
59 self.write('Exiting ...', refresh=False)
111 self.write('Exiting ...', refresh=False)
60
112
61
113
62 def do_exit(self):
114 def do_exit(self):
63 """ Exits the interpreter, kills the windows.
115 """ Exits the interpreter, kills the windows.
64 """
116 """
65 WxController.do_exit(self)
117 WxController.do_exit(self)
66 self.release_output()
118 self.release_output()
67 wx.CallAfter(wx.Exit)
119 wx.CallAfter(wx.Exit)
68
120
69
121
70
122
71 class IPythonX(wx.Frame):
123 class IPythonX(wx.Frame):
72 """ Main frame of the IPythonX app.
124 """ Main frame of the IPythonX app.
73 """
125 """
74
126
75 def __init__(self, parent, id, title, debug=False):
127 def __init__(self, parent, id, title, debug=False, colorset='white'):
76 wx.Frame.__init__(self, parent, id, title, size=(300,250))
128 wx.Frame.__init__(self, parent, id, title, size=(300,250))
77 self._sizer = wx.BoxSizer(wx.VERTICAL)
129 self._sizer = wx.BoxSizer(wx.VERTICAL)
78 self.shell = IPythonXController(self, debug=debug)
130 self.shell = IPythonXController(self, debug=debug, colorset=colorset)
79 self._sizer.Add(self.shell, 1, wx.EXPAND)
131 self._sizer.Add(self.shell, 1, wx.EXPAND)
80 self.SetSizer(self._sizer)
132 self.SetSizer(self._sizer)
81 self.SetAutoLayout(1)
133 self.SetAutoLayout(1)
82 self.Show(True)
134 self.Show(True)
83 wx.EVT_CLOSE(self, self.on_close)
135 wx.EVT_CLOSE(self, self.on_close)
84
136
85
137
86 def on_close(self, event):
138 def on_close(self, event):
87 """ Called on closing the windows.
139 """ Called on closing the windows.
88
140
89 Stops the event loop, to close all the child windows.
141 Stops the event loop, to close all the child windows.
90 """
142 """
91 wx.CallAfter(wx.Exit)
143 wx.CallAfter(wx.Exit)
92
144
93
145
94 def main():
146 def main():
95 from optparse import OptionParser
147 from optparse import OptionParser
96 usage = """usage: %prog [options]
148 usage = """usage: %prog [options]
97
149
98 Simple graphical frontend to IPython, using WxWidgets."""
150 Simple graphical frontend to IPython, using WxWidgets."""
99 parser = OptionParser(usage=usage)
151 parser = OptionParser(usage=usage)
100 parser.add_option("-d", "--debug",
152 parser.add_option("-d", "--debug",
101 action="store_true", dest="debug", default=False,
153 action="store_true", dest="debug", default=False,
102 help="Enable debug message for the wx frontend.")
154 help="Enable debug message for the wx frontend.")
103
155
156 parser.add_option("-s", "--style",
157 dest="colorset", default="white",
158 help="style: white, black")
159
104 options, args = parser.parse_args()
160 options, args = parser.parse_args()
105
161
106 # Clear the options, to avoid having the ipython0 instance complain
162 # Clear the options, to avoid having the ipython0 instance complain
107 import sys
163 import sys
108 sys.argv = sys.argv[:1]
164 sys.argv = sys.argv[:1]
109
165
110 app = wx.PySimpleApp()
166 app = wx.PySimpleApp()
111 frame = IPythonX(None, wx.ID_ANY, 'IPythonX', debug=options.debug)
167 frame = IPythonX(None, wx.ID_ANY, 'IPythonX', debug=options.debug, colorset=options.colorset)
112 frame.shell.SetFocus()
168 frame.shell.SetFocus()
113 frame.shell.app = app
169 frame.shell.app = app
114 frame.SetSize((680, 460))
170 frame.SetSize((680, 460))
115
171
116 app.MainLoop()
172 app.MainLoop()
117
173
118 if __name__ == '__main__':
174 if __name__ == '__main__':
119 main()
175 main()
@@ -1,558 +1,546 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.wx.tests.test_wx_frontend -*-
3 # ipython1.frontend.wx.tests.test_wx_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 This class inherits from ConsoleWidget, that provides a console-like
8 This class inherits from ConsoleWidget, that provides a console-like
9 widget to provide a text-rendering widget suitable for a terminal.
9 widget to provide a text-rendering widget suitable for a terminal.
10 """
10 """
11
11
12 __docformat__ = "restructuredtext en"
12 __docformat__ = "restructuredtext en"
13
13
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15 # Copyright (C) 2008 The IPython Development Team
15 # Copyright (C) 2008 The IPython Development Team
16 #
16 #
17 # Distributed under the terms of the BSD License. The full license is in
17 # Distributed under the terms of the BSD License. The full license is in
18 # the file COPYING, distributed as part of this software.
18 # the file COPYING, distributed as part of this software.
19 #-------------------------------------------------------------------------------
19 #-------------------------------------------------------------------------------
20
20
21 #-------------------------------------------------------------------------------
21 #-------------------------------------------------------------------------------
22 # Imports
22 # Imports
23 #-------------------------------------------------------------------------------
23 #-------------------------------------------------------------------------------
24
24
25 # Major library imports
25 # Major library imports
26 import re
26 import re
27 import __builtin__
27 import __builtin__
28 import sys
28 import sys
29 from threading import Lock
29 from threading import Lock
30 import string
30 import string
31
31
32 import wx
32 import wx
33 from wx import stc
33 from wx import stc
34
34
35 # Ipython-specific imports.
35 # Ipython-specific imports.
36 from IPython.frontend._process import PipedProcess
36 from IPython.frontend._process import PipedProcess
37 from console_widget import ConsoleWidget
37 from console_widget import ConsoleWidget
38 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
38 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
39
39
40 #-------------------------------------------------------------------------------
40 #-------------------------------------------------------------------------------
41 # Constants
41 # Constants
42 #-------------------------------------------------------------------------------
42 #-------------------------------------------------------------------------------
43
43
44 _COMPLETE_BUFFER_BG = '#FAFAF1' # Nice green
45 _INPUT_BUFFER_BG = '#FDFFD3' # Nice yellow
46 _ERROR_BG = '#FFF1F1' # Nice red
47
48 _COMPLETE_BUFFER_MARKER = 31
44 _COMPLETE_BUFFER_MARKER = 31
49 _ERROR_MARKER = 30
45 _ERROR_MARKER = 30
50 _INPUT_MARKER = 29
46 _INPUT_MARKER = 29
51
47
52 prompt_in1 = \
53 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
54
55 prompt_out = \
56 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
57
58 #-------------------------------------------------------------------------------
48 #-------------------------------------------------------------------------------
59 # Classes to implement the Wx frontend
49 # Classes to implement the Wx frontend
60 #-------------------------------------------------------------------------------
50 #-------------------------------------------------------------------------------
61 class WxController(ConsoleWidget, PrefilterFrontEnd):
51 class WxController(ConsoleWidget, PrefilterFrontEnd):
62 """Classes to provide a Wx frontend to the
52 """Classes to provide a Wx frontend to the
63 IPython.kernel.core.interpreter.
53 IPython.kernel.core.interpreter.
64
54
65 This class inherits from ConsoleWidget, that provides a console-like
55 This class inherits from ConsoleWidget, that provides a console-like
66 widget to provide a text-rendering widget suitable for a terminal.
56 widget to provide a text-rendering widget suitable for a terminal.
67 """
57 """
58 prompt_in1 = \
59 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
68
60
69 output_prompt_template = string.Template(prompt_out)
61 prompt_out = \
70
62 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
71 input_prompt_template = string.Template(prompt_in1)
72
63
73 # Print debug info on what is happening to the console.
64 # Print debug info on what is happening to the console.
74 debug = False
65 debug = False
75
66
76 # The title of the terminal, as captured through the ANSI escape
67 # The title of the terminal, as captured through the ANSI escape
77 # sequences.
68 # sequences.
78 def _set_title(self, title):
69 def _set_title(self, title):
79 return self.Parent.SetTitle(title)
70 return self.Parent.SetTitle(title)
80
71
81 def _get_title(self):
72 def _get_title(self):
82 return self.Parent.GetTitle()
73 return self.Parent.GetTitle()
83
74
84 title = property(_get_title, _set_title)
75 title = property(_get_title, _set_title)
85
76
86
77
87 # The buffer being edited.
78 # The buffer being edited.
88 # We are duplicating the definition here because of multiple
79 # We are duplicating the definition here because of multiple
89 # inheritence
80 # inheritence
90 def _set_input_buffer(self, string):
81 def _set_input_buffer(self, string):
91 ConsoleWidget._set_input_buffer(self, string)
82 ConsoleWidget._set_input_buffer(self, string)
92 self._colorize_input_buffer()
83 self._colorize_input_buffer()
93
84
94 def _get_input_buffer(self):
85 def _get_input_buffer(self):
95 """ Returns the text in current edit buffer.
86 """ Returns the text in current edit buffer.
96 """
87 """
97 return ConsoleWidget._get_input_buffer(self)
88 return ConsoleWidget._get_input_buffer(self)
98
89
99 input_buffer = property(_get_input_buffer, _set_input_buffer)
90 input_buffer = property(_get_input_buffer, _set_input_buffer)
100
91
101
92
102 #--------------------------------------------------------------------------
93 #--------------------------------------------------------------------------
103 # Private Attributes
94 # Private Attributes
104 #--------------------------------------------------------------------------
95 #--------------------------------------------------------------------------
105
96
106 # A flag governing the behavior of the input. Can be:
97 # A flag governing the behavior of the input. Can be:
107 #
98 #
108 # 'readline' for readline-like behavior with a prompt
99 # 'readline' for readline-like behavior with a prompt
109 # and an edit buffer.
100 # and an edit buffer.
110 # 'raw_input' similar to readline, but triggered by a raw-input
101 # 'raw_input' similar to readline, but triggered by a raw-input
111 # call. Can be used by subclasses to act differently.
102 # call. Can be used by subclasses to act differently.
112 # 'subprocess' for sending the raw input directly to a
103 # 'subprocess' for sending the raw input directly to a
113 # subprocess.
104 # subprocess.
114 # 'buffering' for buffering of the input, that will be used
105 # 'buffering' for buffering of the input, that will be used
115 # when the input state switches back to another state.
106 # when the input state switches back to another state.
116 _input_state = 'readline'
107 _input_state = 'readline'
117
108
118 # Attribute to store reference to the pipes of a subprocess, if we
109 # Attribute to store reference to the pipes of a subprocess, if we
119 # are running any.
110 # are running any.
120 _running_process = False
111 _running_process = False
121
112
122 # A queue for writing fast streams to the screen without flooding the
113 # A queue for writing fast streams to the screen without flooding the
123 # event loop
114 # event loop
124 _out_buffer = []
115 _out_buffer = []
125
116
126 # A lock to lock the _out_buffer to make sure we don't empty it
117 # A lock to lock the _out_buffer to make sure we don't empty it
127 # while it is being swapped
118 # while it is being swapped
128 _out_buffer_lock = Lock()
119 _out_buffer_lock = Lock()
129
120
130 # The different line markers used to higlight the prompts.
121 # The different line markers used to higlight the prompts.
131 _markers = dict()
122 _markers = dict()
132
123
133 #--------------------------------------------------------------------------
124 #--------------------------------------------------------------------------
134 # Public API
125 # Public API
135 #--------------------------------------------------------------------------
126 #--------------------------------------------------------------------------
136
127
137 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
128 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
138 size=wx.DefaultSize,
129 size=wx.DefaultSize,
139 style=wx.CLIP_CHILDREN|wx.WANTS_CHARS,
130 style=wx.CLIP_CHILDREN|wx.WANTS_CHARS,
140 *args, **kwds):
131 *args, **kwds):
141 """ Create Shell instance.
132 """ Create Shell instance.
142 """
133 """
134 self.load_prompt()
135
143 ConsoleWidget.__init__(self, parent, id, pos, size, style)
136 ConsoleWidget.__init__(self, parent, id, pos, size, style)
144 PrefilterFrontEnd.__init__(self, **kwds)
137 PrefilterFrontEnd.__init__(self, **kwds)
145
138
146 # Stick in our own raw_input:
139 # Stick in our own raw_input:
147 self.ipython0.raw_input = self.raw_input
140 self.ipython0.raw_input = self.raw_input
148
141
149 # Marker for complete buffer.
150 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
151 background=_COMPLETE_BUFFER_BG)
152 # Marker for current input buffer.
153 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
154 background=_INPUT_BUFFER_BG)
155 # Marker for tracebacks.
156 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
157 background=_ERROR_BG)
158
159 # A time for flushing the write buffer
142 # A time for flushing the write buffer
160 BUFFER_FLUSH_TIMER_ID = 100
143 BUFFER_FLUSH_TIMER_ID = 100
161 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
144 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
162 wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)
145 wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)
163
146
164 if 'debug' in kwds:
147 if 'debug' in kwds:
165 self.debug = kwds['debug']
148 self.debug = kwds['debug']
166 kwds.pop('debug')
149 kwds.pop('debug')
167
150
168 # Inject self in namespace, for debug
151 # Inject self in namespace, for debug
169 if self.debug:
152 if self.debug:
170 self.shell.user_ns['self'] = self
153 self.shell.user_ns['self'] = self
171 # Inject our own raw_input in namespace
154 # Inject our own raw_input in namespace
172 self.shell.user_ns['raw_input'] = self.raw_input
155 self.shell.user_ns['raw_input'] = self.raw_input
173
156
157 def load_prompt(self):
158 self.output_prompt_template = string.Template(self.prompt_out)
159
160 self.input_prompt_template = string.Template(self.prompt_in1)
161
174
162
175 def raw_input(self, prompt=''):
163 def raw_input(self, prompt=''):
176 """ A replacement from python's raw_input.
164 """ A replacement from python's raw_input.
177 """
165 """
178 self.new_prompt(prompt)
166 self.new_prompt(prompt)
179 self._input_state = 'raw_input'
167 self._input_state = 'raw_input'
180 if hasattr(self, '_cursor'):
168 if hasattr(self, '_cursor'):
181 del self._cursor
169 del self._cursor
182 self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
170 self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
183 self.__old_on_enter = self._on_enter
171 self.__old_on_enter = self._on_enter
184 event_loop = wx.EventLoop()
172 event_loop = wx.EventLoop()
185 def my_on_enter():
173 def my_on_enter():
186 event_loop.Exit()
174 event_loop.Exit()
187 self._on_enter = my_on_enter
175 self._on_enter = my_on_enter
188 # XXX: Running a separate event_loop. Ugly.
176 # XXX: Running a separate event_loop. Ugly.
189 event_loop.Run()
177 event_loop.Run()
190 self._on_enter = self.__old_on_enter
178 self._on_enter = self.__old_on_enter
191 self._input_state = 'buffering'
179 self._input_state = 'buffering'
192 self._cursor = wx.BusyCursor()
180 self._cursor = wx.BusyCursor()
193 return self.input_buffer.rstrip('\n')
181 return self.input_buffer.rstrip('\n')
194
182
195
183
196 def system_call(self, command_string):
184 def system_call(self, command_string):
197 self._input_state = 'subprocess'
185 self._input_state = 'subprocess'
198 event_loop = wx.EventLoop()
186 event_loop = wx.EventLoop()
199 def _end_system_call():
187 def _end_system_call():
200 self._input_state = 'buffering'
188 self._input_state = 'buffering'
201 self._running_process = False
189 self._running_process = False
202 event_loop.Exit()
190 event_loop.Exit()
203
191
204 self._running_process = PipedProcess(command_string,
192 self._running_process = PipedProcess(command_string,
205 out_callback=self.buffered_write,
193 out_callback=self.buffered_write,
206 end_callback = _end_system_call)
194 end_callback = _end_system_call)
207 self._running_process.start()
195 self._running_process.start()
208 # XXX: Running a separate event_loop. Ugly.
196 # XXX: Running a separate event_loop. Ugly.
209 event_loop.Run()
197 event_loop.Run()
210 # Be sure to flush the buffer.
198 # Be sure to flush the buffer.
211 self._buffer_flush(event=None)
199 self._buffer_flush(event=None)
212
200
213
201
214 def do_calltip(self):
202 def do_calltip(self):
215 """ Analyse current and displays useful calltip for it.
203 """ Analyse current and displays useful calltip for it.
216 """
204 """
217 if self.debug:
205 if self.debug:
218 print >>sys.__stdout__, "do_calltip"
206 print >>sys.__stdout__, "do_calltip"
219 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
207 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
220 symbol = self.input_buffer
208 symbol = self.input_buffer
221 symbol_string = separators.split(symbol)[-1]
209 symbol_string = separators.split(symbol)[-1]
222 base_symbol_string = symbol_string.split('.')[0]
210 base_symbol_string = symbol_string.split('.')[0]
223 if base_symbol_string in self.shell.user_ns:
211 if base_symbol_string in self.shell.user_ns:
224 symbol = self.shell.user_ns[base_symbol_string]
212 symbol = self.shell.user_ns[base_symbol_string]
225 elif base_symbol_string in self.shell.user_global_ns:
213 elif base_symbol_string in self.shell.user_global_ns:
226 symbol = self.shell.user_global_ns[base_symbol_string]
214 symbol = self.shell.user_global_ns[base_symbol_string]
227 elif base_symbol_string in __builtin__.__dict__:
215 elif base_symbol_string in __builtin__.__dict__:
228 symbol = __builtin__.__dict__[base_symbol_string]
216 symbol = __builtin__.__dict__[base_symbol_string]
229 else:
217 else:
230 return False
218 return False
231 try:
219 try:
232 for name in symbol_string.split('.')[1:] + ['__doc__']:
220 for name in symbol_string.split('.')[1:] + ['__doc__']:
233 symbol = getattr(symbol, name)
221 symbol = getattr(symbol, name)
234 self.AutoCompCancel()
222 self.AutoCompCancel()
235 # Check that the symbol can indeed be converted to a string:
223 # Check that the symbol can indeed be converted to a string:
236 symbol += ''
224 symbol += ''
237 wx.CallAfter(self.CallTipShow, self.GetCurrentPos(), symbol)
225 wx.CallAfter(self.CallTipShow, self.GetCurrentPos(), symbol)
238 except:
226 except:
239 # The retrieve symbol couldn't be converted to a string
227 # The retrieve symbol couldn't be converted to a string
240 pass
228 pass
241
229
242
230
243 def _popup_completion(self, create=False):
231 def _popup_completion(self, create=False):
244 """ Updates the popup completion menu if it exists. If create is
232 """ Updates the popup completion menu if it exists. If create is
245 true, open the menu.
233 true, open the menu.
246 """
234 """
247 if self.debug:
235 if self.debug:
248 print >>sys.__stdout__, "_popup_completion"
236 print >>sys.__stdout__, "_popup_completion"
249 line = self.input_buffer
237 line = self.input_buffer
250 if (self.AutoCompActive() and line and not line[-1] == '.') \
238 if (self.AutoCompActive() and line and not line[-1] == '.') \
251 or create==True:
239 or create==True:
252 suggestion, completions = self.complete(line)
240 suggestion, completions = self.complete(line)
253 offset=0
241 offset=0
254 if completions:
242 if completions:
255 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
243 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
256 residual = complete_sep.split(line)[-1]
244 residual = complete_sep.split(line)[-1]
257 offset = len(residual)
245 offset = len(residual)
258 self.pop_completion(completions, offset=offset)
246 self.pop_completion(completions, offset=offset)
259 if self.debug:
247 if self.debug:
260 print >>sys.__stdout__, completions
248 print >>sys.__stdout__, completions
261
249
262
250
263 def buffered_write(self, text):
251 def buffered_write(self, text):
264 """ A write method for streams, that caches the stream in order
252 """ A write method for streams, that caches the stream in order
265 to avoid flooding the event loop.
253 to avoid flooding the event loop.
266
254
267 This can be called outside of the main loop, in separate
255 This can be called outside of the main loop, in separate
268 threads.
256 threads.
269 """
257 """
270 self._out_buffer_lock.acquire()
258 self._out_buffer_lock.acquire()
271 self._out_buffer.append(text)
259 self._out_buffer.append(text)
272 self._out_buffer_lock.release()
260 self._out_buffer_lock.release()
273 if not self._buffer_flush_timer.IsRunning():
261 if not self._buffer_flush_timer.IsRunning():
274 wx.CallAfter(self._buffer_flush_timer.Start,
262 wx.CallAfter(self._buffer_flush_timer.Start,
275 milliseconds=100, oneShot=True)
263 milliseconds=100, oneShot=True)
276
264
277
265
278 #--------------------------------------------------------------------------
266 #--------------------------------------------------------------------------
279 # LineFrontEnd interface
267 # LineFrontEnd interface
280 #--------------------------------------------------------------------------
268 #--------------------------------------------------------------------------
281
269
282 def execute(self, python_string, raw_string=None):
270 def execute(self, python_string, raw_string=None):
283 self._input_state = 'buffering'
271 self._input_state = 'buffering'
284 self.CallTipCancel()
272 self.CallTipCancel()
285 self._cursor = wx.BusyCursor()
273 self._cursor = wx.BusyCursor()
286 if raw_string is None:
274 if raw_string is None:
287 raw_string = python_string
275 raw_string = python_string
288 end_line = self.current_prompt_line \
276 end_line = self.current_prompt_line \
289 + max(1, len(raw_string.split('\n'))-1)
277 + max(1, len(raw_string.split('\n'))-1)
290 for i in range(self.current_prompt_line, end_line):
278 for i in range(self.current_prompt_line, end_line):
291 if i in self._markers:
279 if i in self._markers:
292 self.MarkerDeleteHandle(self._markers[i])
280 self.MarkerDeleteHandle(self._markers[i])
293 self._markers[i] = self.MarkerAdd(i, _COMPLETE_BUFFER_MARKER)
281 self._markers[i] = self.MarkerAdd(i, _COMPLETE_BUFFER_MARKER)
294 # Use a callafter to update the display robustly under windows
282 # Use a callafter to update the display robustly under windows
295 def callback():
283 def callback():
296 self.GotoPos(self.GetLength())
284 self.GotoPos(self.GetLength())
297 PrefilterFrontEnd.execute(self, python_string,
285 PrefilterFrontEnd.execute(self, python_string,
298 raw_string=raw_string)
286 raw_string=raw_string)
299 wx.CallAfter(callback)
287 wx.CallAfter(callback)
300
288
301
289
302 def execute_command(self, command, hidden=False):
290 def execute_command(self, command, hidden=False):
303 """ Execute a command, not only in the model, but also in the
291 """ Execute a command, not only in the model, but also in the
304 view.
292 view.
305 """
293 """
306 if hidden:
294 if hidden:
307 return self.shell.execute(command)
295 return self.shell.execute(command)
308 else:
296 else:
309 # XXX: we are not storing the input buffer previous to the
297 # XXX: we are not storing the input buffer previous to the
310 # execution, as this forces us to run the execution
298 # execution, as this forces us to run the execution
311 # input_buffer a yield, which is not good.
299 # input_buffer a yield, which is not good.
312 ##current_buffer = self.shell.control.input_buffer
300 ##current_buffer = self.shell.control.input_buffer
313 command = command.rstrip()
301 command = command.rstrip()
314 if len(command.split('\n')) > 1:
302 if len(command.split('\n')) > 1:
315 # The input command is several lines long, we need to
303 # The input command is several lines long, we need to
316 # force the execution to happen
304 # force the execution to happen
317 command += '\n'
305 command += '\n'
318 cleaned_command = self.prefilter_input(command)
306 cleaned_command = self.prefilter_input(command)
319 self.input_buffer = command
307 self.input_buffer = command
320 # Do not use wx.Yield() (aka GUI.process_events()) to avoid
308 # Do not use wx.Yield() (aka GUI.process_events()) to avoid
321 # recursive yields.
309 # recursive yields.
322 self.ProcessEvent(wx.PaintEvent())
310 self.ProcessEvent(wx.PaintEvent())
323 self.write('\n')
311 self.write('\n')
324 if not self.is_complete(cleaned_command + '\n'):
312 if not self.is_complete(cleaned_command + '\n'):
325 self._colorize_input_buffer()
313 self._colorize_input_buffer()
326 self.render_error('Incomplete or invalid input')
314 self.render_error('Incomplete or invalid input')
327 self.new_prompt(self.input_prompt_template.substitute(
315 self.new_prompt(self.input_prompt_template.substitute(
328 number=(self.last_result['number'] + 1)))
316 number=(self.last_result['number'] + 1)))
329 return False
317 return False
330 self._on_enter()
318 self._on_enter()
331 return True
319 return True
332
320
333
321
334 def save_output_hooks(self):
322 def save_output_hooks(self):
335 self.__old_raw_input = __builtin__.raw_input
323 self.__old_raw_input = __builtin__.raw_input
336 PrefilterFrontEnd.save_output_hooks(self)
324 PrefilterFrontEnd.save_output_hooks(self)
337
325
338 def capture_output(self):
326 def capture_output(self):
339 self.SetLexer(stc.STC_LEX_NULL)
327 self.SetLexer(stc.STC_LEX_NULL)
340 PrefilterFrontEnd.capture_output(self)
328 PrefilterFrontEnd.capture_output(self)
341 __builtin__.raw_input = self.raw_input
329 __builtin__.raw_input = self.raw_input
342
330
343
331
344 def release_output(self):
332 def release_output(self):
345 __builtin__.raw_input = self.__old_raw_input
333 __builtin__.raw_input = self.__old_raw_input
346 PrefilterFrontEnd.release_output(self)
334 PrefilterFrontEnd.release_output(self)
347 self.SetLexer(stc.STC_LEX_PYTHON)
335 self.SetLexer(stc.STC_LEX_PYTHON)
348
336
349
337
350 def after_execute(self):
338 def after_execute(self):
351 PrefilterFrontEnd.after_execute(self)
339 PrefilterFrontEnd.after_execute(self)
352 # Clear the wait cursor
340 # Clear the wait cursor
353 if hasattr(self, '_cursor'):
341 if hasattr(self, '_cursor'):
354 del self._cursor
342 del self._cursor
355 self.SetCursor(wx.StockCursor(wx.CURSOR_CHAR))
343 self.SetCursor(wx.StockCursor(wx.CURSOR_CHAR))
356
344
357
345
358 def show_traceback(self):
346 def show_traceback(self):
359 start_line = self.GetCurrentLine()
347 start_line = self.GetCurrentLine()
360 PrefilterFrontEnd.show_traceback(self)
348 PrefilterFrontEnd.show_traceback(self)
361 self.ProcessEvent(wx.PaintEvent())
349 self.ProcessEvent(wx.PaintEvent())
362 #wx.Yield()
350 #wx.Yield()
363 for i in range(start_line, self.GetCurrentLine()):
351 for i in range(start_line, self.GetCurrentLine()):
364 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
352 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
365
353
366
354
367 #--------------------------------------------------------------------------
355 #--------------------------------------------------------------------------
368 # FrontEndBase interface
356 # FrontEndBase interface
369 #--------------------------------------------------------------------------
357 #--------------------------------------------------------------------------
370
358
371 def render_error(self, e):
359 def render_error(self, e):
372 start_line = self.GetCurrentLine()
360 start_line = self.GetCurrentLine()
373 self.write('\n' + e + '\n')
361 self.write('\n' + e + '\n')
374 for i in range(start_line, self.GetCurrentLine()):
362 for i in range(start_line, self.GetCurrentLine()):
375 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
363 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
376
364
377
365
378 #--------------------------------------------------------------------------
366 #--------------------------------------------------------------------------
379 # ConsoleWidget interface
367 # ConsoleWidget interface
380 #--------------------------------------------------------------------------
368 #--------------------------------------------------------------------------
381
369
382 def new_prompt(self, prompt):
370 def new_prompt(self, prompt):
383 """ Display a new prompt, and start a new input buffer.
371 """ Display a new prompt, and start a new input buffer.
384 """
372 """
385 self._input_state = 'readline'
373 self._input_state = 'readline'
386 ConsoleWidget.new_prompt(self, prompt)
374 ConsoleWidget.new_prompt(self, prompt)
387 i = self.current_prompt_line
375 i = self.current_prompt_line
388 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
376 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
389
377
390
378
391 def write(self, *args, **kwargs):
379 def write(self, *args, **kwargs):
392 # Avoid multiple inheritence, be explicit about which
380 # Avoid multiple inheritence, be explicit about which
393 # parent method class gets called
381 # parent method class gets called
394 ConsoleWidget.write(self, *args, **kwargs)
382 ConsoleWidget.write(self, *args, **kwargs)
395
383
396
384
397 def _on_key_down(self, event, skip=True):
385 def _on_key_down(self, event, skip=True):
398 """ Capture the character events, let the parent
386 """ Capture the character events, let the parent
399 widget handle them, and put our logic afterward.
387 widget handle them, and put our logic afterward.
400 """
388 """
401 # FIXME: This method needs to be broken down in smaller ones.
389 # FIXME: This method needs to be broken down in smaller ones.
402 current_line_number = self.GetCurrentLine()
390 current_line_number = self.GetCurrentLine()
403 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
391 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
404 # Capture Control-C
392 # Capture Control-C
405 if self._input_state == 'subprocess':
393 if self._input_state == 'subprocess':
406 if self.debug:
394 if self.debug:
407 print >>sys.__stderr__, 'Killing running process'
395 print >>sys.__stderr__, 'Killing running process'
408 if hasattr(self._running_process, 'process'):
396 if hasattr(self._running_process, 'process'):
409 self._running_process.process.kill()
397 self._running_process.process.kill()
410 elif self._input_state == 'buffering':
398 elif self._input_state == 'buffering':
411 if self.debug:
399 if self.debug:
412 print >>sys.__stderr__, 'Raising KeyboardInterrupt'
400 print >>sys.__stderr__, 'Raising KeyboardInterrupt'
413 raise KeyboardInterrupt
401 raise KeyboardInterrupt
414 # XXX: We need to make really sure we
402 # XXX: We need to make really sure we
415 # get back to a prompt.
403 # get back to a prompt.
416 elif self._input_state == 'subprocess' and (
404 elif self._input_state == 'subprocess' and (
417 ( event.KeyCode<256 and
405 ( event.KeyCode<256 and
418 not event.ControlDown() )
406 not event.ControlDown() )
419 or
407 or
420 ( event.KeyCode in (ord('d'), ord('D')) and
408 ( event.KeyCode in (ord('d'), ord('D')) and
421 event.ControlDown())):
409 event.ControlDown())):
422 # We are running a process, we redirect keys.
410 # We are running a process, we redirect keys.
423 ConsoleWidget._on_key_down(self, event, skip=skip)
411 ConsoleWidget._on_key_down(self, event, skip=skip)
424 char = chr(event.KeyCode)
412 char = chr(event.KeyCode)
425 # Deal with some inconsistency in wx keycodes:
413 # Deal with some inconsistency in wx keycodes:
426 if char == '\r':
414 if char == '\r':
427 char = '\n'
415 char = '\n'
428 elif not event.ShiftDown():
416 elif not event.ShiftDown():
429 char = char.lower()
417 char = char.lower()
430 if event.ControlDown() and event.KeyCode in (ord('d'), ord('D')):
418 if event.ControlDown() and event.KeyCode in (ord('d'), ord('D')):
431 char = '\04'
419 char = '\04'
432 self._running_process.process.stdin.write(char)
420 self._running_process.process.stdin.write(char)
433 self._running_process.process.stdin.flush()
421 self._running_process.process.stdin.flush()
434 elif event.KeyCode in (ord('('), 57, 53):
422 elif event.KeyCode in (ord('('), 57, 53):
435 # Calltips
423 # Calltips
436 event.Skip()
424 event.Skip()
437 self.do_calltip()
425 self.do_calltip()
438 elif self.AutoCompActive() and not event.KeyCode == ord('\t'):
426 elif self.AutoCompActive() and not event.KeyCode == ord('\t'):
439 event.Skip()
427 event.Skip()
440 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
428 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
441 wx.CallAfter(self._popup_completion, create=True)
429 wx.CallAfter(self._popup_completion, create=True)
442 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
430 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
443 wx.WXK_RIGHT, wx.WXK_ESCAPE):
431 wx.WXK_RIGHT, wx.WXK_ESCAPE):
444 wx.CallAfter(self._popup_completion)
432 wx.CallAfter(self._popup_completion)
445 else:
433 else:
446 # Up history
434 # Up history
447 if event.KeyCode == wx.WXK_UP and (
435 if event.KeyCode == wx.WXK_UP and (
448 ( current_line_number == self.current_prompt_line and
436 ( current_line_number == self.current_prompt_line and
449 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
437 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
450 or event.ControlDown() ):
438 or event.ControlDown() ):
451 new_buffer = self.get_history_previous(
439 new_buffer = self.get_history_previous(
452 self.input_buffer)
440 self.input_buffer)
453 if new_buffer is not None:
441 if new_buffer is not None:
454 self.input_buffer = new_buffer
442 self.input_buffer = new_buffer
455 if self.GetCurrentLine() > self.current_prompt_line:
443 if self.GetCurrentLine() > self.current_prompt_line:
456 # Go to first line, for seemless history up.
444 # Go to first line, for seemless history up.
457 self.GotoPos(self.current_prompt_pos)
445 self.GotoPos(self.current_prompt_pos)
458 # Down history
446 # Down history
459 elif event.KeyCode == wx.WXK_DOWN and (
447 elif event.KeyCode == wx.WXK_DOWN and (
460 ( current_line_number == self.LineCount -1 and
448 ( current_line_number == self.LineCount -1 and
461 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
449 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
462 or event.ControlDown() ):
450 or event.ControlDown() ):
463 new_buffer = self.get_history_next()
451 new_buffer = self.get_history_next()
464 if new_buffer is not None:
452 if new_buffer is not None:
465 self.input_buffer = new_buffer
453 self.input_buffer = new_buffer
466 # Tab-completion
454 # Tab-completion
467 elif event.KeyCode == ord('\t'):
455 elif event.KeyCode == ord('\t'):
468 current_line, current_line_number = self.CurLine
456 current_line, current_line_number = self.CurLine
469 if not re.match(r'^\s*$', current_line):
457 if not re.match(r'^\s*$', current_line):
470 self.complete_current_input()
458 self.complete_current_input()
471 if self.AutoCompActive():
459 if self.AutoCompActive():
472 wx.CallAfter(self._popup_completion, create=True)
460 wx.CallAfter(self._popup_completion, create=True)
473 else:
461 else:
474 event.Skip()
462 event.Skip()
475 else:
463 else:
476 ConsoleWidget._on_key_down(self, event, skip=skip)
464 ConsoleWidget._on_key_down(self, event, skip=skip)
477
465
478
466
479 def _on_key_up(self, event, skip=True):
467 def _on_key_up(self, event, skip=True):
480 """ Called when any key is released.
468 """ Called when any key is released.
481 """
469 """
482 if event.KeyCode in (59, ord('.')):
470 if event.KeyCode in (59, ord('.')):
483 # Intercepting '.'
471 # Intercepting '.'
484 event.Skip()
472 event.Skip()
485 wx.CallAfter(self._popup_completion, create=True)
473 wx.CallAfter(self._popup_completion, create=True)
486 else:
474 else:
487 ConsoleWidget._on_key_up(self, event, skip=skip)
475 ConsoleWidget._on_key_up(self, event, skip=skip)
488
476
489
477
490 def _on_enter(self):
478 def _on_enter(self):
491 """ Called on return key down, in readline input_state.
479 """ Called on return key down, in readline input_state.
492 """
480 """
493 if self.debug:
481 if self.debug:
494 print >>sys.__stdout__, repr(self.input_buffer)
482 print >>sys.__stdout__, repr(self.input_buffer)
495 PrefilterFrontEnd._on_enter(self)
483 PrefilterFrontEnd._on_enter(self)
496
484
497
485
498 #--------------------------------------------------------------------------
486 #--------------------------------------------------------------------------
499 # EditWindow API
487 # EditWindow API
500 #--------------------------------------------------------------------------
488 #--------------------------------------------------------------------------
501
489
502 def OnUpdateUI(self, event):
490 def OnUpdateUI(self, event):
503 """ Override the OnUpdateUI of the EditWindow class, to prevent
491 """ Override the OnUpdateUI of the EditWindow class, to prevent
504 syntax highlighting both for faster redraw, and for more
492 syntax highlighting both for faster redraw, and for more
505 consistent look and feel.
493 consistent look and feel.
506 """
494 """
507 if not self._input_state == 'readline':
495 if not self._input_state == 'readline':
508 ConsoleWidget.OnUpdateUI(self, event)
496 ConsoleWidget.OnUpdateUI(self, event)
509
497
510 #--------------------------------------------------------------------------
498 #--------------------------------------------------------------------------
511 # Private API
499 # Private API
512 #--------------------------------------------------------------------------
500 #--------------------------------------------------------------------------
513
501
514 def _buffer_flush(self, event):
502 def _buffer_flush(self, event):
515 """ Called by the timer to flush the write buffer.
503 """ Called by the timer to flush the write buffer.
516
504
517 This is always called in the mainloop, by the wx timer.
505 This is always called in the mainloop, by the wx timer.
518 """
506 """
519 self._out_buffer_lock.acquire()
507 self._out_buffer_lock.acquire()
520 _out_buffer = self._out_buffer
508 _out_buffer = self._out_buffer
521 self._out_buffer = []
509 self._out_buffer = []
522 self._out_buffer_lock.release()
510 self._out_buffer_lock.release()
523 self.write(''.join(_out_buffer), refresh=False)
511 self.write(''.join(_out_buffer), refresh=False)
524
512
525
513
526 def _colorize_input_buffer(self):
514 def _colorize_input_buffer(self):
527 """ Keep the input buffer lines at a bright color.
515 """ Keep the input buffer lines at a bright color.
528 """
516 """
529 if not self._input_state in ('readline', 'raw_input'):
517 if not self._input_state in ('readline', 'raw_input'):
530 return
518 return
531 end_line = self.GetCurrentLine()
519 end_line = self.GetCurrentLine()
532 if not sys.platform == 'win32':
520 if not sys.platform == 'win32':
533 end_line += 1
521 end_line += 1
534 for i in range(self.current_prompt_line, end_line):
522 for i in range(self.current_prompt_line, end_line):
535 if i in self._markers:
523 if i in self._markers:
536 self.MarkerDeleteHandle(self._markers[i])
524 self.MarkerDeleteHandle(self._markers[i])
537 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
525 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
538
526
539
527
540 if __name__ == '__main__':
528 if __name__ == '__main__':
541 class MainWindow(wx.Frame):
529 class MainWindow(wx.Frame):
542 def __init__(self, parent, id, title):
530 def __init__(self, parent, id, title):
543 wx.Frame.__init__(self, parent, id, title, size=(300,250))
531 wx.Frame.__init__(self, parent, id, title, size=(300,250))
544 self._sizer = wx.BoxSizer(wx.VERTICAL)
532 self._sizer = wx.BoxSizer(wx.VERTICAL)
545 self.shell = WxController(self)
533 self.shell = WxController(self)
546 self._sizer.Add(self.shell, 1, wx.EXPAND)
534 self._sizer.Add(self.shell, 1, wx.EXPAND)
547 self.SetSizer(self._sizer)
535 self.SetSizer(self._sizer)
548 self.SetAutoLayout(1)
536 self.SetAutoLayout(1)
549 self.Show(True)
537 self.Show(True)
550
538
551 app = wx.PySimpleApp()
539 app = wx.PySimpleApp()
552 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
540 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
553 frame.shell.SetFocus()
541 frame.shell.SetFocus()
554 frame.SetSize((680, 460))
542 frame.SetSize((680, 460))
555 self = frame.shell
543 self = frame.shell
556
544
557 app.MainLoop()
545 app.MainLoop()
558
546
General Comments 0
You need to be logged in to leave comments. Login now