##// END OF EJS Templates
Changed tabs to whitespace in the wx frontend...
Gael Varoquaux -
Show More
@@ -1,411 +1,411 b''
1 #!/usr/bin/python
1 #!/usr/bin/python
2 # -*- coding: iso-8859-15 -*-
2 # -*- coding: iso-8859-15 -*-
3 import wx
3 import wx
4 import wx.stc as stc
4 import wx.stc as stc
5 import keyword
5 import keyword
6
6
7 #-----------------------------------------
7 #-----------------------------------------
8 # History widget for IPython
8 # History widget for IPython
9 __version__ = 0.5
9 __version__ = 0.5
10 __author__ = "Laurent Dufrechou"
10 __author__ = "Laurent Dufrechou"
11 __email__ = "laurent.dufrechou _at_ gmail.com"
11 __email__ = "laurent.dufrechou _at_ gmail.com"
12 __license__ = "BSD"
12 __license__ = "BSD"
13 #-----------------------------------------
13 #-----------------------------------------
14 class IPythonHistoryPanel(wx.Panel):
14 class IPythonHistoryPanel(wx.Panel):
15
15
16 def __init__(self, parent,flt_empty=True,
16 def __init__(self, parent,flt_empty=True,
17 flt_doc=True,flt_cmd=True,flt_magic=True):
17 flt_doc=True,flt_cmd=True,flt_magic=True):
18
18
19 wx.Panel.__init__(self,parent,-1)
19 wx.Panel.__init__(self,parent,-1)
20 #text_ctrl = wx.TextCtrl(self, -1, style=wx.TE_MULTILINE)
20 #text_ctrl = wx.TextCtrl(self, -1, style=wx.TE_MULTILINE)
21 text_ctrl = PythonSTC(self, -1)
21 text_ctrl = PythonSTC(self, -1)
22
22
23
23
24 st_filt = wx.StaticText(self, -1, " Filter:")
24 st_filt = wx.StaticText(self, -1, " Filter:")
25
25
26 self.filter_empty = wx.CheckBox(self, -1, "Empty commands")
26 self.filter_empty = wx.CheckBox(self, -1, "Empty commands")
27 self.filter_doc = wx.CheckBox(self, -1, "?: Doc commands")
27 self.filter_doc = wx.CheckBox(self, -1, "?: Doc commands")
28 self.filter_cmd = wx.CheckBox(self, -1, "!: Sys commands")
28 self.filter_cmd = wx.CheckBox(self, -1, "!: Sys commands")
29 self.filter_magic = wx.CheckBox(self, -1, "%: Magic keys")
29 self.filter_magic = wx.CheckBox(self, -1, "%: Magic keys")
30
30
31 self.filter_empty.SetValue(flt_empty)
31 self.filter_empty.SetValue(flt_empty)
32 self.filter_doc.SetValue(flt_doc)
32 self.filter_doc.SetValue(flt_doc)
33 self.filter_cmd.SetValue(flt_cmd)
33 self.filter_cmd.SetValue(flt_cmd)
34 self.filter_magic.SetValue(flt_magic)
34 self.filter_magic.SetValue(flt_magic)
35
35
36 sizer = wx.BoxSizer(wx.VERTICAL)
36 sizer = wx.BoxSizer(wx.VERTICAL)
37
37
38 sizer.Add(text_ctrl, 1, wx.EXPAND)
38 sizer.Add(text_ctrl, 1, wx.EXPAND)
39 sizer.AddMany( [(5,5),
39 sizer.AddMany( [(5,5),
40 st_filt,
40 st_filt,
41 (10,10),
41 (10,10),
42 self.filter_empty,
42 self.filter_empty,
43 self.filter_doc,
43 self.filter_doc,
44 self.filter_cmd,
44 self.filter_cmd,
45 self.filter_magic,
45 self.filter_magic,
46 (10,10),
46 (10,10),
47 ])
47 ])
48 self.SetAutoLayout(True)
48 self.SetAutoLayout(True)
49 sizer.Fit(self)
49 sizer.Fit(self)
50 sizer.SetSizeHints(self)
50 sizer.SetSizeHints(self)
51 self.SetSizer(sizer)
51 self.SetSizer(sizer)
52 self.text_ctrl=text_ctrl
52 self.text_ctrl=text_ctrl
53 #text_ctrl.SetText(demoText + open('Main.py').read())
53 #text_ctrl.SetText(demoText + open('Main.py').read())
54 text_ctrl.EmptyUndoBuffer()
54 text_ctrl.EmptyUndoBuffer()
55 text_ctrl.Colourise(0, -1)
55 text_ctrl.Colourise(0, -1)
56
56
57 # line numbers in the margin
57 # line numbers in the margin
58 text_ctrl.SetMarginType(1, stc.STC_MARGIN_NUMBER)
58 text_ctrl.SetMarginType(1, stc.STC_MARGIN_NUMBER)
59 text_ctrl.SetMarginWidth(1, 15)
59 text_ctrl.SetMarginWidth(1, 15)
60
60
61
61
62 def write(self,history_line):
62 def write(self,history_line):
63 add = True
63 add = True
64 if self.filter_empty.GetValue() == True and history_line == '':
64 if self.filter_empty.GetValue() == True and history_line == '':
65 add = False
65 add = False
66 if len(history_line)>0:
66 if len(history_line)>0:
67 if self.filter_doc.GetValue() == True and history_line[-1:] == '?':
67 if self.filter_doc.GetValue() == True and history_line[-1:] == '?':
68 add = False
68 add = False
69 if self.filter_cmd.GetValue() == True and history_line[0] == '!':
69 if self.filter_cmd.GetValue() == True and history_line[0] == '!':
70 add = False
70 add = False
71 if self.filter_magic.GetValue() == True and history_line[0] == '%':
71 if self.filter_magic.GetValue() == True and history_line[0] == '%':
72 add = False
72 add = False
73 if add:
73 if add:
74 self.text_ctrl.AppendText(history_line+'\n')
74 self.text_ctrl.AppendText(history_line+'\n')
75
75
76
76
77 #----------------------------------------------------------------------
77 #----------------------------------------------------------------------
78 # Font definition for Styled Text Control
78 # Font definition for Styled Text Control
79
79
80 if wx.Platform == '__WXMSW__':
80 if wx.Platform == '__WXMSW__':
81 faces = { 'times': 'Times New Roman',
81 faces = { 'times': 'Times New Roman',
82 'mono' : 'Courier New',
82 'mono' : 'Courier New',
83 'helv' : 'Arial',
83 'helv' : 'Arial',
84 'other': 'Comic Sans MS',
84 'other': 'Comic Sans MS',
85 'size' : 8,
85 'size' : 8,
86 'size2': 6,
86 'size2': 6,
87 }
87 }
88 elif wx.Platform == '__WXMAC__':
88 elif wx.Platform == '__WXMAC__':
89 faces = { 'times': 'Times New Roman',
89 faces = { 'times': 'Times New Roman',
90 'mono' : 'Monaco',
90 'mono' : 'Monaco',
91 'helv' : 'Arial',
91 'helv' : 'Arial',
92 'other': 'Comic Sans MS',
92 'other': 'Comic Sans MS',
93 'size' : 8,
93 'size' : 8,
94 'size2': 6,
94 'size2': 6,
95 }
95 }
96 else:
96 else:
97 faces = { 'times': 'Times',
97 faces = { 'times': 'Times',
98 'mono' : 'Courier',
98 'mono' : 'Courier',
99 'helv' : 'Helvetica',
99 'helv' : 'Helvetica',
100 'other': 'new century schoolbook',
100 'other': 'new century schoolbook',
101 'size' : 8,
101 'size' : 8,
102 'size2': 6,
102 'size2': 6,
103 }
103 }
104
104
105
105
106 #----------------------------------------------------------------------
106 #----------------------------------------------------------------------
107
107
108 class PythonSTC(stc.StyledTextCtrl):
108 class PythonSTC(stc.StyledTextCtrl):
109
109
110 fold_symbols = 3
110 fold_symbols = 3
111
111
112 def __init__(self, parent, ID,
112 def __init__(self, parent, ID,
113 pos=wx.DefaultPosition, size=wx.DefaultSize,
113 pos=wx.DefaultPosition, size=wx.DefaultSize,
114 style=0):
114 style=0):
115 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
115 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
116 #self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
116 #self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
117 #self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
117 #self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
118
118
119 self.SetLexer(stc.STC_LEX_PYTHON)
119 self.SetLexer(stc.STC_LEX_PYTHON)
120 self.SetKeyWords(0, " ".join(keyword.kwlist))
120 self.SetKeyWords(0, " ".join(keyword.kwlist))
121
121
122 #self.SetProperty("fold", "1")
122 #self.SetProperty("fold", "1")
123 #self.SetProperty("tab.timmy.whinge.level", "1")
123 #self.SetProperty("tab.timmy.whinge.level", "1")
124 #self.SetMargins(0,0)
124 #self.SetMargins(0,0)
125
125
126 #self.SetViewWhiteSpace(False)
126 #self.SetViewWhiteSpace(False)
127 #self.SetBufferedDraw(False)
127 #self.SetBufferedDraw(False)
128 #self.SetViewEOL(True)
128 #self.SetViewEOL(True)
129 self.SetEOLMode(stc.STC_EOL_CRLF)
129 self.SetEOLMode(stc.STC_EOL_CRLF)
130 #self.SetUseAntiAliasing(True)
130 #self.SetUseAntiAliasing(True)
131
131
132 self.SetEdgeMode(stc.STC_EDGE_LINE)
132 self.SetEdgeMode(stc.STC_EDGE_LINE)
133 self.SetEdgeColumn(80)
133 self.SetEdgeColumn(80)
134 self.SetEdgeColour(wx.LIGHT_GREY)
134 self.SetEdgeColour(wx.LIGHT_GREY)
135 self.SetLayoutCache(stc.STC_CACHE_PAGE)
135 self.SetLayoutCache(stc.STC_CACHE_PAGE)
136
136
137 # Setup a margin to hold fold markers
137 # Setup a margin to hold fold markers
138 #self.SetFoldFlags(16) ### WHAT IS THIS VALUE? WHAT ARE THE OTHER FLAGS? DOES IT MATTER?
138 #self.SetFoldFlags(16) ### WHAT IS THIS VALUE? WHAT ARE THE OTHER FLAGS? DOES IT MATTER?
139 self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
139 self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
140 self.SetMarginMask(2, stc.STC_MASK_FOLDERS)
140 self.SetMarginMask(2, stc.STC_MASK_FOLDERS)
141 self.SetMarginSensitive(2, True)
141 self.SetMarginSensitive(2, True)
142 self.SetMarginWidth(2, 12)
142 self.SetMarginWidth(2, 12)
143
143
144 if self.fold_symbols == 0:
144 if self.fold_symbols == 0:
145 # Arrow pointing right for contracted folders, arrow pointing down for expanded
145 # Arrow pointing right for contracted folders, arrow pointing down for expanded
146 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_ARROWDOWN, "black", "black")
146 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_ARROWDOWN, "black", "black")
147 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_ARROW, "black", "black")
147 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_ARROW, "black", "black")
148 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_EMPTY, "black", "black")
148 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_EMPTY, "black", "black")
149 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_EMPTY, "black", "black")
149 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_EMPTY, "black", "black")
150 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_EMPTY, "white", "black")
150 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_EMPTY, "white", "black")
151 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY, "white", "black")
151 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY, "white", "black")
152 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY, "white", "black")
152 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY, "white", "black")
153
153
154 elif self.fold_symbols == 1:
154 elif self.fold_symbols == 1:
155 # Plus for contracted folders, minus for expanded
155 # Plus for contracted folders, minus for expanded
156 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_MINUS, "white", "black")
156 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_MINUS, "white", "black")
157 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_PLUS, "white", "black")
157 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_PLUS, "white", "black")
158 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_EMPTY, "white", "black")
158 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_EMPTY, "white", "black")
159 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_EMPTY, "white", "black")
159 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_EMPTY, "white", "black")
160 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_EMPTY, "white", "black")
160 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_EMPTY, "white", "black")
161 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY, "white", "black")
161 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY, "white", "black")
162 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY, "white", "black")
162 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY, "white", "black")
163
163
164 elif self.fold_symbols == 2:
164 elif self.fold_symbols == 2:
165 # Like a flattened tree control using circular headers and curved joins
165 # Like a flattened tree control using circular headers and curved joins
166 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_CIRCLEMINUS, "white", "#404040")
166 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_CIRCLEMINUS, "white", "#404040")
167 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_CIRCLEPLUS, "white", "#404040")
167 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_CIRCLEPLUS, "white", "#404040")
168 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "#404040")
168 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "#404040")
169 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNERCURVE, "white", "#404040")
169 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNERCURVE, "white", "#404040")
170 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_CIRCLEPLUSCONNECTED, "white", "#404040")
170 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_CIRCLEPLUSCONNECTED, "white", "#404040")
171 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_CIRCLEMINUSCONNECTED, "white", "#404040")
171 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_CIRCLEMINUSCONNECTED, "white", "#404040")
172 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNERCURVE, "white", "#404040")
172 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNERCURVE, "white", "#404040")
173
173
174 elif self.fold_symbols == 3:
174 elif self.fold_symbols == 3:
175 # Like a flattened tree control using square headers
175 # Like a flattened tree control using square headers
176 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_BOXMINUS, "white", "#808080")
176 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_BOXMINUS, "white", "#808080")
177 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_BOXPLUS, "white", "#808080")
177 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_BOXPLUS, "white", "#808080")
178 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "#808080")
178 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "#808080")
179 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNER, "white", "#808080")
179 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNER, "white", "#808080")
180 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_BOXPLUSCONNECTED, "white", "#808080")
180 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_BOXPLUSCONNECTED, "white", "#808080")
181 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED, "white", "#808080")
181 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED, "white", "#808080")
182 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER, "white", "#808080")
182 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER, "white", "#808080")
183
183
184
184
185 self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
185 self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
186 self.Bind(stc.EVT_STC_MARGINCLICK, self.OnMarginClick)
186 self.Bind(stc.EVT_STC_MARGINCLICK, self.OnMarginClick)
187 self.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed)
187 self.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed)
188
188
189 # Make some styles, The lexer defines what each style is used for, we
189 # Make some styles, The lexer defines what each style is used for, we
190 # just have to define what each style looks like. This set is adapted from
190 # just have to define what each style looks like. This set is adapted from
191 # Scintilla sample property files.
191 # Scintilla sample property files.
192
192
193 # Global default styles for all languages
193 # Global default styles for all languages
194 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(helv)s,size:%(size)d" % faces)
194 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(helv)s,size:%(size)d" % faces)
195 self.StyleClearAll() # Reset all to be like the default
195 self.StyleClearAll() # Reset all to be like the default
196
196
197 # Global default styles for all languages
197 # Global default styles for all languages
198 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(helv)s,size:%(size)d" % faces)
198 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(helv)s,size:%(size)d" % faces)
199 self.StyleSetSpec(stc.STC_STYLE_LINENUMBER, "back:#C0C0C0,face:%(helv)s,size:%(size2)d" % faces)
199 self.StyleSetSpec(stc.STC_STYLE_LINENUMBER, "back:#C0C0C0,face:%(helv)s,size:%(size2)d" % faces)
200 self.StyleSetSpec(stc.STC_STYLE_CONTROLCHAR, "face:%(other)s" % faces)
200 self.StyleSetSpec(stc.STC_STYLE_CONTROLCHAR, "face:%(other)s" % faces)
201 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, "fore:#FFFFFF,back:#0000FF,bold")
201 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, "fore:#FFFFFF,back:#0000FF,bold")
202 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold")
202 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold")
203
203
204 # Python styles
204 # Python styles
205 # Default
205 # Default
206 self.StyleSetSpec(stc.STC_P_DEFAULT, "fore:#000000,face:%(helv)s,size:%(size)d" % faces)
206 self.StyleSetSpec(stc.STC_P_DEFAULT, "fore:#000000,face:%(helv)s,size:%(size)d" % faces)
207 # Comments
207 # Comments
208 self.StyleSetSpec(stc.STC_P_COMMENTLINE, "fore:#007F00,face:%(other)s,size:%(size)d" % faces)
208 self.StyleSetSpec(stc.STC_P_COMMENTLINE, "fore:#007F00,face:%(other)s,size:%(size)d" % faces)
209 # Number
209 # Number
210 self.StyleSetSpec(stc.STC_P_NUMBER, "fore:#007F7F,size:%(size)d" % faces)
210 self.StyleSetSpec(stc.STC_P_NUMBER, "fore:#007F7F,size:%(size)d" % faces)
211 # String
211 # String
212 self.StyleSetSpec(stc.STC_P_STRING, "fore:#7F007F,face:%(helv)s,size:%(size)d" % faces)
212 self.StyleSetSpec(stc.STC_P_STRING, "fore:#7F007F,face:%(helv)s,size:%(size)d" % faces)
213 # Single quoted string
213 # Single quoted string
214 self.StyleSetSpec(stc.STC_P_CHARACTER, "fore:#7F007F,face:%(helv)s,size:%(size)d" % faces)
214 self.StyleSetSpec(stc.STC_P_CHARACTER, "fore:#7F007F,face:%(helv)s,size:%(size)d" % faces)
215 # Keyword
215 # Keyword
216 self.StyleSetSpec(stc.STC_P_WORD, "fore:#00007F,bold,size:%(size)d" % faces)
216 self.StyleSetSpec(stc.STC_P_WORD, "fore:#00007F,bold,size:%(size)d" % faces)
217 # Triple quotes
217 # Triple quotes
218 self.StyleSetSpec(stc.STC_P_TRIPLE, "fore:#7F0000,size:%(size)d" % faces)
218 self.StyleSetSpec(stc.STC_P_TRIPLE, "fore:#7F0000,size:%(size)d" % faces)
219 # Triple double quotes
219 # Triple double quotes
220 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, "fore:#7F0000,size:%(size)d" % faces)
220 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, "fore:#7F0000,size:%(size)d" % faces)
221 # Class name definition
221 # Class name definition
222 self.StyleSetSpec(stc.STC_P_CLASSNAME, "fore:#0000FF,bold,underline,size:%(size)d" % faces)
222 self.StyleSetSpec(stc.STC_P_CLASSNAME, "fore:#0000FF,bold,underline,size:%(size)d" % faces)
223 # Function or method name definition
223 # Function or method name definition
224 self.StyleSetSpec(stc.STC_P_DEFNAME, "fore:#007F7F,bold,size:%(size)d" % faces)
224 self.StyleSetSpec(stc.STC_P_DEFNAME, "fore:#007F7F,bold,size:%(size)d" % faces)
225 # Operators
225 # Operators
226 self.StyleSetSpec(stc.STC_P_OPERATOR, "bold,size:%(size)d" % faces)
226 self.StyleSetSpec(stc.STC_P_OPERATOR, "bold,size:%(size)d" % faces)
227 # Identifiers
227 # Identifiers
228 self.StyleSetSpec(stc.STC_P_IDENTIFIER, "fore:#000000,face:%(helv)s,size:%(size)d" % faces)
228 self.StyleSetSpec(stc.STC_P_IDENTIFIER, "fore:#000000,face:%(helv)s,size:%(size)d" % faces)
229 # Comment-blocks
229 # Comment-blocks
230 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, "fore:#7F7F7F,size:%(size)d" % faces)
230 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, "fore:#7F7F7F,size:%(size)d" % faces)
231 # End of line where string is not closed
231 # End of line where string is not closed
232 self.StyleSetSpec(stc.STC_P_STRINGEOL, "fore:#000000,face:%(mono)s,back:#E0C0E0,eol,size:%(size)d" % faces)
232 self.StyleSetSpec(stc.STC_P_STRINGEOL, "fore:#000000,face:%(mono)s,back:#E0C0E0,eol,size:%(size)d" % faces)
233
233
234 self.SetCaretForeground("BLUE")
234 self.SetCaretForeground("BLUE")
235
235
236
236
237 # register some images for use in the AutoComplete box.
237 # register some images for use in the AutoComplete box.
238 #self.RegisterImage(1, images.getSmilesBitmap())
238 #self.RegisterImage(1, images.getSmilesBitmap())
239 #self.RegisterImage(2,
239 #self.RegisterImage(2,
240 # wx.ArtProvider.GetBitmap(wx.ART_NEW, size=(16,16)))
240 # wx.ArtProvider.GetBitmap(wx.ART_NEW, size=(16,16)))
241 #self.RegisterImage(3,
241 #self.RegisterImage(3,
242 # wx.ArtProvider.GetBitmap(wx.ART_COPY, size=(16,16)))
242 # wx.ArtProvider.GetBitmap(wx.ART_COPY, size=(16,16)))
243
243
244
244
245 def OnKeyPressed(self, event):
245 def OnKeyPressed(self, event):
246 if self.CallTipActive():
246 if self.CallTipActive():
247 self.CallTipCancel()
247 self.CallTipCancel()
248 key = event.GetKeyCode()
248 key = event.GetKeyCode()
249
249
250 if key == 32 and event.ControlDown():
250 if key == 32 and event.ControlDown():
251 pos = self.GetCurrentPos()
251 pos = self.GetCurrentPos()
252
252
253 # Tips
253 # Tips
254 if event.ShiftDown():
254 if event.ShiftDown():
255 self.CallTipSetBackground("yellow")
255 self.CallTipSetBackground("yellow")
256 self.CallTipShow(pos, 'lots of of text: blah, blah, blah\n\n'
256 self.CallTipShow(pos, 'lots of of text: blah, blah, blah\n\n'
257 'show some suff, maybe parameters..\n\n'
257 'show some suff, maybe parameters..\n\n'
258 'fubar(param1, param2)')
258 'fubar(param1, param2)')
259 # Code completion
259 # Code completion
260 else:
260 else:
261 #lst = []
261 #lst = []
262 #for x in range(50000):
262 #for x in range(50000):
263 # lst.append('%05d' % x)
263 # lst.append('%05d' % x)
264 #st = " ".join(lst)
264 #st = " ".join(lst)
265 #print len(st)
265 #print len(st)
266 #self.AutoCompShow(0, st)
266 #self.AutoCompShow(0, st)
267
267
268 kw = keyword.kwlist[:]
268 kw = keyword.kwlist[:]
269
269
270 kw.sort() # Python sorts are case sensitive
270 kw.sort() # Python sorts are case sensitive
271 self.AutoCompSetIgnoreCase(False) # so this needs to match
271 self.AutoCompSetIgnoreCase(False) # so this needs to match
272
272
273 # Images are specified with a appended "?type"
273 # Images are specified with a appended "?type"
274 for i in range(len(kw)):
274 for i in range(len(kw)):
275 if kw[i] in keyword.kwlist:
275 if kw[i] in keyword.kwlist:
276 kw[i] = kw[i]# + "?1"
276 kw[i] = kw[i]# + "?1"
277
277
278 self.AutoCompShow(0, " ".join(kw))
278 self.AutoCompShow(0, " ".join(kw))
279 else:
279 else:
280 event.Skip()
280 event.Skip()
281
281
282
282
283 def OnUpdateUI(self, evt):
283 def OnUpdateUI(self, evt):
284 # check for matching braces
284 # check for matching braces
285 braceAtCaret = -1
285 braceAtCaret = -1
286 braceOpposite = -1
286 braceOpposite = -1
287 charBefore = None
287 charBefore = None
288 caretPos = self.GetCurrentPos()
288 caretPos = self.GetCurrentPos()
289
289
290 if caretPos > 0:
290 if caretPos > 0:
291 charBefore = self.GetCharAt(caretPos - 1)
291 charBefore = self.GetCharAt(caretPos - 1)
292 styleBefore = self.GetStyleAt(caretPos - 1)
292 styleBefore = self.GetStyleAt(caretPos - 1)
293
293
294 # check before
294 # check before
295 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
295 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
296 braceAtCaret = caretPos - 1
296 braceAtCaret = caretPos - 1
297
297
298 # check after
298 # check after
299 if braceAtCaret < 0:
299 if braceAtCaret < 0:
300 charAfter = self.GetCharAt(caretPos)
300 charAfter = self.GetCharAt(caretPos)
301 styleAfter = self.GetStyleAt(caretPos)
301 styleAfter = self.GetStyleAt(caretPos)
302
302
303 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
303 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
304 braceAtCaret = caretPos
304 braceAtCaret = caretPos
305
305
306 if braceAtCaret >= 0:
306 if braceAtCaret >= 0:
307 braceOpposite = self.BraceMatch(braceAtCaret)
307 braceOpposite = self.BraceMatch(braceAtCaret)
308
308
309 if braceAtCaret != -1 and braceOpposite == -1:
309 if braceAtCaret != -1 and braceOpposite == -1:
310 self.BraceBadLight(braceAtCaret)
310 self.BraceBadLight(braceAtCaret)
311 else:
311 else:
312 self.BraceHighlight(braceAtCaret, braceOpposite)
312 self.BraceHighlight(braceAtCaret, braceOpposite)
313 #pt = self.PointFromPosition(braceOpposite)
313 #pt = self.PointFromPosition(braceOpposite)
314 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
314 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
315 #print pt
315 #print pt
316 #self.Refresh(False)
316 #self.Refresh(False)
317
317
318
318
319 def OnMarginClick(self, evt):
319 def OnMarginClick(self, evt):
320 # fold and unfold as needed
320 # fold and unfold as needed
321 if evt.GetMargin() == 2:
321 if evt.GetMargin() == 2:
322 if evt.GetShift() and evt.GetControl():
322 if evt.GetShift() and evt.GetControl():
323 self.FoldAll()
323 self.FoldAll()
324 else:
324 else:
325 lineClicked = self.LineFromPosition(evt.GetPosition())
325 lineClicked = self.LineFromPosition(evt.GetPosition())
326
326
327 if self.GetFoldLevel(lineClicked) & stc.STC_FOLDLEVELHEADERFLAG:
327 if self.GetFoldLevel(lineClicked) & stc.STC_FOLDLEVELHEADERFLAG:
328 if evt.GetShift():
328 if evt.GetShift():
329 self.SetFoldExpanded(lineClicked, True)
329 self.SetFoldExpanded(lineClicked, True)
330 self.Expand(lineClicked, True, True, 1)
330 self.Expand(lineClicked, True, True, 1)
331 elif evt.GetControl():
331 elif evt.GetControl():
332 if self.GetFoldExpanded(lineClicked):
332 if self.GetFoldExpanded(lineClicked):
333 self.SetFoldExpanded(lineClicked, False)
333 self.SetFoldExpanded(lineClicked, False)
334 self.Expand(lineClicked, False, True, 0)
334 self.Expand(lineClicked, False, True, 0)
335 else:
335 else:
336 self.SetFoldExpanded(lineClicked, True)
336 self.SetFoldExpanded(lineClicked, True)
337 self.Expand(lineClicked, True, True, 100)
337 self.Expand(lineClicked, True, True, 100)
338 else:
338 else:
339 self.ToggleFold(lineClicked)
339 self.ToggleFold(lineClicked)
340
340
341
341
342 def FoldAll(self):
342 def FoldAll(self):
343 lineCount = self.GetLineCount()
343 lineCount = self.GetLineCount()
344 expanding = True
344 expanding = True
345
345
346 # find out if we are folding or unfolding
346 # find out if we are folding or unfolding
347 for lineNum in range(lineCount):
347 for lineNum in range(lineCount):
348 if self.GetFoldLevel(lineNum) & stc.STC_FOLDLEVELHEADERFLAG:
348 if self.GetFoldLevel(lineNum) & stc.STC_FOLDLEVELHEADERFLAG:
349 expanding = not self.GetFoldExpanded(lineNum)
349 expanding = not self.GetFoldExpanded(lineNum)
350 break
350 break
351
351
352 lineNum = 0
352 lineNum = 0
353
353
354 while lineNum < lineCount:
354 while lineNum < lineCount:
355 level = self.GetFoldLevel(lineNum)
355 level = self.GetFoldLevel(lineNum)
356 if level & stc.STC_FOLDLEVELHEADERFLAG and \
356 if level & stc.STC_FOLDLEVELHEADERFLAG and \
357 (level & stc.STC_FOLDLEVELNUMBERMASK) == stc.STC_FOLDLEVELBASE:
357 (level & stc.STC_FOLDLEVELNUMBERMASK) == stc.STC_FOLDLEVELBASE:
358
358
359 if expanding:
359 if expanding:
360 self.SetFoldExpanded(lineNum, True)
360 self.SetFoldExpanded(lineNum, True)
361 lineNum = self.Expand(lineNum, True)
361 lineNum = self.Expand(lineNum, True)
362 lineNum = lineNum - 1
362 lineNum = lineNum - 1
363 else:
363 else:
364 lastChild = self.GetLastChild(lineNum, -1)
364 lastChild = self.GetLastChild(lineNum, -1)
365 self.SetFoldExpanded(lineNum, False)
365 self.SetFoldExpanded(lineNum, False)
366
366
367 if lastChild > lineNum:
367 if lastChild > lineNum:
368 self.HideLines(lineNum+1, lastChild)
368 self.HideLines(lineNum+1, lastChild)
369
369
370 lineNum = lineNum + 1
370 lineNum = lineNum + 1
371
371
372
372
373
373
374 def Expand(self, line, doExpand, force=False, visLevels=0, level=-1):
374 def Expand(self, line, doExpand, force=False, visLevels=0, level=-1):
375 lastChild = self.GetLastChild(line, level)
375 lastChild = self.GetLastChild(line, level)
376 line = line + 1
376 line = line + 1
377
377
378 while line <= lastChild:
378 while line <= lastChild:
379 if force:
379 if force:
380 if visLevels > 0:
380 if visLevels > 0:
381 self.ShowLines(line, line)
381 self.ShowLines(line, line)
382 else:
382 else:
383 self.HideLines(line, line)
383 self.HideLines(line, line)
384 else:
384 else:
385 if doExpand:
385 if doExpand:
386 self.ShowLines(line, line)
386 self.ShowLines(line, line)
387
387
388 if level == -1:
388 if level == -1:
389 level = self.GetFoldLevel(line)
389 level = self.GetFoldLevel(line)
390
390
391 if level & stc.STC_FOLDLEVELHEADERFLAG:
391 if level & stc.STC_FOLDLEVELHEADERFLAG:
392 if force:
392 if force:
393 if visLevels > 1:
393 if visLevels > 1:
394 self.SetFoldExpanded(line, True)
394 self.SetFoldExpanded(line, True)
395 else:
395 else:
396 self.SetFoldExpanded(line, False)
396 self.SetFoldExpanded(line, False)
397
397
398 line = self.Expand(line, doExpand, force, visLevels-1)
398 line = self.Expand(line, doExpand, force, visLevels-1)
399
399
400 else:
400 else:
401 if doExpand and self.GetFoldExpanded(line):
401 if doExpand and self.GetFoldExpanded(line):
402 line = self.Expand(line, True, force, visLevels-1)
402 line = self.Expand(line, True, force, visLevels-1)
403 else:
403 else:
404 line = self.Expand(line, False, force, visLevels-1)
404 line = self.Expand(line, False, force, visLevels-1)
405 else:
405 else:
406 line = line + 1
406 line = line + 1
407
407
408 return line
408 return line
409
409
410
410
411 #----------------------------------------------------------------------
411 #----------------------------------------------------------------------
@@ -1,767 +1,767 b''
1 #!/usr/bin/python
1 #!/usr/bin/python
2 # -*- coding: iso-8859-15 -*-
2 # -*- coding: iso-8859-15 -*-
3 '''
3 '''
4 Provides IPython WX console widgets.
4 Provides IPython WX console widgets.
5
5
6 @author: Laurent Dufrechou
6 @author: Laurent Dufrechou
7 laurent.dufrechou _at_ gmail.com
7 laurent.dufrechou _at_ gmail.com
8 This WX widget is based on the original work of Eitan Isaacson
8 This WX widget is based on the original work of Eitan Isaacson
9 that provided the console for the GTK toolkit.
9 that provided the console for the GTK toolkit.
10
10
11 Original work from:
11 Original work from:
12 @author: Eitan Isaacson
12 @author: Eitan Isaacson
13 @organization: IBM Corporation
13 @organization: IBM Corporation
14 @copyright: Copyright (c) 2007 IBM Corporation
14 @copyright: Copyright (c) 2007 IBM Corporation
15 @license: BSD
15 @license: BSD
16
16
17 All rights reserved. This program and the accompanying materials are made
17 All rights reserved. This program and the accompanying materials are made
18 available under the terms of the BSD which accompanies this distribution, and
18 available under the terms of the BSD which accompanies this distribution, and
19 is available at U{http://www.opensource.org/licenses/bsd-license.php}
19 is available at U{http://www.opensource.org/licenses/bsd-license.php}
20 '''
20 '''
21
21
22 __version__ = 0.8
22 __version__ = 0.8
23 __author__ = "Laurent Dufrechou"
23 __author__ = "Laurent Dufrechou"
24 __email__ = "laurent.dufrechou _at_ gmail.com"
24 __email__ = "laurent.dufrechou _at_ gmail.com"
25 __license__ = "BSD"
25 __license__ = "BSD"
26
26
27 import wx
27 import wx
28 import wx.stc as stc
28 import wx.stc as stc
29 import wx.lib.newevent
29 import wx.lib.newevent
30
30
31 import re
31 import re
32 import sys
32 import sys
33 import os
33 import os
34 import locale
34 import locale
35 import time
35 import time
36 from StringIO import StringIO
36 from StringIO import StringIO
37 try:
37 try:
38 import IPython
38 import IPython
39 except Exception,e:
39 except Exception,e:
40 raise "Error importing IPython (%s)" % str(e)
40 raise "Error importing IPython (%s)" % str(e)
41
41
42
42
43 from non_blocking_ip_shell import NonBlockingIPShell
43 from non_blocking_ip_shell import NonBlockingIPShell
44
44
45 class WxNonBlockingIPShell(NonBlockingIPShell):
45 class WxNonBlockingIPShell(NonBlockingIPShell):
46 '''
46 '''
47 An NonBlockingIPShell Thread that is WX dependent.
47 An NonBlockingIPShell Thread that is WX dependent.
48 '''
48 '''
49 def __init__(self, parent,
49 def __init__(self, parent,
50 argv=[],user_ns={},user_global_ns=None,
50 argv=[],user_ns={},user_global_ns=None,
51 cin=None, cout=None, cerr=None,
51 cin=None, cout=None, cerr=None,
52 ask_exit_handler=None):
52 ask_exit_handler=None):
53
53
54 NonBlockingIPShell.__init__(self,argv,user_ns,user_global_ns,
54 NonBlockingIPShell.__init__(self,argv,user_ns,user_global_ns,
55 cin, cout, cerr,
55 cin, cout, cerr,
56 ask_exit_handler)
56 ask_exit_handler)
57
57
58 self.parent = parent
58 self.parent = parent
59
59
60 self.ask_exit_callback = ask_exit_handler
60 self.ask_exit_callback = ask_exit_handler
61 self._IP.ask_exit = self._askExit
61 self._IP.ask_exit = self._askExit
62
62
63
63
64 def addGUIShortcut(self,text,func):
64 def addGUIShortcut(self,text,func):
65 wx.CallAfter(self.parent.add_button_handler,
65 wx.CallAfter(self.parent.add_button_handler,
66 button_info={ 'text':text,
66 button_info={ 'text':text,
67 'func':self.parent.doExecuteLine(func)})
67 'func':self.parent.doExecuteLine(func)})
68
68
69 def _askExit(self):
69 def _askExit(self):
70 wx.CallAfter(self.ask_exit_callback, ())
70 wx.CallAfter(self.ask_exit_callback, ())
71
71
72 def _afterExecute(self):
72 def _afterExecute(self):
73 wx.CallAfter(self.parent.evtStateExecuteDone, ())
73 wx.CallAfter(self.parent.evtStateExecuteDone, ())
74
74
75
75
76 class WxConsoleView(stc.StyledTextCtrl):
76 class WxConsoleView(stc.StyledTextCtrl):
77 '''
77 '''
78 Specialized styled text control view for console-like workflow.
78 Specialized styled text control view for console-like workflow.
79 We use here a scintilla frontend thus it can be reused in any GUI taht supports
79 We use here a scintilla frontend thus it can be reused in any GUI taht supports
80 scintilla with less work.
80 scintilla with less work.
81
81
82 @cvar ANSI_COLORS_BLACK: Mapping of terminal colors to X11 names.(with Black background)
82 @cvar ANSI_COLORS_BLACK: Mapping of terminal colors to X11 names.(with Black background)
83 @type ANSI_COLORS_BLACK: dictionary
83 @type ANSI_COLORS_BLACK: dictionary
84
84
85 @cvar ANSI_COLORS_WHITE: Mapping of terminal colors to X11 names.(with White background)
85 @cvar ANSI_COLORS_WHITE: Mapping of terminal colors to X11 names.(with White background)
86 @type ANSI_COLORS_WHITE: dictionary
86 @type ANSI_COLORS_WHITE: dictionary
87
87
88 @ivar color_pat: Regex of terminal color pattern
88 @ivar color_pat: Regex of terminal color pattern
89 @type color_pat: _sre.SRE_Pattern
89 @type color_pat: _sre.SRE_Pattern
90 '''
90 '''
91 ANSI_STYLES_BLACK ={'0;30': [0,'WHITE'], '0;31': [1,'RED'],
91 ANSI_STYLES_BLACK ={'0;30': [0,'WHITE'], '0;31': [1,'RED'],
92 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
92 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
93 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
93 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
94 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
94 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
95 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
95 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
96 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
96 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
97 '1;34': [12,'LIGHT BLUE'], '1;35': [13,'MEDIUM VIOLET RED'],
97 '1;34': [12,'LIGHT BLUE'], '1;35': [13,'MEDIUM VIOLET RED'],
98 '1;36': [14,'LIGHT STEEL BLUE'], '1;37': [15,'YELLOW']}
98 '1;36': [14,'LIGHT STEEL BLUE'], '1;37': [15,'YELLOW']}
99
99
100 ANSI_STYLES_WHITE ={'0;30': [0,'BLACK'], '0;31': [1,'RED'],
100 ANSI_STYLES_WHITE ={'0;30': [0,'BLACK'], '0;31': [1,'RED'],
101 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
101 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
102 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
102 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
103 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
103 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
104 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
104 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
105 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
105 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
106 '1;34': [12,'LIGHT BLUE'], '1;35': [13,'MEDIUM VIOLET RED'],
106 '1;34': [12,'LIGHT BLUE'], '1;35': [13,'MEDIUM VIOLET RED'],
107 '1;36': [14,'LIGHT STEEL BLUE'], '1;37': [15,'YELLOW']}
107 '1;36': [14,'LIGHT STEEL BLUE'], '1;37': [15,'YELLOW']}
108
108
109 def __init__(self,parent,prompt,intro="",background_color="BLACK",pos=wx.DefaultPosition, ID = -1, size=wx.DefaultSize,
109 def __init__(self,parent,prompt,intro="",background_color="BLACK",pos=wx.DefaultPosition, ID = -1, size=wx.DefaultSize,
110 style=0):
110 style=0):
111 '''
111 '''
112 Initialize console view.
112 Initialize console view.
113
113
114 @param parent: Parent widget
114 @param parent: Parent widget
115 @param prompt: User specified prompt
115 @param prompt: User specified prompt
116 @type intro: string
116 @type intro: string
117 @param intro: User specified startup introduction string
117 @param intro: User specified startup introduction string
118 @type intro: string
118 @type intro: string
119 @param background_color: Can be BLACK or WHITE
119 @param background_color: Can be BLACK or WHITE
120 @type background_color: string
120 @type background_color: string
121 @param other: init param of styledTextControl (can be used as-is)
121 @param other: init param of styledTextControl (can be used as-is)
122 '''
122 '''
123 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
123 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
124
124
125 ####### Scintilla configuration ##################################################
125 ####### Scintilla configuration ##################################################
126
126
127 # Ctrl + B or Ctrl + N can be used to zoomin/zoomout the text inside the widget
127 # Ctrl + B or Ctrl + N can be used to zoomin/zoomout the text inside the widget
128 self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
128 self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
129 self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
129 self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
130
130
131 #we define platform specific fonts
131 #we define platform specific fonts
132 if wx.Platform == '__WXMSW__':
132 if wx.Platform == '__WXMSW__':
133 faces = { 'times': 'Times New Roman',
133 faces = { 'times': 'Times New Roman',
134 'mono' : 'Courier New',
134 'mono' : 'Courier New',
135 'helv' : 'Arial',
135 'helv' : 'Arial',
136 'other': 'Comic Sans MS',
136 'other': 'Comic Sans MS',
137 'size' : 10,
137 'size' : 10,
138 'size2': 8,
138 'size2': 8,
139 }
139 }
140 elif wx.Platform == '__WXMAC__':
140 elif wx.Platform == '__WXMAC__':
141 faces = { 'times': 'Times New Roman',
141 faces = { 'times': 'Times New Roman',
142 'mono' : 'Monaco',
142 'mono' : 'Monaco',
143 'helv' : 'Arial',
143 'helv' : 'Arial',
144 'other': 'Comic Sans MS',
144 'other': 'Comic Sans MS',
145 'size' : 10,
145 'size' : 10,
146 'size2': 8,
146 'size2': 8,
147 }
147 }
148 else:
148 else:
149 faces = { 'times': 'Times',
149 faces = { 'times': 'Times',
150 'mono' : 'Courier',
150 'mono' : 'Courier',
151 'helv' : 'Helvetica',
151 'helv' : 'Helvetica',
152 'other': 'new century schoolbook',
152 'other': 'new century schoolbook',
153 'size' : 10,
153 'size' : 10,
154 'size2': 8,
154 'size2': 8,
155 }
155 }
156
156
157 #We draw a line at position 80
157 #We draw a line at position 80
158 self.SetEdgeMode(stc.STC_EDGE_LINE)
158 self.SetEdgeMode(stc.STC_EDGE_LINE)
159 self.SetEdgeColumn(80)
159 self.SetEdgeColumn(80)
160 self.SetEdgeColour(wx.LIGHT_GREY)
160 self.SetEdgeColour(wx.LIGHT_GREY)
161
161
162 #self.SetViewWhiteSpace(True)
162 #self.SetViewWhiteSpace(True)
163 #self.SetViewEOL(True)
163 #self.SetViewEOL(True)
164 self.SetEOLMode(stc.STC_EOL_CRLF)
164 self.SetEOLMode(stc.STC_EOL_CRLF)
165 #self.SetWrapMode(stc.STC_WRAP_CHAR)
165 #self.SetWrapMode(stc.STC_WRAP_CHAR)
166 #self.SetWrapMode(stc.STC_WRAP_WORD)
166 #self.SetWrapMode(stc.STC_WRAP_WORD)
167 self.SetBufferedDraw(True)
167 self.SetBufferedDraw(True)
168 #self.SetUseAntiAliasing(True)
168 #self.SetUseAntiAliasing(True)
169 self.SetLayoutCache(stc.STC_CACHE_PAGE)
169 self.SetLayoutCache(stc.STC_CACHE_PAGE)
170
170
171 self.EnsureCaretVisible()
171 self.EnsureCaretVisible()
172
172
173 self.SetMargins(3,3) #text is moved away from border with 3px
173 self.SetMargins(3,3) #text is moved away from border with 3px
174 # Suppressing Scintilla margins
174 # Suppressing Scintilla margins
175 self.SetMarginWidth(0,0)
175 self.SetMarginWidth(0,0)
176 self.SetMarginWidth(1,0)
176 self.SetMarginWidth(1,0)
177 self.SetMarginWidth(2,0)
177 self.SetMarginWidth(2,0)
178
178
179 # make some styles
179 # make some styles
180 if background_color != "BLACK":
180 if background_color != "BLACK":
181 self.background_color = "WHITE"
181 self.background_color = "WHITE"
182 self.SetCaretForeground("BLACK")
182 self.SetCaretForeground("BLACK")
183 self.ANSI_STYLES = self.ANSI_STYLES_WHITE
183 self.ANSI_STYLES = self.ANSI_STYLES_WHITE
184 else:
184 else:
185 self.background_color = background_color
185 self.background_color = background_color
186 self.SetCaretForeground("WHITE")
186 self.SetCaretForeground("WHITE")
187 self.ANSI_STYLES = self.ANSI_STYLES_BLACK
187 self.ANSI_STYLES = self.ANSI_STYLES_BLACK
188
188
189 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "fore:%s,back:%s,size:%d,face:%s" % (self.ANSI_STYLES['0;30'][1],
189 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "fore:%s,back:%s,size:%d,face:%s" % (self.ANSI_STYLES['0;30'][1],
190 self.background_color,
190 self.background_color,
191 faces['size'], faces['mono']))
191 faces['size'], faces['mono']))
192 self.StyleClearAll()
192 self.StyleClearAll()
193 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, "fore:#FF0000,back:#0000FF,bold")
193 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, "fore:#FF0000,back:#0000FF,bold")
194 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold")
194 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold")
195
195
196 for style in self.ANSI_STYLES.values():
196 for style in self.ANSI_STYLES.values():
197 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
197 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
198
198
199 #######################################################################
199 #######################################################################
200
200
201 self.indent = 0
201 self.indent = 0
202 self.prompt_count = 0
202 self.prompt_count = 0
203 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
203 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
204
204
205 self.write(intro)
205 self.write(intro)
206 self.setPrompt(prompt)
206 self.setPrompt(prompt)
207 self.showPrompt()
207 self.showPrompt()
208
208
209 self.Bind(wx.EVT_KEY_DOWN, self._onKeypress, self)
209 self.Bind(wx.EVT_KEY_DOWN, self._onKeypress, self)
210 #self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
210 #self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
211
211
212 def write(self, text):
212 def write(self, text):
213 '''
213 '''
214 Write given text to buffer.
214 Write given text to buffer.
215
215
216 @param text: Text to append.
216 @param text: Text to append.
217 @type text: string
217 @type text: string
218 '''
218 '''
219 segments = self.color_pat.split(text)
219 segments = self.color_pat.split(text)
220 segment = segments.pop(0)
220 segment = segments.pop(0)
221 self.StartStyling(self.getCurrentLineEnd(),0xFF)
221 self.StartStyling(self.getCurrentLineEnd(),0xFF)
222 self.AppendText(segment)
222 self.AppendText(segment)
223
223
224 if segments:
224 if segments:
225 ansi_tags = self.color_pat.findall(text)
225 ansi_tags = self.color_pat.findall(text)
226
226
227 for tag in ansi_tags:
227 for tag in ansi_tags:
228 i = segments.index(tag)
228 i = segments.index(tag)
229 self.StartStyling(self.getCurrentLineEnd(),0xFF)
229 self.StartStyling(self.getCurrentLineEnd(),0xFF)
230 self.AppendText(segments[i+1])
230 self.AppendText(segments[i+1])
231
231
232 if tag != '0':
232 if tag != '0':
233 self.SetStyling(len(segments[i+1]),self.ANSI_STYLES[tag][0])
233 self.SetStyling(len(segments[i+1]),self.ANSI_STYLES[tag][0])
234
234
235 segments.pop(i)
235 segments.pop(i)
236
236
237 self.moveCursor(self.getCurrentLineEnd())
237 self.moveCursor(self.getCurrentLineEnd())
238
238
239 def getPromptLen(self):
239 def getPromptLen(self):
240 '''
240 '''
241 Return the length of current prompt
241 Return the length of current prompt
242 '''
242 '''
243 return len(str(self.prompt_count)) + 7
243 return len(str(self.prompt_count)) + 7
244
244
245 def setPrompt(self,prompt):
245 def setPrompt(self,prompt):
246 self.prompt = prompt
246 self.prompt = prompt
247
247
248 def setIndentation(self,indentation):
248 def setIndentation(self,indentation):
249 self.indent = indentation
249 self.indent = indentation
250
250
251 def setPromptCount(self,count):
251 def setPromptCount(self,count):
252 self.prompt_count = count
252 self.prompt_count = count
253
253
254 def showPrompt(self):
254 def showPrompt(self):
255 '''
255 '''
256 Prints prompt at start of line.
256 Prints prompt at start of line.
257
257
258 @param prompt: Prompt to print.
258 @param prompt: Prompt to print.
259 @type prompt: string
259 @type prompt: string
260 '''
260 '''
261 self.write(self.prompt)
261 self.write(self.prompt)
262 #now we update the position of end of prompt
262 #now we update the position of end of prompt
263 self.current_start = self.getCurrentLineEnd()
263 self.current_start = self.getCurrentLineEnd()
264
264
265 autoindent = self.indent*' '
265 autoindent = self.indent*' '
266 autoindent = autoindent.replace(' ','\t')
266 autoindent = autoindent.replace(' ','\t')
267 self.write(autoindent)
267 self.write(autoindent)
268
268
269 def changeLine(self, text):
269 def changeLine(self, text):
270 '''
270 '''
271 Replace currently entered command line with given text.
271 Replace currently entered command line with given text.
272
272
273 @param text: Text to use as replacement.
273 @param text: Text to use as replacement.
274 @type text: string
274 @type text: string
275 '''
275 '''
276 self.SetSelection(self.getCurrentPromptStart(),self.getCurrentLineEnd())
276 self.SetSelection(self.getCurrentPromptStart(),self.getCurrentLineEnd())
277 self.ReplaceSelection(text)
277 self.ReplaceSelection(text)
278 self.moveCursor(self.getCurrentLineEnd())
278 self.moveCursor(self.getCurrentLineEnd())
279
279
280 def getCurrentPromptStart(self):
280 def getCurrentPromptStart(self):
281 return self.current_start
281 return self.current_start
282
282
283 def getCurrentLineStart(self):
283 def getCurrentLineStart(self):
284 return self.GotoLine(self.LineFromPosition(self.GetCurrentPos()))
284 return self.GotoLine(self.LineFromPosition(self.GetCurrentPos()))
285
285
286 def getCurrentLineEnd(self):
286 def getCurrentLineEnd(self):
287 return self.GetLength()
287 return self.GetLength()
288
288
289 def getCurrentLine(self):
289 def getCurrentLine(self):
290 '''
290 '''
291 Get text in current command line.
291 Get text in current command line.
292
292
293 @return: Text of current command line.
293 @return: Text of current command line.
294 @rtype: string
294 @rtype: string
295 '''
295 '''
296 return self.GetTextRange(self.getCurrentPromptStart(),
296 return self.GetTextRange(self.getCurrentPromptStart(),
297 self.getCurrentLineEnd())
297 self.getCurrentLineEnd())
298
298
299 def showReturned(self, text):
299 def showReturned(self, text):
300 '''
300 '''
301 Show returned text from last command and print new prompt.
301 Show returned text from last command and print new prompt.
302
302
303 @param text: Text to show.
303 @param text: Text to show.
304 @type text: string
304 @type text: string
305 '''
305 '''
306 self.write('\n'+text)
306 self.write('\n'+text)
307 if text:
307 if text:
308 self.write('\n')
308 self.write('\n')
309 self.showPrompt()
309 self.showPrompt()
310
310
311 def moveCursorOnNewValidKey(self):
311 def moveCursorOnNewValidKey(self):
312 #If cursor is at wrong position put it at last line...
312 #If cursor is at wrong position put it at last line...
313 if self.GetCurrentPos() < self.getCurrentPromptStart():
313 if self.GetCurrentPos() < self.getCurrentPromptStart():
314 self.GotoPos(self.getCurrentPromptStart())
314 self.GotoPos(self.getCurrentPromptStart())
315
315
316 def removeFromTo(self,from_pos,to_pos):
316 def removeFromTo(self,from_pos,to_pos):
317 if from_pos < to_pos:
317 if from_pos < to_pos:
318 self.SetSelection(from_pos,to_pos)
318 self.SetSelection(from_pos,to_pos)
319 self.DeleteBack()
319 self.DeleteBack()
320
320
321 def removeCurrentLine(self):
321 def removeCurrentLine(self):
322 self.LineDelete()
322 self.LineDelete()
323
323
324 def moveCursor(self,position):
324 def moveCursor(self,position):
325 self.GotoPos(position)
325 self.GotoPos(position)
326
326
327 def getCursorPos(self):
327 def getCursorPos(self):
328 return self.GetCurrentPos()
328 return self.GetCurrentPos()
329
329
330 def selectFromTo(self,from_pos,to_pos):
330 def selectFromTo(self,from_pos,to_pos):
331 self.SetSelectionStart(from_pos)
331 self.SetSelectionStart(from_pos)
332 self.SetSelectionEnd(to_pos)
332 self.SetSelectionEnd(to_pos)
333
333
334 def writeHistory(self,history):
334 def writeHistory(self,history):
335 self.removeFromTo(self.getCurrentPromptStart(),self.getCurrentLineEnd())
335 self.removeFromTo(self.getCurrentPromptStart(),self.getCurrentLineEnd())
336 self.changeLine(history)
336 self.changeLine(history)
337
337
338 def writeCompletion(self, possibilities):
338 def writeCompletion(self, possibilities):
339 max_len = len(max(possibilities,key=len))
339 max_len = len(max(possibilities,key=len))
340 max_symbol =' '*max_len
340 max_symbol =' '*max_len
341
341
342 #now we check how much symbol we can put on a line...
342 #now we check how much symbol we can put on a line...
343 cursor_pos = self.getCursorPos()
343 cursor_pos = self.getCursorPos()
344 test_buffer = max_symbol + ' '*4
344 test_buffer = max_symbol + ' '*4
345 current_lines = self.GetLineCount()
345 current_lines = self.GetLineCount()
346
346
347 allowed_symbols = 80/len(test_buffer)
347 allowed_symbols = 80/len(test_buffer)
348 if allowed_symbols == 0:
348 if allowed_symbols == 0:
349 allowed_symbols = 1
349 allowed_symbols = 1
350
350
351 pos = 1
351 pos = 1
352 buf = ''
352 buf = ''
353 for symbol in possibilities:
353 for symbol in possibilities:
354 #buf += symbol+'\n'#*spaces)
354 #buf += symbol+'\n'#*spaces)
355 if pos<allowed_symbols:
355 if pos<allowed_symbols:
356 spaces = max_len - len(symbol) + 4
356 spaces = max_len - len(symbol) + 4
357 buf += symbol+' '*spaces
357 buf += symbol+' '*spaces
358 pos += 1
358 pos += 1
359 else:
359 else:
360 buf+=symbol+'\n'
360 buf+=symbol+'\n'
361 pos = 1
361 pos = 1
362 self.write(buf)
362 self.write(buf)
363
363
364 def _onKeypress(self, event, skip=True):
364 def _onKeypress(self, event, skip=True):
365 '''
365 '''
366 Key press callback used for correcting behavior for console-like
366 Key press callback used for correcting behavior for console-like
367 interfaces. For example 'home' should go to prompt, not to begining of
367 interfaces. For example 'home' should go to prompt, not to begining of
368 line.
368 line.
369
369
370 @param widget: Widget that key press accored in.
370 @param widget: Widget that key press accored in.
371 @type widget: gtk.Widget
371 @type widget: gtk.Widget
372 @param event: Event object
372 @param event: Event object
373 @type event: gtk.gdk.Event
373 @type event: gtk.gdk.Event
374
374
375 @return: Return True if event as been catched.
375 @return: Return True if event as been catched.
376 @rtype: boolean
376 @rtype: boolean
377 '''
377 '''
378
378
379 if event.GetKeyCode() == wx.WXK_HOME:
379 if event.GetKeyCode() == wx.WXK_HOME:
380 if event.Modifiers == wx.MOD_NONE:
380 if event.Modifiers == wx.MOD_NONE:
381 self.moveCursorOnNewValidKey()
381 self.moveCursorOnNewValidKey()
382 self.moveCursor(self.getCurrentPromptStart())
382 self.moveCursor(self.getCurrentPromptStart())
383 return True
383 return True
384 elif event.Modifiers == wx.MOD_SHIFT:
384 elif event.Modifiers == wx.MOD_SHIFT:
385 self.moveCursorOnNewValidKey()
385 self.moveCursorOnNewValidKey()
386 self.selectFromTo(self.getCurrentPromptStart(),self.getCursorPos())
386 self.selectFromTo(self.getCurrentPromptStart(),self.getCursorPos())
387 return True
387 return True
388 else:
388 else:
389 return False
389 return False
390
390
391 elif event.GetKeyCode() == wx.WXK_LEFT:
391 elif event.GetKeyCode() == wx.WXK_LEFT:
392 if event.Modifiers == wx.MOD_NONE:
392 if event.Modifiers == wx.MOD_NONE:
393 self.moveCursorOnNewValidKey()
393 self.moveCursorOnNewValidKey()
394
394
395 self.moveCursor(self.getCursorPos()-1)
395 self.moveCursor(self.getCursorPos()-1)
396 if self.getCursorPos() < self.getCurrentPromptStart():
396 if self.getCursorPos() < self.getCurrentPromptStart():
397 self.moveCursor(self.getCurrentPromptStart())
397 self.moveCursor(self.getCurrentPromptStart())
398 return True
398 return True
399
399
400 elif event.GetKeyCode() == wx.WXK_BACK:
400 elif event.GetKeyCode() == wx.WXK_BACK:
401 self.moveCursorOnNewValidKey()
401 self.moveCursorOnNewValidKey()
402 if self.getCursorPos() > self.getCurrentPromptStart():
402 if self.getCursorPos() > self.getCurrentPromptStart():
403 self.removeFromTo(self.getCursorPos()-1,self.getCursorPos())
403 self.removeFromTo(self.getCursorPos()-1,self.getCursorPos())
404 return True
404 return True
405
405
406 if skip:
406 if skip:
407 if event.GetKeyCode() not in [wx.WXK_PAGEUP,wx.WXK_PAGEDOWN] and event.Modifiers == wx.MOD_NONE:
407 if event.GetKeyCode() not in [wx.WXK_PAGEUP,wx.WXK_PAGEDOWN] and event.Modifiers == wx.MOD_NONE:
408 self.moveCursorOnNewValidKey()
408 self.moveCursorOnNewValidKey()
409
409
410 event.Skip()
410 event.Skip()
411 return True
411 return True
412 return False
412 return False
413
413
414 def OnUpdateUI(self, evt):
414 def OnUpdateUI(self, evt):
415 # check for matching braces
415 # check for matching braces
416 braceAtCaret = -1
416 braceAtCaret = -1
417 braceOpposite = -1
417 braceOpposite = -1
418 charBefore = None
418 charBefore = None
419 caretPos = self.GetCurrentPos()
419 caretPos = self.GetCurrentPos()
420
420
421 if caretPos > 0:
421 if caretPos > 0:
422 charBefore = self.GetCharAt(caretPos - 1)
422 charBefore = self.GetCharAt(caretPos - 1)
423 styleBefore = self.GetStyleAt(caretPos - 1)
423 styleBefore = self.GetStyleAt(caretPos - 1)
424
424
425 # check before
425 # check before
426 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
426 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
427 braceAtCaret = caretPos - 1
427 braceAtCaret = caretPos - 1
428
428
429 # check after
429 # check after
430 if braceAtCaret < 0:
430 if braceAtCaret < 0:
431 charAfter = self.GetCharAt(caretPos)
431 charAfter = self.GetCharAt(caretPos)
432 styleAfter = self.GetStyleAt(caretPos)
432 styleAfter = self.GetStyleAt(caretPos)
433
433
434 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
434 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
435 braceAtCaret = caretPos
435 braceAtCaret = caretPos
436
436
437 if braceAtCaret >= 0:
437 if braceAtCaret >= 0:
438 braceOpposite = self.BraceMatch(braceAtCaret)
438 braceOpposite = self.BraceMatch(braceAtCaret)
439
439
440 if braceAtCaret != -1 and braceOpposite == -1:
440 if braceAtCaret != -1 and braceOpposite == -1:
441 self.BraceBadLight(braceAtCaret)
441 self.BraceBadLight(braceAtCaret)
442 else:
442 else:
443 self.BraceHighlight(braceAtCaret, braceOpposite)
443 self.BraceHighlight(braceAtCaret, braceOpposite)
444 #pt = self.PointFromPosition(braceOpposite)
444 #pt = self.PointFromPosition(braceOpposite)
445 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
445 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
446 #print pt
446 #print pt
447 #self.Refresh(False)
447 #self.Refresh(False)
448
448
449 class WxIPythonViewPanel(wx.Panel):
449 class WxIPythonViewPanel(wx.Panel):
450 '''
450 '''
451 This is wx.Panel that embbed the IPython Thread and the wx.StyledTextControl
451 This is wx.Panel that embbed the IPython Thread and the wx.StyledTextControl
452 If you want to port this to any other GUI toolkit, just replace the WxConsoleView
452 If you want to port this to any other GUI toolkit, just replace the WxConsoleView
453 by YOURGUIConsoleView and make YOURGUIIPythonView derivate from whatever container you want.
453 by YOURGUIConsoleView and make YOURGUIIPythonView derivate from whatever container you want.
454 I've choosed to derivate from a wx.Panel because it seems to be ore usefull
454 I've choosed to derivate from a wx.Panel because it seems to be ore usefull
455 Any idea to make it more 'genric' welcomed.
455 Any idea to make it more 'genric' welcomed.
456 '''
456 '''
457
457
458 def __init__(self, parent, ask_exit_handler=None, intro=None,
458 def __init__(self, parent, ask_exit_handler=None, intro=None,
459 background_color="BLACK", add_button_handler=None,
459 background_color="BLACK", add_button_handler=None,
460 wx_ip_shell=None,
460 wx_ip_shell=None,
461 ):
461 ):
462 '''
462 '''
463 Initialize.
463 Initialize.
464 Instanciate an IPython thread.
464 Instanciate an IPython thread.
465 Instanciate a WxConsoleView.
465 Instanciate a WxConsoleView.
466 Redirect I/O to console.
466 Redirect I/O to console.
467 '''
467 '''
468 wx.Panel.__init__(self,parent,-1)
468 wx.Panel.__init__(self,parent,-1)
469
469
470 ### IPython thread instanciation ###
470 ### IPython thread instanciation ###
471 self.cout = StringIO()
471 self.cout = StringIO()
472
472
473 self.add_button_handler = add_button_handler
473 self.add_button_handler = add_button_handler
474 self.ask_exit_handler = ask_exit_handler
474 self.ask_exit_handler = ask_exit_handler
475
475
476 if wx_ip_shell is not None:
476 if wx_ip_shell is not None:
477 self.IP = wx_ip_shell
477 self.IP = wx_ip_shell
478 else:
478 else:
479 self.IP = WxNonBlockingIPShell(self,
479 self.IP = WxNonBlockingIPShell(self,
480 cout=self.cout,cerr=self.cout,
480 cout=self.cout,cerr=self.cout,
481 ask_exit_handler = ask_exit_handler)
481 ask_exit_handler = ask_exit_handler)
482 ### IPython wx console view instanciation ###
482 ### IPython wx console view instanciation ###
483 #If user didn't defined an intro text, we create one for him
483 #If user didn't defined an intro text, we create one for him
484 #If you really wnat an empty intrp just call wxIPythonViewPanel with intro=''
484 #If you really wnat an empty intrp just call wxIPythonViewPanel with intro=''
485 if intro == None:
485 if intro == None:
486 welcome_text = "Welcome to WxIPython Shell.\n\n"
486 welcome_text = "Welcome to WxIPython Shell.\n\n"
487 welcome_text+= self.IP.getBanner()
487 welcome_text+= self.IP.getBanner()
488 welcome_text+= "!command -> Execute command in shell\n"
488 welcome_text+= "!command -> Execute command in shell\n"
489 welcome_text+= "TAB -> Autocompletion\n"
489 welcome_text+= "TAB -> Autocompletion\n"
490
490
491 self.text_ctrl = WxConsoleView(self,
491 self.text_ctrl = WxConsoleView(self,
492 self.IP.getPrompt(),
492 self.IP.getPrompt(),
493 intro=welcome_text,
493 intro=welcome_text,
494 background_color=background_color)
494 background_color=background_color)
495
495
496 self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.keyPress, self.text_ctrl)
496 self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.keyPress, self.text_ctrl)
497
497
498 ### making the layout of the panel ###
498 ### making the layout of the panel ###
499 sizer = wx.BoxSizer(wx.VERTICAL)
499 sizer = wx.BoxSizer(wx.VERTICAL)
500 sizer.Add(self.text_ctrl, 1, wx.EXPAND)
500 sizer.Add(self.text_ctrl, 1, wx.EXPAND)
501 self.SetAutoLayout(True)
501 self.SetAutoLayout(True)
502 sizer.Fit(self)
502 sizer.Fit(self)
503 sizer.SetSizeHints(self)
503 sizer.SetSizeHints(self)
504 self.SetSizer(sizer)
504 self.SetSizer(sizer)
505 #and we focus on the widget :)
505 #and we focus on the widget :)
506 self.SetFocus()
506 self.SetFocus()
507
507
508 ### below are the thread communication variable ###
508 ### below are the thread communication variable ###
509 # the IPython thread is managed via unidirectional communication.
509 # the IPython thread is managed via unidirectional communication.
510 # It's a thread slave that can't interact by itself with the GUI.
510 # It's a thread slave that can't interact by itself with the GUI.
511 # When the GUI event loop is done runStateMachine() is called and the thread sate is then
511 # When the GUI event loop is done runStateMachine() is called and the thread sate is then
512 # managed.
512 # managed.
513
513
514 #Initialize the state machine #kept for information
514 #Initialize the state machine #kept for information
515 #self.states = ['IDLE',
515 #self.states = ['IDLE',
516 # 'DO_EXECUTE_LINE',
516 # 'DO_EXECUTE_LINE',
517 # 'WAIT_END_OF_EXECUTION',
517 # 'WAIT_END_OF_EXECUTION',
518 # 'SHOW_DOC',
518 # 'SHOW_DOC',
519 # 'SHOW_PROMPT']
519 # 'SHOW_PROMPT']
520
520
521 self.cur_state = 'IDLE'
521 self.cur_state = 'IDLE'
522 self.pager_state = 'DONE'
522 self.pager_state = 'DONE'
523 #wx.CallAfter(self.runStateMachine)
523 #wx.CallAfter(self.runStateMachine)
524
524
525 # This creates a new Event class and a EVT binder function
525 # This creates a new Event class and a EVT binder function
526 #(self.AskExitEvent, EVT_ASK_EXIT) = wx.lib.newevent.NewEvent()
526 #(self.AskExitEvent, EVT_ASK_EXIT) = wx.lib.newevent.NewEvent()
527 #(self.AddButtonEvent, EVT_ADDBUTTON_EXIT) = wx.lib.newevent.NewEvent()
527 #(self.AddButtonEvent, EVT_ADDBUTTON_EXIT) = wx.lib.newevent.NewEvent()
528
528
529
529
530 #self.Bind(wx.EVT_IDLE, self.runStateMachine)
530 #self.Bind(wx.EVT_IDLE, self.runStateMachine)
531
531
532 def __del__(self):
532 def __del__(self):
533 WxConsoleView.__del__()
533 WxConsoleView.__del__()
534
534
535 #---------------------------- IPython Thread Management ---------------------------------------
535 #---------------------------- IPython Thread Management ---------------------------------------
536 def stateDoExecuteLine(self):
536 def stateDoExecuteLine(self):
537 #print >>sys.__stdout__,"command:",self.getCurrentLine()
537 #print >>sys.__stdout__,"command:",self.getCurrentLine()
538 self.doExecuteLine(self.text_ctrl.getCurrentLine())
538 self.doExecuteLine(self.text_ctrl.getCurrentLine())
539
539
540 def doExecuteLine(self,line):
540 def doExecuteLine(self,line):
541 #print >>sys.__stdout__,"command:",line
541 #print >>sys.__stdout__,"command:",line
542 self.IP.doExecute(line.replace('\t',' '*4))
542 self.IP.doExecute(line.replace('\t',' '*4))
543 self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
543 self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
544 self.cur_state = 'WAIT_END_OF_EXECUTION'
544 self.cur_state = 'WAIT_END_OF_EXECUTION'
545
545
546
546
547 def evtStateExecuteDone(self,evt):
547 def evtStateExecuteDone(self,evt):
548 self.doc = self.IP.getDocText()
548 self.doc = self.IP.getDocText()
549 self.help = self.IP.getHelpText()
549 self.help = self.IP.getHelpText()
550 if self.doc:
550 if self.doc:
551 self.pager_state = 'INIT'
551 self.pager_state = 'INIT'
552 self.cur_state = 'SHOW_DOC'
552 self.cur_state = 'SHOW_DOC'
553 self.pager(self.doc)
553 self.pager(self.doc)
554 #if self.pager_state == 'DONE':
554 #if self.pager_state == 'DONE':
555 if self.help:
555 if self.help:
556 self.pager_state = 'INIT_HELP'
556 self.pager_state = 'INIT_HELP'
557 self.cur_state = 'SHOW_DOC'
557 self.cur_state = 'SHOW_DOC'
558 self.pager(self.help)
558 self.pager(self.help)
559
559
560 else:
560 else:
561 self.stateShowPrompt()
561 self.stateShowPrompt()
562
562
563 def stateShowPrompt(self):
563 def stateShowPrompt(self):
564 self.cur_state = 'SHOW_PROMPT'
564 self.cur_state = 'SHOW_PROMPT'
565 self.text_ctrl.setPrompt(self.IP.getPrompt())
565 self.text_ctrl.setPrompt(self.IP.getPrompt())
566 self.text_ctrl.setIndentation(self.IP.getIndentation())
566 self.text_ctrl.setIndentation(self.IP.getIndentation())
567 self.text_ctrl.setPromptCount(self.IP.getPromptCount())
567 self.text_ctrl.setPromptCount(self.IP.getPromptCount())
568 rv = self.cout.getvalue()
568 rv = self.cout.getvalue()
569 if rv: rv = rv.strip('\n')
569 if rv: rv = rv.strip('\n')
570 self.text_ctrl.showReturned(rv)
570 self.text_ctrl.showReturned(rv)
571 self.cout.truncate(0)
571 self.cout.truncate(0)
572 self.IP.initHistoryIndex()
572 self.IP.initHistoryIndex()
573 self.cur_state = 'IDLE'
573 self.cur_state = 'IDLE'
574
574
575 ## def runStateMachine(self,event):
575 ## def runStateMachine(self,event):
576 ## #print >>sys.__stdout__,"state:",self.cur_state
576 ## #print >>sys.__stdout__,"state:",self.cur_state
577 ## self.updateStatusTracker(self.cur_state)
577 ## self.updateStatusTracker(self.cur_state)
578 ##
578 ##
579 ## #if self.cur_state == 'DO_EXECUTE_LINE':
579 ## #if self.cur_state == 'DO_EXECUTE_LINE':
580 ## # self.doExecuteLine()
580 ## # self.doExecuteLine()
581 ##
581 ##
582 ## if self.cur_state == 'WAIT_END_OF_EXECUTION':
582 ## if self.cur_state == 'WAIT_END_OF_EXECUTION':
583 ## if self.IP.isExecuteDone():
583 ## if self.IP.isExecuteDone():
584 ## #self.button = self.IP.getAddButton()
584 ## #self.button = self.IP.getAddButton()
585 ## #if self.IP.getAskExit():
585 ## #if self.IP.getAskExit():
586 ## # evt = self.AskExitEvent()
586 ## # evt = self.AskExitEvent()
587 ## # wx.PostEvent(self, evt)
587 ## # wx.PostEvent(self, evt)
588 ## # self.IP.clearAskExit()
588 ## # self.IP.clearAskExit()
589 ## self.doc = self.IP.getDocText()
589 ## self.doc = self.IP.getDocText()
590 ## if self.doc:
590 ## if self.doc:
591 ## self.pager_state = 'INIT'
591 ## self.pager_state = 'INIT'
592 ## self.cur_state = 'SHOW_DOC'
592 ## self.cur_state = 'SHOW_DOC'
593 ## #if self.button:
593 ## #if self.button:
594 ## #self.IP.doExecute('print "cool"')#self.button['func'])
594 ## #self.IP.doExecute('print "cool"')#self.button['func'])
595 ## #self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
595 ## #self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
596 ##
596 ##
597 ## # self.button['func']='print "cool!"'
597 ## # self.button['func']='print "cool!"'
598 ## # self.add_button_handler(self.button)
598 ## # self.add_button_handler(self.button)
599 ## # self.IP.shortcutProcessed()
599 ## # self.IP.shortcutProcessed()
600 ##
600 ##
601 ## else:
601 ## else:
602 ## self.cur_state = 'SHOW_PROMPT'
602 ## self.cur_state = 'SHOW_PROMPT'
603 ##
603 ##
604 ## if self.cur_state == 'SHOW_PROMPT':
604 ## if self.cur_state == 'SHOW_PROMPT':
605 ## self.text_ctrl.setPrompt(self.IP.getPrompt())
605 ## self.text_ctrl.setPrompt(self.IP.getPrompt())
606 ## self.text_ctrl.setIndentation(self.IP.getIndentation())
606 ## self.text_ctrl.setIndentation(self.IP.getIndentation())
607 ## self.text_ctrl.setPromptCount(self.IP.getPromptCount())
607 ## self.text_ctrl.setPromptCount(self.IP.getPromptCount())
608 ## rv = self.cout.getvalue()
608 ## rv = self.cout.getvalue()
609 ## if rv: rv = rv.strip('\n')
609 ## if rv: rv = rv.strip('\n')
610 ## self.text_ctrl.showReturned(rv)
610 ## self.text_ctrl.showReturned(rv)
611 ## self.cout.truncate(0)
611 ## self.cout.truncate(0)
612 ## self.IP.initHistoryIndex()
612 ## self.IP.initHistoryIndex()
613 ## self.cur_state = 'IDLE'
613 ## self.cur_state = 'IDLE'
614 ##
614 ##
615 ## if self.cur_state == 'SHOW_DOC':
615 ## if self.cur_state == 'SHOW_DOC':
616 ## self.pager(self.doc)
616 ## self.pager(self.doc)
617 ## if self.pager_state == 'DONE':
617 ## if self.pager_state == 'DONE':
618 ## self.cur_state = 'SHOW_PROMPT'
618 ## self.cur_state = 'SHOW_PROMPT'
619 ##
619 ##
620 ## event.Skip()
620 ## event.Skip()
621
621
622 #---------------------------- IPython pager ---------------------------------------
622 #---------------------------- IPython pager ---------------------------------------
623 def pager(self,text):#,start=0,screen_lines=0,pager_cmd = None):
623 def pager(self,text):#,start=0,screen_lines=0,pager_cmd = None):
624 if self.pager_state == 'WAITING':
624 if self.pager_state == 'WAITING':
625 #print >>sys.__stdout__,"PAGER waiting"
625 #print >>sys.__stdout__,"PAGER waiting"
626 return
626 return
627
627
628 if self.pager_state == 'INIT':
628 if self.pager_state == 'INIT':
629 #print >>sys.__stdout__,"PAGER state:",self.pager_state
629 #print >>sys.__stdout__,"PAGER state:",self.pager_state
630 self.pager_lines = text[7:].split('\n')
630 self.pager_lines = text[7:].split('\n')
631 self.pager_nb_lines = len(self.pager_lines)
631 self.pager_nb_lines = len(self.pager_lines)
632 self.pager_index = 0
632 self.pager_index = 0
633 self.pager_do_remove = False
633 self.pager_do_remove = False
634 self.text_ctrl.write('\n')
634 self.text_ctrl.write('\n')
635 self.pager_state = 'PROCESS_LINES'
635 self.pager_state = 'PROCESS_LINES'
636
636
637 if self.pager_state == 'INIT_HELP':
637 if self.pager_state == 'INIT_HELP':
638 #print >>sys.__stdout__,"HELP PAGER state:",self.pager_state
638 #print >>sys.__stdout__,"HELP PAGER state:",self.pager_state
639 self.pager_lines = text[:].split('\n')
639 self.pager_lines = text[:].split('\n')
640 self.pager_nb_lines = len(self.pager_lines)
640 self.pager_nb_lines = len(self.pager_lines)
641 self.pager_index = 0
641 self.pager_index = 0
642 self.pager_do_remove = False
642 self.pager_do_remove = False
643 self.text_ctrl.write('\n')
643 self.text_ctrl.write('\n')
644 self.pager_state = 'PROCESS_LINES'
644 self.pager_state = 'PROCESS_LINES'
645
645
646 if self.pager_state == 'PROCESS_LINES':
646 if self.pager_state == 'PROCESS_LINES':
647 #print >>sys.__stdout__,"PAGER state:",self.pager_state
647 #print >>sys.__stdout__,"PAGER state:",self.pager_state
648 if self.pager_do_remove == True:
648 if self.pager_do_remove == True:
649 self.text_ctrl.removeCurrentLine()
649 self.text_ctrl.removeCurrentLine()
650 self.pager_do_remove = False
650 self.pager_do_remove = False
651
651
652 if self.pager_nb_lines > 10:
652 if self.pager_nb_lines > 10:
653 #print >>sys.__stdout__,"PAGER processing 10 lines"
653 #print >>sys.__stdout__,"PAGER processing 10 lines"
654 if self.pager_index > 0:
654 if self.pager_index > 0:
655 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
655 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
656 else:
656 else:
657 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
657 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
658
658
659 for line in self.pager_lines[self.pager_index+1:self.pager_index+9]:
659 for line in self.pager_lines[self.pager_index+1:self.pager_index+9]:
660 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
660 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
661 self.pager_index += 10
661 self.pager_index += 10
662 self.pager_nb_lines -= 10
662 self.pager_nb_lines -= 10
663 self.text_ctrl.write("--- Push Enter to continue or 'Q' to quit---")
663 self.text_ctrl.write("--- Push Enter to continue or 'Q' to quit---")
664 self.pager_do_remove = True
664 self.pager_do_remove = True
665 self.pager_state = 'WAITING'
665 self.pager_state = 'WAITING'
666 return
666 return
667 else:
667 else:
668 #print >>sys.__stdout__,"PAGER processing last lines"
668 #print >>sys.__stdout__,"PAGER processing last lines"
669 if self.pager_nb_lines > 0:
669 if self.pager_nb_lines > 0:
670 if self.pager_index > 0:
670 if self.pager_index > 0:
671 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
671 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
672 else:
672 else:
673 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
673 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
674
674
675 self.pager_index += 1
675 self.pager_index += 1
676 self.pager_nb_lines -= 1
676 self.pager_nb_lines -= 1
677 if self.pager_nb_lines > 0:
677 if self.pager_nb_lines > 0:
678 for line in self.pager_lines[self.pager_index:]:
678 for line in self.pager_lines[self.pager_index:]:
679 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
679 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
680 self.pager_nb_lines = 0
680 self.pager_nb_lines = 0
681 self.pager_state = 'DONE'
681 self.pager_state = 'DONE'
682 self.stateShowPrompt()
682 self.stateShowPrompt()
683
683
684 #---------------------------- Key Handler --------------------------------------------
684 #---------------------------- Key Handler --------------------------------------------
685 def keyPress(self, event):
685 def keyPress(self, event):
686 '''
686 '''
687 Key press callback with plenty of shell goodness, like history,
687 Key press callback with plenty of shell goodness, like history,
688 autocompletions, etc.
688 autocompletions, etc.
689 '''
689 '''
690
690
691 if event.GetKeyCode() == ord('C'):
691 if event.GetKeyCode() == ord('C'):
692 if event.Modifiers == wx.MOD_CONTROL:
692 if event.Modifiers == wx.MOD_CONTROL:
693 if self.cur_state == 'WAIT_END_OF_EXECUTION':
693 if self.cur_state == 'WAIT_END_OF_EXECUTION':
694 #we raise an exception inside the IPython thread container
694 #we raise an exception inside the IPython thread container
695 self.IP.ce.raise_exc(KeyboardInterrupt)
695 self.IP.ce.raise_exc(KeyboardInterrupt)
696 return
696 return
697
697
698 if event.KeyCode == wx.WXK_RETURN:
698 if event.KeyCode == wx.WXK_RETURN:
699 if self.cur_state == 'IDLE':
699 if self.cur_state == 'IDLE':
700 #we change the state ot the state machine
700 #we change the state ot the state machine
701 self.cur_state = 'DO_EXECUTE_LINE'
701 self.cur_state = 'DO_EXECUTE_LINE'
702 self.stateDoExecuteLine()
702 self.stateDoExecuteLine()
703 return
703 return
704 if self.pager_state == 'WAITING':
704 if self.pager_state == 'WAITING':
705 self.pager_state = 'PROCESS_LINES'
705 self.pager_state = 'PROCESS_LINES'
706 self.pager(self.doc)
706 self.pager(self.doc)
707 return
707 return
708
708
709 if event.GetKeyCode() in [ord('q'),ord('Q')]:
709 if event.GetKeyCode() in [ord('q'),ord('Q')]:
710 if self.pager_state == 'WAITING':
710 if self.pager_state == 'WAITING':
711 self.pager_state = 'DONE'
711 self.pager_state = 'DONE'
712 self.stateShowPrompt()
712 self.stateShowPrompt()
713 return
713 return
714
714
715 #scroll_position = self.text_ctrl.GetScrollPos(wx.VERTICAL)
715 #scroll_position = self.text_ctrl.GetScrollPos(wx.VERTICAL)
716 if self.cur_state == 'IDLE':
716 if self.cur_state == 'IDLE':
717 if event.KeyCode == wx.WXK_UP:
717 if event.KeyCode == wx.WXK_UP:
718 history = self.IP.historyBack()
718 history = self.IP.historyBack()
719 self.text_ctrl.writeHistory(history)
719 self.text_ctrl.writeHistory(history)
720 return
720 return
721 if event.KeyCode == wx.WXK_DOWN:
721 if event.KeyCode == wx.WXK_DOWN:
722 history = self.IP.historyForward()
722 history = self.IP.historyForward()
723 self.text_ctrl.writeHistory(history)
723 self.text_ctrl.writeHistory(history)
724 return
724 return
725 if event.KeyCode == wx.WXK_TAB:
725 if event.KeyCode == wx.WXK_TAB:
726 #if line empty we disable tab completion
726 #if line empty we disable tab completion
727 if not self.text_ctrl.getCurrentLine().strip():
727 if not self.text_ctrl.getCurrentLine().strip():
728 self.text_ctrl.write('\t')
728 self.text_ctrl.write('\t')
729 return
729 return
730 completed, possibilities = self.IP.complete(self.text_ctrl.getCurrentLine())
730 completed, possibilities = self.IP.complete(self.text_ctrl.getCurrentLine())
731 if len(possibilities) > 1:
731 if len(possibilities) > 1:
732 cur_slice = self.text_ctrl.getCurrentLine()
732 cur_slice = self.text_ctrl.getCurrentLine()
733 self.text_ctrl.write('\n')
733 self.text_ctrl.write('\n')
734 self.text_ctrl.writeCompletion(possibilities)
734 self.text_ctrl.writeCompletion(possibilities)
735 self.text_ctrl.write('\n')
735 self.text_ctrl.write('\n')
736
736
737 self.text_ctrl.showPrompt()
737 self.text_ctrl.showPrompt()
738 self.text_ctrl.write(cur_slice)
738 self.text_ctrl.write(cur_slice)
739 self.text_ctrl.changeLine(completed or cur_slice)
739 self.text_ctrl.changeLine(completed or cur_slice)
740
740
741 return
741 return
742 event.Skip()
742 event.Skip()
743
743
744 #---------------------------- Hook Section --------------------------------------------
744 #---------------------------- Hook Section --------------------------------------------
745 def updateHistoryTracker(self,command_line):
745 def updateHistoryTracker(self,command_line):
746 '''
746 '''
747 Default history tracker (does nothing)
747 Default history tracker (does nothing)
748 '''
748 '''
749 pass
749 pass
750
750
751 def setHistoryTrackerHook(self,func):
751 def setHistoryTrackerHook(self,func):
752 '''
752 '''
753 Define a new history tracker
753 Define a new history tracker
754 '''
754 '''
755 self.updateHistoryTracker = func
755 self.updateHistoryTracker = func
756 def updateStatusTracker(self,status):
756 def updateStatusTracker(self,status):
757 '''
757 '''
758 Default status tracker (does nothing)
758 Default status tracker (does nothing)
759 '''
759 '''
760 pass
760 pass
761
761
762 def setStatusTrackerHook(self,func):
762 def setStatusTrackerHook(self,func):
763 '''
763 '''
764 Define a new status tracker
764 Define a new status tracker
765 '''
765 '''
766 self.updateStatusTracker = func
766 self.updateStatusTracker = func
767
767
@@ -1,473 +1,473 b''
1 #!/usr/bin/python
1 #!/usr/bin/python
2 # -*- coding: iso-8859-15 -*-
2 # -*- coding: iso-8859-15 -*-
3 '''
3 '''
4 Provides IPython remote instance.
4 Provides IPython remote instance.
5
5
6 @author: Laurent Dufrechou
6 @author: Laurent Dufrechou
7 laurent.dufrechou _at_ gmail.com
7 laurent.dufrechou _at_ gmail.com
8 @license: BSD
8 @license: BSD
9
9
10 All rights reserved. This program and the accompanying materials are made
10 All rights reserved. This program and the accompanying materials are made
11 available under the terms of the BSD which accompanies this distribution, and
11 available under the terms of the BSD which accompanies this distribution, and
12 is available at U{http://www.opensource.org/licenses/bsd-license.php}
12 is available at U{http://www.opensource.org/licenses/bsd-license.php}
13 '''
13 '''
14
14
15 __version__ = 0.9
15 __version__ = 0.9
16 __author__ = "Laurent Dufrechou"
16 __author__ = "Laurent Dufrechou"
17 __email__ = "laurent.dufrechou _at_ gmail.com"
17 __email__ = "laurent.dufrechou _at_ gmail.com"
18 __license__ = "BSD"
18 __license__ = "BSD"
19
19
20 import re
20 import re
21 import sys
21 import sys
22 import os
22 import os
23 import locale
23 import locale
24 import time
24 import time
25 import pydoc,__builtin__,site
25 import pydoc,__builtin__,site
26 from thread_ex import ThreadEx
26 from thread_ex import ThreadEx
27 from StringIO import StringIO
27 from StringIO import StringIO
28
28
29 try:
29 try:
30 import IPython
30 import IPython
31 except Exception,e:
31 except Exception,e:
32 raise "Error importing IPython (%s)" % str(e)
32 raise "Error importing IPython (%s)" % str(e)
33
33
34 ##############################################################################
34 ##############################################################################
35 class _Helper(object):
35 class _Helper(object):
36 """Redefine the built-in 'help'.
36 """Redefine the built-in 'help'.
37 This is a wrapper around pydoc.help (with a twist).
37 This is a wrapper around pydoc.help (with a twist).
38 """
38 """
39
39
40 def __init__(self,pager):
40 def __init__(self,pager):
41 self._pager = pager
41 self._pager = pager
42
42
43 def __repr__(self):
43 def __repr__(self):
44 return "Type help() for interactive help, " \
44 return "Type help() for interactive help, " \
45 "or help(object) for help about object."
45 "or help(object) for help about object."
46
46
47 def __call__(self, *args, **kwds):
47 def __call__(self, *args, **kwds):
48 class DummyWriter(object):
48 class DummyWriter(object):
49 def __init__(self,pager):
49 def __init__(self,pager):
50 self._pager = pager
50 self._pager = pager
51
51
52 def write(self,data):
52 def write(self,data):
53 self._pager(data)
53 self._pager(data)
54
54
55 import pydoc
55 import pydoc
56 pydoc.help.output = DummyWriter(self._pager)
56 pydoc.help.output = DummyWriter(self._pager)
57 pydoc.help.interact = lambda :1
57 pydoc.help.interact = lambda :1
58
58
59 #helper.output.write = self.doc.append
59 #helper.output.write = self.doc.append
60 return pydoc.help(*args, **kwds)
60 return pydoc.help(*args, **kwds)
61
61
62
62
63 ##############################################################################
63 ##############################################################################
64 class _CodeExecutor(ThreadEx):
64 class _CodeExecutor(ThreadEx):
65
65
66 def __init__(self, instance, after):
66 def __init__(self, instance, after):
67 ThreadEx.__init__(self)
67 ThreadEx.__init__(self)
68 self.instance = instance
68 self.instance = instance
69 self._afterExecute=after
69 self._afterExecute=after
70
70
71 def run(self):
71 def run(self):
72 try:
72 try:
73 self.instance._doc_text = None
73 self.instance._doc_text = None
74 self.instance._help_text = None
74 self.instance._help_text = None
75 self.instance._execute()
75 self.instance._execute()
76 # used for uper class to generate event after execution
76 # used for uper class to generate event after execution
77 self._afterExecute()
77 self._afterExecute()
78
78
79 except KeyboardInterrupt:
79 except KeyboardInterrupt:
80 pass
80 pass
81
81
82
82
83 ##############################################################################
83 ##############################################################################
84 class NonBlockingIPShell(object):
84 class NonBlockingIPShell(object):
85 '''
85 '''
86 Create an IPython instance, running the commands in a separate,
86 Create an IPython instance, running the commands in a separate,
87 non-blocking thread.
87 non-blocking thread.
88 This allows embedding in any GUI without blockage.
88 This allows embedding in any GUI without blockage.
89
89
90 Note: The ThreadEx class supports asynchroneous function call
90 Note: The ThreadEx class supports asynchroneous function call
91 via raise_exc()
91 via raise_exc()
92 '''
92 '''
93
93
94 def __init__(self,argv
94 def __init__(self,argv
95 =[],user_ns={},user_global_ns=None,
95 =[],user_ns={},user_global_ns=None,
96 cin=None, cout=None, cerr=None,
96 cin=None, cout=None, cerr=None,
97 ask_exit_handler=None):
97 ask_exit_handler=None):
98 '''
98 '''
99 @param argv: Command line options for IPython
99 @param argv: Command line options for IPython
100 @type argv: list
100 @type argv: list
101 @param user_ns: User namespace.
101 @param user_ns: User namespace.
102 @type user_ns: dictionary
102 @type user_ns: dictionary
103 @param user_global_ns: User global namespace.
103 @param user_global_ns: User global namespace.
104 @type user_global_ns: dictionary.
104 @type user_global_ns: dictionary.
105 @param cin: Console standard input.
105 @param cin: Console standard input.
106 @type cin: IO stream
106 @type cin: IO stream
107 @param cout: Console standard output.
107 @param cout: Console standard output.
108 @type cout: IO stream
108 @type cout: IO stream
109 @param cerr: Console standard error.
109 @param cerr: Console standard error.
110 @type cerr: IO stream
110 @type cerr: IO stream
111 @param exit_handler: Replacement for builtin exit() function
111 @param exit_handler: Replacement for builtin exit() function
112 @type exit_handler: function
112 @type exit_handler: function
113 @param time_loop: Define the sleep time between two thread's loop
113 @param time_loop: Define the sleep time between two thread's loop
114 @type int
114 @type int
115 '''
115 '''
116 #first we redefine in/out/error functions of IPython
116 #first we redefine in/out/error functions of IPython
117 if cin:
117 if cin:
118 IPython.Shell.Term.cin = cin
118 IPython.Shell.Term.cin = cin
119 if cout:
119 if cout:
120 IPython.Shell.Term.cout = cout
120 IPython.Shell.Term.cout = cout
121 if cerr:
121 if cerr:
122 IPython.Shell.Term.cerr = cerr
122 IPython.Shell.Term.cerr = cerr
123
123
124 # This is to get rid of the blockage that accurs during
124 # This is to get rid of the blockage that accurs during
125 # IPython.Shell.InteractiveShell.user_setup()
125 # IPython.Shell.InteractiveShell.user_setup()
126 IPython.iplib.raw_input = lambda x: None
126 IPython.iplib.raw_input = lambda x: None
127
127
128 self._term = IPython.genutils.IOTerm(cin=cin, cout=cout, cerr=cerr)
128 self._term = IPython.genutils.IOTerm(cin=cin, cout=cout, cerr=cerr)
129
129
130 excepthook = sys.excepthook
130 excepthook = sys.excepthook
131
131
132 self._IP = IPython.Shell.make_IPython(
132 self._IP = IPython.Shell.make_IPython(
133 argv,user_ns=user_ns,
133 argv,user_ns=user_ns,
134 user_global_ns=user_global_ns,
134 user_global_ns=user_global_ns,
135 embedded=True,
135 embedded=True,
136 shell_class=IPython.Shell.InteractiveShell)
136 shell_class=IPython.Shell.InteractiveShell)
137
137
138 #we replace IPython default encoding by wx locale encoding
138 #we replace IPython default encoding by wx locale encoding
139 loc = locale.getpreferredencoding()
139 loc = locale.getpreferredencoding()
140 if loc:
140 if loc:
141 self._IP.stdin_encoding = loc
141 self._IP.stdin_encoding = loc
142 #we replace the ipython default pager by our pager
142 #we replace the ipython default pager by our pager
143 self._IP.set_hook('show_in_pager',self._pager)
143 self._IP.set_hook('show_in_pager',self._pager)
144
144
145 #we replace the ipython default shell command caller by our shell handler
145 #we replace the ipython default shell command caller by our shell handler
146 self._IP.set_hook('shell_hook',self._shell)
146 self._IP.set_hook('shell_hook',self._shell)
147
147
148 #we replace the ipython default input command caller by our method
148 #we replace the ipython default input command caller by our method
149 IPython.iplib.raw_input_original = self._raw_input
149 IPython.iplib.raw_input_original = self._raw_input
150 #we replace the ipython default exit command by our method
150 #we replace the ipython default exit command by our method
151 self._IP.exit = self._setAskExit
151 self._IP.exit = self._setAskExit
152 #we modify Exit and Quit Magic
152 #we modify Exit and Quit Magic
153 ip = IPython.ipapi.get()
153 ip = IPython.ipapi.get()
154 ip.expose_magic('Exit', self._setDoExit)
154 ip.expose_magic('Exit', self._setDoExit)
155 ip.expose_magic('Quit', self._setDoExit)
155 ip.expose_magic('Quit', self._setDoExit)
156
156
157 sys.excepthook = excepthook
157 sys.excepthook = excepthook
158
158
159 self._iter_more = 0
159 self._iter_more = 0
160 self._history_level = 0
160 self._history_level = 0
161 self._complete_sep = re.compile('[\s\{\}\[\]\(\)]')
161 self._complete_sep = re.compile('[\s\{\}\[\]\(\)]')
162 self._prompt = str(self._IP.outputcache.prompt1).strip()
162 self._prompt = str(self._IP.outputcache.prompt1).strip()
163
163
164 #thread working vars
164 #thread working vars
165 self._line_to_execute = ''
165 self._line_to_execute = ''
166
166
167 #vars that will be checked by GUI loop to handle thread states...
167 #vars that will be checked by GUI loop to handle thread states...
168 #will be replaced later by PostEvent GUI funtions...
168 #will be replaced later by PostEvent GUI funtions...
169 self._doc_text = None
169 self._doc_text = None
170 self._help_text = None
170 self._help_text = None
171 self._ask_exit = False
171 self._ask_exit = False
172 self._add_button = None
172 self._add_button = None
173
173
174 #we replace the help command
174 #we replace the help command
175 self._IP.user_ns['help'] = _Helper(self._pager_help)
175 self._IP.user_ns['help'] = _Helper(self._pager_help)
176
176
177 #----------------------- Thread management section ----------------------
177 #----------------------- Thread management section ----------------------
178 def doExecute(self,line):
178 def doExecute(self,line):
179 """
179 """
180 Tell the thread to process the 'line' command
180 Tell the thread to process the 'line' command
181 """
181 """
182
182
183 self._line_to_execute = line
183 self._line_to_execute = line
184
184
185 self.ce = _CodeExecutor(self,self._afterExecute)
185 self.ce = _CodeExecutor(self,self._afterExecute)
186 self.ce.start()
186 self.ce.start()
187
187
188 #----------------------- IPython management section ----------------------
188 #----------------------- IPython management section ----------------------
189 def getAskExit(self):
189 def getAskExit(self):
190 '''
190 '''
191 returns the _ask_exit variable that can be checked by GUI to see if
191 returns the _ask_exit variable that can be checked by GUI to see if
192 IPython request an exit handling
192 IPython request an exit handling
193 '''
193 '''
194 return self._ask_exit
194 return self._ask_exit
195
195
196 def clearAskExit(self):
196 def clearAskExit(self):
197 '''
197 '''
198 clear the _ask_exit var when GUI as handled the request.
198 clear the _ask_exit var when GUI as handled the request.
199 '''
199 '''
200 self._ask_exit = False
200 self._ask_exit = False
201
201
202 def getDocText(self):
202 def getDocText(self):
203 """
203 """
204 Returns the output of the processing that need to be paged (if any)
204 Returns the output of the processing that need to be paged (if any)
205
205
206 @return: The std output string.
206 @return: The std output string.
207 @rtype: string
207 @rtype: string
208 """
208 """
209 return self._doc_text
209 return self._doc_text
210
210
211 def getHelpText(self):
211 def getHelpText(self):
212 """
212 """
213 Returns the output of the processing that need to be paged via help pager(if any)
213 Returns the output of the processing that need to be paged via help pager(if any)
214
214
215 @return: The std output string.
215 @return: The std output string.
216 @rtype: string
216 @rtype: string
217 """
217 """
218 return self._help_text
218 return self._help_text
219
219
220 def getBanner(self):
220 def getBanner(self):
221 """
221 """
222 Returns the IPython banner for useful info on IPython instance
222 Returns the IPython banner for useful info on IPython instance
223
223
224 @return: The banner string.
224 @return: The banner string.
225 @rtype: string
225 @rtype: string
226 """
226 """
227 return self._IP.BANNER
227 return self._IP.BANNER
228
228
229 def getPromptCount(self):
229 def getPromptCount(self):
230 """
230 """
231 Returns the prompt number.
231 Returns the prompt number.
232 Each time a user execute a line in the IPython shell the prompt count is increased
232 Each time a user execute a line in the IPython shell the prompt count is increased
233
233
234 @return: The prompt number
234 @return: The prompt number
235 @rtype: int
235 @rtype: int
236 """
236 """
237 return self._IP.outputcache.prompt_count
237 return self._IP.outputcache.prompt_count
238
238
239 def getPrompt(self):
239 def getPrompt(self):
240 """
240 """
241 Returns current prompt inside IPython instance
241 Returns current prompt inside IPython instance
242 (Can be In [...]: ot ...:)
242 (Can be In [...]: ot ...:)
243
243
244 @return: The current prompt.
244 @return: The current prompt.
245 @rtype: string
245 @rtype: string
246 """
246 """
247 return self._prompt
247 return self._prompt
248
248
249 def getIndentation(self):
249 def getIndentation(self):
250 """
250 """
251 Returns the current indentation level
251 Returns the current indentation level
252 Usefull to put the caret at the good start position if we want to do autoindentation.
252 Usefull to put the caret at the good start position if we want to do autoindentation.
253
253
254 @return: The indentation level.
254 @return: The indentation level.
255 @rtype: int
255 @rtype: int
256 """
256 """
257 return self._IP.indent_current_nsp
257 return self._IP.indent_current_nsp
258
258
259 def updateNamespace(self, ns_dict):
259 def updateNamespace(self, ns_dict):
260 '''
260 '''
261 Add the current dictionary to the shell namespace.
261 Add the current dictionary to the shell namespace.
262
262
263 @param ns_dict: A dictionary of symbol-values.
263 @param ns_dict: A dictionary of symbol-values.
264 @type ns_dict: dictionary
264 @type ns_dict: dictionary
265 '''
265 '''
266 self._IP.user_ns.update(ns_dict)
266 self._IP.user_ns.update(ns_dict)
267
267
268 def complete(self, line):
268 def complete(self, line):
269 '''
269 '''
270 Returns an auto completed line and/or posibilities for completion.
270 Returns an auto completed line and/or posibilities for completion.
271
271
272 @param line: Given line so far.
272 @param line: Given line so far.
273 @type line: string
273 @type line: string
274
274
275 @return: Line completed as for as possible,
275 @return: Line completed as for as possible,
276 and possible further completions.
276 and possible further completions.
277 @rtype: tuple
277 @rtype: tuple
278 '''
278 '''
279 split_line = self._complete_sep.split(line)
279 split_line = self._complete_sep.split(line)
280 possibilities = self._IP.complete(split_line[-1])
280 possibilities = self._IP.complete(split_line[-1])
281 if possibilities:
281 if possibilities:
282
282
283 def _commonPrefix(str1, str2):
283 def _commonPrefix(str1, str2):
284 '''
284 '''
285 Reduction function. returns common prefix of two given strings.
285 Reduction function. returns common prefix of two given strings.
286
286
287 @param str1: First string.
287 @param str1: First string.
288 @type str1: string
288 @type str1: string
289 @param str2: Second string
289 @param str2: Second string
290 @type str2: string
290 @type str2: string
291
291
292 @return: Common prefix to both strings.
292 @return: Common prefix to both strings.
293 @rtype: string
293 @rtype: string
294 '''
294 '''
295 for i in range(len(str1)):
295 for i in range(len(str1)):
296 if not str2.startswith(str1[:i+1]):
296 if not str2.startswith(str1[:i+1]):
297 return str1[:i]
297 return str1[:i]
298 return str1
298 return str1
299 common_prefix = reduce(_commonPrefix, possibilities)
299 common_prefix = reduce(_commonPrefix, possibilities)
300 completed = line[:-len(split_line[-1])]+common_prefix
300 completed = line[:-len(split_line[-1])]+common_prefix
301 else:
301 else:
302 completed = line
302 completed = line
303 return completed, possibilities
303 return completed, possibilities
304
304
305 def historyBack(self):
305 def historyBack(self):
306 '''
306 '''
307 Provides one history command back.
307 Provides one history command back.
308
308
309 @return: The command string.
309 @return: The command string.
310 @rtype: string
310 @rtype: string
311 '''
311 '''
312 history = ''
312 history = ''
313 #the below while loop is used to suppress empty history lines
313 #the below while loop is used to suppress empty history lines
314 while((history == '' or history == '\n') and self._history_level >0):
314 while((history == '' or history == '\n') and self._history_level >0):
315 if self._history_level>=1:
315 if self._history_level>=1:
316 self._history_level -= 1
316 self._history_level -= 1
317 history = self._getHistory()
317 history = self._getHistory()
318 return history
318 return history
319
319
320 def historyForward(self):
320 def historyForward(self):
321 '''
321 '''
322 Provides one history command forward.
322 Provides one history command forward.
323
323
324 @return: The command string.
324 @return: The command string.
325 @rtype: string
325 @rtype: string
326 '''
326 '''
327 history = ''
327 history = ''
328 #the below while loop is used to suppress empty history lines
328 #the below while loop is used to suppress empty history lines
329 while((history == '' or history == '\n') and self._history_level <= self._getHistoryMaxIndex()):
329 while((history == '' or history == '\n') and self._history_level <= self._getHistoryMaxIndex()):
330 if self._history_level < self._getHistoryMaxIndex():
330 if self._history_level < self._getHistoryMaxIndex():
331 self._history_level += 1
331 self._history_level += 1
332 history = self._getHistory()
332 history = self._getHistory()
333 else:
333 else:
334 if self._history_level == self._getHistoryMaxIndex():
334 if self._history_level == self._getHistoryMaxIndex():
335 history = self._getHistory()
335 history = self._getHistory()
336 self._history_level += 1
336 self._history_level += 1
337 else:
337 else:
338 history = ''
338 history = ''
339 return history
339 return history
340
340
341 def initHistoryIndex(self):
341 def initHistoryIndex(self):
342 '''
342 '''
343 set history to last command entered
343 set history to last command entered
344 '''
344 '''
345 self._history_level = self._getHistoryMaxIndex()+1
345 self._history_level = self._getHistoryMaxIndex()+1
346
346
347 #----------------------- IPython PRIVATE management section --------------
347 #----------------------- IPython PRIVATE management section --------------
348 def _afterExecute(self):
348 def _afterExecute(self):
349 '''
349 '''
350 Can be redefined to generate post event after excution is done
350 Can be redefined to generate post event after excution is done
351 '''
351 '''
352 pass
352 pass
353
353
354 def _setAskExit(self):
354 def _setAskExit(self):
355 '''
355 '''
356 set the _ask_exit variable that can be checked by GUI to see if
356 set the _ask_exit variable that can be checked by GUI to see if
357 IPython request an exit handling
357 IPython request an exit handling
358 '''
358 '''
359 self._ask_exit = True
359 self._ask_exit = True
360
360
361 def _setDoExit(self, toto, arg):
361 def _setDoExit(self, toto, arg):
362 '''
362 '''
363 set the _do_exit variable that can be checked by GUI to see if
363 set the _do_exit variable that can be checked by GUI to see if
364 IPython do a direct exit of the app
364 IPython do a direct exit of the app
365 '''
365 '''
366 self._do_exit = True
366 self._do_exit = True
367
367
368 def _getHistoryMaxIndex(self):
368 def _getHistoryMaxIndex(self):
369 '''
369 '''
370 returns the max length of the history buffer
370 returns the max length of the history buffer
371
371
372 @return: history length
372 @return: history length
373 @rtype: int
373 @rtype: int
374 '''
374 '''
375 return len(self._IP.input_hist_raw)-1
375 return len(self._IP.input_hist_raw)-1
376
376
377 def _getHistory(self):
377 def _getHistory(self):
378 '''
378 '''
379 Get's the command string of the current history level.
379 Get's the command string of the current history level.
380
380
381 @return: Historic command stri
381 @return: Historic command stri
382 @rtype: string
382 @rtype: string
383 '''
383 '''
384 rv = self._IP.input_hist_raw[self._history_level].strip('\n')
384 rv = self._IP.input_hist_raw[self._history_level].strip('\n')
385 return rv
385 return rv
386
386
387 def _pager_help(self,text):
387 def _pager_help(self,text):
388 '''
388 '''
389 This function is used as a callback replacment to IPython help pager function
389 This function is used as a callback replacment to IPython help pager function
390
390
391 It puts the 'text' value inside the self._help_text string that can be retrived via getHelpText
391 It puts the 'text' value inside the self._help_text string that can be retrived via getHelpText
392 function.
392 function.
393 '''
393 '''
394 if self._help_text == None:
394 if self._help_text == None:
395 self._help_text = text
395 self._help_text = text
396 else:
396 else:
397 self._help_text += text
397 self._help_text += text
398
398
399 def _pager(self,IP,text):
399 def _pager(self,IP,text):
400 '''
400 '''
401 This function is used as a callback replacment to IPython pager function
401 This function is used as a callback replacment to IPython pager function
402
402
403 It puts the 'text' value inside the self._doc_text string that can be retrived via getDocText
403 It puts the 'text' value inside the self._doc_text string that can be retrived via getDocText
404 function.
404 function.
405 '''
405 '''
406 self._doc_text = text
406 self._doc_text = text
407
407
408 def _raw_input(self, prompt=''):
408 def _raw_input(self, prompt=''):
409 '''
409 '''
410 Custom raw_input() replacement. Get's current line from console buffer.
410 Custom raw_input() replacement. Get's current line from console buffer.
411
411
412 @param prompt: Prompt to print. Here for compatability as replacement.
412 @param prompt: Prompt to print. Here for compatability as replacement.
413 @type prompt: string
413 @type prompt: string
414
414
415 @return: The current command line text.
415 @return: The current command line text.
416 @rtype: string
416 @rtype: string
417 '''
417 '''
418 return self._line_to_execute
418 return self._line_to_execute
419
419
420 def _execute(self):
420 def _execute(self):
421 '''
421 '''
422 Executes the current line provided by the shell object.
422 Executes the current line provided by the shell object.
423 '''
423 '''
424 orig_stdout = sys.stdout
424 orig_stdout = sys.stdout
425 sys.stdout = IPython.Shell.Term.cout
425 sys.stdout = IPython.Shell.Term.cout
426
426
427 try:
427 try:
428 line = self._IP.raw_input(None, self._iter_more)
428 line = self._IP.raw_input(None, self._iter_more)
429 if self._IP.autoindent:
429 if self._IP.autoindent:
430 self._IP.readline_startup_hook(None)
430 self._IP.readline_startup_hook(None)
431
431
432 except KeyboardInterrupt:
432 except KeyboardInterrupt:
433 self._IP.write('\nKeyboardInterrupt\n')
433 self._IP.write('\nKeyboardInterrupt\n')
434 self._IP.resetbuffer()
434 self._IP.resetbuffer()
435 # keep cache in sync with the prompt counter:
435 # keep cache in sync with the prompt counter:
436 self._IP.outputcache.prompt_count -= 1
436 self._IP.outputcache.prompt_count -= 1
437
437
438 if self._IP.autoindent:
438 if self._IP.autoindent:
439 self._IP.indent_current_nsp = 0
439 self._IP.indent_current_nsp = 0
440 self._iter_more = 0
440 self._iter_more = 0
441 except:
441 except:
442 self._IP.showtraceback()
442 self._IP.showtraceback()
443 else:
443 else:
444 self._iter_more = self._IP.push(line)
444 self._iter_more = self._IP.push(line)
445 if (self._IP.SyntaxTB.last_syntax_error and
445 if (self._IP.SyntaxTB.last_syntax_error and
446 self._IP.rc.autoedit_syntax):
446 self._IP.rc.autoedit_syntax):
447 self._IP.edit_syntax_error()
447 self._IP.edit_syntax_error()
448 if self._iter_more:
448 if self._iter_more:
449 self._prompt = str(self._IP.outputcache.prompt2).strip()
449 self._prompt = str(self._IP.outputcache.prompt2).strip()
450 if self._IP.autoindent:
450 if self._IP.autoindent:
451 self._IP.readline_startup_hook(self._IP.pre_readline)
451 self._IP.readline_startup_hook(self._IP.pre_readline)
452 else:
452 else:
453 self._prompt = str(self._IP.outputcache.prompt1).strip()
453 self._prompt = str(self._IP.outputcache.prompt1).strip()
454 self._IP.indent_current_nsp = 0 #we set indentation to 0
454 self._IP.indent_current_nsp = 0 #we set indentation to 0
455 sys.stdout = orig_stdout
455 sys.stdout = orig_stdout
456
456
457 def _shell(self, ip, cmd):
457 def _shell(self, ip, cmd):
458 '''
458 '''
459 Replacement method to allow shell commands without them blocking.
459 Replacement method to allow shell commands without them blocking.
460
460
461 @param ip: Ipython instance, same as self._IP
461 @param ip: Ipython instance, same as self._IP
462 @type cmd: Ipython instance
462 @type cmd: Ipython instance
463 @param cmd: Shell command to execute.
463 @param cmd: Shell command to execute.
464 @type cmd: string
464 @type cmd: string
465 '''
465 '''
466 stdin, stdout = os.popen4(cmd)
466 stdin, stdout = os.popen4(cmd)
467 result = stdout.read().decode('cp437').encode(locale.getpreferredencoding())
467 result = stdout.read().decode('cp437').encode(locale.getpreferredencoding())
468 #we use print command because the shell command is called inside IPython instance and thus is
468 #we use print command because the shell command is called inside IPython instance and thus is
469 #redirected to thread cout
469 #redirected to thread cout
470 #"\x01\x1b[1;36m\x02" <-- add colour to the text...
470 #"\x01\x1b[1;36m\x02" <-- add colour to the text...
471 print "\x01\x1b[1;36m\x02"+result
471 print "\x01\x1b[1;36m\x02"+result
472 stdout.close()
472 stdout.close()
473 stdin.close()
473 stdin.close()
@@ -1,199 +1,199 b''
1 #!/usr/bin/python
1 #!/usr/bin/python
2 # -*- coding: iso-8859-15 -*-
2 # -*- coding: iso-8859-15 -*-
3
3
4 import wx.aui
4 import wx.aui
5 import wx.py
5 import wx.py
6 from wx.lib.wordwrap import wordwrap
6 from wx.lib.wordwrap import wordwrap
7
7
8 from ipython_view import *
8 from ipython_view import *
9 from ipython_history import *
9 from ipython_history import *
10
10
11 __version__ = 0.8
11 __version__ = 0.8
12 __author__ = "Laurent Dufrechou"
12 __author__ = "Laurent Dufrechou"
13 __email__ = "laurent.dufrechou _at_ gmail.com"
13 __email__ = "laurent.dufrechou _at_ gmail.com"
14 __license__ = "BSD"
14 __license__ = "BSD"
15
15
16 #-----------------------------------------
16 #-----------------------------------------
17 # Creating one main frame for our
17 # Creating one main frame for our
18 # application with movables windows
18 # application with movables windows
19 #-----------------------------------------
19 #-----------------------------------------
20 class MyFrame(wx.Frame):
20 class MyFrame(wx.Frame):
21 """Creating one main frame for our
21 """Creating one main frame for our
22 application with movables windows"""
22 application with movables windows"""
23 def __init__(self, parent=None, id=-1, title="WxIPython", pos=wx.DefaultPosition,
23 def __init__(self, parent=None, id=-1, title="WxIPython", pos=wx.DefaultPosition,
24 size=(800, 600), style=wx.DEFAULT_FRAME_STYLE):
24 size=(800, 600), style=wx.DEFAULT_FRAME_STYLE):
25 wx.Frame.__init__(self, parent, id, title, pos, size, style)
25 wx.Frame.__init__(self, parent, id, title, pos, size, style)
26 self._mgr = wx.aui.AuiManager()
26 self._mgr = wx.aui.AuiManager()
27
27
28 # notify PyAUI which frame to use
28 # notify PyAUI which frame to use
29 self._mgr.SetManagedWindow(self)
29 self._mgr.SetManagedWindow(self)
30
30
31 #create differents panels and make them persistant
31 #create differents panels and make them persistant
32 self.history_panel = IPythonHistoryPanel(self)
32 self.history_panel = IPythonHistoryPanel(self)
33
33
34 self.ipython_panel = WxIPythonViewPanel(self,self.OnExitDlg,
34 self.ipython_panel = WxIPythonViewPanel(self,self.OnExitDlg,
35 background_color = "BLACK")
35 background_color = "BLACK")
36
36
37 #self.ipython_panel = WxIPythonViewPanel(self,self.OnExitDlg,
37 #self.ipython_panel = WxIPythonViewPanel(self,self.OnExitDlg,
38 # background_color = "WHITE")
38 # background_color = "WHITE")
39
39
40 self.ipython_panel.setHistoryTrackerHook(self.history_panel.write)
40 self.ipython_panel.setHistoryTrackerHook(self.history_panel.write)
41 self.ipython_panel.setStatusTrackerHook(self.updateStatus)
41 self.ipython_panel.setStatusTrackerHook(self.updateStatus)
42
42
43 self.statusbar = self.createStatus()
43 self.statusbar = self.createStatus()
44 self.createMenu()
44 self.createMenu()
45
45
46 ########################################################################
46 ########################################################################
47 ### add the panes to the manager
47 ### add the panes to the manager
48 # main panels
48 # main panels
49 self._mgr.AddPane(self.ipython_panel , wx.CENTER, "IPython Shell")
49 self._mgr.AddPane(self.ipython_panel , wx.CENTER, "IPython Shell")
50 self._mgr.AddPane(self.history_panel , wx.RIGHT, "IPython history")
50 self._mgr.AddPane(self.history_panel , wx.RIGHT, "IPython history")
51
51
52 # now we specify some panel characteristics
52 # now we specify some panel characteristics
53 self._mgr.GetPane(self.ipython_panel).CaptionVisible(True);
53 self._mgr.GetPane(self.ipython_panel).CaptionVisible(True);
54 self._mgr.GetPane(self.history_panel).CaptionVisible(True);
54 self._mgr.GetPane(self.history_panel).CaptionVisible(True);
55 self._mgr.GetPane(self.history_panel).MinSize((200,400));
55 self._mgr.GetPane(self.history_panel).MinSize((200,400));
56
56
57 # tell the manager to "commit" all the changes just made
57 # tell the manager to "commit" all the changes just made
58 self._mgr.Update()
58 self._mgr.Update()
59
59
60 #global event handling
60 #global event handling
61 self.Bind(wx.EVT_CLOSE, self.OnClose)
61 self.Bind(wx.EVT_CLOSE, self.OnClose)
62 self.Bind(wx.EVT_MENU, self.OnClose,id=wx.ID_EXIT)
62 self.Bind(wx.EVT_MENU, self.OnClose,id=wx.ID_EXIT)
63 self.Bind(wx.EVT_MENU, self.OnShowIPythonPanel,id=wx.ID_HIGHEST+1)
63 self.Bind(wx.EVT_MENU, self.OnShowIPythonPanel,id=wx.ID_HIGHEST+1)
64 self.Bind(wx.EVT_MENU, self.OnShowHistoryPanel,id=wx.ID_HIGHEST+2)
64 self.Bind(wx.EVT_MENU, self.OnShowHistoryPanel,id=wx.ID_HIGHEST+2)
65 self.Bind(wx.EVT_MENU, self.OnShowAbout, id=wx.ID_HIGHEST+3)
65 self.Bind(wx.EVT_MENU, self.OnShowAbout, id=wx.ID_HIGHEST+3)
66 self.Bind(wx.EVT_MENU, self.OnShowAllPanel,id=wx.ID_HIGHEST+6)
66 self.Bind(wx.EVT_MENU, self.OnShowAllPanel,id=wx.ID_HIGHEST+6)
67
67
68 warn_text = 'Hello from IPython and wxPython.\n'
68 warn_text = 'Hello from IPython and wxPython.\n'
69 warn_text +='Please Note that this work is still EXPERIMENTAL\n'
69 warn_text +='Please Note that this work is still EXPERIMENTAL\n'
70 warn_text +='It does NOT emulate currently all the IPython functions.\n'
70 warn_text +='It does NOT emulate currently all the IPython functions.\n'
71
71
72 dlg = wx.MessageDialog(self,
72 dlg = wx.MessageDialog(self,
73 warn_text,
73 warn_text,
74 'Warning Box',
74 'Warning Box',
75 wx.OK | wx.ICON_INFORMATION
75 wx.OK | wx.ICON_INFORMATION
76 )
76 )
77 dlg.ShowModal()
77 dlg.ShowModal()
78 dlg.Destroy()
78 dlg.Destroy()
79
79
80 def createMenu(self):
80 def createMenu(self):
81 """local method used to create one menu bar"""
81 """local method used to create one menu bar"""
82
82
83 mb = wx.MenuBar()
83 mb = wx.MenuBar()
84
84
85 file_menu = wx.Menu()
85 file_menu = wx.Menu()
86 file_menu.Append(wx.ID_EXIT, "Exit")
86 file_menu.Append(wx.ID_EXIT, "Exit")
87
87
88 view_menu = wx.Menu()
88 view_menu = wx.Menu()
89 view_menu.Append(wx.ID_HIGHEST+1, "Show IPython Panel")
89 view_menu.Append(wx.ID_HIGHEST+1, "Show IPython Panel")
90 view_menu.Append(wx.ID_HIGHEST+2, "Show History Panel")
90 view_menu.Append(wx.ID_HIGHEST+2, "Show History Panel")
91 view_menu.AppendSeparator()
91 view_menu.AppendSeparator()
92 view_menu.Append(wx.ID_HIGHEST+6, "Show All")
92 view_menu.Append(wx.ID_HIGHEST+6, "Show All")
93
93
94 about_menu = wx.Menu()
94 about_menu = wx.Menu()
95 about_menu.Append(wx.ID_HIGHEST+3, "About")
95 about_menu.Append(wx.ID_HIGHEST+3, "About")
96
96
97 #view_menu.AppendSeparator()
97 #view_menu.AppendSeparator()
98 #options_menu = wx.Menu()
98 #options_menu = wx.Menu()
99 #options_menu.AppendCheckItem(wx.ID_HIGHEST+7, "Allow Floating")
99 #options_menu.AppendCheckItem(wx.ID_HIGHEST+7, "Allow Floating")
100 #options_menu.AppendCheckItem(wx.ID_HIGHEST+8, "Transparent Hint")
100 #options_menu.AppendCheckItem(wx.ID_HIGHEST+8, "Transparent Hint")
101 #options_menu.AppendCheckItem(wx.ID_HIGHEST+9, "Transparent Hint Fade-in")
101 #options_menu.AppendCheckItem(wx.ID_HIGHEST+9, "Transparent Hint Fade-in")
102
102
103
103
104 mb.Append(file_menu, "File")
104 mb.Append(file_menu, "File")
105 mb.Append(view_menu, "View")
105 mb.Append(view_menu, "View")
106 mb.Append(about_menu, "About")
106 mb.Append(about_menu, "About")
107 #mb.Append(options_menu, "Options")
107 #mb.Append(options_menu, "Options")
108
108
109 self.SetMenuBar(mb)
109 self.SetMenuBar(mb)
110
110
111 def createStatus(self):
111 def createStatus(self):
112 statusbar = self.CreateStatusBar(2, wx.ST_SIZEGRIP)
112 statusbar = self.CreateStatusBar(2, wx.ST_SIZEGRIP)
113 statusbar.SetStatusWidths([-2, -3])
113 statusbar.SetStatusWidths([-2, -3])
114 statusbar.SetStatusText("Ready", 0)
114 statusbar.SetStatusText("Ready", 0)
115 statusbar.SetStatusText("WxIPython "+str(__version__), 1)
115 statusbar.SetStatusText("WxIPython "+str(__version__), 1)
116 return statusbar
116 return statusbar
117
117
118 def updateStatus(self,text):
118 def updateStatus(self,text):
119 states = {'IDLE':'Idle',
119 states = {'IDLE':'Idle',
120 'DO_EXECUTE_LINE':'Send command',
120 'DO_EXECUTE_LINE':'Send command',
121 'WAIT_END_OF_EXECUTION':'Running command',
121 'WAIT_END_OF_EXECUTION':'Running command',
122 'SHOW_DOC':'Showing doc',
122 'SHOW_DOC':'Showing doc',
123 'SHOW_PROMPT':'Showing prompt'}
123 'SHOW_PROMPT':'Showing prompt'}
124 self.statusbar.SetStatusText(states[text], 0)
124 self.statusbar.SetStatusText(states[text], 0)
125
125
126 def OnClose(self, event):
126 def OnClose(self, event):
127 """#event used to close program """
127 """#event used to close program """
128 # deinitialize the frame manager
128 # deinitialize the frame manager
129 self._mgr.UnInit()
129 self._mgr.UnInit()
130 self.Destroy()
130 self.Destroy()
131 event.Skip()
131 event.Skip()
132
132
133 def OnExitDlg(self, event):
133 def OnExitDlg(self, event):
134 dlg = wx.MessageDialog(self, 'Are you sure you want to quit WxIPython',
134 dlg = wx.MessageDialog(self, 'Are you sure you want to quit WxIPython',
135 'WxIPython exit',
135 'WxIPython exit',
136 wx.ICON_QUESTION |
136 wx.ICON_QUESTION |
137 wx.YES_NO | wx.NO_DEFAULT
137 wx.YES_NO | wx.NO_DEFAULT
138 )
138 )
139 if dlg.ShowModal() == wx.ID_YES:
139 if dlg.ShowModal() == wx.ID_YES:
140 dlg.Destroy()
140 dlg.Destroy()
141 self._mgr.UnInit()
141 self._mgr.UnInit()
142 self.Destroy()
142 self.Destroy()
143 dlg.Destroy()
143 dlg.Destroy()
144
144
145 #event to display IPython pannel
145 #event to display IPython pannel
146 def OnShowIPythonPanel(self,event):
146 def OnShowIPythonPanel(self,event):
147 """ #event to display Boxpannel """
147 """ #event to display Boxpannel """
148 self._mgr.GetPane(self.ipython_panel).Show(True)
148 self._mgr.GetPane(self.ipython_panel).Show(True)
149 self._mgr.Update()
149 self._mgr.Update()
150 #event to display History pannel
150 #event to display History pannel
151 def OnShowHistoryPanel(self,event):
151 def OnShowHistoryPanel(self,event):
152 self._mgr.GetPane(self.history_panel).Show(True)
152 self._mgr.GetPane(self.history_panel).Show(True)
153 self._mgr.Update()
153 self._mgr.Update()
154
154
155 def OnShowAllPanel(self,event):
155 def OnShowAllPanel(self,event):
156 """#event to display all Pannels"""
156 """#event to display all Pannels"""
157 self._mgr.GetPane(self.ipython_panel).Show(True)
157 self._mgr.GetPane(self.ipython_panel).Show(True)
158 self._mgr.GetPane(self.history_panel).Show(True)
158 self._mgr.GetPane(self.history_panel).Show(True)
159 self._mgr.Update()
159 self._mgr.Update()
160
160
161 def OnShowAbout(self, event):
161 def OnShowAbout(self, event):
162 # First we create and fill the info object
162 # First we create and fill the info object
163 info = wx.AboutDialogInfo()
163 info = wx.AboutDialogInfo()
164 info.Name = "WxIPython"
164 info.Name = "WxIPython"
165 info.Version = str(__version__)
165 info.Version = str(__version__)
166 info.Copyright = "(C) 2007 Laurent Dufrechou"
166 info.Copyright = "(C) 2007 Laurent Dufrechou"
167 info.Description = wordwrap(
167 info.Description = wordwrap(
168 "A Gui that embbed a multithreaded IPython Shell",
168 "A Gui that embbed a multithreaded IPython Shell",
169 350, wx.ClientDC(self))
169 350, wx.ClientDC(self))
170 info.WebSite = ("http://ipython.scipy.org/", "IPython home page")
170 info.WebSite = ("http://ipython.scipy.org/", "IPython home page")
171 info.Developers = [ "Laurent Dufrechou" ]
171 info.Developers = [ "Laurent Dufrechou" ]
172 licenseText="BSD License.\nAll rights reserved. This program and the accompanying materials are made available under the terms of the BSD which accompanies this distribution, and is available at http://www.opensource.org/licenses/bsd-license.php"
172 licenseText="BSD License.\nAll rights reserved. This program and the accompanying materials are made available under the terms of the BSD which accompanies this distribution, and is available at http://www.opensource.org/licenses/bsd-license.php"
173 info.License = wordwrap(licenseText, 500, wx.ClientDC(self))
173 info.License = wordwrap(licenseText, 500, wx.ClientDC(self))
174
174
175 # Then we call wx.AboutBox giving it that info object
175 # Then we call wx.AboutBox giving it that info object
176 wx.AboutBox(info)
176 wx.AboutBox(info)
177
177
178 #-----------------------------------------
178 #-----------------------------------------
179 #Creating our application
179 #Creating our application
180 #-----------------------------------------
180 #-----------------------------------------
181 class MyApp(wx.PySimpleApp):
181 class MyApp(wx.PySimpleApp):
182 """Creating our application"""
182 """Creating our application"""
183 def __init__(self):
183 def __init__(self):
184 wx.PySimpleApp.__init__(self)
184 wx.PySimpleApp.__init__(self)
185
185
186 self.frame = MyFrame()
186 self.frame = MyFrame()
187 self.frame.Show()
187 self.frame.Show()
188
188
189 #-----------------------------------------
189 #-----------------------------------------
190 #Main loop
190 #Main loop
191 #-----------------------------------------
191 #-----------------------------------------
192 def main():
192 def main():
193 app = MyApp()
193 app = MyApp()
194 app.SetTopWindow(app.frame)
194 app.SetTopWindow(app.frame)
195 app.MainLoop()
195 app.MainLoop()
196
196
197 #if launched as main program run this
197 #if launched as main program run this
198 if __name__ == '__main__':
198 if __name__ == '__main__':
199 main()
199 main()
General Comments 0
You need to be logged in to leave comments. Login now