##// END OF EJS Templates
* Added a pager with several different options to ConsoleWidget....
epatters -
Show More
@@ -11,9 +11,17 b' from completion_widget import CompletionWidget'
11
11
12
12
13 class ConsoleWidget(QtGui.QWidget):
13 class ConsoleWidget(QtGui.QWidget):
14 """ Base class for console-type widgets. This class is mainly concerned with
14 """ An abstract base class for console-type widgets. This class has
15 dealing with the prompt, keeping the cursor inside the editing line, and
15 functionality for:
16 handling ANSI escape sequences.
16
17 * Maintaining a prompt and editing region
18 * Providing the traditional Unix-style console keyboard shortcuts
19 * Performing tab completion
20 * Paging text
21 * Handling ANSI escape codes
22
23 ConsoleWidget also provides a number of utility methods that will be
24 convenient to implementors of a console-style widget.
17 """
25 """
18
26
19 # Whether to process ANSI escape codes.
27 # Whether to process ANSI escape codes.
@@ -35,6 +43,10 b' class ConsoleWidget(QtGui.QWidget):'
35 redo_available = QtCore.pyqtSignal(bool)
43 redo_available = QtCore.pyqtSignal(bool)
36 undo_available = QtCore.pyqtSignal(bool)
44 undo_available = QtCore.pyqtSignal(bool)
37
45
46 # Signal emitted when paging is needed and the paging style has been
47 # specified as 'custom'.
48 custom_page_requested = QtCore.pyqtSignal(QtCore.QString)
49
38 # Protected class variables.
50 # Protected class variables.
39 _ctrl_down_remap = { QtCore.Qt.Key_B : QtCore.Qt.Key_Left,
51 _ctrl_down_remap = { QtCore.Qt.Key_B : QtCore.Qt.Key_Left,
40 QtCore.Qt.Key_F : QtCore.Qt.Key_Right,
52 QtCore.Qt.Key_F : QtCore.Qt.Key_Right,
@@ -50,25 +62,61 b' class ConsoleWidget(QtGui.QWidget):'
50 # 'QObject' interface
62 # 'QObject' interface
51 #---------------------------------------------------------------------------
63 #---------------------------------------------------------------------------
52
64
53 def __init__(self, kind='plain', parent=None):
65 def __init__(self, kind='plain', paging='inside', parent=None):
54 """ Create a ConsoleWidget.
66 """ Create a ConsoleWidget.
55
67
56 Parameters
68 Parameters
57 ----------
69 ----------
58 kind : str, optional [default 'plain']
70 kind : str, optional [default 'plain']
59 The type of text widget to use. Valid values are 'plain', which
71 The type of underlying text widget to use. Valid values are 'plain',
60 specifies a QPlainTextEdit, and 'rich', which specifies a QTextEdit.
72 which specifies a QPlainTextEdit, and 'rich', which specifies a
73 QTextEdit.
74
75 paging : str, optional [default 'inside']
76 The type of paging to use. Valid values are:
77 'inside' : The widget pages like a traditional terminal pager.
78 'hsplit' : When paging is requested, the widget is split
79 horizontally. The top pane contains the console,
80 and the bottom pane contains the paged text.
81 'vsplit' : Similar to 'hsplit', except that a vertical splitter
82 used.
83 'custom' : No action is taken by the widget beyond emitting a
84 'custom_page_requested(QString)' signal.
85 'none' : The text is written directly to the console.
61
86
62 parent : QWidget, optional [default None]
87 parent : QWidget, optional [default None]
63 The parent for this widget.
88 The parent for this widget.
64 """
89 """
65 super(ConsoleWidget, self).__init__(parent)
90 super(ConsoleWidget, self).__init__(parent)
66
91
67 # Create and set the underlying text widget.
92 # Create the layout and underlying text widget.
68 layout = QtGui.QVBoxLayout(self)
93 layout = QtGui.QStackedLayout(self)
69 layout.setMargin(0)
94 layout.setMargin(0)
70 self._control = self._create_control(kind)
95 self._control = self._create_control(kind)
71 layout.addWidget(self._control)
96 self._page_control = None
97 self._splitter = None
98 if paging in ('hsplit', 'vsplit'):
99 self._splitter = QtGui.QSplitter()
100 if paging == 'hsplit':
101 self._splitter.setOrientation(QtCore.Qt.Horizontal)
102 else:
103 self._splitter.setOrientation(QtCore.Qt.Vertical)
104 self._splitter.addWidget(self._control)
105 layout.addWidget(self._splitter)
106 else:
107 layout.addWidget(self._control)
108
109 # Create the paging widget, if necessary.
110 self._page_style = paging
111 if paging in ('inside', 'hsplit', 'vsplit'):
112 self._page_control = self._create_page_control()
113 if self._splitter:
114 self._page_control.hide()
115 self._splitter.addWidget(self._page_control)
116 else:
117 layout.addWidget(self._page_control)
118 elif paging not in ('custom', 'none'):
119 raise ValueError('Paging style %s unknown.' % repr(paging))
72
120
73 # Initialize protected variables. Some variables contain useful state
121 # Initialize protected variables. Some variables contain useful state
74 # information for subclasses; they should be considered read-only.
122 # information for subclasses; they should be considered read-only.
@@ -118,11 +166,39 b' class ConsoleWidget(QtGui.QWidget):'
118 return True
166 return True
119
167
120 elif etype == QtCore.QEvent.KeyPress:
168 elif etype == QtCore.QEvent.KeyPress:
121 return self._event_filter_keypress(event)
169 return self._event_filter_console_keypress(event)
170
171 elif obj == self._page_control:
172 if etype == QtCore.QEvent.KeyPress:
173 return self._event_filter_page_keypress(event)
122
174
123 return super(ConsoleWidget, self).eventFilter(obj, event)
175 return super(ConsoleWidget, self).eventFilter(obj, event)
124
176
125 #---------------------------------------------------------------------------
177 #---------------------------------------------------------------------------
178 # 'QWidget' interface
179 #---------------------------------------------------------------------------
180
181 def sizeHint(self):
182 """ Reimplemented to suggest a size that is 80 characters wide and
183 25 lines high.
184 """
185 style = self.style()
186 opt = QtGui.QStyleOptionHeader()
187 font_metrics = QtGui.QFontMetrics(self.font)
188 splitwidth = style.pixelMetric(QtGui.QStyle.PM_SplitterWidth, opt, self)
189
190 width = font_metrics.width(' ') * 80
191 width += style.pixelMetric(QtGui.QStyle.PM_ScrollBarExtent, opt, self)
192 if self._page_style == 'hsplit':
193 width = width * 2 + splitwidth
194
195 height = font_metrics.height() * 25
196 if self._page_style == 'vsplit':
197 height = height * 2 + splitwidth
198
199 return QtCore.QSize(width, height)
200
201 #---------------------------------------------------------------------------
126 # 'ConsoleWidget' public interface
202 # 'ConsoleWidget' public interface
127 #---------------------------------------------------------------------------
203 #---------------------------------------------------------------------------
128
204
@@ -410,14 +486,7 b' class ConsoleWidget(QtGui.QWidget):'
410 ANSI codes if enabled.
486 ANSI codes if enabled.
411 """
487 """
412 cursor = self._get_end_cursor()
488 cursor = self._get_end_cursor()
413 cursor.beginEditBlock()
489 self._insert_plain_text(cursor, text)
414 if self.ansi_codes:
415 for substring in self._ansi_processor.split_string(text):
416 format = self._ansi_processor.get_format()
417 cursor.insertText(substring, format)
418 else:
419 cursor.insertText(text)
420 cursor.endEditBlock()
421
490
422 def _append_plain_text_keeping_prompt(self, text):
491 def _append_plain_text_keeping_prompt(self, text):
423 """ Writes 'text' after the current prompt, then restores the old prompt
492 """ Writes 'text' after the current prompt, then restores the old prompt
@@ -478,7 +547,16 b' class ConsoleWidget(QtGui.QWidget):'
478 control.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
547 control.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
479 return control
548 return control
480
549
481 def _event_filter_keypress(self, event):
550 def _create_page_control(self):
551 """ Creates and connects the underlying paging widget.
552 """
553 control = QtGui.QPlainTextEdit()
554 control.installEventFilter(self)
555 control.setReadOnly(True)
556 control.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
557 return control
558
559 def _event_filter_console_keypress(self, event):
482 """ Filter key events for the underlying text widget to create a
560 """ Filter key events for the underlying text widget to create a
483 console-like interface.
561 console-like interface.
484 """
562 """
@@ -612,6 +690,28 b' class ConsoleWidget(QtGui.QWidget):'
612
690
613 return intercepted
691 return intercepted
614
692
693 def _event_filter_page_keypress(self, event):
694 """ Filter key events for the paging widget to create console-like
695 interface.
696 """
697 key = event.key()
698
699 if key in (QtCore.Qt.Key_Q, QtCore.Qt.Key_Escape):
700 if self._splitter:
701 self._page_control.hide()
702 else:
703 self.layout().setCurrentWidget(self._control)
704 return True
705
706 elif key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return):
707 new_event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress,
708 QtCore.Qt.Key_Down,
709 QtCore.Qt.NoModifier)
710 QtGui.qApp.sendEvent(self._page_control, new_event)
711 return True
712
713 return False
714
615 def _format_as_columns(self, items, separator=' '):
715 def _format_as_columns(self, items, separator=' '):
616 """ Transform a list of strings into a single string with columns.
716 """ Transform a list of strings into a single string with columns.
617
717
@@ -786,6 +886,19 b' class ConsoleWidget(QtGui.QWidget):'
786 cursor.insertText(' ', QtGui.QTextCharFormat())
886 cursor.insertText(' ', QtGui.QTextCharFormat())
787 cursor.endEditBlock()
887 cursor.endEditBlock()
788
888
889 def _insert_plain_text(self, cursor, text):
890 """ Inserts plain text using the specified cursor, processing ANSI codes
891 if enabled.
892 """
893 cursor.beginEditBlock()
894 if self.ansi_codes:
895 for substring in self._ansi_processor.split_string(text):
896 format = self._ansi_processor.get_format()
897 cursor.insertText(substring, format)
898 else:
899 cursor.insertText(text)
900 cursor.endEditBlock()
901
789 def _insert_into_buffer(self, text):
902 def _insert_into_buffer(self, text):
790 """ Inserts text into the input buffer at the current cursor position,
903 """ Inserts text into the input buffer at the current cursor position,
791 ensuring that continuation prompts are inserted as necessary.
904 ensuring that continuation prompts are inserted as necessary.
@@ -831,6 +944,26 b' class ConsoleWidget(QtGui.QWidget):'
831 cursor.movePosition(QtGui.QTextCursor.End)
944 cursor.movePosition(QtGui.QTextCursor.End)
832 self._control.setTextCursor(cursor)
945 self._control.setTextCursor(cursor)
833 return True
946 return True
947
948 def _page(self, text):
949 """ Displays text using the pager.
950 """
951 if self._page_style == 'custom':
952 self.custom_page_requested.emit(text)
953 elif self._page_style == 'none':
954 self._append_plain_text(text)
955 else:
956 self._page_control.clear()
957 cursor = self._page_control.textCursor()
958 self._insert_plain_text(cursor, text)
959 self._page_control.moveCursor(QtGui.QTextCursor.Start)
960
961 self._page_control.viewport().resize(self._control.size())
962 if self._splitter:
963 self._page_control.show()
964 self._page_control.setFocus()
965 else:
966 self.layout().setCurrentWidget(self._page_control)
834
967
835 def _prompt_started(self):
968 def _prompt_started(self):
836 """ Called immediately after a new prompt is displayed.
969 """ Called immediately after a new prompt is displayed.
@@ -45,7 +45,6 b' def main():'
45 widget = IPythonWidget()
45 widget = IPythonWidget()
46 widget.kernel_manager = kernel_manager
46 widget.kernel_manager = kernel_manager
47 widget.setWindowTitle('Python')
47 widget.setWindowTitle('Python')
48 widget.resize(640, 480)
49 widget.show()
48 widget.show()
50 app.exec_()
49 app.exec_()
51
50
@@ -16,13 +16,14 b' class RichIPythonWidget(IPythonWidget):'
16 _svg_text_format_property = 1
16 _svg_text_format_property = 1
17
17
18 #---------------------------------------------------------------------------
18 #---------------------------------------------------------------------------
19 # 'QObject' interface
19 # 'object' interface
20 #---------------------------------------------------------------------------
20 #---------------------------------------------------------------------------
21
21
22 def __init__(self, parent=None):
22 def __init__(self, *args, **kw):
23 """ Create a RichIPythonWidget.
23 """ Create a RichIPythonWidget.
24 """
24 """
25 super(RichIPythonWidget, self).__init__(kind='rich', parent=parent)
25 kw['kind'] = 'rich'
26 super(RichIPythonWidget, self).__init__(*args, **kw)
26
27
27 #---------------------------------------------------------------------------
28 #---------------------------------------------------------------------------
28 # 'ConsoleWidget' protected interface
29 # 'ConsoleWidget' protected interface
General Comments 0
You need to be logged in to leave comments. Login now