Show More
@@ -0,0 +1,160 b'' | |||||
|
1 | """Return compact set of columns as a string with newlines for an | |||
|
2 | array of strings. | |||
|
3 | ||||
|
4 | Adapted from the routine of the same name inside cmd.py | |||
|
5 | ||||
|
6 | Author: Rocky Bernstein. | |||
|
7 | License: MIT Open Source License. | |||
|
8 | """ | |||
|
9 | ||||
|
10 | import types | |||
|
11 | ||||
|
12 | def columnize(array, displaywidth=80, colsep = ' ', | |||
|
13 | arrange_vertical=True, ljust=True, lineprefix=''): | |||
|
14 | """Return a list of strings as a compact set of columns arranged | |||
|
15 | horizontally or vertically. | |||
|
16 | ||||
|
17 | For example, for a line width of 4 characters (arranged vertically): | |||
|
18 | ['1', '2,', '3', '4'] => '1 3\n2 4\n' | |||
|
19 | ||||
|
20 | or arranged horizontally: | |||
|
21 | ['1', '2,', '3', '4'] => '1 2\n3 4\n' | |||
|
22 | ||||
|
23 | Each column is only as wide as necessary. By default, columns are | |||
|
24 | separated by two spaces - one was not legible enough. Set "colsep" | |||
|
25 | to adjust the string separate columns. Set `displaywidth' to set | |||
|
26 | the line width. | |||
|
27 | ||||
|
28 | Normally, consecutive items go down from the top to bottom from | |||
|
29 | the left-most column to the right-most. If "arrange_vertical" is | |||
|
30 | set false, consecutive items will go across, left to right, top to | |||
|
31 | bottom.""" | |||
|
32 | if not isinstance(array, list) and not isinstance(array, tuple): | |||
|
33 | raise TypeError, ( | |||
|
34 | 'array needs to be an instance of a list or a tuple') | |||
|
35 | ||||
|
36 | array = [str(i) for i in array] | |||
|
37 | ||||
|
38 | # Some degenerate cases | |||
|
39 | size = len(array) | |||
|
40 | if 0 == size: | |||
|
41 | return "<empty>\n" | |||
|
42 | elif size == 1: | |||
|
43 | return '%s\n' % str(array[0]) | |||
|
44 | ||||
|
45 | displaywidth = max(4, displaywidth - len(lineprefix)) | |||
|
46 | if arrange_vertical: | |||
|
47 | array_index = lambda nrows, row, col: nrows*col + row | |||
|
48 | # Try every row count from 1 upwards | |||
|
49 | for nrows in range(1, size): | |||
|
50 | ncols = (size+nrows-1) // nrows | |||
|
51 | colwidths = [] | |||
|
52 | totwidth = -len(colsep) | |||
|
53 | for col in range(ncols): | |||
|
54 | # get max column width for this column | |||
|
55 | colwidth = 0 | |||
|
56 | for row in range(nrows): | |||
|
57 | i = array_index(nrows, row, col) | |||
|
58 | if i >= size: break | |||
|
59 | x = array[i] | |||
|
60 | colwidth = max(colwidth, len(x)) | |||
|
61 | pass | |||
|
62 | colwidths.append(colwidth) | |||
|
63 | totwidth += colwidth + len(colsep) | |||
|
64 | if totwidth > displaywidth: | |||
|
65 | break | |||
|
66 | pass | |||
|
67 | if totwidth <= displaywidth: | |||
|
68 | break | |||
|
69 | pass | |||
|
70 | # The smallest number of rows computed and the | |||
|
71 | # max widths for each column has been obtained. | |||
|
72 | # Now we just have to format each of the | |||
|
73 | # rows. | |||
|
74 | s = '' | |||
|
75 | for row in range(nrows): | |||
|
76 | texts = [] | |||
|
77 | for col in range(ncols): | |||
|
78 | i = row + nrows*col | |||
|
79 | if i >= size: | |||
|
80 | x = "" | |||
|
81 | else: | |||
|
82 | x = array[i] | |||
|
83 | texts.append(x) | |||
|
84 | while texts and not texts[-1]: | |||
|
85 | del texts[-1] | |||
|
86 | for col in range(len(texts)): | |||
|
87 | if ljust: | |||
|
88 | texts[col] = texts[col].ljust(colwidths[col]) | |||
|
89 | else: | |||
|
90 | texts[col] = texts[col].rjust(colwidths[col]) | |||
|
91 | pass | |||
|
92 | pass | |||
|
93 | s += "%s%s\n" % (lineprefix, str(colsep.join(texts))) | |||
|
94 | pass | |||
|
95 | return s | |||
|
96 | else: | |||
|
97 | array_index = lambda nrows, row, col: ncols*(row-1) + col | |||
|
98 | # Try every column count from size downwards | |||
|
99 | prev_colwidths = [] | |||
|
100 | colwidths = [] | |||
|
101 | for ncols in range(size, 0, -1): | |||
|
102 | # Try every row count from 1 upwards | |||
|
103 | min_rows = (size+ncols-1) // ncols | |||
|
104 | for nrows in range(min_rows, size): | |||
|
105 | rounded_size = nrows * ncols | |||
|
106 | colwidths = [] | |||
|
107 | totwidth = -len(colsep) | |||
|
108 | for col in range(ncols): | |||
|
109 | # get max column width for this column | |||
|
110 | colwidth = 0 | |||
|
111 | for row in range(1, nrows+1): | |||
|
112 | i = array_index(nrows, row, col) | |||
|
113 | if i >= rounded_size: break | |||
|
114 | elif i < size: | |||
|
115 | x = array[i] | |||
|
116 | colwidth = max(colwidth, len(x)) | |||
|
117 | pass | |||
|
118 | pass | |||
|
119 | colwidths.append(colwidth) | |||
|
120 | totwidth += colwidth + len(colsep) | |||
|
121 | if totwidth >= displaywidth: | |||
|
122 | break | |||
|
123 | pass | |||
|
124 | if totwidth <= displaywidth and i >= rounded_size-1: | |||
|
125 | # Found the right nrows and ncols | |||
|
126 | nrows = row | |||
|
127 | break | |||
|
128 | elif totwidth >= displaywidth: | |||
|
129 | # Need to reduce ncols | |||
|
130 | break | |||
|
131 | pass | |||
|
132 | if totwidth <= displaywidth and i >= rounded_size-1: | |||
|
133 | break | |||
|
134 | pass | |||
|
135 | # The smallest number of rows computed and the | |||
|
136 | # max widths for each column has been obtained. | |||
|
137 | # Now we just have to format each of the | |||
|
138 | # rows. | |||
|
139 | s = '' | |||
|
140 | for row in range(1, nrows+1): | |||
|
141 | texts = [] | |||
|
142 | for col in range(ncols): | |||
|
143 | i = array_index(nrows, row, col) | |||
|
144 | if i >= size: | |||
|
145 | break | |||
|
146 | else: x = array[i] | |||
|
147 | texts.append(x) | |||
|
148 | pass | |||
|
149 | for col in range(len(texts)): | |||
|
150 | if ljust: | |||
|
151 | texts[col] = texts[col].ljust(colwidths[col]) | |||
|
152 | else: | |||
|
153 | texts[col] = texts[col].rjust(colwidths[col]) | |||
|
154 | pass | |||
|
155 | pass | |||
|
156 | s += "%s%s\n" % (lineprefix, str(colsep.join(texts))) | |||
|
157 | pass | |||
|
158 | return s | |||
|
159 | pass | |||
|
160 |
@@ -5,6 +5,7 b' import sys' | |||||
5 | from PyQt4 import QtCore, QtGui |
|
5 | from PyQt4 import QtCore, QtGui | |
6 |
|
6 | |||
7 | # Local imports |
|
7 | # Local imports | |
|
8 | from IPython.external.columnize import columnize | |||
8 | from ansi_code_processor import QtAnsiCodeProcessor |
|
9 | from ansi_code_processor import QtAnsiCodeProcessor | |
9 | from completion_widget import CompletionWidget |
|
10 | from completion_widget import CompletionWidget | |
10 |
|
11 | |||
@@ -29,9 +30,6 b' class ConsoleWidget(QtGui.QPlainTextEdit):' | |||||
29 | # priority (when it has focus) over, e.g., window-level menu shortcuts. |
|
30 | # priority (when it has focus) over, e.g., window-level menu shortcuts. | |
30 | override_shortcuts = False |
|
31 | override_shortcuts = False | |
31 |
|
32 | |||
32 | # The number of spaces to show for a tab character. |
|
|||
33 | tab_width = 8 |
|
|||
34 |
|
||||
35 | # Protected class variables. |
|
33 | # Protected class variables. | |
36 | _ctrl_down_remap = { QtCore.Qt.Key_B : QtCore.Qt.Key_Left, |
|
34 | _ctrl_down_remap = { QtCore.Qt.Key_B : QtCore.Qt.Key_Left, | |
37 | QtCore.Qt.Key_F : QtCore.Qt.Key_Right, |
|
35 | QtCore.Qt.Key_F : QtCore.Qt.Key_Right, | |
@@ -62,6 +60,7 b' class ConsoleWidget(QtGui.QPlainTextEdit):' | |||||
62 | self._prompt_pos = 0 |
|
60 | self._prompt_pos = 0 | |
63 | self._reading = False |
|
61 | self._reading = False | |
64 | self._reading_callback = None |
|
62 | self._reading_callback = None | |
|
63 | self._tab_width = 8 | |||
65 |
|
64 | |||
66 | # Set a monospaced font. |
|
65 | # Set a monospaced font. | |
67 | self.reset_font() |
|
66 | self.reset_font() | |
@@ -113,7 +112,7 b' class ConsoleWidget(QtGui.QPlainTextEdit):' | |||||
113 | self._context_menu.exec_(event.globalPos()) |
|
112 | self._context_menu.exec_(event.globalPos()) | |
114 |
|
113 | |||
115 | def dragMoveEvent(self, event): |
|
114 | def dragMoveEvent(self, event): | |
116 |
""" Reimplemented to disable |
|
115 | """ Reimplemented to disable moving text by drag and drop. | |
117 | """ |
|
116 | """ | |
118 | event.ignore() |
|
117 | event.ignore() | |
119 |
|
118 | |||
@@ -467,6 +466,21 b' class ConsoleWidget(QtGui.QPlainTextEdit):' | |||||
467 | font = QtGui.QFont(name, QtGui.qApp.font().pointSize()) |
|
466 | font = QtGui.QFont(name, QtGui.qApp.font().pointSize()) | |
468 | font.setStyleHint(QtGui.QFont.TypeWriter) |
|
467 | font.setStyleHint(QtGui.QFont.TypeWriter) | |
469 | self._set_font(font) |
|
468 | self._set_font(font) | |
|
469 | ||||
|
470 | def _get_tab_width(self): | |||
|
471 | """ The width (in terms of space characters) for tab characters. | |||
|
472 | """ | |||
|
473 | return self._tab_width | |||
|
474 | ||||
|
475 | def _set_tab_width(self, tab_width): | |||
|
476 | """ Sets the width (in terms of space characters) for tab characters. | |||
|
477 | """ | |||
|
478 | font_metrics = QtGui.QFontMetrics(self.font) | |||
|
479 | self.setTabStopWidth(tab_width * font_metrics.width(' ')) | |||
|
480 | ||||
|
481 | self._tab_width = tab_width | |||
|
482 | ||||
|
483 | tab_width = property(_get_tab_width, _set_tab_width) | |||
470 |
|
484 | |||
471 | #--------------------------------------------------------------------------- |
|
485 | #--------------------------------------------------------------------------- | |
472 | # 'ConsoleWidget' abstract interface |
|
486 | # 'ConsoleWidget' abstract interface | |
@@ -563,9 +577,34 b' class ConsoleWidget(QtGui.QPlainTextEdit):' | |||||
563 | if self.gui_completion: |
|
577 | if self.gui_completion: | |
564 | self._completion_widget.show_items(cursor, items) |
|
578 | self._completion_widget.show_items(cursor, items) | |
565 | else: |
|
579 | else: | |
566 |
text = |
|
580 | text = self.format_as_columns(items) | |
567 | self._append_plain_text_keeping_prompt(text) |
|
581 | self._append_plain_text_keeping_prompt(text) | |
568 |
|
582 | |||
|
583 | def format_as_columns(self, items, separator=' ', vertical=True): | |||
|
584 | """ Transform a list of strings into a single string with columns. | |||
|
585 | ||||
|
586 | Parameters | |||
|
587 | ---------- | |||
|
588 | items : sequence [str] | |||
|
589 | The strings to process. | |||
|
590 | ||||
|
591 | separator : str, optional [default is two spaces] | |||
|
592 | The string that separates columns. | |||
|
593 | ||||
|
594 | vertical: bool, optional [default True] | |||
|
595 | If set, consecutive items will be arranged from top to bottom, then | |||
|
596 | from left to right. Otherwise, consecutive items will be aranged | |||
|
597 | from left to right, then from top to bottom. | |||
|
598 | ||||
|
599 | Returns | |||
|
600 | ------- | |||
|
601 | The formatted string. | |||
|
602 | """ | |||
|
603 | font_metrics = QtGui.QFontMetrics(self.font) | |||
|
604 | width = self.width() / font_metrics.width(' ') | |||
|
605 | return columnize(items, displaywidth=width, | |||
|
606 | colsep=separator, arrange_vertical=vertical) | |||
|
607 | ||||
569 | def _get_block_plain_text(self, block): |
|
608 | def _get_block_plain_text(self, block): | |
570 | """ Given a QTextBlock, return its unformatted text. |
|
609 | """ Given a QTextBlock, return its unformatted text. | |
571 | """ |
|
610 | """ |
@@ -63,10 +63,7 b' class FrontendHighlighter(PygmentsHighlighter):' | |||||
63 | class FrontendWidget(HistoryConsoleWidget): |
|
63 | class FrontendWidget(HistoryConsoleWidget): | |
64 | """ A Qt frontend for a generic Python kernel. |
|
64 | """ A Qt frontend for a generic Python kernel. | |
65 | """ |
|
65 | """ | |
66 |
|
66 | |||
67 | # ConsoleWidget interface. |
|
|||
68 | tab_width = 4 |
|
|||
69 |
|
||||
70 | # Emitted when an 'execute_reply' is received from the kernel. |
|
67 | # Emitted when an 'execute_reply' is received from the kernel. | |
71 | executed = QtCore.pyqtSignal(object) |
|
68 | executed = QtCore.pyqtSignal(object) | |
72 |
|
69 | |||
@@ -86,6 +83,7 b' class FrontendWidget(HistoryConsoleWidget):' | |||||
86 | self._kernel_manager = None |
|
83 | self._kernel_manager = None | |
87 |
|
84 | |||
88 | # Configure the ConsoleWidget. |
|
85 | # Configure the ConsoleWidget. | |
|
86 | self.tab_width = 4 | |||
89 | self._set_continuation_prompt('... ') |
|
87 | self._set_continuation_prompt('... ') | |
90 |
|
88 | |||
91 | self.document().contentsChange.connect(self._document_contents_change) |
|
89 | self.document().contentsChange.connect(self._document_contents_change) | |
@@ -121,7 +119,7 b' class FrontendWidget(HistoryConsoleWidget):' | |||||
121 | prompt created. When triggered by an Enter/Return key press, |
|
119 | prompt created. When triggered by an Enter/Return key press, | |
122 | 'interactive' is True; otherwise, it is False. |
|
120 | 'interactive' is True; otherwise, it is False. | |
123 | """ |
|
121 | """ | |
124 |
complete = self._input_splitter.push(source. |
|
122 | complete = self._input_splitter.push(source.expandtabs(4)) | |
125 | if interactive: |
|
123 | if interactive: | |
126 | complete = not self._input_splitter.push_accepts_more() |
|
124 | complete = not self._input_splitter.push_accepts_more() | |
127 | return complete |
|
125 | return complete |
General Comments 0
You need to be logged in to leave comments.
Login now