##// 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 13 class ConsoleWidget(QtGui.QWidget):
14 """ Base class for console-type widgets. This class is mainly concerned with
15 dealing with the prompt, keeping the cursor inside the editing line, and
16 handling ANSI escape sequences.
14 """ An abstract base class for console-type widgets. This class has
15 functionality for:
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 27 # Whether to process ANSI escape codes.
@@ -35,6 +43,10 b' class ConsoleWidget(QtGui.QWidget):'
35 43 redo_available = QtCore.pyqtSignal(bool)
36 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 50 # Protected class variables.
39 51 _ctrl_down_remap = { QtCore.Qt.Key_B : QtCore.Qt.Key_Left,
40 52 QtCore.Qt.Key_F : QtCore.Qt.Key_Right,
@@ -50,26 +62,62 b' class ConsoleWidget(QtGui.QWidget):'
50 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 66 """ Create a ConsoleWidget.
55 67
56 68 Parameters
57 69 ----------
58 70 kind : str, optional [default 'plain']
59 The type of text widget to use. Valid values are 'plain', which
60 specifies a QPlainTextEdit, and 'rich', which specifies a QTextEdit.
71 The type of underlying text widget to use. Valid values are 'plain',
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 87 parent : QWidget, optional [default None]
63 88 The parent for this widget.
64 89 """
65 90 super(ConsoleWidget, self).__init__(parent)
66 91
67 # Create and set the underlying text widget.
68 layout = QtGui.QVBoxLayout(self)
92 # Create the layout and underlying text widget.
93 layout = QtGui.QStackedLayout(self)
69 94 layout.setMargin(0)
70 95 self._control = self._create_control(kind)
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:
71 107 layout.addWidget(self._control)
72 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))
120
73 121 # Initialize protected variables. Some variables contain useful state
74 122 # information for subclasses; they should be considered read-only.
75 123 self._ansi_processor = QtAnsiCodeProcessor()
@@ -118,11 +166,39 b' class ConsoleWidget(QtGui.QWidget):'
118 166 return True
119 167
120 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 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 202 # 'ConsoleWidget' public interface
127 203 #---------------------------------------------------------------------------
128 204
@@ -410,14 +486,7 b' class ConsoleWidget(QtGui.QWidget):'
410 486 ANSI codes if enabled.
411 487 """
412 488 cursor = self._get_end_cursor()
413 cursor.beginEditBlock()
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()
489 self._insert_plain_text(cursor, text)
421 490
422 491 def _append_plain_text_keeping_prompt(self, text):
423 492 """ Writes 'text' after the current prompt, then restores the old prompt
@@ -478,7 +547,16 b' class ConsoleWidget(QtGui.QWidget):'
478 547 control.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
479 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 560 """ Filter key events for the underlying text widget to create a
483 561 console-like interface.
484 562 """
@@ -612,6 +690,28 b' class ConsoleWidget(QtGui.QWidget):'
612 690
613 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 715 def _format_as_columns(self, items, separator=' '):
616 716 """ Transform a list of strings into a single string with columns.
617 717
@@ -786,6 +886,19 b' class ConsoleWidget(QtGui.QWidget):'
786 886 cursor.insertText(' ', QtGui.QTextCharFormat())
787 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 902 def _insert_into_buffer(self, text):
790 903 """ Inserts text into the input buffer at the current cursor position,
791 904 ensuring that continuation prompts are inserted as necessary.
@@ -832,6 +945,26 b' class ConsoleWidget(QtGui.QWidget):'
832 945 self._control.setTextCursor(cursor)
833 946 return True
834 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)
967
835 968 def _prompt_started(self):
836 969 """ Called immediately after a new prompt is displayed.
837 970 """
@@ -45,7 +45,6 b' def main():'
45 45 widget = IPythonWidget()
46 46 widget.kernel_manager = kernel_manager
47 47 widget.setWindowTitle('Python')
48 widget.resize(640, 480)
49 48 widget.show()
50 49 app.exec_()
51 50
@@ -16,13 +16,14 b' class RichIPythonWidget(IPythonWidget):'
16 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 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 29 # 'ConsoleWidget' protected interface
General Comments 0
You need to be logged in to leave comments. Login now