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 | 5 | from PyQt4 import QtCore, QtGui |
|
6 | 6 | |
|
7 | 7 | # Local imports |
|
8 | from IPython.external.columnize import columnize | |
|
8 | 9 | from ansi_code_processor import QtAnsiCodeProcessor |
|
9 | 10 | from completion_widget import CompletionWidget |
|
10 | 11 | |
@@ -29,9 +30,6 b' class ConsoleWidget(QtGui.QPlainTextEdit):' | |||
|
29 | 30 | # priority (when it has focus) over, e.g., window-level menu shortcuts. |
|
30 | 31 | override_shortcuts = False |
|
31 | 32 | |
|
32 | # The number of spaces to show for a tab character. | |
|
33 | tab_width = 8 | |
|
34 | ||
|
35 | 33 | # Protected class variables. |
|
36 | 34 | _ctrl_down_remap = { QtCore.Qt.Key_B : QtCore.Qt.Key_Left, |
|
37 | 35 | QtCore.Qt.Key_F : QtCore.Qt.Key_Right, |
@@ -62,6 +60,7 b' class ConsoleWidget(QtGui.QPlainTextEdit):' | |||
|
62 | 60 | self._prompt_pos = 0 |
|
63 | 61 | self._reading = False |
|
64 | 62 | self._reading_callback = None |
|
63 | self._tab_width = 8 | |
|
65 | 64 | |
|
66 | 65 | # Set a monospaced font. |
|
67 | 66 | self.reset_font() |
@@ -113,7 +112,7 b' class ConsoleWidget(QtGui.QPlainTextEdit):' | |||
|
113 | 112 | self._context_menu.exec_(event.globalPos()) |
|
114 | 113 | |
|
115 | 114 | def dragMoveEvent(self, event): |
|
116 |
""" Reimplemented to disable |
|
|
115 | """ Reimplemented to disable moving text by drag and drop. | |
|
117 | 116 | """ |
|
118 | 117 | event.ignore() |
|
119 | 118 | |
@@ -467,6 +466,21 b' class ConsoleWidget(QtGui.QPlainTextEdit):' | |||
|
467 | 466 | font = QtGui.QFont(name, QtGui.qApp.font().pointSize()) |
|
468 | 467 | font.setStyleHint(QtGui.QFont.TypeWriter) |
|
469 | 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 | 486 | # 'ConsoleWidget' abstract interface |
@@ -563,9 +577,34 b' class ConsoleWidget(QtGui.QPlainTextEdit):' | |||
|
563 | 577 | if self.gui_completion: |
|
564 | 578 | self._completion_widget.show_items(cursor, items) |
|
565 | 579 | else: |
|
566 |
text = |
|
|
580 | text = self.format_as_columns(items) | |
|
567 | 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 | 608 | def _get_block_plain_text(self, block): |
|
570 | 609 | """ Given a QTextBlock, return its unformatted text. |
|
571 | 610 | """ |
@@ -63,10 +63,7 b' class FrontendHighlighter(PygmentsHighlighter):' | |||
|
63 | 63 | class FrontendWidget(HistoryConsoleWidget): |
|
64 | 64 | """ A Qt frontend for a generic Python kernel. |
|
65 | 65 | """ |
|
66 | ||
|
67 | # ConsoleWidget interface. | |
|
68 | tab_width = 4 | |
|
69 | ||
|
66 | ||
|
70 | 67 | # Emitted when an 'execute_reply' is received from the kernel. |
|
71 | 68 | executed = QtCore.pyqtSignal(object) |
|
72 | 69 | |
@@ -86,6 +83,7 b' class FrontendWidget(HistoryConsoleWidget):' | |||
|
86 | 83 | self._kernel_manager = None |
|
87 | 84 | |
|
88 | 85 | # Configure the ConsoleWidget. |
|
86 | self.tab_width = 4 | |
|
89 | 87 | self._set_continuation_prompt('... ') |
|
90 | 88 | |
|
91 | 89 | self.document().contentsChange.connect(self._document_contents_change) |
@@ -121,7 +119,7 b' class FrontendWidget(HistoryConsoleWidget):' | |||
|
121 | 119 | prompt created. When triggered by an Enter/Return key press, |
|
122 | 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 | 123 | if interactive: |
|
126 | 124 | complete = not self._input_splitter.push_accepts_more() |
|
127 | 125 | return complete |
General Comments 0
You need to be logged in to leave comments.
Login now