##// END OF EJS Templates
Make process execution work under windows.
gvaroquaux -
Show More
@@ -0,0 +1,264 b''
1 # A module to expose various thread/process/job related structures and
2 # methods from kernel32
3 #
4 # The MIT License
5 #
6 # Copyright (c) 2006 the Mozilla Foundation <http://www.mozilla.org>
7 #
8 # Permission is hereby granted, free of charge, to any person obtaining a
9 # copy of this software and associated documentation files (the "Software"),
10 # to deal in the Software without restriction, including without limitation
11 # the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 # and/or sell copies of the Software, and to permit persons to whom the
13 # Software is furnished to do so, subject to the following conditions:
14 #
15 # The above copyright notice and this permission notice shall be included in
16 # all copies or substantial portions of the Software.
17 #
18 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 # DEALINGS IN THE SOFTWARE.
25
26 from ctypes import c_void_p, POINTER, sizeof, Structure, windll, WinError, WINFUNCTYPE
27 from ctypes.wintypes import BOOL, BYTE, DWORD, HANDLE, LPCWSTR, LPWSTR, UINT, WORD
28
29 LPVOID = c_void_p
30 LPBYTE = POINTER(BYTE)
31 LPDWORD = POINTER(DWORD)
32
33 SW_HIDE = 0
34
35 def ErrCheckBool(result, func, args):
36 """errcheck function for Windows functions that return a BOOL True
37 on success"""
38 if not result:
39 raise WinError()
40 return args
41
42 # CloseHandle()
43
44 CloseHandleProto = WINFUNCTYPE(BOOL, HANDLE)
45 CloseHandle = CloseHandleProto(("CloseHandle", windll.kernel32))
46 CloseHandle.errcheck = ErrCheckBool
47
48 # AutoHANDLE
49
50 class AutoHANDLE(HANDLE):
51 """Subclass of HANDLE which will call CloseHandle() on deletion."""
52 def Close(self):
53 if self.value:
54 CloseHandle(self)
55 self.value = 0
56
57 def __del__(self):
58 self.Close()
59
60 def __int__(self):
61 return self.value
62
63 def ErrCheckHandle(result, func, args):
64 """errcheck function for Windows functions that return a HANDLE."""
65 if not result:
66 raise WinError()
67 return AutoHANDLE(result)
68
69 # PROCESS_INFORMATION structure
70
71 class PROCESS_INFORMATION(Structure):
72 _fields_ = [("hProcess", HANDLE),
73 ("hThread", HANDLE),
74 ("dwProcessID", DWORD),
75 ("dwThreadID", DWORD)]
76
77 def __init__(self):
78 Structure.__init__(self)
79
80 self.cb = sizeof(self)
81
82 LPPROCESS_INFORMATION = POINTER(PROCESS_INFORMATION)
83
84 # STARTUPINFO structure
85
86 class STARTUPINFO(Structure):
87 _fields_ = [("cb", DWORD),
88 ("lpReserved", LPWSTR),
89 ("lpDesktop", LPWSTR),
90 ("lpTitle", LPWSTR),
91 ("dwX", DWORD),
92 ("dwY", DWORD),
93 ("dwXSize", DWORD),
94 ("dwYSize", DWORD),
95 ("dwXCountChars", DWORD),
96 ("dwYCountChars", DWORD),
97 ("dwFillAttribute", DWORD),
98 ("dwFlags", DWORD),
99 ("wShowWindow", WORD),
100 ("cbReserved2", WORD),
101 ("lpReserved2", LPBYTE),
102 ("hStdInput", HANDLE),
103 ("hStdOutput", HANDLE),
104 ("hStdError", HANDLE)
105 ]
106 LPSTARTUPINFO = POINTER(STARTUPINFO)
107
108 STARTF_USESHOWWINDOW = 0x01
109 STARTF_USESIZE = 0x02
110 STARTF_USEPOSITION = 0x04
111 STARTF_USECOUNTCHARS = 0x08
112 STARTF_USEFILLATTRIBUTE = 0x10
113 STARTF_RUNFULLSCREEN = 0x20
114 STARTF_FORCEONFEEDBACK = 0x40
115 STARTF_FORCEOFFFEEDBACK = 0x80
116 STARTF_USESTDHANDLES = 0x100
117
118 # EnvironmentBlock
119
120 class EnvironmentBlock:
121 """An object which can be passed as the lpEnv parameter of CreateProcess.
122 It is initialized with a dictionary."""
123
124 def __init__(self, dict):
125 if not dict:
126 self._as_parameter_ = None
127 else:
128 values = ["%s=%s" % (key, value)
129 for (key, value) in dict.iteritems()]
130 values.append("")
131 self._as_parameter_ = LPCWSTR("\0".join(values))
132
133 # CreateProcess()
134
135 CreateProcessProto = WINFUNCTYPE(BOOL, # Return type
136 LPCWSTR, # lpApplicationName
137 LPWSTR, # lpCommandLine
138 LPVOID, # lpProcessAttributes
139 LPVOID, # lpThreadAttributes
140 BOOL, # bInheritHandles
141 DWORD, # dwCreationFlags
142 LPVOID, # lpEnvironment
143 LPCWSTR, # lpCurrentDirectory
144 LPSTARTUPINFO, # lpStartupInfo
145 LPPROCESS_INFORMATION # lpProcessInformation
146 )
147
148 CreateProcessFlags = ((1, "lpApplicationName", None),
149 (1, "lpCommandLine"),
150 (1, "lpProcessAttributes", None),
151 (1, "lpThreadAttributes", None),
152 (1, "bInheritHandles", True),
153 (1, "dwCreationFlags", 0),
154 (1, "lpEnvironment", None),
155 (1, "lpCurrentDirectory", None),
156 (1, "lpStartupInfo"),
157 (2, "lpProcessInformation"))
158
159 def ErrCheckCreateProcess(result, func, args):
160 ErrCheckBool(result, func, args)
161 # return a tuple (hProcess, hThread, dwProcessID, dwThreadID)
162 pi = args[9]
163 return AutoHANDLE(pi.hProcess), AutoHANDLE(pi.hThread), pi.dwProcessID, pi.dwThreadID
164
165 CreateProcess = CreateProcessProto(("CreateProcessW", windll.kernel32),
166 CreateProcessFlags)
167 CreateProcess.errcheck = ErrCheckCreateProcess
168
169 CREATE_BREAKAWAY_FROM_JOB = 0x01000000
170 CREATE_DEFAULT_ERROR_MODE = 0x04000000
171 CREATE_NEW_CONSOLE = 0x00000010
172 CREATE_NEW_PROCESS_GROUP = 0x00000200
173 CREATE_NO_WINDOW = 0x08000000
174 CREATE_SUSPENDED = 0x00000004
175 CREATE_UNICODE_ENVIRONMENT = 0x00000400
176 DEBUG_ONLY_THIS_PROCESS = 0x00000002
177 DEBUG_PROCESS = 0x00000001
178 DETACHED_PROCESS = 0x00000008
179
180 # CreateJobObject()
181
182 CreateJobObjectProto = WINFUNCTYPE(HANDLE, # Return type
183 LPVOID, # lpJobAttributes
184 LPCWSTR # lpName
185 )
186
187 CreateJobObjectFlags = ((1, "lpJobAttributes", None),
188 (1, "lpName", None))
189
190 CreateJobObject = CreateJobObjectProto(("CreateJobObjectW", windll.kernel32),
191 CreateJobObjectFlags)
192 CreateJobObject.errcheck = ErrCheckHandle
193
194 # AssignProcessToJobObject()
195
196 AssignProcessToJobObjectProto = WINFUNCTYPE(BOOL, # Return type
197 HANDLE, # hJob
198 HANDLE # hProcess
199 )
200 AssignProcessToJobObjectFlags = ((1, "hJob"),
201 (1, "hProcess"))
202 AssignProcessToJobObject = AssignProcessToJobObjectProto(
203 ("AssignProcessToJobObject", windll.kernel32),
204 AssignProcessToJobObjectFlags)
205 AssignProcessToJobObject.errcheck = ErrCheckBool
206
207 # ResumeThread()
208
209 def ErrCheckResumeThread(result, func, args):
210 if result == -1:
211 raise WinError()
212
213 return args
214
215 ResumeThreadProto = WINFUNCTYPE(DWORD, # Return type
216 HANDLE # hThread
217 )
218 ResumeThreadFlags = ((1, "hThread"),)
219 ResumeThread = ResumeThreadProto(("ResumeThread", windll.kernel32),
220 ResumeThreadFlags)
221 ResumeThread.errcheck = ErrCheckResumeThread
222
223 # TerminateJobObject()
224
225 TerminateJobObjectProto = WINFUNCTYPE(BOOL, # Return type
226 HANDLE, # hJob
227 UINT # uExitCode
228 )
229 TerminateJobObjectFlags = ((1, "hJob"),
230 (1, "uExitCode", 127))
231 TerminateJobObject = TerminateJobObjectProto(
232 ("TerminateJobObject", windll.kernel32),
233 TerminateJobObjectFlags)
234 TerminateJobObject.errcheck = ErrCheckBool
235
236 # WaitForSingleObject()
237
238 WaitForSingleObjectProto = WINFUNCTYPE(DWORD, # Return type
239 HANDLE, # hHandle
240 DWORD, # dwMilliseconds
241 )
242 WaitForSingleObjectFlags = ((1, "hHandle"),
243 (1, "dwMilliseconds", -1))
244 WaitForSingleObject = WaitForSingleObjectProto(
245 ("WaitForSingleObject", windll.kernel32),
246 WaitForSingleObjectFlags)
247
248 INFINITE = -1
249 WAIT_TIMEOUT = 0x0102
250 WAIT_OBJECT_0 = 0x0
251 WAIT_ABANDONED = 0x0080
252
253 # GetExitCodeProcess()
254
255 GetExitCodeProcessProto = WINFUNCTYPE(BOOL, # Return type
256 HANDLE, # hProcess
257 LPDWORD, # lpExitCode
258 )
259 GetExitCodeProcessFlags = ((1, "hProcess"),
260 (2, "lpExitCode"))
261 GetExitCodeProcess = GetExitCodeProcessProto(
262 ("GetExitCodeProcess", windll.kernel32),
263 GetExitCodeProcessFlags)
264 GetExitCodeProcess.errcheck = ErrCheckBool
1 NO CONTENT: modified file
NO CONTENT: modified file
@@ -1,432 +1,433 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 sys
26 import sys
27 LINESEP = '\n'
27 LINESEP = '\n'
28 if sys.platform == 'win32':
28 if sys.platform == 'win32':
29 LINESEP = '\n\r'
29 LINESEP = '\n\r'
30
30
31 import re
31 import re
32
32
33 # FIXME: Need to provide an API for non user-generated display on the
33 # FIXME: Need to provide an API for non user-generated display on the
34 # screen: this should not be editable by the user.
34 # screen: this should not be editable by the user.
35
35
36 _DEFAULT_SIZE = 10
36 _DEFAULT_SIZE = 10
37
37
38 _DEFAULT_STYLE = {
38 _DEFAULT_STYLE = {
39 'stdout' : 'fore:#0000FF',
39 'stdout' : 'fore:#0000FF',
40 'stderr' : 'fore:#007f00',
40 'stderr' : 'fore:#007f00',
41 'trace' : 'fore:#FF0000',
41 'trace' : 'fore:#FF0000',
42
42
43 'default' : 'size:%d' % _DEFAULT_SIZE,
43 'default' : 'size:%d' % _DEFAULT_SIZE,
44 'bracegood' : 'fore:#FFFFFF,back:#0000FF,bold',
44 'bracegood' : 'fore:#FFFFFF,back:#0000FF,bold',
45 'bracebad' : 'fore:#000000,back:#FF0000,bold',
45 'bracebad' : 'fore:#000000,back:#FF0000,bold',
46
46
47 # properties for the various Python lexer styles
47 # properties for the various Python lexer styles
48 'comment' : 'fore:#007F00',
48 'comment' : 'fore:#007F00',
49 'number' : 'fore:#007F7F',
49 'number' : 'fore:#007F7F',
50 'string' : 'fore:#7F007F,italic',
50 'string' : 'fore:#7F007F,italic',
51 'char' : 'fore:#7F007F,italic',
51 'char' : 'fore:#7F007F,italic',
52 'keyword' : 'fore:#00007F,bold',
52 'keyword' : 'fore:#00007F,bold',
53 'triple' : 'fore:#7F0000',
53 'triple' : 'fore:#7F0000',
54 'tripledouble' : 'fore:#7F0000',
54 'tripledouble' : 'fore:#7F0000',
55 'class' : 'fore:#0000FF,bold,underline',
55 'class' : 'fore:#0000FF,bold,underline',
56 'def' : 'fore:#007F7F,bold',
56 'def' : 'fore:#007F7F,bold',
57 'operator' : 'bold'
57 'operator' : 'bold'
58 }
58 }
59
59
60 # new style numbers
60 # new style numbers
61 _STDOUT_STYLE = 15
61 _STDOUT_STYLE = 15
62 _STDERR_STYLE = 16
62 _STDERR_STYLE = 16
63 _TRACE_STYLE = 17
63 _TRACE_STYLE = 17
64
64
65
65
66 # system colors
66 # system colors
67 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
67 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
68
68
69 #-------------------------------------------------------------------------------
69 #-------------------------------------------------------------------------------
70 # The console widget class
70 # The console widget class
71 #-------------------------------------------------------------------------------
71 #-------------------------------------------------------------------------------
72 class ConsoleWidget(editwindow.EditWindow):
72 class ConsoleWidget(editwindow.EditWindow):
73 """ Specialized styled text control view for console-like workflow.
73 """ Specialized styled text control view for console-like workflow.
74
74
75 This widget is mainly interested in dealing with the prompt and
75 This widget is mainly interested in dealing with the prompt and
76 keeping the cursor inside the editing line.
76 keeping the cursor inside the editing line.
77 """
77 """
78
78
79 title = 'Console'
79 title = 'Console'
80
80
81 style = _DEFAULT_STYLE.copy()
81 style = _DEFAULT_STYLE.copy()
82
82
83 # Translation table from ANSI escape sequences to color. Override
83 # Translation table from ANSI escape sequences to color. Override
84 # this to specify your colors.
84 # this to specify your colors.
85 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
85 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
86 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
86 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
87 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
87 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
88 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
88 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
89 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
89 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
90 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
90 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
91 '1;34': [12, 'LIGHT BLUE'], '1;35':
91 '1;34': [12, 'LIGHT BLUE'], '1;35':
92 [13, 'MEDIUM VIOLET RED'],
92 [13, 'MEDIUM VIOLET RED'],
93 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
93 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
94
94
95 # The color of the carret (call _apply_style() after setting)
95 # The color of the carret (call _apply_style() after setting)
96 carret_color = 'BLACK'
96 carret_color = 'BLACK'
97
97
98 #--------------------------------------------------------------------------
98 #--------------------------------------------------------------------------
99 # Public API
99 # Public API
100 #--------------------------------------------------------------------------
100 #--------------------------------------------------------------------------
101
101
102 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
102 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
103 size=wx.DefaultSize, style=0, ):
103 size=wx.DefaultSize, style=0, ):
104 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
104 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
105 self.configure_scintilla()
105 self.configure_scintilla()
106
106
107 # FIXME: we need to retrieve this from the interpreter.
107 # FIXME: we need to retrieve this from the interpreter.
108 self.prompt = \
108 self.prompt = \
109 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02%i\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
109 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02%i\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
110 self.new_prompt(self.prompt % 1)
110 self.new_prompt(self.prompt % 1)
111
111
112 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
112 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
113 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
113 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
114
114
115
115
116 def configure_scintilla(self):
116 def configure_scintilla(self):
117 self.SetEOLMode(stc.STC_EOL_LF)
117 self.SetEOLMode(stc.STC_EOL_LF)
118
118
119 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
119 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
120 # the widget
120 # the widget
121 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
121 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
122 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
122 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
123 # Also allow Ctrl Shift "=" for poor non US keyboard users.
123 # Also allow Ctrl Shift "=" for poor non US keyboard users.
124 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
124 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
125 stc.STC_CMD_ZOOMIN)
125 stc.STC_CMD_ZOOMIN)
126
126
127 #self.CmdKeyAssign(stc.STC_KEY_PRIOR, stc.STC_SCMOD_SHIFT,
127 #self.CmdKeyAssign(stc.STC_KEY_PRIOR, stc.STC_SCMOD_SHIFT,
128 # stc.STC_CMD_PAGEUP)
128 # stc.STC_CMD_PAGEUP)
129
129
130 #self.CmdKeyAssign(stc.STC_KEY_NEXT, stc.STC_SCMOD_SHIFT,
130 #self.CmdKeyAssign(stc.STC_KEY_NEXT, stc.STC_SCMOD_SHIFT,
131 # stc.STC_CMD_PAGEDOWN)
131 # stc.STC_CMD_PAGEDOWN)
132
132
133 # Keys: we need to clear some of the keys the that don't play
133 # Keys: we need to clear some of the keys the that don't play
134 # well with a console.
134 # well with a console.
135 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
135 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
136 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
136 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
137 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
137 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
138
138
139
139
140 self.SetEOLMode(stc.STC_EOL_CRLF)
140 self.SetEOLMode(stc.STC_EOL_CRLF)
141 self.SetWrapMode(stc.STC_WRAP_CHAR)
141 self.SetWrapMode(stc.STC_WRAP_CHAR)
142 self.SetWrapMode(stc.STC_WRAP_WORD)
142 self.SetWrapMode(stc.STC_WRAP_WORD)
143 self.SetBufferedDraw(True)
143 self.SetBufferedDraw(True)
144 self.SetUseAntiAliasing(True)
144 self.SetUseAntiAliasing(True)
145 self.SetLayoutCache(stc.STC_CACHE_PAGE)
145 self.SetLayoutCache(stc.STC_CACHE_PAGE)
146 self.SetUndoCollection(False)
146 self.SetUndoCollection(False)
147 self.SetUseTabs(True)
147 self.SetUseTabs(True)
148 self.SetIndent(4)
148 self.SetIndent(4)
149 self.SetTabWidth(4)
149 self.SetTabWidth(4)
150
150
151 self.EnsureCaretVisible()
151 self.EnsureCaretVisible()
152 # we don't want scintilla's autocompletion to choose
152 # we don't want scintilla's autocompletion to choose
153 # automaticaly out of a single choice list, as we pop it up
153 # automaticaly out of a single choice list, as we pop it up
154 # automaticaly
154 # automaticaly
155 self.AutoCompSetChooseSingle(False)
155 self.AutoCompSetChooseSingle(False)
156 self.AutoCompSetMaxHeight(10)
156 self.AutoCompSetMaxHeight(10)
157
157
158 self.SetMargins(3, 3) #text is moved away from border with 3px
158 self.SetMargins(3, 3) #text is moved away from border with 3px
159 # Suppressing Scintilla margins
159 # Suppressing Scintilla margins
160 self.SetMarginWidth(0, 0)
160 self.SetMarginWidth(0, 0)
161 self.SetMarginWidth(1, 0)
161 self.SetMarginWidth(1, 0)
162 self.SetMarginWidth(2, 0)
162 self.SetMarginWidth(2, 0)
163
163
164 self._apply_style()
164 self._apply_style()
165
165
166 # Xterm escape sequences
166 # Xterm escape sequences
167 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
167 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
168 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
168 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
169
169
170 #self.SetEdgeMode(stc.STC_EDGE_LINE)
170 #self.SetEdgeMode(stc.STC_EDGE_LINE)
171 #self.SetEdgeColumn(80)
171 #self.SetEdgeColumn(80)
172
172
173 # styles
173 # styles
174 p = self.style
174 p = self.style
175 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
175 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
176 self.StyleClearAll()
176 self.StyleClearAll()
177 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
177 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
178 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
178 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
179 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
179 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
180
180
181 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
181 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
182 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
182 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
183 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
183 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
184 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
184 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
185 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
185 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
186 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
186 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
187 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
187 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
188 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
188 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
189 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
189 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
190 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
190 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
191 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
191 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
192 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
192 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
193 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
193 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
194 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
194 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
195
195
196
196
197 def write(self, text):
197 def write(self, text, refresh=True):
198 """ Write given text to buffer, while translating the ansi escape
198 """ Write given text to buffer, while translating the ansi escape
199 sequences.
199 sequences.
200 """
200 """
201 # XXX: do not put print statements to sys.stdout/sys.stderr in
201 # XXX: do not put print statements to sys.stdout/sys.stderr in
202 # this method, the print statements will call this method, as
202 # this method, the print statements will call this method, as
203 # you will end up with an infinit loop
203 # you will end up with an infinit loop
204 if self.debug:
204 if self.debug:
205 print >>sys.__stderr__, text
205 print >>sys.__stderr__, text
206 title = self.title_pat.split(text)
206 title = self.title_pat.split(text)
207 if len(title)>1:
207 if len(title)>1:
208 self.title = title[-2]
208 self.title = title[-2]
209
209
210 text = self.title_pat.sub('', text)
210 text = self.title_pat.sub('', text)
211 segments = self.color_pat.split(text)
211 segments = self.color_pat.split(text)
212 segment = segments.pop(0)
212 segment = segments.pop(0)
213 self.GotoPos(self.GetLength())
213 self.GotoPos(self.GetLength())
214 self.StartStyling(self.GetLength(), 0xFF)
214 self.StartStyling(self.GetLength(), 0xFF)
215 self.AppendText(segment)
215 self.AppendText(segment)
216
216
217 if segments:
217 if segments:
218 for ansi_tag, text in zip(segments[::2], segments[1::2]):
218 for ansi_tag, text in zip(segments[::2], segments[1::2]):
219 self.StartStyling(self.GetLength(), 0xFF)
219 self.StartStyling(self.GetLength(), 0xFF)
220 self.AppendText(text)
220 self.AppendText(text)
221
221
222 if ansi_tag not in self.ANSI_STYLES:
222 if ansi_tag not in self.ANSI_STYLES:
223 style = 0
223 style = 0
224 else:
224 else:
225 style = self.ANSI_STYLES[ansi_tag][0]
225 style = self.ANSI_STYLES[ansi_tag][0]
226
226
227 self.SetStyling(len(text), style)
227 self.SetStyling(len(text), style)
228
228
229 self.GotoPos(self.GetLength())
229 self.GotoPos(self.GetLength())
230 if refresh:
230 wx.Yield()
231 wx.Yield()
231
232
232
233
233 def new_prompt(self, prompt):
234 def new_prompt(self, prompt):
234 """ Prints a prompt at start of line, and move the start of the
235 """ Prints a prompt at start of line, and move the start of the
235 current block there.
236 current block there.
236
237
237 The prompt can be give with ascii escape sequences.
238 The prompt can be give with ascii escape sequences.
238 """
239 """
239 self.write(prompt)
240 self.write(prompt)
240 # now we update our cursor giving end of prompt
241 # now we update our cursor giving end of prompt
241 self.current_prompt_pos = self.GetLength()
242 self.current_prompt_pos = self.GetLength()
242 self.current_prompt_line = self.GetCurrentLine()
243 self.current_prompt_line = self.GetCurrentLine()
243 wx.Yield()
244 wx.Yield()
244 self.EnsureCaretVisible()
245 self.EnsureCaretVisible()
245
246
246
247
247 def replace_current_edit_buffer(self, text):
248 def replace_current_edit_buffer(self, text):
248 """ Replace currently entered command line with given text.
249 """ Replace currently entered command line with given text.
249 """
250 """
250 self.SetSelection(self.current_prompt_pos, self.GetLength())
251 self.SetSelection(self.current_prompt_pos, self.GetLength())
251 self.ReplaceSelection(text)
252 self.ReplaceSelection(text)
252 self.GotoPos(self.GetLength())
253 self.GotoPos(self.GetLength())
253
254
254
255
255 def get_current_edit_buffer(self):
256 def get_current_edit_buffer(self):
256 """ Returns the text in current edit buffer.
257 """ Returns the text in current edit buffer.
257 """
258 """
258 current_edit_buffer = self.GetTextRange(self.current_prompt_pos,
259 current_edit_buffer = self.GetTextRange(self.current_prompt_pos,
259 self.GetLength())
260 self.GetLength())
260 current_edit_buffer = current_edit_buffer.replace(LINESEP, '\n')
261 current_edit_buffer = current_edit_buffer.replace(LINESEP, '\n')
261 return current_edit_buffer
262 return current_edit_buffer
262
263
263
264
264 #--------------------------------------------------------------------------
265 #--------------------------------------------------------------------------
265 # Private API
266 # Private API
266 #--------------------------------------------------------------------------
267 #--------------------------------------------------------------------------
267
268
268 def _apply_style(self):
269 def _apply_style(self):
269 """ Applies the colors for the different text elements and the
270 """ Applies the colors for the different text elements and the
270 carret.
271 carret.
271 """
272 """
272 self.SetCaretForeground(self.carret_color)
273 self.SetCaretForeground(self.carret_color)
273
274
274 #self.StyleClearAll()
275 #self.StyleClearAll()
275 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
276 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
276 "fore:#FF0000,back:#0000FF,bold")
277 "fore:#FF0000,back:#0000FF,bold")
277 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
278 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
278 "fore:#000000,back:#FF0000,bold")
279 "fore:#000000,back:#FF0000,bold")
279
280
280 for style in self.ANSI_STYLES.values():
281 for style in self.ANSI_STYLES.values():
281 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
282 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
282
283
283
284
284 def write_completion(self, possibilities):
285 def write_completion(self, possibilities):
285 # FIXME: This is non Wx specific and needs to be moved into
286 # FIXME: This is non Wx specific and needs to be moved into
286 # the base class.
287 # the base class.
287 current_buffer = self.get_current_edit_buffer()
288 current_buffer = self.get_current_edit_buffer()
288
289
289 self.write('\n')
290 self.write('\n')
290 max_len = len(max(possibilities, key=len)) + 1
291 max_len = len(max(possibilities, key=len)) + 1
291
292
292 #now we check how much symbol we can put on a line...
293 #now we check how much symbol we can put on a line...
293 chars_per_line = self.GetSize()[0]/self.GetCharWidth()
294 chars_per_line = self.GetSize()[0]/self.GetCharWidth()
294 symbols_per_line = max(1, chars_per_line/max_len)
295 symbols_per_line = max(1, chars_per_line/max_len)
295
296
296 pos = 1
297 pos = 1
297 buf = []
298 buf = []
298 for symbol in possibilities:
299 for symbol in possibilities:
299 if pos < symbols_per_line:
300 if pos < symbols_per_line:
300 buf.append(symbol.ljust(max_len))
301 buf.append(symbol.ljust(max_len))
301 pos += 1
302 pos += 1
302 else:
303 else:
303 buf.append(symbol.rstrip() + '\n')
304 buf.append(symbol.rstrip() + '\n')
304 pos = 1
305 pos = 1
305 self.write(''.join(buf))
306 self.write(''.join(buf))
306 self.new_prompt(self.prompt % (self.last_result['number'] + 1))
307 self.new_prompt(self.prompt % (self.last_result['number'] + 1))
307 self.replace_current_edit_buffer(current_buffer)
308 self.replace_current_edit_buffer(current_buffer)
308
309
309
310
310 def pop_completion(self, possibilities, offset=0):
311 def pop_completion(self, possibilities, offset=0):
311 """ Pops up an autocompletion menu. Offset is the offset
312 """ Pops up an autocompletion menu. Offset is the offset
312 in characters of the position at which the menu should
313 in characters of the position at which the menu should
313 appear, relativ to the cursor.
314 appear, relativ to the cursor.
314 """
315 """
315 self.AutoCompSetIgnoreCase(False)
316 self.AutoCompSetIgnoreCase(False)
316 self.AutoCompSetAutoHide(False)
317 self.AutoCompSetAutoHide(False)
317 self.AutoCompSetMaxHeight(len(possibilities))
318 self.AutoCompSetMaxHeight(len(possibilities))
318 self.AutoCompShow(offset, " ".join(possibilities))
319 self.AutoCompShow(offset, " ".join(possibilities))
319
320
320
321
321 def scroll_to_bottom(self):
322 def scroll_to_bottom(self):
322 maxrange = self.GetScrollRange(wx.VERTICAL)
323 maxrange = self.GetScrollRange(wx.VERTICAL)
323 self.ScrollLines(maxrange)
324 self.ScrollLines(maxrange)
324
325
325
326
326 def _on_key_down(self, event, skip=True):
327 def _on_key_down(self, event, skip=True):
327 """ Key press callback used for correcting behavior for
328 """ Key press callback used for correcting behavior for
328 console-like interfaces: the cursor is constraint to be after
329 console-like interfaces: the cursor is constraint to be after
329 the last prompt.
330 the last prompt.
330
331
331 Return True if event as been catched.
332 Return True if event as been catched.
332 """
333 """
333 catched = True
334 catched = True
334 # Intercept some specific keys.
335 # Intercept some specific keys.
335 if event.KeyCode == ord('L') and event.ControlDown() :
336 if event.KeyCode == ord('L') and event.ControlDown() :
336 self.scroll_to_bottom()
337 self.scroll_to_bottom()
337 elif event.KeyCode == ord('K') and event.ControlDown() :
338 elif event.KeyCode == ord('K') and event.ControlDown() :
338 self.replace_current_edit_buffer('')
339 self.replace_current_edit_buffer('')
339 elif event.KeyCode == wx.WXK_PAGEUP and event.ShiftDown():
340 elif event.KeyCode == wx.WXK_PAGEUP and event.ShiftDown():
340 self.ScrollPages(-1)
341 self.ScrollPages(-1)
341 elif event.KeyCode == wx.WXK_PAGEDOWN and event.ShiftDown():
342 elif event.KeyCode == wx.WXK_PAGEDOWN and event.ShiftDown():
342 self.ScrollPages(1)
343 self.ScrollPages(1)
343 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
344 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
344 self.ScrollLines(-1)
345 self.ScrollLines(-1)
345 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
346 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
346 self.ScrollLines(1)
347 self.ScrollLines(1)
347 else:
348 else:
348 catched = False
349 catched = False
349
350
350 if self.AutoCompActive():
351 if self.AutoCompActive():
351 event.Skip()
352 event.Skip()
352 else:
353 else:
353 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
354 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
354 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
355 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
355 catched = True
356 catched = True
356 self.CallTipCancel()
357 self.CallTipCancel()
357 self.write('\n')
358 self.write('\n')
358 # Under windows scintilla seems to be doing funny stuff to the
359 # Under windows scintilla seems to be doing funny stuff to the
359 # line returns here, but get_current_edit_buffer filters this
360 # line returns here, but get_current_edit_buffer filters this
360 # out.
361 # out.
361 if sys.platform == 'win32':
362 if sys.platform == 'win32':
362 self.replace_current_edit_buffer(
363 self.replace_current_edit_buffer(
363 self.get_current_edit_buffer())
364 self.get_current_edit_buffer())
364 self._on_enter()
365 self._on_enter()
365
366
366 elif event.KeyCode == wx.WXK_HOME:
367 elif event.KeyCode == wx.WXK_HOME:
367 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
368 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
368 self.GotoPos(self.current_prompt_pos)
369 self.GotoPos(self.current_prompt_pos)
369 catched = True
370 catched = True
370
371
371 elif event.Modifiers in (wx.MOD_SHIFT, wx.MOD_WIN) :
372 elif event.Modifiers in (wx.MOD_SHIFT, wx.MOD_WIN) :
372 # FIXME: This behavior is not ideal: if the selection
373 # FIXME: This behavior is not ideal: if the selection
373 # is already started, it will jump.
374 # is already started, it will jump.
374 self.SetSelectionStart(self.current_prompt_pos)
375 self.SetSelectionStart(self.current_prompt_pos)
375 self.SetSelectionEnd(self.GetCurrentPos())
376 self.SetSelectionEnd(self.GetCurrentPos())
376 catched = True
377 catched = True
377
378
378 elif event.KeyCode == wx.WXK_UP:
379 elif event.KeyCode == wx.WXK_UP:
379 if self.GetCurrentLine() > self.current_prompt_line:
380 if self.GetCurrentLine() > self.current_prompt_line:
380 if self.GetCurrentLine() == self.current_prompt_line + 1 \
381 if self.GetCurrentLine() == self.current_prompt_line + 1 \
381 and self.GetColumn(self.GetCurrentPos()) < \
382 and self.GetColumn(self.GetCurrentPos()) < \
382 self.GetColumn(self.current_prompt_pos):
383 self.GetColumn(self.current_prompt_pos):
383 self.GotoPos(self.current_prompt_pos)
384 self.GotoPos(self.current_prompt_pos)
384 else:
385 else:
385 event.Skip()
386 event.Skip()
386 catched = True
387 catched = True
387
388
388 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
389 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
389 if self.GetCurrentPos() > self.current_prompt_pos:
390 if self.GetCurrentPos() > self.current_prompt_pos:
390 event.Skip()
391 event.Skip()
391 catched = True
392 catched = True
392
393
393 if skip and not catched:
394 if skip and not catched:
394 # Put the cursor back in the edit region
395 # Put the cursor back in the edit region
395 if self.GetCurrentPos() < self.current_prompt_pos:
396 if self.GetCurrentPos() < self.current_prompt_pos:
396 self.GotoPos(self.current_prompt_pos)
397 self.GotoPos(self.current_prompt_pos)
397 else:
398 else:
398 event.Skip()
399 event.Skip()
399
400
400 return catched
401 return catched
401
402
402
403
403 def _on_key_up(self, event, skip=True):
404 def _on_key_up(self, event, skip=True):
404 """ If cursor is outside the editing region, put it back.
405 """ If cursor is outside the editing region, put it back.
405 """
406 """
406 event.Skip()
407 event.Skip()
407 if self.GetCurrentPos() < self.current_prompt_pos:
408 if self.GetCurrentPos() < self.current_prompt_pos:
408 self.GotoPos(self.current_prompt_pos)
409 self.GotoPos(self.current_prompt_pos)
409
410
410
411
411
412
412
413
413 if __name__ == '__main__':
414 if __name__ == '__main__':
414 # Some simple code to test the console widget.
415 # Some simple code to test the console widget.
415 class MainWindow(wx.Frame):
416 class MainWindow(wx.Frame):
416 def __init__(self, parent, id, title):
417 def __init__(self, parent, id, title):
417 wx.Frame.__init__(self, parent, id, title, size=(300,250))
418 wx.Frame.__init__(self, parent, id, title, size=(300,250))
418 self._sizer = wx.BoxSizer(wx.VERTICAL)
419 self._sizer = wx.BoxSizer(wx.VERTICAL)
419 self.console_widget = ConsoleWidget(self)
420 self.console_widget = ConsoleWidget(self)
420 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
421 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
421 self.SetSizer(self._sizer)
422 self.SetSizer(self._sizer)
422 self.SetAutoLayout(1)
423 self.SetAutoLayout(1)
423 self.Show(True)
424 self.Show(True)
424
425
425 app = wx.PySimpleApp()
426 app = wx.PySimpleApp()
426 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
427 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
427 w.SetSize((780, 460))
428 w.SetSize((780, 460))
428 w.Show()
429 w.Show()
429
430
430 app.MainLoop()
431 app.MainLoop()
431
432
432
433
@@ -1,75 +1,75 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 import wx
6 import wx
7 from wx_frontend import WxController
7 from wx_frontend import WxController
8 import __builtin__
8 import __builtin__
9
9
10 class IPythonXController(WxController):
10 class IPythonXController(WxController):
11 """ Sub class of WxController that adds some application-specific
11 """ Sub class of WxController that adds some application-specific
12 bindings.
12 bindings.
13 """
13 """
14
14
15 def __init__(self, *args, **kwargs):
15 def __init__(self, *args, **kwargs):
16 WxController.__init__(self, *args, **kwargs)
16 WxController.__init__(self, *args, **kwargs)
17 self.ipython0.ask_exit = self.do_exit
17 self.ipython0.ask_exit = self.do_exit
18
18
19
19
20 def _on_key_down(self, event, skip=True):
20 def _on_key_down(self, event, skip=True):
21 # Intercept Ctrl-D to quit
21 # Intercept Ctrl-D to quit
22 if event.KeyCode == ord('D') and event.ControlDown() and \
22 if event.KeyCode == ord('D') and event.ControlDown() and \
23 self.get_current_edit_buffer()=='' and \
23 self.get_current_edit_buffer()=='' and \
24 not self.raw_input == __builtin__.raw_input:
24 self._input_state == 'readline':
25 wx.CallAfter(self.ask_exit)
25 wx.CallAfter(self.ask_exit)
26 else:
26 else:
27 WxController._on_key_down(self, event, skip=skip)
27 WxController._on_key_down(self, event, skip=skip)
28
28
29
29
30 def ask_exit(self):
30 def ask_exit(self):
31 """ Ask the user whether to exit.
31 """ Ask the user whether to exit.
32 """
32 """
33 self.write('\n')
33 self.write('\n')
34 self.capture_output()
34 self.capture_output()
35 self.ipython0.shell.exit()
35 self.ipython0.shell.exit()
36 self.release_output()
36 self.release_output()
37 wx.Yield()
37 wx.Yield()
38 if not self.ipython0.exit_now:
38 if not self.ipython0.exit_now:
39 self.new_prompt(self.prompt % (self.last_result['number'] + 1))
39 self.new_prompt(self.prompt % (self.last_result['number'] + 1))
40
40
41
41
42 def do_exit(self):
42 def do_exit(self):
43 """ Exits the interpreter, kills the windows.
43 """ Exits the interpreter, kills the windows.
44 """
44 """
45 WxController.do_exit(self)
45 WxController.do_exit(self)
46 self.release_output()
46 self.release_output()
47 wx.CallAfter(wx.Exit)
47 wx.CallAfter(wx.Exit)
48
48
49
49
50
50
51 class IPythonX(wx.Frame):
51 class IPythonX(wx.Frame):
52 """ Main frame of the IPythonX app.
52 """ Main frame of the IPythonX app.
53 """
53 """
54
54
55 def __init__(self, parent, id, title):
55 def __init__(self, parent, id, title):
56 wx.Frame.__init__(self, parent, id, title, size=(300,250))
56 wx.Frame.__init__(self, parent, id, title, size=(300,250))
57 self._sizer = wx.BoxSizer(wx.VERTICAL)
57 self._sizer = wx.BoxSizer(wx.VERTICAL)
58 self.shell = IPythonXController(self)
58 self.shell = IPythonXController(self)
59 self._sizer.Add(self.shell, 1, wx.EXPAND)
59 self._sizer.Add(self.shell, 1, wx.EXPAND)
60 self.SetSizer(self._sizer)
60 self.SetSizer(self._sizer)
61 self.SetAutoLayout(1)
61 self.SetAutoLayout(1)
62 self.Show(True)
62 self.Show(True)
63
63
64
64
65 def main():
65 def main():
66 app = wx.PySimpleApp()
66 app = wx.PySimpleApp()
67 frame = IPythonX(None, wx.ID_ANY, 'IPythonX')
67 frame = IPythonX(None, wx.ID_ANY, 'IPythonX')
68 frame.shell.SetFocus()
68 frame.shell.SetFocus()
69 frame.shell.app = app
69 frame.shell.app = app
70 frame.SetSize((680, 460))
70 frame.SetSize((680, 460))
71
71
72 app.MainLoop()
72 app.MainLoop()
73
73
74 if __name__ == '__main__':
74 if __name__ == '__main__':
75 main()
75 main()
@@ -1,397 +1,398 b''
1 # encoding: utf-8 -*- test-case-name:
1 # encoding: utf-8 -*- test-case-name:
2 # FIXME: Need to add tests.
2 # FIXME: Need to add tests.
3 # ipython1.frontend.cocoa.tests.test_cocoa_frontend -*-
3 # ipython1.frontend.cocoa.tests.test_cocoa_frontend -*-
4
4
5 """Classes to provide a Wx frontend to the
5 """Classes to provide a Wx frontend to the
6 IPython.kernel.core.interpreter.
6 IPython.kernel.core.interpreter.
7
7
8 """
8 """
9
9
10 __docformat__ = "restructuredtext en"
10 __docformat__ = "restructuredtext en"
11
11
12 #-------------------------------------------------------------------------------
12 #-------------------------------------------------------------------------------
13 # Copyright (C) 2008 The IPython Development Team
13 # Copyright (C) 2008 The IPython Development Team
14 #
14 #
15 # Distributed under the terms of the BSD License. The full license is in
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
16 # the file COPYING, distributed as part of this software.
17 #-------------------------------------------------------------------------------
17 #-------------------------------------------------------------------------------
18
18
19 #-------------------------------------------------------------------------------
19 #-------------------------------------------------------------------------------
20 # Imports
20 # Imports
21 #-------------------------------------------------------------------------------
21 #-------------------------------------------------------------------------------
22
22
23
23
24 import wx
24 import wx
25 import re
25 import re
26 from wx import stc
26 from wx import stc
27 from console_widget import ConsoleWidget
27 from console_widget import ConsoleWidget
28 import __builtin__
28 import __builtin__
29 from time import sleep
29 from time import sleep
30 import sys
30 import sys
31 import signal
31 import signal
32
32
33 from threading import Lock
33 from threading import Lock
34
34
35 from IPython.frontend.piped_process import PipedProcess
35 from IPython.frontend.piped_process import PipedProcess
36 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
36 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
37
37
38 #_COMMAND_BG = '#FAFAF1' # Nice green
38 #_COMMAND_BG = '#FAFAF1' # Nice green
39 _RUNNING_BUFFER_BG = '#FDFFD3' # Nice yellow
39 _RUNNING_BUFFER_BG = '#FDFFD3' # Nice yellow
40 _ERROR_BG = '#FFF1F1' # Nice red
40 _ERROR_BG = '#FFF1F1' # Nice red
41
41
42 _RUNNING_BUFFER_MARKER = 31
42 _RUNNING_BUFFER_MARKER = 31
43 _ERROR_MARKER = 30
43 _ERROR_MARKER = 30
44
44
45 #-------------------------------------------------------------------------------
45 #-------------------------------------------------------------------------------
46 # Classes to implement the Wx frontend
46 # Classes to implement the Wx frontend
47 #-------------------------------------------------------------------------------
47 #-------------------------------------------------------------------------------
48 class WxController(PrefilterFrontEnd, ConsoleWidget):
48 class WxController(PrefilterFrontEnd, ConsoleWidget):
49
49
50 output_prompt = \
50 output_prompt = \
51 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02%i\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
51 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02%i\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
52
52
53 # Print debug info on what is happening to the console.
53 # Print debug info on what is happening to the console.
54 debug = True
54 debug = True
55
55
56 # The title of the terminal, as captured through the ANSI escape
56 # The title of the terminal, as captured through the ANSI escape
57 # sequences.
57 # sequences.
58
58
59 def _set_title(self, title):
59 def _set_title(self, title):
60 return self.Parent.SetTitle(title)
60 return self.Parent.SetTitle(title)
61
61
62 def _get_title(self):
62 def _get_title(self):
63 return self.Parent.GetTitle()
63 return self.Parent.GetTitle()
64
64
65 title = property(_get_title, _set_title)
65 title = property(_get_title, _set_title)
66
66
67 #--------------------------------------------------------------------------
67 #--------------------------------------------------------------------------
68 # Private Attributes
68 # Private Attributes
69 #--------------------------------------------------------------------------
69 #--------------------------------------------------------------------------
70
70
71 # A flag governing the behavior of the input. Can be:
71 # A flag governing the behavior of the input. Can be:
72 #
72 #
73 # 'readline' for readline-like behavior with a prompt
73 # 'readline' for readline-like behavior with a prompt
74 # and an edit buffer.
74 # and an edit buffer.
75 # 'subprocess' for sending the raw input directly to a
75 # 'subprocess' for sending the raw input directly to a
76 # subprocess.
76 # subprocess.
77 # 'buffering' for buffering of the input, that will be used
77 # 'buffering' for buffering of the input, that will be used
78 # when the input state switches back to another state.
78 # when the input state switches back to another state.
79 _input_state = 'readline'
79 _input_state = 'readline'
80
80
81 # Attribute to store reference to the pipes of a subprocess, if we
81 # Attribute to store reference to the pipes of a subprocess, if we
82 # are running any.
82 # are running any.
83 _running_process = False
83 _running_process = False
84
84
85 # A queue for writing fast streams to the screen without flooding the
85 # A queue for writing fast streams to the screen without flooding the
86 # event loop
86 # event loop
87 _out_buffer = []
87 _out_buffer = []
88
88
89 # A lock to lock the _out_buffer to make sure we don't empty it
89 # A lock to lock the _out_buffer to make sure we don't empty it
90 # while it is being swapped
90 # while it is being swapped
91 _out_buffer_lock = Lock()
91 _out_buffer_lock = Lock()
92
92
93 #--------------------------------------------------------------------------
93 #--------------------------------------------------------------------------
94 # Public API
94 # Public API
95 #--------------------------------------------------------------------------
95 #--------------------------------------------------------------------------
96
96
97 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
97 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
98 size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
98 size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
99 *args, **kwds):
99 *args, **kwds):
100 """ Create Shell instance.
100 """ Create Shell instance.
101 """
101 """
102 ConsoleWidget.__init__(self, parent, id, pos, size, style)
102 ConsoleWidget.__init__(self, parent, id, pos, size, style)
103 PrefilterFrontEnd.__init__(self)
103 PrefilterFrontEnd.__init__(self)
104
104
105 # Marker for running buffer.
105 # Marker for running buffer.
106 self.MarkerDefine(_RUNNING_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
106 self.MarkerDefine(_RUNNING_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
107 background=_RUNNING_BUFFER_BG)
107 background=_RUNNING_BUFFER_BG)
108 # Marker for tracebacks.
108 # Marker for tracebacks.
109 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
109 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
110 background=_ERROR_BG)
110 background=_ERROR_BG)
111
111
112 # A time for flushing the write buffer
112 # A time for flushing the write buffer
113 BUFFER_FLUSH_TIMER_ID = 100
113 BUFFER_FLUSH_TIMER_ID = 100
114 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
114 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
115 wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)
115 wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)
116
116
117 def do_completion(self):
117 def do_completion(self):
118 """ Do code completion.
118 """ Do code completion.
119 """
119 """
120 if self.debug:
120 if self.debug:
121 print >>sys.__stdout__, "do_completion",
121 print >>sys.__stdout__, "do_completion",
122 line = self.get_current_edit_buffer()
122 line = self.get_current_edit_buffer()
123 new_line, completions = self.complete(line)
123 new_line, completions = self.complete(line)
124 if len(completions)>1:
124 if len(completions)>1:
125 self.write_completion(completions)
125 self.write_completion(completions)
126 self.replace_current_edit_buffer(new_line)
126 self.replace_current_edit_buffer(new_line)
127 if self.debug:
127 if self.debug:
128 print >>sys.__stdout__, completions
128 print >>sys.__stdout__, completions
129
129
130
130
131 def do_calltip(self):
131 def do_calltip(self):
132 if self.debug:
132 if self.debug:
133 print >>sys.__stdout__, "do_calltip"
133 print >>sys.__stdout__, "do_calltip"
134 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
134 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
135 symbol = self.get_current_edit_buffer()
135 symbol = self.get_current_edit_buffer()
136 symbol_string = separators.split(symbol)[-1]
136 symbol_string = separators.split(symbol)[-1]
137 base_symbol_string = symbol_string.split('.')[0]
137 base_symbol_string = symbol_string.split('.')[0]
138 if base_symbol_string in self.shell.user_ns:
138 if base_symbol_string in self.shell.user_ns:
139 symbol = self.shell.user_ns[base_symbol_string]
139 symbol = self.shell.user_ns[base_symbol_string]
140 elif base_symbol_string in self.shell.user_global_ns:
140 elif base_symbol_string in self.shell.user_global_ns:
141 symbol = self.shell.user_global_ns[base_symbol_string]
141 symbol = self.shell.user_global_ns[base_symbol_string]
142 elif base_symbol_string in __builtin__.__dict__:
142 elif base_symbol_string in __builtin__.__dict__:
143 symbol = __builtin__.__dict__[base_symbol_string]
143 symbol = __builtin__.__dict__[base_symbol_string]
144 else:
144 else:
145 return False
145 return False
146 for name in symbol_string.split('.')[1:] + ['__doc__']:
146 for name in symbol_string.split('.')[1:] + ['__doc__']:
147 symbol = getattr(symbol, name)
147 symbol = getattr(symbol, name)
148 try:
148 try:
149 self.AutoCompCancel()
149 self.AutoCompCancel()
150 wx.Yield()
150 wx.Yield()
151 self.CallTipShow(self.GetCurrentPos(), symbol)
151 self.CallTipShow(self.GetCurrentPos(), symbol)
152 except:
152 except:
153 # The retrieve symbol couldn't be converted to a string
153 # The retrieve symbol couldn't be converted to a string
154 pass
154 pass
155
155
156
156
157 def popup_completion(self, create=False):
157 def popup_completion(self, create=False):
158 """ Updates the popup completion menu if it exists. If create is
158 """ Updates the popup completion menu if it exists. If create is
159 true, open the menu.
159 true, open the menu.
160 """
160 """
161 if self.debug:
161 if self.debug:
162 print >>sys.__stdout__, "popup_completion",
162 print >>sys.__stdout__, "popup_completion",
163 line = self.get_current_edit_buffer()
163 line = self.get_current_edit_buffer()
164 if (self.AutoCompActive() and not line[-1] == '.') \
164 if (self.AutoCompActive() and not line[-1] == '.') \
165 or create==True:
165 or create==True:
166 suggestion, completions = self.complete(line)
166 suggestion, completions = self.complete(line)
167 offset=0
167 offset=0
168 if completions:
168 if completions:
169 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
169 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
170 residual = complete_sep.split(line)[-1]
170 residual = complete_sep.split(line)[-1]
171 offset = len(residual)
171 offset = len(residual)
172 self.pop_completion(completions, offset=offset)
172 self.pop_completion(completions, offset=offset)
173 if self.debug:
173 if self.debug:
174 print >>sys.__stdout__, completions
174 print >>sys.__stdout__, completions
175
175
176
176
177 def new_prompt(self, prompt):
177 def new_prompt(self, prompt):
178 self._input_state = 'readline'
178 self._input_state = 'readline'
179 ConsoleWidget.new_prompt(self, prompt)
179 ConsoleWidget.new_prompt(self, prompt)
180
180
181
181
182 def raw_input(self, prompt):
182 def raw_input(self, prompt):
183 """ A replacement from python's raw_input.
183 """ A replacement from python's raw_input.
184 """
184 """
185 self.new_prompt(prompt)
185 self.new_prompt(prompt)
186 self.waiting = True
186 self.waiting = True
187 self.__old_on_enter = self._on_enter
187 self.__old_on_enter = self._on_enter
188 def my_on_enter():
188 def my_on_enter():
189 self.waiting = False
189 self.waiting = False
190 self._on_enter = my_on_enter
190 self._on_enter = my_on_enter
191 # XXX: Busy waiting, ugly.
191 # XXX: Busy waiting, ugly.
192 while self.waiting:
192 while self.waiting:
193 wx.Yield()
193 wx.Yield()
194 sleep(0.1)
194 sleep(0.1)
195 self._on_enter = self.__old_on_enter
195 self._on_enter = self.__old_on_enter
196 self._input_state = 'buffering'
196 self._input_state = 'buffering'
197 return self.get_current_edit_buffer().rstrip('\n')
197 return self.get_current_edit_buffer().rstrip('\n')
198
198
199
199
200 def execute(self, python_string, raw_string=None):
200 def execute(self, python_string, raw_string=None):
201 self._input_state = 'buffering'
201 self._input_state = 'buffering'
202 self.CallTipCancel()
202 self.CallTipCancel()
203 self._cursor = wx.BusyCursor()
203 self._cursor = wx.BusyCursor()
204 if raw_string is None:
204 if raw_string is None:
205 raw_string = python_string
205 raw_string = python_string
206 end_line = self.current_prompt_line \
206 end_line = self.current_prompt_line \
207 + max(1, len(raw_string.split('\n'))-1)
207 + max(1, len(raw_string.split('\n'))-1)
208 for i in range(self.current_prompt_line, end_line):
208 for i in range(self.current_prompt_line, end_line):
209 self.MarkerAdd(i, _RUNNING_BUFFER_MARKER)
209 self.MarkerAdd(i, _RUNNING_BUFFER_MARKER)
210 # Update the display:
210 # Update the display:
211 wx.Yield()
211 wx.Yield()
212 self.GotoPos(self.GetLength())
212 self.GotoPos(self.GetLength())
213 PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string)
213 PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string)
214
214
215
215
216 def capture_output(self):
216 def capture_output(self):
217 self.__old_raw_input = __builtin__.raw_input
217 self.__old_raw_input = __builtin__.raw_input
218 __builtin__.raw_input = self.raw_input
218 __builtin__.raw_input = self.raw_input
219 PrefilterFrontEnd.capture_output(self)
219 PrefilterFrontEnd.capture_output(self)
220
220
221
221
222 def release_output(self):
222 def release_output(self):
223 __builtin__.raw_input = self.__old_raw_input
223 __builtin__.raw_input = self.__old_raw_input
224 PrefilterFrontEnd.capture_output(self)
224 PrefilterFrontEnd.capture_output(self)
225
225
226
226
227 def after_execute(self):
227 def after_execute(self):
228 PrefilterFrontEnd.after_execute(self)
228 PrefilterFrontEnd.after_execute(self)
229 if hasattr(self, '_cursor'):
229 if hasattr(self, '_cursor'):
230 del self._cursor
230 del self._cursor
231
231
232
232
233 def system_call(self, command_string):
233 def system_call(self, command_string):
234 self._input_state = 'subprocess'
234 self._input_state = 'subprocess'
235 self._running_process = PipedProcess(command_string,
235 self._running_process = PipedProcess(command_string,
236 out_callback=self.buffered_write,
236 out_callback=self.buffered_write,
237 end_callback = self._end_system_call)
237 end_callback = self._end_system_call)
238 self._running_process.start()
238 self._running_process.start()
239 # XXX: another one of these polling loops to have a blocking
239 # XXX: another one of these polling loops to have a blocking
240 # call
240 # call
241 wx.Yield()
241 wx.Yield()
242 while self._running_process:
242 while self._running_process:
243 wx.Yield()
243 wx.Yield()
244 sleep(0.1)
244 sleep(0.1)
245 # Be sure to flush the buffer.
245 # Be sure to flush the buffer.
246 self._buffer_flush(event=None)
246 self._buffer_flush(event=None)
247
247
248
248
249 def buffered_write(self, text):
249 def buffered_write(self, text):
250 """ A write method for streams, that caches the stream in order
250 """ A write method for streams, that caches the stream in order
251 to avoid flooding the event loop.
251 to avoid flooding the event loop.
252
252
253 This can be called outside of the main loop, in separate
253 This can be called outside of the main loop, in separate
254 threads.
254 threads.
255 """
255 """
256 self._out_buffer_lock.acquire()
256 self._out_buffer_lock.acquire()
257 self._out_buffer.append(text)
257 self._out_buffer.append(text)
258 self._out_buffer_lock.release()
258 self._out_buffer_lock.release()
259 if not self._buffer_flush_timer.IsRunning():
259 if not self._buffer_flush_timer.IsRunning():
260 self._buffer_flush_timer.Start(100) # milliseconds
260 wx.CallAfter(self._buffer_flush_timer.Start, 100) # milliseconds
261
261
262
262
263 def show_traceback(self):
263 def show_traceback(self):
264 start_line = self.GetCurrentLine()
264 start_line = self.GetCurrentLine()
265 PrefilterFrontEnd.show_traceback(self)
265 PrefilterFrontEnd.show_traceback(self)
266 wx.Yield()
266 wx.Yield()
267 for i in range(start_line, self.GetCurrentLine()):
267 for i in range(start_line, self.GetCurrentLine()):
268 self.MarkerAdd(i, _ERROR_MARKER)
268 self.MarkerAdd(i, _ERROR_MARKER)
269
269
270
270
271 #--------------------------------------------------------------------------
271 #--------------------------------------------------------------------------
272 # Private API
272 # Private API
273 #--------------------------------------------------------------------------
273 #--------------------------------------------------------------------------
274
274
275 def _on_key_down(self, event, skip=True):
275 def _on_key_down(self, event, skip=True):
276 """ Capture the character events, let the parent
276 """ Capture the character events, let the parent
277 widget handle them, and put our logic afterward.
277 widget handle them, and put our logic afterward.
278 """
278 """
279 print >>sys.__stderr__, event.KeyCode
279 print >>sys.__stderr__, event.KeyCode
280 current_line_number = self.GetCurrentLine()
280 current_line_number = self.GetCurrentLine()
281 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
281 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
282 # Capture Control-C
282 # Capture Control-C
283 if self._input_state == 'subprocess':
283 if self._input_state == 'subprocess':
284 if self.debug:
284 if self.debug:
285 print >>sys.__stderr__, 'Killing running process'
285 print >>sys.__stderr__, 'Killing running process'
286 self._running_process.process.kill()
286 self._running_process.process.kill()
287 elif self._input_state == 'buffering':
287 elif self._input_state == 'buffering':
288 if self.debug:
288 if self.debug:
289 print >>sys.__stderr__, 'Raising KeyboardException'
289 print >>sys.__stderr__, 'Raising KeyboardException'
290 raise KeyboardException
290 raise KeyboardException
291 # XXX: We need to make really sure we
291 # XXX: We need to make really sure we
292 # get back to a prompt.
292 # get back to a prompt.
293 elif self._input_state == 'subprocess' and event.KeyCode<256 \
293 elif self._input_state == 'subprocess' and event.KeyCode<256 \
294 and event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
294 and event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
295 # We are running a process, we redirect keys.
295 # We are running a process, we redirect keys.
296 ConsoleWidget._on_key_down(self, event, skip=skip)
296 ConsoleWidget._on_key_down(self, event, skip=skip)
297 if self.debug:
297 if self.debug:
298 print >>sys.__stderr__, chr(event.KeyCode)
298 print >>sys.__stderr__, chr(event.KeyCode)
299 self._running_process.process.stdin.write(chr(event.KeyCode))
299 self._running_process.process.stdin.write(chr(event.KeyCode))
300 self._running_process.process.stdin.flush()
300 elif event.KeyCode in (ord('('), 57):
301 elif event.KeyCode in (ord('('), 57):
301 # Calltips
302 # Calltips
302 event.Skip()
303 event.Skip()
303 self.do_calltip()
304 self.do_calltip()
304 elif self.AutoCompActive():
305 elif self.AutoCompActive():
305 event.Skip()
306 event.Skip()
306 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
307 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
307 wx.CallAfter(self.popup_completion, create=True)
308 wx.CallAfter(self.popup_completion, create=True)
308 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
309 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
309 wx.WXK_RIGHT):
310 wx.WXK_RIGHT):
310 wx.CallAfter(self.popup_completion)
311 wx.CallAfter(self.popup_completion)
311 else:
312 else:
312 # Up history
313 # Up history
313 if event.KeyCode == wx.WXK_UP and (
314 if event.KeyCode == wx.WXK_UP and (
314 ( current_line_number == self.current_prompt_line and
315 ( current_line_number == self.current_prompt_line and
315 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
316 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
316 or event.ControlDown() ):
317 or event.ControlDown() ):
317 new_buffer = self.get_history_previous(
318 new_buffer = self.get_history_previous(
318 self.get_current_edit_buffer())
319 self.get_current_edit_buffer())
319 if new_buffer is not None:
320 if new_buffer is not None:
320 self.replace_current_edit_buffer(new_buffer)
321 self.replace_current_edit_buffer(new_buffer)
321 if self.GetCurrentLine() > self.current_prompt_line:
322 if self.GetCurrentLine() > self.current_prompt_line:
322 # Go to first line, for seemless history up.
323 # Go to first line, for seemless history up.
323 self.GotoPos(self.current_prompt_pos)
324 self.GotoPos(self.current_prompt_pos)
324 # Down history
325 # Down history
325 elif event.KeyCode == wx.WXK_DOWN and (
326 elif event.KeyCode == wx.WXK_DOWN and (
326 ( current_line_number == self.LineCount -1 and
327 ( current_line_number == self.LineCount -1 and
327 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
328 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
328 or event.ControlDown() ):
329 or event.ControlDown() ):
329 new_buffer = self.get_history_next()
330 new_buffer = self.get_history_next()
330 if new_buffer is not None:
331 if new_buffer is not None:
331 self.replace_current_edit_buffer(new_buffer)
332 self.replace_current_edit_buffer(new_buffer)
332 # Tab-completion
333 # Tab-completion
333 elif event.KeyCode == ord('\t'):
334 elif event.KeyCode == ord('\t'):
334 last_line = self.get_current_edit_buffer().split('\n')[-1]
335 last_line = self.get_current_edit_buffer().split('\n')[-1]
335 if not re.match(r'^\s*$', last_line):
336 if not re.match(r'^\s*$', last_line):
336 self.do_completion()
337 self.do_completion()
337 else:
338 else:
338 event.Skip()
339 event.Skip()
339 else:
340 else:
340 ConsoleWidget._on_key_down(self, event, skip=skip)
341 ConsoleWidget._on_key_down(self, event, skip=skip)
341
342
342
343
343 def _on_key_up(self, event, skip=True):
344 def _on_key_up(self, event, skip=True):
344 if event.KeyCode in (59, ord('.')):
345 if event.KeyCode in (59, ord('.')):
345 # Intercepting '.'
346 # Intercepting '.'
346 event.Skip()
347 event.Skip()
347 self.popup_completion(create=True)
348 self.popup_completion(create=True)
348 else:
349 else:
349 ConsoleWidget._on_key_up(self, event, skip=skip)
350 ConsoleWidget._on_key_up(self, event, skip=skip)
350
351
351
352
352 def _on_enter(self):
353 def _on_enter(self):
353 if self.debug:
354 if self.debug:
354 print >>sys.__stdout__, repr(self.get_current_edit_buffer())
355 print >>sys.__stdout__, repr(self.get_current_edit_buffer())
355 PrefilterFrontEnd._on_enter(self)
356 PrefilterFrontEnd._on_enter(self)
356
357
357
358
358 def _end_system_call(self):
359 def _end_system_call(self):
359 """ Called at the end of a system call.
360 """ Called at the end of a system call.
360 """
361 """
361 print >>sys.__stderr__, 'End of system call'
362 print >>sys.__stderr__, 'End of system call'
362 self._input_state = 'buffering'
363 self._input_state = 'buffering'
363 self._running_process = False
364 self._running_process = False
364
365
365
366
366 def _buffer_flush(self, event):
367 def _buffer_flush(self, event):
367 """ Called by the timer to flush the write buffer.
368 """ Called by the timer to flush the write buffer.
368
369
369 This is always called in the mainloop, by the wx timer.
370 This is always called in the mainloop, by the wx timer.
370 """
371 """
371 self._out_buffer_lock.acquire()
372 self._out_buffer_lock.acquire()
372 _out_buffer = self._out_buffer
373 _out_buffer = self._out_buffer
373 self._out_buffer = []
374 self._out_buffer = []
374 self._out_buffer_lock.release()
375 self._out_buffer_lock.release()
375 self.write(''.join(_out_buffer))
376 self.write(''.join(_out_buffer), refresh=False)
376 self._buffer_flush_timer.Stop()
377 self._buffer_flush_timer.Stop()
377
378
378
379
379 if __name__ == '__main__':
380 if __name__ == '__main__':
380 class MainWindow(wx.Frame):
381 class MainWindow(wx.Frame):
381 def __init__(self, parent, id, title):
382 def __init__(self, parent, id, title):
382 wx.Frame.__init__(self, parent, id, title, size=(300,250))
383 wx.Frame.__init__(self, parent, id, title, size=(300,250))
383 self._sizer = wx.BoxSizer(wx.VERTICAL)
384 self._sizer = wx.BoxSizer(wx.VERTICAL)
384 self.shell = WxController(self)
385 self.shell = WxController(self)
385 self._sizer.Add(self.shell, 1, wx.EXPAND)
386 self._sizer.Add(self.shell, 1, wx.EXPAND)
386 self.SetSizer(self._sizer)
387 self.SetSizer(self._sizer)
387 self.SetAutoLayout(1)
388 self.SetAutoLayout(1)
388 self.Show(True)
389 self.Show(True)
389
390
390 app = wx.PySimpleApp()
391 app = wx.PySimpleApp()
391 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
392 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
392 frame.shell.SetFocus()
393 frame.shell.SetFocus()
393 frame.SetSize((680, 460))
394 frame.SetSize((680, 460))
394 self = frame.shell
395 self = frame.shell
395
396
396 app.MainLoop()
397 app.MainLoop()
397
398
General Comments 0
You need to be logged in to leave comments. Login now