##// END OF EJS Templates
exposed pygments styles as ipythonqt options
MinRK -
Show More
@@ -0,0 +1,102 b''
1 """ Style utilities, templates, and defaults for syntax highlighting widgets.
2 """
3 #-----------------------------------------------------------------------------
4 # Imports
5 #-----------------------------------------------------------------------------
6
7 from colorsys import rgb_to_hls
8 from pygments.styles import get_style_by_name
9 from pygments.token import Token
10
11 #-----------------------------------------------------------------------------
12 # Constants
13 #-----------------------------------------------------------------------------
14
15 # The default light style sheet: black text on a white background.
16 default_light_style_template = '''
17 QPlainTextEdit, QTextEdit { background-color: %(bgcolor)s;
18 color: %(fgcolor)s ;
19 selection-background-color: %(select)s}
20 .error { color: red; }
21 .in-prompt { color: navy; }
22 .in-prompt-number { font-weight: bold; }
23 .out-prompt { color: darkred; }
24 .out-prompt-number { font-weight: bold; }
25 '''
26 default_light_style_sheet = default_light_style_template%dict(
27 bgcolor='white', fgcolor='black', select="#ccc")
28 default_light_syntax_style = 'default'
29
30 # The default dark style sheet: white text on a black background.
31 default_dark_style_template = '''
32 QPlainTextEdit, QTextEdit { background-color: %(bgcolor)s;
33 color: %(fgcolor)s ;
34 selection-background-color: %(select)s}
35 QFrame { border: 1px solid grey; }
36 .error { color: red; }
37 .in-prompt { color: lime; }
38 .in-prompt-number { color: lime; font-weight: bold; }
39 .out-prompt { color: red; }
40 .out-prompt-number { color: red; font-weight: bold; }
41 '''
42 default_dark_style_sheet = default_dark_style_template%dict(
43 bgcolor='black', fgcolor='white', select="#555")
44 default_dark_syntax_style = 'monokai'
45
46 def hex_to_rgb(color):
47 """Convert a hex color to rgb integer tuple."""
48 if color.startswith('#'):
49 color = color[1:]
50 if len(color) == 3:
51 color = ''.join([c*2 for c in color])
52 if len(color) != 6:
53 return False
54 try:
55 r = int(color[:2],16)
56 g = int(color[:2],16)
57 b = int(color[:2],16)
58 except ValueError:
59 return False
60 else:
61 return r,g,b
62
63 def dark_color(color):
64 """Check whether a color is 'dark'.
65
66 Currently, this is simply whether the luminance is <50%"""
67 rgb = hex_to_rgb(color)
68 if rgb:
69 return rgb_to_hls(*rgb)[1] < 128
70 else: # default to False
71 return False
72
73 def dark_style(stylename):
74 """Guess whether the background of the style with name 'stylename'
75 counts as 'dark'."""
76 return dark_color(get_style_by_name(stylename).background_color)
77
78 def get_colors(stylename):
79 """Construct the keys to be used building the base stylesheet."""
80 style = get_style_by_name(stylename)
81 fgcolor = style.style_for_token(Token.Text)['color'] or ''
82 if len(fgcolor) in (3,6):
83 # could be 'abcdef' or 'ace' hex, which needs '#' prefix
84 try:
85 int(fgcolor, 16)
86 except TypeError:
87 pass
88 else:
89 fgcolor = "#"+fgcolor
90
91 return dict(
92 bgcolor = style.background_color,
93 select = style.highlight_color,
94 fgcolor = fgcolor
95 )
96
97 def sheet_from_template(name, lightbg=True):
98 """Use one of the base templates, and set bg/fg/select colors."""
99 if lightbg:
100 return default_light_style_template%get_colors(name)
101 else:
102 return default_dark_style_template%get_colors(name) No newline at end of file
@@ -1,476 +1,456 b''
1 """ A FrontendWidget that emulates the interface of the console IPython and
1 """ A FrontendWidget that emulates the interface of the console IPython and
2 supports the additional functionality provided by the IPython kernel.
2 supports the additional functionality provided by the IPython kernel.
3
3
4 TODO: Add support for retrieving the system default editor. Requires code
4 TODO: Add support for retrieving the system default editor. Requires code
5 paths for Windows (use the registry), Mac OS (use LaunchServices), and
5 paths for Windows (use the registry), Mac OS (use LaunchServices), and
6 Linux (use the xdg system).
6 Linux (use the xdg system).
7 """
7 """
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Imports
10 # Imports
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 # Standard library imports
13 # Standard library imports
14 from collections import namedtuple
14 from collections import namedtuple
15 import re
15 import re
16 from subprocess import Popen
16 from subprocess import Popen
17 from textwrap import dedent
17 from textwrap import dedent
18
18
19 # System library imports
19 # System library imports
20 from PyQt4 import QtCore, QtGui
20 from PyQt4 import QtCore, QtGui
21
21
22 # Local imports
22 # Local imports
23 from IPython.core.inputsplitter import IPythonInputSplitter, \
23 from IPython.core.inputsplitter import IPythonInputSplitter, \
24 transform_ipy_prompt
24 transform_ipy_prompt
25 from IPython.core.usage import default_gui_banner
25 from IPython.core.usage import default_gui_banner
26 from IPython.utils.traitlets import Bool, Str
26 from IPython.utils.traitlets import Bool, Str
27 from frontend_widget import FrontendWidget
27 from frontend_widget import FrontendWidget
28 from styles import (default_light_style_sheet, default_dark_style_sheet,
29 default_light_syntax_style, default_dark_syntax_style)
28
30
29 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
30 # Constants
32 # Constants
31 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
32
34
33 # The default light style sheet: black text on a white background.
34 default_light_style_sheet = '''
35 .error { color: red; }
36 .in-prompt { color: navy; }
37 .in-prompt-number { font-weight: bold; }
38 .out-prompt { color: darkred; }
39 .out-prompt-number { font-weight: bold; }
40 '''
41 default_light_syntax_style = 'default'
42
43 # The default dark style sheet: white text on a black background.
44 default_dark_style_sheet = '''
45 QPlainTextEdit, QTextEdit { background-color: black; color: white }
46 QFrame { border: 1px solid grey; }
47 .error { color: red; }
48 .in-prompt { color: lime; }
49 .in-prompt-number { color: lime; font-weight: bold; }
50 .out-prompt { color: red; }
51 .out-prompt-number { color: red; font-weight: bold; }
52 '''
53 default_dark_syntax_style = 'monokai'
54
55 # Default strings to build and display input and output prompts (and separators
35 # Default strings to build and display input and output prompts (and separators
56 # in between)
36 # in between)
57 default_in_prompt = 'In [<span class="in-prompt-number">%i</span>]: '
37 default_in_prompt = 'In [<span class="in-prompt-number">%i</span>]: '
58 default_out_prompt = 'Out[<span class="out-prompt-number">%i</span>]: '
38 default_out_prompt = 'Out[<span class="out-prompt-number">%i</span>]: '
59 default_input_sep = '\n'
39 default_input_sep = '\n'
60 default_output_sep = ''
40 default_output_sep = ''
61 default_output_sep2 = ''
41 default_output_sep2 = ''
62
42
63 # Base path for most payload sources.
43 # Base path for most payload sources.
64 zmq_shell_source = 'IPython.zmq.zmqshell.ZMQInteractiveShell'
44 zmq_shell_source = 'IPython.zmq.zmqshell.ZMQInteractiveShell'
65
45
66 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
67 # IPythonWidget class
47 # IPythonWidget class
68 #-----------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
69
49
70 class IPythonWidget(FrontendWidget):
50 class IPythonWidget(FrontendWidget):
71 """ A FrontendWidget for an IPython kernel.
51 """ A FrontendWidget for an IPython kernel.
72 """
52 """
73
53
74 # If set, the 'custom_edit_requested(str, int)' signal will be emitted when
54 # If set, the 'custom_edit_requested(str, int)' signal will be emitted when
75 # an editor is needed for a file. This overrides 'editor' and 'editor_line'
55 # an editor is needed for a file. This overrides 'editor' and 'editor_line'
76 # settings.
56 # settings.
77 custom_edit = Bool(False)
57 custom_edit = Bool(False)
78 custom_edit_requested = QtCore.pyqtSignal(object, object)
58 custom_edit_requested = QtCore.pyqtSignal(object, object)
79
59
80 # A command for invoking a system text editor. If the string contains a
60 # A command for invoking a system text editor. If the string contains a
81 # {filename} format specifier, it will be used. Otherwise, the filename will
61 # {filename} format specifier, it will be used. Otherwise, the filename will
82 # be appended to the end the command.
62 # be appended to the end the command.
83 editor = Str('default', config=True)
63 editor = Str('default', config=True)
84
64
85 # The editor command to use when a specific line number is requested. The
65 # The editor command to use when a specific line number is requested. The
86 # string should contain two format specifiers: {line} and {filename}. If
66 # string should contain two format specifiers: {line} and {filename}. If
87 # this parameter is not specified, the line number option to the %edit magic
67 # this parameter is not specified, the line number option to the %edit magic
88 # will be ignored.
68 # will be ignored.
89 editor_line = Str(config=True)
69 editor_line = Str(config=True)
90
70
91 # A CSS stylesheet. The stylesheet can contain classes for:
71 # A CSS stylesheet. The stylesheet can contain classes for:
92 # 1. Qt: QPlainTextEdit, QFrame, QWidget, etc
72 # 1. Qt: QPlainTextEdit, QFrame, QWidget, etc
93 # 2. Pygments: .c, .k, .o, etc (see PygmentsHighlighter)
73 # 2. Pygments: .c, .k, .o, etc (see PygmentsHighlighter)
94 # 3. IPython: .error, .in-prompt, .out-prompt, etc
74 # 3. IPython: .error, .in-prompt, .out-prompt, etc
95 style_sheet = Str(config=True)
75 style_sheet = Str(config=True)
96
76
97 # If not empty, use this Pygments style for syntax highlighting. Otherwise,
77 # If not empty, use this Pygments style for syntax highlighting. Otherwise,
98 # the style sheet is queried for Pygments style information.
78 # the style sheet is queried for Pygments style information.
99 syntax_style = Str(config=True)
79 syntax_style = Str(config=True)
100
80
101 # Prompts.
81 # Prompts.
102 in_prompt = Str(default_in_prompt, config=True)
82 in_prompt = Str(default_in_prompt, config=True)
103 out_prompt = Str(default_out_prompt, config=True)
83 out_prompt = Str(default_out_prompt, config=True)
104 input_sep = Str(default_input_sep, config=True)
84 input_sep = Str(default_input_sep, config=True)
105 output_sep = Str(default_output_sep, config=True)
85 output_sep = Str(default_output_sep, config=True)
106 output_sep2 = Str(default_output_sep2, config=True)
86 output_sep2 = Str(default_output_sep2, config=True)
107
87
108 # FrontendWidget protected class variables.
88 # FrontendWidget protected class variables.
109 _input_splitter_class = IPythonInputSplitter
89 _input_splitter_class = IPythonInputSplitter
110
90
111 # IPythonWidget protected class variables.
91 # IPythonWidget protected class variables.
112 _PromptBlock = namedtuple('_PromptBlock', ['block', 'length', 'number'])
92 _PromptBlock = namedtuple('_PromptBlock', ['block', 'length', 'number'])
113 _payload_source_edit = zmq_shell_source + '.edit_magic'
93 _payload_source_edit = zmq_shell_source + '.edit_magic'
114 _payload_source_exit = zmq_shell_source + '.ask_exit'
94 _payload_source_exit = zmq_shell_source + '.ask_exit'
115 _payload_source_loadpy = zmq_shell_source + '.magic_loadpy'
95 _payload_source_loadpy = zmq_shell_source + '.magic_loadpy'
116 _payload_source_page = 'IPython.zmq.page.page'
96 _payload_source_page = 'IPython.zmq.page.page'
117
97
118 #---------------------------------------------------------------------------
98 #---------------------------------------------------------------------------
119 # 'object' interface
99 # 'object' interface
120 #---------------------------------------------------------------------------
100 #---------------------------------------------------------------------------
121
101
122 def __init__(self, *args, **kw):
102 def __init__(self, *args, **kw):
123 super(IPythonWidget, self).__init__(*args, **kw)
103 super(IPythonWidget, self).__init__(*args, **kw)
124
104
125 # IPythonWidget protected variables.
105 # IPythonWidget protected variables.
126 self._code_to_load = None
106 self._code_to_load = None
127 self._payload_handlers = {
107 self._payload_handlers = {
128 self._payload_source_edit : self._handle_payload_edit,
108 self._payload_source_edit : self._handle_payload_edit,
129 self._payload_source_exit : self._handle_payload_exit,
109 self._payload_source_exit : self._handle_payload_exit,
130 self._payload_source_page : self._handle_payload_page,
110 self._payload_source_page : self._handle_payload_page,
131 self._payload_source_loadpy : self._handle_payload_loadpy }
111 self._payload_source_loadpy : self._handle_payload_loadpy }
132 self._previous_prompt_obj = None
112 self._previous_prompt_obj = None
133
113
134 # Initialize widget styling.
114 # Initialize widget styling.
135 if self.style_sheet:
115 if self.style_sheet:
136 self._style_sheet_changed()
116 self._style_sheet_changed()
137 self._syntax_style_changed()
117 self._syntax_style_changed()
138 else:
118 else:
139 self.set_default_style()
119 self.set_default_style()
140
120
141 #---------------------------------------------------------------------------
121 #---------------------------------------------------------------------------
142 # 'BaseFrontendMixin' abstract interface
122 # 'BaseFrontendMixin' abstract interface
143 #---------------------------------------------------------------------------
123 #---------------------------------------------------------------------------
144
124
145 def _handle_complete_reply(self, rep):
125 def _handle_complete_reply(self, rep):
146 """ Reimplemented to support IPython's improved completion machinery.
126 """ Reimplemented to support IPython's improved completion machinery.
147 """
127 """
148 cursor = self._get_cursor()
128 cursor = self._get_cursor()
149 info = self._request_info.get('complete')
129 info = self._request_info.get('complete')
150 if info and info.id == rep['parent_header']['msg_id'] and \
130 if info and info.id == rep['parent_header']['msg_id'] and \
151 info.pos == cursor.position():
131 info.pos == cursor.position():
152 matches = rep['content']['matches']
132 matches = rep['content']['matches']
153 text = rep['content']['matched_text']
133 text = rep['content']['matched_text']
154 offset = len(text)
134 offset = len(text)
155
135
156 # Clean up matches with period and path separators if the matched
136 # Clean up matches with period and path separators if the matched
157 # text has not been transformed. This is done by truncating all
137 # text has not been transformed. This is done by truncating all
158 # but the last component and then suitably decreasing the offset
138 # but the last component and then suitably decreasing the offset
159 # between the current cursor position and the start of completion.
139 # between the current cursor position and the start of completion.
160 if len(matches) > 1 and matches[0][:offset] == text:
140 if len(matches) > 1 and matches[0][:offset] == text:
161 parts = re.split(r'[./\\]', text)
141 parts = re.split(r'[./\\]', text)
162 sep_count = len(parts) - 1
142 sep_count = len(parts) - 1
163 if sep_count:
143 if sep_count:
164 chop_length = sum(map(len, parts[:sep_count])) + sep_count
144 chop_length = sum(map(len, parts[:sep_count])) + sep_count
165 matches = [ match[chop_length:] for match in matches ]
145 matches = [ match[chop_length:] for match in matches ]
166 offset -= chop_length
146 offset -= chop_length
167
147
168 # Move the cursor to the start of the match and complete.
148 # Move the cursor to the start of the match and complete.
169 cursor.movePosition(QtGui.QTextCursor.Left, n=offset)
149 cursor.movePosition(QtGui.QTextCursor.Left, n=offset)
170 self._complete_with_items(cursor, matches)
150 self._complete_with_items(cursor, matches)
171
151
172 def _handle_execute_reply(self, msg):
152 def _handle_execute_reply(self, msg):
173 """ Reimplemented to support prompt requests.
153 """ Reimplemented to support prompt requests.
174 """
154 """
175 info = self._request_info.get('execute')
155 info = self._request_info.get('execute')
176 if info and info.id == msg['parent_header']['msg_id']:
156 if info and info.id == msg['parent_header']['msg_id']:
177 if info.kind == 'prompt':
157 if info.kind == 'prompt':
178 number = msg['content']['execution_count'] + 1
158 number = msg['content']['execution_count'] + 1
179 self._show_interpreter_prompt(number)
159 self._show_interpreter_prompt(number)
180 else:
160 else:
181 super(IPythonWidget, self)._handle_execute_reply(msg)
161 super(IPythonWidget, self)._handle_execute_reply(msg)
182
162
183 def _handle_history_reply(self, msg):
163 def _handle_history_reply(self, msg):
184 """ Implemented to handle history replies, which are only supported by
164 """ Implemented to handle history replies, which are only supported by
185 the IPython kernel.
165 the IPython kernel.
186 """
166 """
187 history_dict = msg['content']['history']
167 history_dict = msg['content']['history']
188 items = [ history_dict[key] for key in sorted(history_dict.keys()) ]
168 items = [ history_dict[key] for key in sorted(history_dict.keys()) ]
189 self._set_history(items)
169 self._set_history(items)
190
170
191 def _handle_pyout(self, msg):
171 def _handle_pyout(self, msg):
192 """ Reimplemented for IPython-style "display hook".
172 """ Reimplemented for IPython-style "display hook".
193 """
173 """
194 if not self._hidden and self._is_from_this_session(msg):
174 if not self._hidden and self._is_from_this_session(msg):
195 content = msg['content']
175 content = msg['content']
196 prompt_number = content['execution_count']
176 prompt_number = content['execution_count']
197 self._append_plain_text(self.output_sep)
177 self._append_plain_text(self.output_sep)
198 self._append_html(self._make_out_prompt(prompt_number))
178 self._append_html(self._make_out_prompt(prompt_number))
199 self._append_plain_text(content['data']+self.output_sep2)
179 self._append_plain_text(content['data']+self.output_sep2)
200
180
201 def _started_channels(self):
181 def _started_channels(self):
202 """ Reimplemented to make a history request.
182 """ Reimplemented to make a history request.
203 """
183 """
204 super(IPythonWidget, self)._started_channels()
184 super(IPythonWidget, self)._started_channels()
205 # FIXME: Disabled until history requests are properly implemented.
185 # FIXME: Disabled until history requests are properly implemented.
206 #self.kernel_manager.xreq_channel.history(raw=True, output=False)
186 #self.kernel_manager.xreq_channel.history(raw=True, output=False)
207
187
208 #---------------------------------------------------------------------------
188 #---------------------------------------------------------------------------
209 # 'ConsoleWidget' public interface
189 # 'ConsoleWidget' public interface
210 #---------------------------------------------------------------------------
190 #---------------------------------------------------------------------------
211
191
212 def copy(self):
192 def copy(self):
213 """ Copy the currently selected text to the clipboard, removing prompts
193 """ Copy the currently selected text to the clipboard, removing prompts
214 if possible.
194 if possible.
215 """
195 """
216 text = unicode(self._control.textCursor().selection().toPlainText())
196 text = unicode(self._control.textCursor().selection().toPlainText())
217 if text:
197 if text:
218 lines = map(transform_ipy_prompt, text.splitlines())
198 lines = map(transform_ipy_prompt, text.splitlines())
219 text = '\n'.join(lines)
199 text = '\n'.join(lines)
220 QtGui.QApplication.clipboard().setText(text)
200 QtGui.QApplication.clipboard().setText(text)
221
201
222 #---------------------------------------------------------------------------
202 #---------------------------------------------------------------------------
223 # 'FrontendWidget' public interface
203 # 'FrontendWidget' public interface
224 #---------------------------------------------------------------------------
204 #---------------------------------------------------------------------------
225
205
226 def execute_file(self, path, hidden=False):
206 def execute_file(self, path, hidden=False):
227 """ Reimplemented to use the 'run' magic.
207 """ Reimplemented to use the 'run' magic.
228 """
208 """
229 self.execute('%%run %s' % path, hidden=hidden)
209 self.execute('%%run %s' % path, hidden=hidden)
230
210
231 #---------------------------------------------------------------------------
211 #---------------------------------------------------------------------------
232 # 'FrontendWidget' protected interface
212 # 'FrontendWidget' protected interface
233 #---------------------------------------------------------------------------
213 #---------------------------------------------------------------------------
234
214
235 def _complete(self):
215 def _complete(self):
236 """ Reimplemented to support IPython's improved completion machinery.
216 """ Reimplemented to support IPython's improved completion machinery.
237 """
217 """
238 # We let the kernel split the input line, so we *always* send an empty
218 # We let the kernel split the input line, so we *always* send an empty
239 # text field. Readline-based frontends do get a real text field which
219 # text field. Readline-based frontends do get a real text field which
240 # they can use.
220 # they can use.
241 text = ''
221 text = ''
242
222
243 # Send the completion request to the kernel
223 # Send the completion request to the kernel
244 msg_id = self.kernel_manager.xreq_channel.complete(
224 msg_id = self.kernel_manager.xreq_channel.complete(
245 text, # text
225 text, # text
246 self._get_input_buffer_cursor_line(), # line
226 self._get_input_buffer_cursor_line(), # line
247 self._get_input_buffer_cursor_column(), # cursor_pos
227 self._get_input_buffer_cursor_column(), # cursor_pos
248 self.input_buffer) # block
228 self.input_buffer) # block
249 pos = self._get_cursor().position()
229 pos = self._get_cursor().position()
250 info = self._CompletionRequest(msg_id, pos)
230 info = self._CompletionRequest(msg_id, pos)
251 self._request_info['complete'] = info
231 self._request_info['complete'] = info
252
232
253 def _get_banner(self):
233 def _get_banner(self):
254 """ Reimplemented to return IPython's default banner.
234 """ Reimplemented to return IPython's default banner.
255 """
235 """
256 return default_gui_banner
236 return default_gui_banner
257
237
258 def _process_execute_error(self, msg):
238 def _process_execute_error(self, msg):
259 """ Reimplemented for IPython-style traceback formatting.
239 """ Reimplemented for IPython-style traceback formatting.
260 """
240 """
261 content = msg['content']
241 content = msg['content']
262 traceback = '\n'.join(content['traceback']) + '\n'
242 traceback = '\n'.join(content['traceback']) + '\n'
263 if False:
243 if False:
264 # FIXME: For now, tracebacks come as plain text, so we can't use
244 # FIXME: For now, tracebacks come as plain text, so we can't use
265 # the html renderer yet. Once we refactor ultratb to produce
245 # the html renderer yet. Once we refactor ultratb to produce
266 # properly styled tracebacks, this branch should be the default
246 # properly styled tracebacks, this branch should be the default
267 traceback = traceback.replace(' ', '&nbsp;')
247 traceback = traceback.replace(' ', '&nbsp;')
268 traceback = traceback.replace('\n', '<br/>')
248 traceback = traceback.replace('\n', '<br/>')
269
249
270 ename = content['ename']
250 ename = content['ename']
271 ename_styled = '<span class="error">%s</span>' % ename
251 ename_styled = '<span class="error">%s</span>' % ename
272 traceback = traceback.replace(ename, ename_styled)
252 traceback = traceback.replace(ename, ename_styled)
273
253
274 self._append_html(traceback)
254 self._append_html(traceback)
275 else:
255 else:
276 # This is the fallback for now, using plain text with ansi escapes
256 # This is the fallback for now, using plain text with ansi escapes
277 self._append_plain_text(traceback)
257 self._append_plain_text(traceback)
278
258
279 def _process_execute_payload(self, item):
259 def _process_execute_payload(self, item):
280 """ Reimplemented to dispatch payloads to handler methods.
260 """ Reimplemented to dispatch payloads to handler methods.
281 """
261 """
282 handler = self._payload_handlers.get(item['source'])
262 handler = self._payload_handlers.get(item['source'])
283 if handler is None:
263 if handler is None:
284 # We have no handler for this type of payload, simply ignore it
264 # We have no handler for this type of payload, simply ignore it
285 return False
265 return False
286 else:
266 else:
287 handler(item)
267 handler(item)
288 return True
268 return True
289
269
290 def _show_interpreter_prompt(self, number=None):
270 def _show_interpreter_prompt(self, number=None):
291 """ Reimplemented for IPython-style prompts.
271 """ Reimplemented for IPython-style prompts.
292 """
272 """
293 # If a number was not specified, make a prompt number request.
273 # If a number was not specified, make a prompt number request.
294 if number is None:
274 if number is None:
295 msg_id = self.kernel_manager.xreq_channel.execute('', silent=True)
275 msg_id = self.kernel_manager.xreq_channel.execute('', silent=True)
296 info = self._ExecutionRequest(msg_id, 'prompt')
276 info = self._ExecutionRequest(msg_id, 'prompt')
297 self._request_info['execute'] = info
277 self._request_info['execute'] = info
298 return
278 return
299
279
300 # Show a new prompt and save information about it so that it can be
280 # Show a new prompt and save information about it so that it can be
301 # updated later if the prompt number turns out to be wrong.
281 # updated later if the prompt number turns out to be wrong.
302 self._prompt_sep = self.input_sep
282 self._prompt_sep = self.input_sep
303 self._show_prompt(self._make_in_prompt(number), html=True)
283 self._show_prompt(self._make_in_prompt(number), html=True)
304 block = self._control.document().lastBlock()
284 block = self._control.document().lastBlock()
305 length = len(self._prompt)
285 length = len(self._prompt)
306 self._previous_prompt_obj = self._PromptBlock(block, length, number)
286 self._previous_prompt_obj = self._PromptBlock(block, length, number)
307
287
308 # Update continuation prompt to reflect (possibly) new prompt length.
288 # Update continuation prompt to reflect (possibly) new prompt length.
309 self._set_continuation_prompt(
289 self._set_continuation_prompt(
310 self._make_continuation_prompt(self._prompt), html=True)
290 self._make_continuation_prompt(self._prompt), html=True)
311
291
312 # Load code from the %loadpy magic, if necessary.
292 # Load code from the %loadpy magic, if necessary.
313 if self._code_to_load is not None:
293 if self._code_to_load is not None:
314 self.input_buffer = dedent(unicode(self._code_to_load).rstrip())
294 self.input_buffer = dedent(unicode(self._code_to_load).rstrip())
315 self._code_to_load = None
295 self._code_to_load = None
316
296
317 def _show_interpreter_prompt_for_reply(self, msg):
297 def _show_interpreter_prompt_for_reply(self, msg):
318 """ Reimplemented for IPython-style prompts.
298 """ Reimplemented for IPython-style prompts.
319 """
299 """
320 # Update the old prompt number if necessary.
300 # Update the old prompt number if necessary.
321 content = msg['content']
301 content = msg['content']
322 previous_prompt_number = content['execution_count']
302 previous_prompt_number = content['execution_count']
323 if self._previous_prompt_obj and \
303 if self._previous_prompt_obj and \
324 self._previous_prompt_obj.number != previous_prompt_number:
304 self._previous_prompt_obj.number != previous_prompt_number:
325 block = self._previous_prompt_obj.block
305 block = self._previous_prompt_obj.block
326
306
327 # Make sure the prompt block has not been erased.
307 # Make sure the prompt block has not been erased.
328 if block.isValid() and not block.text().isEmpty():
308 if block.isValid() and not block.text().isEmpty():
329
309
330 # Remove the old prompt and insert a new prompt.
310 # Remove the old prompt and insert a new prompt.
331 cursor = QtGui.QTextCursor(block)
311 cursor = QtGui.QTextCursor(block)
332 cursor.movePosition(QtGui.QTextCursor.Right,
312 cursor.movePosition(QtGui.QTextCursor.Right,
333 QtGui.QTextCursor.KeepAnchor,
313 QtGui.QTextCursor.KeepAnchor,
334 self._previous_prompt_obj.length)
314 self._previous_prompt_obj.length)
335 prompt = self._make_in_prompt(previous_prompt_number)
315 prompt = self._make_in_prompt(previous_prompt_number)
336 self._prompt = self._insert_html_fetching_plain_text(
316 self._prompt = self._insert_html_fetching_plain_text(
337 cursor, prompt)
317 cursor, prompt)
338
318
339 # When the HTML is inserted, Qt blows away the syntax
319 # When the HTML is inserted, Qt blows away the syntax
340 # highlighting for the line, so we need to rehighlight it.
320 # highlighting for the line, so we need to rehighlight it.
341 self._highlighter.rehighlightBlock(cursor.block())
321 self._highlighter.rehighlightBlock(cursor.block())
342
322
343 self._previous_prompt_obj = None
323 self._previous_prompt_obj = None
344
324
345 # Show a new prompt with the kernel's estimated prompt number.
325 # Show a new prompt with the kernel's estimated prompt number.
346 self._show_interpreter_prompt(previous_prompt_number + 1)
326 self._show_interpreter_prompt(previous_prompt_number + 1)
347
327
348 #---------------------------------------------------------------------------
328 #---------------------------------------------------------------------------
349 # 'IPythonWidget' interface
329 # 'IPythonWidget' interface
350 #---------------------------------------------------------------------------
330 #---------------------------------------------------------------------------
351
331
352 def set_default_style(self, lightbg=True):
332 def set_default_style(self, lightbg=True):
353 """ Sets the widget style to the class defaults.
333 """ Sets the widget style to the class defaults.
354
334
355 Parameters:
335 Parameters:
356 -----------
336 -----------
357 lightbg : bool, optional (default True)
337 lightbg : bool, optional (default True)
358 Whether to use the default IPython light background or dark
338 Whether to use the default IPython light background or dark
359 background style.
339 background style.
360 """
340 """
361 if lightbg:
341 if lightbg:
362 self.style_sheet = default_light_style_sheet
342 self.style_sheet = default_light_style_sheet
363 self.syntax_style = default_light_syntax_style
343 self.syntax_style = default_light_syntax_style
364 else:
344 else:
365 self.style_sheet = default_dark_style_sheet
345 self.style_sheet = default_dark_style_sheet
366 self.syntax_style = default_dark_syntax_style
346 self.syntax_style = default_dark_syntax_style
367
347
368 #---------------------------------------------------------------------------
348 #---------------------------------------------------------------------------
369 # 'IPythonWidget' protected interface
349 # 'IPythonWidget' protected interface
370 #---------------------------------------------------------------------------
350 #---------------------------------------------------------------------------
371
351
372 def _edit(self, filename, line=None):
352 def _edit(self, filename, line=None):
373 """ Opens a Python script for editing.
353 """ Opens a Python script for editing.
374
354
375 Parameters:
355 Parameters:
376 -----------
356 -----------
377 filename : str
357 filename : str
378 A path to a local system file.
358 A path to a local system file.
379
359
380 line : int, optional
360 line : int, optional
381 A line of interest in the file.
361 A line of interest in the file.
382 """
362 """
383 if self.custom_edit:
363 if self.custom_edit:
384 self.custom_edit_requested.emit(filename, line)
364 self.custom_edit_requested.emit(filename, line)
385 elif self.editor == 'default':
365 elif self.editor == 'default':
386 self._append_plain_text('No default editor available.\n')
366 self._append_plain_text('No default editor available.\n')
387 else:
367 else:
388 try:
368 try:
389 filename = '"%s"' % filename
369 filename = '"%s"' % filename
390 if line and self.editor_line:
370 if line and self.editor_line:
391 command = self.editor_line.format(filename=filename,
371 command = self.editor_line.format(filename=filename,
392 line=line)
372 line=line)
393 else:
373 else:
394 try:
374 try:
395 command = self.editor.format()
375 command = self.editor.format()
396 except KeyError:
376 except KeyError:
397 command = self.editor.format(filename=filename)
377 command = self.editor.format(filename=filename)
398 else:
378 else:
399 command += ' ' + filename
379 command += ' ' + filename
400 except KeyError:
380 except KeyError:
401 self._append_plain_text('Invalid editor command.\n')
381 self._append_plain_text('Invalid editor command.\n')
402 else:
382 else:
403 try:
383 try:
404 Popen(command, shell=True)
384 Popen(command, shell=True)
405 except OSError:
385 except OSError:
406 msg = 'Opening editor with command "%s" failed.\n'
386 msg = 'Opening editor with command "%s" failed.\n'
407 self._append_plain_text(msg % command)
387 self._append_plain_text(msg % command)
408
388
409 def _make_in_prompt(self, number):
389 def _make_in_prompt(self, number):
410 """ Given a prompt number, returns an HTML In prompt.
390 """ Given a prompt number, returns an HTML In prompt.
411 """
391 """
412 body = self.in_prompt % number
392 body = self.in_prompt % number
413 return '<span class="in-prompt">%s</span>' % body
393 return '<span class="in-prompt">%s</span>' % body
414
394
415 def _make_continuation_prompt(self, prompt):
395 def _make_continuation_prompt(self, prompt):
416 """ Given a plain text version of an In prompt, returns an HTML
396 """ Given a plain text version of an In prompt, returns an HTML
417 continuation prompt.
397 continuation prompt.
418 """
398 """
419 end_chars = '...: '
399 end_chars = '...: '
420 space_count = len(prompt.lstrip('\n')) - len(end_chars)
400 space_count = len(prompt.lstrip('\n')) - len(end_chars)
421 body = '&nbsp;' * space_count + end_chars
401 body = '&nbsp;' * space_count + end_chars
422 return '<span class="in-prompt">%s</span>' % body
402 return '<span class="in-prompt">%s</span>' % body
423
403
424 def _make_out_prompt(self, number):
404 def _make_out_prompt(self, number):
425 """ Given a prompt number, returns an HTML Out prompt.
405 """ Given a prompt number, returns an HTML Out prompt.
426 """
406 """
427 body = self.out_prompt % number
407 body = self.out_prompt % number
428 return '<span class="out-prompt">%s</span>' % body
408 return '<span class="out-prompt">%s</span>' % body
429
409
430 #------ Payload handlers --------------------------------------------------
410 #------ Payload handlers --------------------------------------------------
431
411
432 # Payload handlers with a generic interface: each takes the opaque payload
412 # Payload handlers with a generic interface: each takes the opaque payload
433 # dict, unpacks it and calls the underlying functions with the necessary
413 # dict, unpacks it and calls the underlying functions with the necessary
434 # arguments.
414 # arguments.
435
415
436 def _handle_payload_edit(self, item):
416 def _handle_payload_edit(self, item):
437 self._edit(item['filename'], item['line_number'])
417 self._edit(item['filename'], item['line_number'])
438
418
439 def _handle_payload_exit(self, item):
419 def _handle_payload_exit(self, item):
440 self.exit_requested.emit()
420 self.exit_requested.emit()
441
421
442 def _handle_payload_loadpy(self, item):
422 def _handle_payload_loadpy(self, item):
443 # Simple save the text of the .py file for later. The text is written
423 # Simple save the text of the .py file for later. The text is written
444 # to the buffer when _prompt_started_hook is called.
424 # to the buffer when _prompt_started_hook is called.
445 self._code_to_load = item['text']
425 self._code_to_load = item['text']
446
426
447 def _handle_payload_page(self, item):
427 def _handle_payload_page(self, item):
448 # Since the plain text widget supports only a very small subset of HTML
428 # Since the plain text widget supports only a very small subset of HTML
449 # and we have no control over the HTML source, we only page HTML
429 # and we have no control over the HTML source, we only page HTML
450 # payloads in the rich text widget.
430 # payloads in the rich text widget.
451 if item['html'] and self.kind == 'rich':
431 if item['html'] and self.kind == 'rich':
452 self._page(item['html'], html=True)
432 self._page(item['html'], html=True)
453 else:
433 else:
454 self._page(item['text'], html=False)
434 self._page(item['text'], html=False)
455
435
456 #------ Trait change handlers ---------------------------------------------
436 #------ Trait change handlers ---------------------------------------------
457
437
458 def _style_sheet_changed(self):
438 def _style_sheet_changed(self):
459 """ Set the style sheets of the underlying widgets.
439 """ Set the style sheets of the underlying widgets.
460 """
440 """
461 self.setStyleSheet(self.style_sheet)
441 self.setStyleSheet(self.style_sheet)
462 self._control.document().setDefaultStyleSheet(self.style_sheet)
442 self._control.document().setDefaultStyleSheet(self.style_sheet)
463 if self._page_control:
443 if self._page_control:
464 self._page_control.document().setDefaultStyleSheet(self.style_sheet)
444 self._page_control.document().setDefaultStyleSheet(self.style_sheet)
465
445
466 bg_color = self._control.palette().background().color()
446 bg_color = self._control.palette().background().color()
467 self._ansi_processor.set_background_color(bg_color)
447 self._ansi_processor.set_background_color(bg_color)
468
448
469 def _syntax_style_changed(self):
449 def _syntax_style_changed(self):
470 """ Set the style for the syntax highlighter.
450 """ Set the style for the syntax highlighter.
471 """
451 """
472 if self.syntax_style:
452 if self.syntax_style:
473 self._highlighter.set_style(self.syntax_style)
453 self._highlighter.set_style(self.syntax_style)
474 else:
454 else:
475 self._highlighter.set_style_sheet(self.style_sheet)
455 self._highlighter.set_style_sheet(self.style_sheet)
476
456
@@ -1,199 +1,235 b''
1 """ A minimal application using the Qt console-style IPython frontend.
1 """ A minimal application using the Qt console-style IPython frontend.
2 """
2 """
3
3
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Imports
5 # Imports
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7
7
8 # Systemm library imports
8 # Systemm library imports
9 from PyQt4 import QtGui
9 from PyQt4 import QtGui
10
10 from pygments.styles import get_all_styles
11 # Local imports
11 # Local imports
12 from IPython.external.argparse import ArgumentParser
12 from IPython.external.argparse import ArgumentParser
13 from IPython.frontend.qt.console.frontend_widget import FrontendWidget
13 from IPython.frontend.qt.console.frontend_widget import FrontendWidget
14 from IPython.frontend.qt.console.ipython_widget import IPythonWidget
14 from IPython.frontend.qt.console.ipython_widget import IPythonWidget
15 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
15 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
16 from IPython.frontend.qt.console import styles
16 from IPython.frontend.qt.kernelmanager import QtKernelManager
17 from IPython.frontend.qt.kernelmanager import QtKernelManager
17
18
18 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
19 # Network Constants
20 # Network Constants
20 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
21
22
22 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
23 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
23
24
24 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
25 # Classes
26 # Classes
26 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
27
28
28 class MainWindow(QtGui.QMainWindow):
29 class MainWindow(QtGui.QMainWindow):
29
30
30 #---------------------------------------------------------------------------
31 #---------------------------------------------------------------------------
31 # 'object' interface
32 # 'object' interface
32 #---------------------------------------------------------------------------
33 #---------------------------------------------------------------------------
33
34
34 def __init__(self, app, frontend, existing=False, may_close=True):
35 def __init__(self, app, frontend, existing=False, may_close=True):
35 """ Create a MainWindow for the specified FrontendWidget.
36 """ Create a MainWindow for the specified FrontendWidget.
36
37
37 The app is passed as an argument to allow for different
38 The app is passed as an argument to allow for different
38 closing behavior depending on whether we are the Kernel's parent.
39 closing behavior depending on whether we are the Kernel's parent.
39
40
40 If existing is True, then this Console does not own the Kernel.
41 If existing is True, then this Console does not own the Kernel.
41
42
42 If may_close is True, then this Console is permitted to close the kernel
43 If may_close is True, then this Console is permitted to close the kernel
43 """
44 """
44 super(MainWindow, self).__init__()
45 super(MainWindow, self).__init__()
45 self._app = app
46 self._app = app
46 self._frontend = frontend
47 self._frontend = frontend
47 self._existing = existing
48 self._existing = existing
48 if existing:
49 if existing:
49 self._may_close = may_close
50 self._may_close = may_close
50 else:
51 else:
51 self._may_close = True
52 self._may_close = True
52 self._frontend.exit_requested.connect(self.close)
53 self._frontend.exit_requested.connect(self.close)
53 self.setCentralWidget(frontend)
54 self.setCentralWidget(frontend)
54
55
55 #---------------------------------------------------------------------------
56 #---------------------------------------------------------------------------
56 # QWidget interface
57 # QWidget interface
57 #---------------------------------------------------------------------------
58 #---------------------------------------------------------------------------
58
59
59 def closeEvent(self, event):
60 def closeEvent(self, event):
60 """ Reimplemented to prompt the user and close the kernel cleanly.
61 """ Reimplemented to prompt the user and close the kernel cleanly.
61 """
62 """
62 kernel_manager = self._frontend.kernel_manager
63 kernel_manager = self._frontend.kernel_manager
63 if kernel_manager and kernel_manager.channels_running:
64 if kernel_manager and kernel_manager.channels_running:
64 title = self.window().windowTitle()
65 title = self.window().windowTitle()
65 cancel = QtGui.QMessageBox.Cancel
66 cancel = QtGui.QMessageBox.Cancel
66 okay = QtGui.QMessageBox.Ok
67 okay = QtGui.QMessageBox.Ok
67 if self._may_close:
68 if self._may_close:
68 msg = "You are closing this Console window."
69 msg = "You are closing this Console window."
69 info = "Would you like to quit the Kernel and all attached Consoles as well?"
70 info = "Would you like to quit the Kernel and all attached Consoles as well?"
70 justthis = QtGui.QPushButton("&No, just this Console", self)
71 justthis = QtGui.QPushButton("&No, just this Console", self)
71 justthis.setShortcut('N')
72 justthis.setShortcut('N')
72 closeall = QtGui.QPushButton("&Yes, quit everything", self)
73 closeall = QtGui.QPushButton("&Yes, quit everything", self)
73 closeall.setShortcut('Y')
74 closeall.setShortcut('Y')
74 box = QtGui.QMessageBox(QtGui.QMessageBox.Question, title, msg)
75 box = QtGui.QMessageBox(QtGui.QMessageBox.Question, title, msg)
75 box.setInformativeText(info)
76 box.setInformativeText(info)
76 box.addButton(cancel)
77 box.addButton(cancel)
77 box.addButton(justthis, QtGui.QMessageBox.NoRole)
78 box.addButton(justthis, QtGui.QMessageBox.NoRole)
78 box.addButton(closeall, QtGui.QMessageBox.YesRole)
79 box.addButton(closeall, QtGui.QMessageBox.YesRole)
79 box.setDefaultButton(closeall)
80 box.setDefaultButton(closeall)
80 box.setEscapeButton(cancel)
81 box.setEscapeButton(cancel)
81 reply = box.exec_()
82 reply = box.exec_()
82 if reply == 1: # close All
83 if reply == 1: # close All
83 kernel_manager.shutdown_kernel()
84 kernel_manager.shutdown_kernel()
84 #kernel_manager.stop_channels()
85 #kernel_manager.stop_channels()
85 event.accept()
86 event.accept()
86 elif reply == 0: # close Console
87 elif reply == 0: # close Console
87 if not self._existing:
88 if not self._existing:
88 # I have the kernel: don't quit, just close the window
89 # I have the kernel: don't quit, just close the window
89 self._app.setQuitOnLastWindowClosed(False)
90 self._app.setQuitOnLastWindowClosed(False)
90 self.deleteLater()
91 self.deleteLater()
91 event.accept()
92 event.accept()
92 else:
93 else:
93 event.ignore()
94 event.ignore()
94 else:
95 else:
95 reply = QtGui.QMessageBox.question(self, title,
96 reply = QtGui.QMessageBox.question(self, title,
96 "Are you sure you want to close this Console?"+
97 "Are you sure you want to close this Console?"+
97 "\nThe Kernel and other Consoles will remain active.",
98 "\nThe Kernel and other Consoles will remain active.",
98 okay|cancel,
99 okay|cancel,
99 defaultButton=okay
100 defaultButton=okay
100 )
101 )
101 if reply == okay:
102 if reply == okay:
102 event.accept()
103 event.accept()
103 else:
104 else:
104 event.ignore()
105 event.ignore()
105
106
106
107
107 #-----------------------------------------------------------------------------
108 #-----------------------------------------------------------------------------
108 # Main entry point
109 # Main entry point
109 #-----------------------------------------------------------------------------
110 #-----------------------------------------------------------------------------
110
111
111 def main():
112 def main():
112 """ Entry point for application.
113 """ Entry point for application.
113 """
114 """
114 # Parse command line arguments.
115 # Parse command line arguments.
115 parser = ArgumentParser()
116 parser = ArgumentParser()
116 kgroup = parser.add_argument_group('kernel options')
117 kgroup = parser.add_argument_group('kernel options')
117 kgroup.add_argument('-e', '--existing', action='store_true',
118 kgroup.add_argument('-e', '--existing', action='store_true',
118 help='connect to an existing kernel')
119 help='connect to an existing kernel')
119 kgroup.add_argument('--ip', type=str, default=LOCALHOST,
120 kgroup.add_argument('--ip', type=str, default=LOCALHOST,
120 help=\
121 help=\
121 "set the kernel\'s IP address [default localhost].\
122 "set the kernel\'s IP address [default localhost].\
122 If the IP address is something other than localhost, then \
123 If the IP address is something other than localhost, then \
123 Consoles on other machines will be able to connect\
124 Consoles on other machines will be able to connect\
124 to the Kernel, so be careful!")
125 to the Kernel, so be careful!")
125 kgroup.add_argument('--xreq', type=int, metavar='PORT', default=0,
126 kgroup.add_argument('--xreq', type=int, metavar='PORT', default=0,
126 help='set the XREQ channel port [default random]')
127 help='set the XREQ channel port [default random]')
127 kgroup.add_argument('--sub', type=int, metavar='PORT', default=0,
128 kgroup.add_argument('--sub', type=int, metavar='PORT', default=0,
128 help='set the SUB channel port [default random]')
129 help='set the SUB channel port [default random]')
129 kgroup.add_argument('--rep', type=int, metavar='PORT', default=0,
130 kgroup.add_argument('--rep', type=int, metavar='PORT', default=0,
130 help='set the REP channel port [default random]')
131 help='set the REP channel port [default random]')
131 kgroup.add_argument('--hb', type=int, metavar='PORT', default=0,
132 kgroup.add_argument('--hb', type=int, metavar='PORT', default=0,
132 help='set the heartbeat port [default: random]')
133 help='set the heartbeat port [default random]')
133
134
134 egroup = kgroup.add_mutually_exclusive_group()
135 egroup = kgroup.add_mutually_exclusive_group()
135 egroup.add_argument('--pure', action='store_true', help = \
136 egroup.add_argument('--pure', action='store_true', help = \
136 'use a pure Python kernel instead of an IPython kernel')
137 'use a pure Python kernel instead of an IPython kernel')
137 egroup.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
138 egroup.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
138 const='auto', help = \
139 const='auto', help = \
139 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
140 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
140 given, the GUI backend is matplotlib's, otherwise use one of: \
141 given, the GUI backend is matplotlib's, otherwise use one of: \
141 ['tk', 'gtk', 'qt', 'wx', 'inline'].")
142 ['tk', 'gtk', 'qt', 'wx', 'inline'].")
142
143
143 wgroup = parser.add_argument_group('widget options')
144 wgroup = parser.add_argument_group('widget options')
144 wgroup.add_argument('--paging', type=str, default='inside',
145 wgroup.add_argument('--paging', type=str, default='inside',
145 choices = ['inside', 'hsplit', 'vsplit', 'none'],
146 choices = ['inside', 'hsplit', 'vsplit', 'none'],
146 help='set the paging style [default inside]')
147 help='set the paging style [default inside]')
147 wgroup.add_argument('--rich', action='store_true',
148 wgroup.add_argument('--rich', action='store_true',
148 help='enable rich text support')
149 help='enable rich text support')
149 wgroup.add_argument('--gui-completion', action='store_true',
150 wgroup.add_argument('--gui-completion', action='store_true',
150 help='use a GUI widget for tab completion')
151 help='use a GUI widget for tab completion')
152 wgroup.add_argument('--style', type=str,
153 help='specify a pygments style by name. \
154 Valid are: %s'%(list(get_all_styles())))
155 wgroup.add_argument('--stylesheet', type=str,
156 help="path to a custom CSS stylesheet.")
157 wgroup.add_argument('--dark', action='store_true',
158 help="use the dark style template instead of lightbg.\
159 If --style is not specified, the default dark style is used.")
151
160
152 args = parser.parse_args()
161 args = parser.parse_args()
153
162
154 # Don't let Qt or ZMQ swallow KeyboardInterupts.
163 # Don't let Qt or ZMQ swallow KeyboardInterupts.
155 import signal
164 import signal
156 signal.signal(signal.SIGINT, signal.SIG_DFL)
165 signal.signal(signal.SIGINT, signal.SIG_DFL)
157
166
158 # Create a KernelManager and start a kernel.
167 # Create a KernelManager and start a kernel.
159 kernel_manager = QtKernelManager(xreq_address=(args.ip, args.xreq),
168 kernel_manager = QtKernelManager(xreq_address=(args.ip, args.xreq),
160 sub_address=(args.ip, args.sub),
169 sub_address=(args.ip, args.sub),
161 rep_address=(args.ip, args.rep),
170 rep_address=(args.ip, args.rep),
162 hb_address=(args.ip, args.hb))
171 hb_address=(args.ip, args.hb))
163 if not args.existing:
172 if not args.existing:
164 # if not args.ip in LOCAL_IPS+ALL_ALIAS:
173 # if not args.ip in LOCAL_IPS+ALL_ALIAS:
165 # raise ValueError("Must bind a local ip, such as: %s"%LOCAL_IPS)
174 # raise ValueError("Must bind a local ip, such as: %s"%LOCAL_IPS)
166
175
167 kwargs = dict(ip=args.ip)
176 kwargs = dict(ip=args.ip)
168 if args.pure:
177 if args.pure:
169 kwargs['ipython']=False
178 kwargs['ipython']=False
170 elif args.pylab:
179 elif args.pylab:
171 kwargs['pylab']=args.pylab
180 kwargs['pylab']=args.pylab
172
181
173 kernel_manager.start_kernel(**kwargs)
182 kernel_manager.start_kernel(**kwargs)
174 kernel_manager.start_channels()
183 kernel_manager.start_channels()
175
184
176 local_kernel = (not args.existing) or args.ip in LOCAL_IPS
185 local_kernel = (not args.existing) or args.ip in LOCAL_IPS
177 # Create the widget.
186 # Create the widget.
178 app = QtGui.QApplication([])
187 app = QtGui.QApplication([])
179 if args.pure:
188 if args.pure:
180 kind = 'rich' if args.rich else 'plain'
189 kind = 'rich' if args.rich else 'plain'
181 widget = FrontendWidget(kind=kind, paging=args.paging, local_kernel=local_kernel)
190 widget = FrontendWidget(kind=kind, paging=args.paging, local_kernel=local_kernel)
182 elif args.rich or args.pylab:
191 elif args.rich or args.pylab:
183 widget = RichIPythonWidget(paging=args.paging, local_kernel=local_kernel)
192 widget = RichIPythonWidget(paging=args.paging, local_kernel=local_kernel)
184 else:
193 else:
185 widget = IPythonWidget(paging=args.paging, local_kernel=local_kernel)
194 widget = IPythonWidget(paging=args.paging, local_kernel=local_kernel)
186 widget.gui_completion = args.gui_completion
195 widget.gui_completion = args.gui_completion
187 widget.kernel_manager = kernel_manager
196 widget.kernel_manager = kernel_manager
188
197
198 # configure the style:
199 if not args.pure: # only IPythonWidget supports styles
200 if args.style:
201 # guess whether it's a dark style:
202 dark = args.dark or styles.dark_style(args.style)
203 widget.syntax_style = args.style
204 widget.style_sheet = styles.sheet_from_template(args.style, not dark)
205 widget._syntax_style_changed()
206 widget._style_sheet_changed()
207 elif args.dark:
208 # use default dark style
209 widget.set_default_style(lightbg=False)
210 else:
211 # this is redundant for now, but allows the widget's
212 # defaults to change
213 widget.set_default_style(lightbg=True)
214
215 if args.stylesheet:
216 # we got an expicit stylesheet
217 if os.path.isfile(args.stylesheet):
218 with open(args.stylesheet) as f:
219 sheet = f.read()
220 widget.style_sheet = sheet
221 widget._style_sheet_changed()
222 else:
223 raise IOError("Stylesheet %r not found."%args.stylesheet)
224
189 # Create the main window.
225 # Create the main window.
190 window = MainWindow(app, widget, args.existing, may_close=local_kernel)
226 window = MainWindow(app, widget, args.existing, may_close=local_kernel)
191 window.setWindowTitle('Python' if args.pure else 'IPython')
227 window.setWindowTitle('Python' if args.pure else 'IPython')
192 window.show()
228 window.show()
193
229
194 # Start the application main loop.
230 # Start the application main loop.
195 app.exec_()
231 app.exec_()
196
232
197
233
198 if __name__ == '__main__':
234 if __name__ == '__main__':
199 main()
235 main()
General Comments 0
You need to be logged in to leave comments. Login now