##// END OF EJS Templates
Full patch for better user color tweaking + patch for 'enter' synchro bug
Laurent Dufrechou -
Show More
@@ -107,8 +107,7 b' class PrefilterFrontEnd(LineFrontEndBase):'
107
107
108
108
109 if not 'banner' in kwargs and self.banner is None:
109 if not 'banner' in kwargs and self.banner is None:
110 self.banner = self.ipython0.BANNER + """
110 self.banner = self.ipython0.BANNER
111 This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code."""
112
111
113 self.start()
112 self.start()
114
113
@@ -33,6 +33,12 b' import re'
33
33
34 # FIXME: Need to provide an API for non user-generated display on the
34 # FIXME: Need to provide an API for non user-generated display on the
35 # screen: this should not be editable by the user.
35 # screen: this should not be editable by the user.
36 #-------------------------------------------------------------------------------
37 # Constants
38 #-------------------------------------------------------------------------------
39 _COMPLETE_BUFFER_MARKER = 31
40 _ERROR_MARKER = 30
41 _INPUT_MARKER = 29
36
42
37 _DEFAULT_SIZE = 10
43 _DEFAULT_SIZE = 10
38 if sys.platform == 'darwin':
44 if sys.platform == 'darwin':
@@ -109,13 +115,44 b' class ConsoleWidget(editwindow.EditWindow):'
109 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
115 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
110 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
116 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
111 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
117 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
112 '1;34': [12, 'LIGHT BLUE'], '1;35':
118 '1;34': [12, 'LIGHT BLUE'], '1;35': [13, 'MEDIUM VIOLET RED'],
113 [13, 'MEDIUM VIOLET RED'],
114 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
119 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
115
120
116 # The color of the carret (call _apply_style() after setting)
121 # The color of the carret (call _apply_style() after setting)
117 carret_color = 'BLACK'
122 carret_color = 'BLACK'
118
123
124 _COMPLETE_BUFFER_BG = '#FAFAF1' # Nice green
125 _INPUT_BUFFER_BG = '#FDFFD3' # Nice yellow
126 _ERROR_BG = '#FFF1F1' # Nice red
127
128 background_color = 'WHITE'
129
130 #we define platform specific fonts
131 if wx.Platform == '__WXMSW__':
132 faces = { 'times': 'Times New Roman',
133 'mono' : 'Courier New',
134 'helv' : 'Arial',
135 'other': 'Comic Sans MS',
136 'size' : 10,
137 'size2': 8,
138 }
139 elif wx.Platform == '__WXMAC__':
140 faces = { 'times': 'Times New Roman',
141 'mono' : 'Monaco',
142 'helv' : 'Arial',
143 'other': 'Comic Sans MS',
144 'size' : 10,
145 'size2': 8,
146 }
147 else:
148 faces = { 'times': 'Times',
149 'mono' : 'Courier',
150 'helv' : 'Helvetica',
151 'other': 'new century schoolbook',
152 'size' : 10,
153 'size2': 8,
154 }
155
119 # Store the last time a refresh was done
156 # Store the last time a refresh was done
120 _last_refresh_time = 0
157 _last_refresh_time = 0
121
158
@@ -127,6 +164,9 b' class ConsoleWidget(editwindow.EditWindow):'
127 size=wx.DefaultSize, style=wx.WANTS_CHARS, ):
164 size=wx.DefaultSize, style=wx.WANTS_CHARS, ):
128 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
165 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
129 self._configure_scintilla()
166 self._configure_scintilla()
167 self.enter_catched = False #this var track if 'enter' key as ever been processed
168 #thus it will only be reallowed until key goes up
169 self.current_prompt_pos = 0
130
170
131 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
171 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
132 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
172 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
@@ -227,26 +267,28 b' class ConsoleWidget(editwindow.EditWindow):'
227 """
267 """
228
268
229 #--------------------------------------------------------------------------
269 #--------------------------------------------------------------------------
230 # Private API
270 # Styling API
231 #--------------------------------------------------------------------------
271 #--------------------------------------------------------------------------
232
233 def _apply_style(self):
234 """ Applies the colors for the different text elements and the
235 carret.
236 """
237 self.SetCaretForeground(self.carret_color)
238
239 #self.StyleClearAll()
240 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
241 "fore:#FF0000,back:#0000FF,bold")
242 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
243 "fore:#000000,back:#FF0000,bold")
244
245 for style in self.ANSI_STYLES.values():
246 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
247
272
273 def set_new_style(self):
274 """ call this method with new style and ansi_style to change colors of the console """
275 self._configure_scintilla()
276
277 #--------------------------------------------------------------------------
278 # Private API
279 #--------------------------------------------------------------------------
248
280
249 def _configure_scintilla(self):
281 def _configure_scintilla(self):
282 # Marker for complete buffer.
283 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
284 background = self._COMPLETE_BUFFER_BG)
285 # Marker for current input buffer.
286 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
287 background = self._INPUT_BUFFER_BG)
288 # Marker for tracebacks.
289 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
290 background = self._ERROR_BG)
291
250 self.SetEOLMode(stc.STC_EOL_LF)
292 self.SetEOLMode(stc.STC_EOL_LF)
251
293
252 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
294 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
@@ -289,8 +331,9 b' class ConsoleWidget(editwindow.EditWindow):'
289 self.SetMarginWidth(1, 0)
331 self.SetMarginWidth(1, 0)
290 self.SetMarginWidth(2, 0)
332 self.SetMarginWidth(2, 0)
291
333
292 self._apply_style()
334 #self._apply_style()
293
335 self.SetCaretForeground(self.carret_color)
336
294 # Xterm escape sequences
337 # Xterm escape sequences
295 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
338 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
296 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
339 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
@@ -298,29 +341,59 b' class ConsoleWidget(editwindow.EditWindow):'
298 #self.SetEdgeMode(stc.STC_EDGE_LINE)
341 #self.SetEdgeMode(stc.STC_EDGE_LINE)
299 #self.SetEdgeColumn(80)
342 #self.SetEdgeColumn(80)
300
343
344
301 # styles
345 # styles
302 p = self.style
346 p = self.style
303 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
347
348 if 'default' in p:
349 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
350 else:
351 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "fore:%s,back:%s,size:%d,face:%s"
352 % (self.ANSI_STYLES['0;30'][1], self.background_color,
353 self.faces['size'], self.faces['mono']))
354
355 #all styles = default one
304 self.StyleClearAll()
356 self.StyleClearAll()
305 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
357
306 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
358 # XXX: two lines below are usefull if not using the lexer
307 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
359 #for style in self.ANSI_STYLES.values():
308
360 # self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
309 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
361
310 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
362 if 'stdout' in p:
311 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
363 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
312 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
364 if 'stderr' in p:
313 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
365 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
314 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
366 if 'trace' in p:
315 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
367 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
316 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
368 if 'bracegood' in p:
317 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
369 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
318 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
370 if 'bracebad' in p:
319 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
371 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
320 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
372 if 'comment' in p:
321 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
373 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
322 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
374 if 'number' in p:
323
375 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
376 if 'string' in p:
377 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
378 if 'char' in p:
379 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
380 if 'keyword' in p:
381 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
382 if 'keyword' in p:
383 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
384 if 'triple' in p:
385 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
386 if 'tripledouble' in p:
387 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
388 if 'class' in p:
389 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
390 if 'def' in p:
391 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
392 if 'operator' in p:
393 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
394 if 'comment' in p:
395 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
396
324 def _on_key_down(self, event, skip=True):
397 def _on_key_down(self, event, skip=True):
325 """ Key press callback used for correcting behavior for
398 """ Key press callback used for correcting behavior for
326 console-like interfaces: the cursor is constraint to be after
399 console-like interfaces: the cursor is constraint to be after
@@ -359,14 +432,16 b' class ConsoleWidget(editwindow.EditWindow):'
359 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
432 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
360 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
433 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
361 catched = True
434 catched = True
362 self.CallTipCancel()
435 if(not self.enter_catched):
363 self.write('\n', refresh=False)
436 self.CallTipCancel()
364 # Under windows scintilla seems to be doing funny stuff to the
437 self.write('\n', refresh=False)
365 # line returns here, but the getter for input_buffer filters
438 # Under windows scintilla seems to be doing funny stuff to the
366 # this out.
439 # line returns here, but the getter for input_buffer filters
367 if sys.platform == 'win32':
440 # this out.
368 self.input_buffer = self.input_buffer
441 if sys.platform == 'win32':
369 self._on_enter()
442 self.input_buffer = self.input_buffer
443 self._on_enter()
444 self.enter_catched = True
370
445
371 elif event.KeyCode == wx.WXK_HOME:
446 elif event.KeyCode == wx.WXK_HOME:
372 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
447 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
@@ -412,6 +487,7 b' class ConsoleWidget(editwindow.EditWindow):'
412 if self.GetCurrentPos() < self.current_prompt_pos:
487 if self.GetCurrentPos() < self.current_prompt_pos:
413 self.GotoPos(self.current_prompt_pos)
488 self.GotoPos(self.current_prompt_pos)
414
489
490 self.enter_catched = False #we re-allow enter event processing
415
491
416
492
417 if __name__ == '__main__':
493 if __name__ == '__main__':
@@ -13,6 +13,8 b' You need wxPython to run this application.'
13 e.args = (e.message, ) + e.args[1:]
13 e.args = (e.message, ) + e.args[1:]
14 raise e
14 raise e
15
15
16 import wx.stc as stc
17
16 from wx_frontend import WxController
18 from wx_frontend import WxController
17 import __builtin__
19 import __builtin__
18
20
@@ -25,8 +27,58 b' class IPythonXController(WxController):'
25 debug = False
27 debug = False
26
28
27 def __init__(self, *args, **kwargs):
29 def __init__(self, *args, **kwargs):
30
31 if kwargs['colorset'] == 'black':
32 self.prompt_in1 = \
33 '\n\x01\x1b[0;30m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;30m\x02]: \x01\x1b[0m\x02'
34
35 self.prompt_out = \
36 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
37
28 WxController.__init__(self, *args, **kwargs)
38 WxController.__init__(self, *args, **kwargs)
29 self.ipython0.ask_exit = self.do_exit
39 self.ipython0.ask_exit = self.do_exit
40
41 if kwargs['colorset'] == 'black':
42
43 self.carret_color = 'WHITE'
44 self.background_color = 'BLACK'
45
46 self.SetEdgeMode(stc.STC_EDGE_LINE)
47 self.SetEdgeColumn(88)
48
49 self.style = {
50 #'stdout' : '',#fore:#0000FF',
51 #'stderr' : '',#fore:#007f00',
52 #'trace' : '',#fore:#FF0000',
53
54 #'bracegood' : 'fore:#0000FF,back:#0000FF,bold',
55 #'bracebad' : 'fore:#FF0000,back:#0000FF,bold',
56 'default' : "fore:%s,back:%s,size:%d,face:%s,bold"
57 % ("#EEEEEE", self.background_color,
58 self.faces['size'], self.faces['mono']),
59
60 # properties for the various Python lexer styles
61 'comment' : 'fore:#BBBBBB,italic',
62 'number' : 'fore:#FF9692',
63 'string' : 'fore:#ed9d13,italic',
64 'char' : 'fore:#FFFFFF,italic',
65 'keyword' : 'fore:#6AB825,bold',
66 'triple' : 'fore:#FF7BDD',
67 'tripledouble' : 'fore:#FF7BDD',
68 'class' : 'fore:#FF00FF,bold,underline',
69 'def' : 'fore:#FFFF00,bold',
70 'operator' : 'bold'
71 }
72
73 #we define the background of old inputs
74 self._COMPLETE_BUFFER_BG = '#000000' # RRGGBB: Black
75 #we define the background of current input
76 self._INPUT_BUFFER_BG = '#444444' # RRGGBB: Light black
77 #we define the background when an error is reported
78 self._ERROR_BG = '#800000' #'#d22323' #'#AE0021' # RRGGBB: Black
79
80 self.set_new_style()
81
30 # Scroll to top
82 # Scroll to top
31 maxrange = self.GetScrollRange(wx.VERTICAL)
83 maxrange = self.GetScrollRange(wx.VERTICAL)
32 self.ScrollLines(-maxrange)
84 self.ScrollLines(-maxrange)
@@ -72,10 +124,10 b' class IPythonX(wx.Frame):'
72 """ Main frame of the IPythonX app.
124 """ Main frame of the IPythonX app.
73 """
125 """
74
126
75 def __init__(self, parent, id, title, debug=False):
127 def __init__(self, parent, id, title, debug=False, colorset='white'):
76 wx.Frame.__init__(self, parent, id, title, size=(300,250))
128 wx.Frame.__init__(self, parent, id, title, size=(300,250))
77 self._sizer = wx.BoxSizer(wx.VERTICAL)
129 self._sizer = wx.BoxSizer(wx.VERTICAL)
78 self.shell = IPythonXController(self, debug=debug)
130 self.shell = IPythonXController(self, debug=debug, colorset=colorset)
79 self._sizer.Add(self.shell, 1, wx.EXPAND)
131 self._sizer.Add(self.shell, 1, wx.EXPAND)
80 self.SetSizer(self._sizer)
132 self.SetSizer(self._sizer)
81 self.SetAutoLayout(1)
133 self.SetAutoLayout(1)
@@ -101,6 +153,10 b' Simple graphical frontend to IPython, using WxWidgets."""'
101 action="store_true", dest="debug", default=False,
153 action="store_true", dest="debug", default=False,
102 help="Enable debug message for the wx frontend.")
154 help="Enable debug message for the wx frontend.")
103
155
156 parser.add_option("-s", "--style",
157 dest="colorset", default="white",
158 help="style: white, black")
159
104 options, args = parser.parse_args()
160 options, args = parser.parse_args()
105
161
106 # Clear the options, to avoid having the ipython0 instance complain
162 # Clear the options, to avoid having the ipython0 instance complain
@@ -108,7 +164,7 b' Simple graphical frontend to IPython, using WxWidgets."""'
108 sys.argv = sys.argv[:1]
164 sys.argv = sys.argv[:1]
109
165
110 app = wx.PySimpleApp()
166 app = wx.PySimpleApp()
111 frame = IPythonX(None, wx.ID_ANY, 'IPythonX', debug=options.debug)
167 frame = IPythonX(None, wx.ID_ANY, 'IPythonX', debug=options.debug, colorset=options.colorset)
112 frame.shell.SetFocus()
168 frame.shell.SetFocus()
113 frame.shell.app = app
169 frame.shell.app = app
114 frame.SetSize((680, 460))
170 frame.SetSize((680, 460))
@@ -41,20 +41,10 b' from IPython.frontend.prefilterfrontend import PrefilterFrontEnd'
41 # Constants
41 # Constants
42 #-------------------------------------------------------------------------------
42 #-------------------------------------------------------------------------------
43
43
44 _COMPLETE_BUFFER_BG = '#FAFAF1' # Nice green
45 _INPUT_BUFFER_BG = '#FDFFD3' # Nice yellow
46 _ERROR_BG = '#FFF1F1' # Nice red
47
48 _COMPLETE_BUFFER_MARKER = 31
44 _COMPLETE_BUFFER_MARKER = 31
49 _ERROR_MARKER = 30
45 _ERROR_MARKER = 30
50 _INPUT_MARKER = 29
46 _INPUT_MARKER = 29
51
47
52 prompt_in1 = \
53 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
54
55 prompt_out = \
56 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
57
58 #-------------------------------------------------------------------------------
48 #-------------------------------------------------------------------------------
59 # Classes to implement the Wx frontend
49 # Classes to implement the Wx frontend
60 #-------------------------------------------------------------------------------
50 #-------------------------------------------------------------------------------
@@ -65,10 +55,11 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
65 This class inherits from ConsoleWidget, that provides a console-like
55 This class inherits from ConsoleWidget, that provides a console-like
66 widget to provide a text-rendering widget suitable for a terminal.
56 widget to provide a text-rendering widget suitable for a terminal.
67 """
57 """
58 prompt_in1 = \
59 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
68
60
69 output_prompt_template = string.Template(prompt_out)
61 prompt_out = \
70
62 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
71 input_prompt_template = string.Template(prompt_in1)
72
63
73 # Print debug info on what is happening to the console.
64 # Print debug info on what is happening to the console.
74 debug = False
65 debug = False
@@ -140,22 +131,14 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
140 *args, **kwds):
131 *args, **kwds):
141 """ Create Shell instance.
132 """ Create Shell instance.
142 """
133 """
134 self.load_prompt()
135
143 ConsoleWidget.__init__(self, parent, id, pos, size, style)
136 ConsoleWidget.__init__(self, parent, id, pos, size, style)
144 PrefilterFrontEnd.__init__(self, **kwds)
137 PrefilterFrontEnd.__init__(self, **kwds)
145
138
146 # Stick in our own raw_input:
139 # Stick in our own raw_input:
147 self.ipython0.raw_input = self.raw_input
140 self.ipython0.raw_input = self.raw_input
148
141
149 # Marker for complete buffer.
150 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
151 background=_COMPLETE_BUFFER_BG)
152 # Marker for current input buffer.
153 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
154 background=_INPUT_BUFFER_BG)
155 # Marker for tracebacks.
156 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
157 background=_ERROR_BG)
158
159 # A time for flushing the write buffer
142 # A time for flushing the write buffer
160 BUFFER_FLUSH_TIMER_ID = 100
143 BUFFER_FLUSH_TIMER_ID = 100
161 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
144 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
@@ -171,7 +154,12 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
171 # Inject our own raw_input in namespace
154 # Inject our own raw_input in namespace
172 self.shell.user_ns['raw_input'] = self.raw_input
155 self.shell.user_ns['raw_input'] = self.raw_input
173
156
157 def load_prompt(self):
158 self.output_prompt_template = string.Template(self.prompt_out)
174
159
160 self.input_prompt_template = string.Template(self.prompt_in1)
161
162
175 def raw_input(self, prompt=''):
163 def raw_input(self, prompt=''):
176 """ A replacement from python's raw_input.
164 """ A replacement from python's raw_input.
177 """
165 """
General Comments 0
You need to be logged in to leave comments. Login now