##// END OF EJS Templates
* Added 'started_listening' and 'stopped_listening' signals to QtKernelManager. The FrontendWidget listens for these signals....
epatters -
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 where the C++ level
345 """ Reimplemented to work around a bug in PyQt: the C++ level 'print_'
334 'print_' slot has the wrong signature.
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(self._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.continuation_prompt):
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, kernel_manager, parent=None):
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 # Connect the new kernel manager.
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, kernel_manager, parent=None):
16 def __init__(self, parent=None):
17 super(IPythonWidget, self).__init__(kernel_manager, parent)
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(kernel_manager)
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 """ A KernelManager that provides channels that use signals and slots.
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 channels that use signals and slots.
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