Show More
@@ -0,0 +1,25 b'' | |||||
|
1 | """ Defines miscellaneous Qt-related helper classes and functions. | |||
|
2 | """ | |||
|
3 | ||||
|
4 | # System library imports. | |||
|
5 | from PyQt4 import QtCore | |||
|
6 | ||||
|
7 | # IPython imports. | |||
|
8 | from IPython.utils.traitlets import HasTraits | |||
|
9 | ||||
|
10 | ||||
|
11 | MetaHasTraits = type(HasTraits) | |||
|
12 | MetaQObject = type(QtCore.QObject) | |||
|
13 | ||||
|
14 | class MetaQObjectHasTraits(MetaQObject, MetaHasTraits): | |||
|
15 | """ A metaclass that inherits from the metaclasses of both HasTraits and | |||
|
16 | QObject. | |||
|
17 | ||||
|
18 | Using this metaclass allows a class to inherit from both HasTraits and | |||
|
19 | QObject. See QtKernelManager for an example. | |||
|
20 | """ | |||
|
21 | ||||
|
22 | def __init__(cls, name, bases, dct): | |||
|
23 | MetaQObject.__init__(cls, name, bases, dct) | |||
|
24 | MetaHasTraits.__init__(cls, name, bases, dct) | |||
|
25 |
@@ -121,10 +121,10 b' class ConsoleWidget(QtGui.QPlainTextEdit):' | |||||
121 | # Initialize public and protected variables |
|
121 | # Initialize public and protected variables | |
122 | self.ansi_codes = True |
|
122 | self.ansi_codes = True | |
123 | self.buffer_size = 500 |
|
123 | self.buffer_size = 500 | |
124 | self.continuation_prompt = '> ' |
|
|||
125 | self.gui_completion = True |
|
124 | self.gui_completion = True | |
126 | self._ansi_processor = QtAnsiCodeProcessor() |
|
125 | self._ansi_processor = QtAnsiCodeProcessor() | |
127 | self._completion_widget = CompletionWidget(self) |
|
126 | self._completion_widget = CompletionWidget(self) | |
|
127 | self._continuation_prompt = '> ' | |||
128 | self._executing = False |
|
128 | self._executing = False | |
129 | self._prompt = '' |
|
129 | self._prompt = '' | |
130 | self._prompt_pos = 0 |
|
130 | self._prompt_pos = 0 | |
@@ -261,7 +261,7 b' class ConsoleWidget(QtGui.QPlainTextEdit):' | |||||
261 | if start_line == self._get_prompt_cursor().blockNumber(): |
|
261 | if start_line == self._get_prompt_cursor().blockNumber(): | |
262 | start_pos += len(self._prompt) |
|
262 | start_pos += len(self._prompt) | |
263 | else: |
|
263 | else: | |
264 | start_pos += len(self.continuation_prompt) |
|
264 | start_pos += len(self._continuation_prompt) | |
265 | if shift_down and self._in_buffer(position): |
|
265 | if shift_down and self._in_buffer(position): | |
266 | self._set_selection(position, start_pos) |
|
266 | self._set_selection(position, start_pos) | |
267 | else: |
|
267 | else: | |
@@ -271,7 +271,7 b' class ConsoleWidget(QtGui.QPlainTextEdit):' | |||||
271 | elif key == QtCore.Qt.Key_Backspace and not alt_down: |
|
271 | elif key == QtCore.Qt.Key_Backspace and not alt_down: | |
272 |
|
272 | |||
273 | # Line deletion (remove continuation prompt) |
|
273 | # Line deletion (remove continuation prompt) | |
274 | len_prompt = len(self.continuation_prompt) |
|
274 | len_prompt = len(self._continuation_prompt) | |
275 | if cursor.columnNumber() == len_prompt and \ |
|
275 | if cursor.columnNumber() == len_prompt and \ | |
276 | position != self._prompt_pos: |
|
276 | position != self._prompt_pos: | |
277 | cursor.setPosition(position - len_prompt, |
|
277 | cursor.setPosition(position - len_prompt, | |
@@ -323,6 +323,18 b' class ConsoleWidget(QtGui.QPlainTextEdit):' | |||||
323 | else: |
|
323 | else: | |
324 | cursor.insertText(text) |
|
324 | cursor.insertText(text) | |
325 |
|
325 | |||
|
326 | def clear(self, keep_input=False): | |||
|
327 | """ Reimplemented to write a new prompt. If 'keep_input' is set, | |||
|
328 | restores the old input buffer when the new prompt is written. | |||
|
329 | """ | |||
|
330 | super(ConsoleWidget, self).clear() | |||
|
331 | ||||
|
332 | if keep_input: | |||
|
333 | input_buffer = self.input_buffer | |||
|
334 | self._show_prompt() | |||
|
335 | if keep_input: | |||
|
336 | self.input_buffer = input_buffer | |||
|
337 | ||||
326 | def paste(self): |
|
338 | def paste(self): | |
327 | """ Reimplemented to ensure that text is pasted in the editing region. |
|
339 | """ Reimplemented to ensure that text is pasted in the editing region. | |
328 | """ |
|
340 | """ | |
@@ -330,8 +342,8 b' class ConsoleWidget(QtGui.QPlainTextEdit):' | |||||
330 | QtGui.QPlainTextEdit.paste(self) |
|
342 | QtGui.QPlainTextEdit.paste(self) | |
331 |
|
343 | |||
332 | def print_(self, printer): |
|
344 | def print_(self, printer): | |
333 |
""" Reimplemented to work around bug in PyQt |
|
345 | """ Reimplemented to work around a bug in PyQt: the C++ level 'print_' | |
334 |
|
|
346 | slot has the wrong signature. | |
335 | """ |
|
347 | """ | |
336 | QtGui.QPlainTextEdit.print_(self, printer) |
|
348 | QtGui.QPlainTextEdit.print_(self, printer) | |
337 |
|
349 | |||
@@ -363,13 +375,13 b' class ConsoleWidget(QtGui.QPlainTextEdit):' | |||||
363 | input_buffer = str(cursor.selection().toPlainText()) |
|
375 | input_buffer = str(cursor.selection().toPlainText()) | |
364 |
|
376 | |||
365 | # Strip out continuation prompts |
|
377 | # Strip out continuation prompts | |
366 | return input_buffer.replace('\n' + self.continuation_prompt, '\n') |
|
378 | return input_buffer.replace('\n' + self._continuation_prompt, '\n') | |
367 |
|
379 | |||
368 | def _set_input_buffer(self, string): |
|
380 | def _set_input_buffer(self, string): | |
369 | # Add continuation prompts where necessary |
|
381 | # Add continuation prompts where necessary | |
370 | lines = string.splitlines() |
|
382 | lines = string.splitlines() | |
371 | for i in xrange(1, len(lines)): |
|
383 | for i in xrange(1, len(lines)): | |
372 | lines[i] = self.continuation_prompt + lines[i] |
|
384 | lines[i] = self._continuation_prompt + lines[i] | |
373 | string = '\n'.join(lines) |
|
385 | string = '\n'.join(lines) | |
374 |
|
386 | |||
375 | # Replace buffer with new text |
|
387 | # Replace buffer with new text | |
@@ -389,7 +401,7 b' class ConsoleWidget(QtGui.QPlainTextEdit):' | |||||
389 | if cursor.blockNumber() == self._get_prompt_cursor().blockNumber(): |
|
401 | if cursor.blockNumber() == self._get_prompt_cursor().blockNumber(): | |
390 | return text[len(self._prompt):] |
|
402 | return text[len(self._prompt):] | |
391 | else: |
|
403 | else: | |
392 | return text[len(self.continuation_prompt):] |
|
404 | return text[len(self._continuation_prompt):] | |
393 | else: |
|
405 | else: | |
394 | return None |
|
406 | return None | |
395 |
|
407 | |||
@@ -547,18 +559,20 b' class ConsoleWidget(QtGui.QPlainTextEdit):' | |||||
547 | """ |
|
559 | """ | |
548 | self.setTextCursor(self._get_selection_cursor(start, end)) |
|
560 | self.setTextCursor(self._get_selection_cursor(start, end)) | |
549 |
|
561 | |||
550 | def _show_prompt(self, prompt): |
|
562 | def _show_prompt(self, prompt=None): | |
551 | """ Writes a new prompt at the end of the buffer. |
|
563 | """ Writes a new prompt at the end of the buffer. If 'prompt' is not | |
|
564 | specified, uses the previous prompt. | |||
552 | """ |
|
565 | """ | |
553 | self.appendPlainText('\n' + prompt) |
|
566 | if prompt is not None: | |
554 | self._prompt = prompt |
|
567 | self._prompt = prompt | |
|
568 | self.appendPlainText('\n' + self._prompt) | |||
555 | self._prompt_pos = self._get_end_cursor().position() |
|
569 | self._prompt_pos = self._get_end_cursor().position() | |
556 | self._prompt_started() |
|
570 | self._prompt_started() | |
557 |
|
571 | |||
558 | def _show_continuation_prompt(self): |
|
572 | def _show_continuation_prompt(self): | |
559 | """ Writes a new continuation prompt at the end of the buffer. |
|
573 | """ Writes a new continuation prompt at the end of the buffer. | |
560 | """ |
|
574 | """ | |
561 | self.appendPlainText(self.continuation_prompt) |
|
575 | self.appendPlainText(self._continuation_prompt) | |
562 | self._prompt_started() |
|
576 | self._prompt_started() | |
563 |
|
577 | |||
564 | def _write_text_keeping_prompt(self, text): |
|
578 | def _write_text_keeping_prompt(self, text): | |
@@ -570,7 +584,7 b' class ConsoleWidget(QtGui.QPlainTextEdit):' | |||||
570 | self._prompt_finished() |
|
584 | self._prompt_finished() | |
571 |
|
585 | |||
572 | self.appendPlainText(text) |
|
586 | self.appendPlainText(text) | |
573 |
self._show_prompt( |
|
587 | self._show_prompt() | |
574 | self.input_buffer = input_buffer |
|
588 | self.input_buffer = input_buffer | |
575 |
|
589 | |||
576 | def _in_buffer(self, position): |
|
590 | def _in_buffer(self, position): |
@@ -26,8 +26,8 b' class FrontendHighlighter(PygmentsHighlighter):' | |||||
26 | """ Highlight a block of text. Reimplemented to highlight selectively. |
|
26 | """ Highlight a block of text. Reimplemented to highlight selectively. | |
27 | """ |
|
27 | """ | |
28 | if self.highlighting_on: |
|
28 | if self.highlighting_on: | |
29 |
for prompt in (self._frontend._prompt, |
|
29 | for prompt in (self._frontend._continuation_prompt, | |
30 |
self._frontend. |
|
30 | self._frontend._prompt): | |
31 | if qstring.startsWith(prompt): |
|
31 | if qstring.startsWith(prompt): | |
32 | qstring.remove(0, len(prompt)) |
|
32 | qstring.remove(0, len(prompt)) | |
33 | self._current_offset = len(prompt) |
|
33 | self._current_offset = len(prompt) | |
@@ -52,9 +52,14 b' class FrontendWidget(HistoryConsoleWidget):' | |||||
52 | # 'QWidget' interface |
|
52 | # 'QWidget' interface | |
53 | #--------------------------------------------------------------------------- |
|
53 | #--------------------------------------------------------------------------- | |
54 |
|
54 | |||
55 |
def __init__(self, |
|
55 | def __init__(self, parent=None): | |
56 | super(FrontendWidget, self).__init__(parent) |
|
56 | super(FrontendWidget, self).__init__(parent) | |
57 |
|
57 | |||
|
58 | # ConsoleWidget protected variables. | |||
|
59 | self._continuation_prompt = '... ' | |||
|
60 | self._prompt = '>>> ' | |||
|
61 | ||||
|
62 | # FrontendWidget protected variables. | |||
58 | self._blockbreaker = BlockBreaker(input_mode='replace') |
|
63 | self._blockbreaker = BlockBreaker(input_mode='replace') | |
59 | self._call_tip_widget = CallTipWidget(self) |
|
64 | self._call_tip_widget = CallTipWidget(self) | |
60 | self._completion_lexer = CompletionLexer(PythonLexer()) |
|
65 | self._completion_lexer = CompletionLexer(PythonLexer()) | |
@@ -62,9 +67,6 b' class FrontendWidget(HistoryConsoleWidget):' | |||||
62 | self._highlighter = FrontendHighlighter(self) |
|
67 | self._highlighter = FrontendHighlighter(self) | |
63 | self._kernel_manager = None |
|
68 | self._kernel_manager = None | |
64 |
|
69 | |||
65 | self.continuation_prompt = '... ' |
|
|||
66 | self.kernel_manager = kernel_manager |
|
|||
67 |
|
||||
68 | self.document().contentsChange.connect(self._document_contents_change) |
|
70 | self.document().contentsChange.connect(self._document_contents_change) | |
69 |
|
71 | |||
70 | def focusOutEvent(self, event): |
|
72 | def focusOutEvent(self, event): | |
@@ -144,10 +146,17 b' class FrontendWidget(HistoryConsoleWidget):' | |||||
144 | return self._kernel_manager |
|
146 | return self._kernel_manager | |
145 |
|
147 | |||
146 | def _set_kernel_manager(self, kernel_manager): |
|
148 | def _set_kernel_manager(self, kernel_manager): | |
147 | """ Sets a new kernel manager, configuring its channels as necessary. |
|
149 | """ Disconnect from the current kernel manager (if any) and set a new | |
|
150 | kernel manager. | |||
148 | """ |
|
151 | """ | |
149 | # Disconnect the old kernel manager. |
|
152 | # Disconnect the old kernel manager, if necessary. | |
150 | if self._kernel_manager is not None: |
|
153 | if self._kernel_manager is not None: | |
|
154 | self._kernel_manager.started_listening.disconnect( | |||
|
155 | self._started_listening) | |||
|
156 | self._kernel_manager.stopped_listening.disconnect( | |||
|
157 | self._stopped_listening) | |||
|
158 | ||||
|
159 | # Disconnect the old kernel manager's channels. | |||
151 | sub = self._kernel_manager.sub_channel |
|
160 | sub = self._kernel_manager.sub_channel | |
152 | xreq = self._kernel_manager.xreq_channel |
|
161 | xreq = self._kernel_manager.xreq_channel | |
153 | sub.message_received.disconnect(self._handle_sub) |
|
162 | sub.message_received.disconnect(self._handle_sub) | |
@@ -155,8 +164,16 b' class FrontendWidget(HistoryConsoleWidget):' | |||||
155 | xreq.complete_reply.disconnect(self._handle_complete_reply) |
|
164 | xreq.complete_reply.disconnect(self._handle_complete_reply) | |
156 | xreq.object_info_reply.disconnect(self._handle_object_info_reply) |
|
165 | xreq.object_info_reply.disconnect(self._handle_object_info_reply) | |
157 |
|
166 | |||
158 |
# |
|
167 | # Set the new kernel manager. | |
159 | self._kernel_manager = kernel_manager |
|
168 | self._kernel_manager = kernel_manager | |
|
169 | if kernel_manager is None: | |||
|
170 | return | |||
|
171 | ||||
|
172 | # Connect the new kernel manager. | |||
|
173 | kernel_manager.started_listening.connect(self._started_listening) | |||
|
174 | kernel_manager.stopped_listening.connect(self._stopped_listening) | |||
|
175 | ||||
|
176 | # Connect the new kernel manager's channels. | |||
160 | sub = kernel_manager.sub_channel |
|
177 | sub = kernel_manager.sub_channel | |
161 | xreq = kernel_manager.xreq_channel |
|
178 | xreq = kernel_manager.xreq_channel | |
162 | sub.message_received.connect(self._handle_sub) |
|
179 | sub.message_received.connect(self._handle_sub) | |
@@ -164,7 +181,10 b' class FrontendWidget(HistoryConsoleWidget):' | |||||
164 | xreq.complete_reply.connect(self._handle_complete_reply) |
|
181 | xreq.complete_reply.connect(self._handle_complete_reply) | |
165 | xreq.object_info_reply.connect(self._handle_object_info_reply) |
|
182 | xreq.object_info_reply.connect(self._handle_object_info_reply) | |
166 |
|
183 | |||
167 | self._show_prompt('>>> ') |
|
184 | # Handle the case where the kernel manager started listening before | |
|
185 | # we connected. | |||
|
186 | if kernel_manager.is_listening: | |||
|
187 | self._started_listening() | |||
168 |
|
188 | |||
169 | kernel_manager = property(_get_kernel_manager, _set_kernel_manager) |
|
189 | kernel_manager = property(_get_kernel_manager, _set_kernel_manager) | |
170 |
|
190 | |||
@@ -256,7 +276,7 b' class FrontendWidget(HistoryConsoleWidget):' | |||||
256 | text = "ERROR: ABORTED\n" |
|
276 | text = "ERROR: ABORTED\n" | |
257 | self.appendPlainText(text) |
|
277 | self.appendPlainText(text) | |
258 | self._hidden = True |
|
278 | self._hidden = True | |
259 |
self._show_prompt( |
|
279 | self._show_prompt() | |
260 | self.executed.emit(rep) |
|
280 | self.executed.emit(rep) | |
261 |
|
281 | |||
262 | def _handle_complete_reply(self, rep): |
|
282 | def _handle_complete_reply(self, rep): | |
@@ -274,3 +294,9 b' class FrontendWidget(HistoryConsoleWidget):' | |||||
274 | doc = rep['content']['docstring'] |
|
294 | doc = rep['content']['docstring'] | |
275 | if doc: |
|
295 | if doc: | |
276 | self._call_tip_widget.show_tip(doc) |
|
296 | self._call_tip_widget.show_tip(doc) | |
|
297 | ||||
|
298 | def _started_listening(self): | |||
|
299 | self.clear() | |||
|
300 | ||||
|
301 | def _stopped_listening(self): | |||
|
302 | pass |
@@ -13,8 +13,8 b' class IPythonWidget(FrontendWidget):' | |||||
13 | # 'FrontendWidget' interface |
|
13 | # 'FrontendWidget' interface | |
14 | #--------------------------------------------------------------------------- |
|
14 | #--------------------------------------------------------------------------- | |
15 |
|
15 | |||
16 |
def __init__(self, |
|
16 | def __init__(self, parent=None): | |
17 |
super(IPythonWidget, self).__init__( |
|
17 | super(IPythonWidget, self).__init__(parent) | |
18 |
|
18 | |||
19 | self._magic_overrides = {} |
|
19 | self._magic_overrides = {} | |
20 |
|
20 | |||
@@ -87,7 +87,8 b" if __name__ == '__main__':" | |||||
87 |
|
87 | |||
88 | # Launch application |
|
88 | # Launch application | |
89 | app = QtGui.QApplication([]) |
|
89 | app = QtGui.QApplication([]) | |
90 |
widget = IPythonWidget( |
|
90 | widget = IPythonWidget() | |
|
91 | widget.kernel_manager = kernel_manager | |||
91 | widget.setWindowTitle('Python') |
|
92 | widget.setWindowTitle('Python') | |
92 | widget.resize(640, 480) |
|
93 | widget.resize(640, 480) | |
93 | widget.show() |
|
94 | widget.show() |
@@ -1,4 +1,4 b'' | |||||
1 |
""" |
|
1 | """ Defines a KernelManager that provides signals and slots. | |
2 | """ |
|
2 | """ | |
3 |
|
3 | |||
4 | # System library imports. |
|
4 | # System library imports. | |
@@ -7,6 +7,7 b' from PyQt4 import QtCore' | |||||
7 | # IPython imports. |
|
7 | # IPython imports. | |
8 | from IPython.zmq.kernelmanager import KernelManager, SubSocketChannel, \ |
|
8 | from IPython.zmq.kernelmanager import KernelManager, SubSocketChannel, \ | |
9 | XReqSocketChannel, RepSocketChannel |
|
9 | XReqSocketChannel, RepSocketChannel | |
|
10 | from util import MetaQObjectHasTraits | |||
10 |
|
11 | |||
11 |
|
12 | |||
12 | class QtSubSocketChannel(SubSocketChannel, QtCore.QObject): |
|
13 | class QtSubSocketChannel(SubSocketChannel, QtCore.QObject): | |
@@ -109,10 +110,35 b' class QtRepSocketChannel(RepSocketChannel, QtCore.QObject):' | |||||
109 | RepSocketChannel.__init__(self, *args, **kw) |
|
110 | RepSocketChannel.__init__(self, *args, **kw) | |
110 |
|
111 | |||
111 |
|
112 | |||
112 | class QtKernelManager(KernelManager): |
|
113 | class QtKernelManager(KernelManager, QtCore.QObject): | |
113 |
""" A KernelManager that provides |
|
114 | """ A KernelManager that provides signals and slots. | |
114 | """ |
|
115 | """ | |
115 |
|
116 | |||
|
117 | __metaclass__ = MetaQObjectHasTraits | |||
|
118 | ||||
|
119 | # Emitted when the kernel manager has started listening. | |||
|
120 | started_listening = QtCore.pyqtSignal() | |||
|
121 | ||||
|
122 | # Emitted when the kernel manager has stopped listening. | |||
|
123 | stopped_listening = QtCore.pyqtSignal() | |||
|
124 | ||||
|
125 | # Use Qt-specific channel classes that emit signals. | |||
116 | sub_channel_class = QtSubSocketChannel |
|
126 | sub_channel_class = QtSubSocketChannel | |
117 | xreq_channel_class = QtXReqSocketChannel |
|
127 | xreq_channel_class = QtXReqSocketChannel | |
118 | rep_channel_class = QtRepSocketChannel |
|
128 | rep_channel_class = QtRepSocketChannel | |
|
129 | ||||
|
130 | #--------------------------------------------------------------------------- | |||
|
131 | # 'KernelManager' interface | |||
|
132 | #--------------------------------------------------------------------------- | |||
|
133 | ||||
|
134 | def start_listening(self): | |||
|
135 | """ Reimplemented to emit signal. | |||
|
136 | """ | |||
|
137 | super(QtKernelManager, self).start_listening() | |||
|
138 | self.started_listening.emit() | |||
|
139 | ||||
|
140 | def stop_listening(self): | |||
|
141 | """ Reimplemented to emit signal. | |||
|
142 | """ | |||
|
143 | super(QtKernelManager, self).stop_listening() | |||
|
144 | self.stopped_listening.emit() |
General Comments 0
You need to be logged in to leave comments.
Login now