Show More
@@ -9,6 +9,7 b' import os.path' | |||||
9 | import re |
|
9 | import re | |
10 | import sys |
|
10 | import sys | |
11 | from textwrap import dedent |
|
11 | from textwrap import dedent | |
|
12 | import time | |||
12 | from unicodedata import category |
|
13 | from unicodedata import category | |
13 | import webbrowser |
|
14 | import webbrowser | |
14 |
|
15 | |||
@@ -291,6 +292,21 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||||
291 | self._reading_callback = None |
|
292 | self._reading_callback = None | |
292 | self._tab_width = 8 |
|
293 | self._tab_width = 8 | |
293 |
|
294 | |||
|
295 | # List of strings pending to be appended as plain text in the widget. | |||
|
296 | # The text is not immediately inserted when available to not | |||
|
297 | # choke the Qt event loop with paint events for the widget in | |||
|
298 | # case of lots of output from kernel. | |||
|
299 | self._pending_insert_text = [] | |||
|
300 | ||||
|
301 | # Timer to flush the pending stream messages. The interval is adjusted | |||
|
302 | # later based on actual time taken for flushing a screen (buffer_size) | |||
|
303 | # of output text. | |||
|
304 | self._pending_text_flush_interval = QtCore.QTimer(self._control) | |||
|
305 | self._pending_text_flush_interval.setInterval(100) | |||
|
306 | self._pending_text_flush_interval.setSingleShot(True) | |||
|
307 | self._pending_text_flush_interval.timeout.connect( | |||
|
308 | self._flush_pending_stream) | |||
|
309 | ||||
294 | # Set a monospaced font. |
|
310 | # Set a monospaced font. | |
295 | self.reset_font() |
|
311 | self.reset_font() | |
296 |
|
312 | |||
@@ -877,8 +893,11 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||||
877 | # Determine where to insert the content. |
|
893 | # Determine where to insert the content. | |
878 | cursor = self._control.textCursor() |
|
894 | cursor = self._control.textCursor() | |
879 | if before_prompt and (self._reading or not self._executing): |
|
895 | if before_prompt and (self._reading or not self._executing): | |
|
896 | self._flush_pending_stream() | |||
880 | cursor.setPosition(self._append_before_prompt_pos) |
|
897 | cursor.setPosition(self._append_before_prompt_pos) | |
881 | else: |
|
898 | else: | |
|
899 | if insert != self._insert_plain_text: | |||
|
900 | self._flush_pending_stream() | |||
882 | cursor.movePosition(QtGui.QTextCursor.End) |
|
901 | cursor.movePosition(QtGui.QTextCursor.End) | |
883 | start_pos = cursor.position() |
|
902 | start_pos = cursor.position() | |
884 |
|
903 | |||
@@ -1459,6 +1478,20 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||||
1459 |
|
1478 | |||
1460 | return False |
|
1479 | return False | |
1461 |
|
1480 | |||
|
1481 | def _flush_pending_stream(self): | |||
|
1482 | """ Flush out pending text into the widget. """ | |||
|
1483 | text = self._pending_insert_text | |||
|
1484 | self._pending_insert_text = [] | |||
|
1485 | buffer_size = self._control.document().maximumBlockCount() | |||
|
1486 | if buffer_size > 0: | |||
|
1487 | text = self._get_last_lines_from_list(text, buffer_size) | |||
|
1488 | text = ''.join(text) | |||
|
1489 | t = time.time() | |||
|
1490 | self._insert_plain_text(self._get_end_cursor(), text, flush=True) | |||
|
1491 | # Set the flush interval to equal the maximum time to update text. | |||
|
1492 | self._pending_text_flush_interval.setInterval(max(100, | |||
|
1493 | (time.time()-t)*1000)) | |||
|
1494 | ||||
1462 | def _format_as_columns(self, items, separator=' '): |
|
1495 | def _format_as_columns(self, items, separator=' '): | |
1463 | """ Transform a list of strings into a single string with columns. |
|
1496 | """ Transform a list of strings into a single string with columns. | |
1464 |
|
1497 | |||
@@ -1540,6 +1573,43 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||||
1540 | else: |
|
1573 | else: | |
1541 | return None |
|
1574 | return None | |
1542 |
|
1575 | |||
|
1576 | def _get_last_lines(self, text, num_lines, return_count=False): | |||
|
1577 | """ Return last specified number of lines of text (like `tail -n`). | |||
|
1578 | If return_count is True, returns a tuple of clipped text and the | |||
|
1579 | number of lines in the clipped text. | |||
|
1580 | """ | |||
|
1581 | pos = len(text) | |||
|
1582 | if pos < num_lines: | |||
|
1583 | if return_count: | |||
|
1584 | return text, text.count('\n') if return_count else text | |||
|
1585 | else: | |||
|
1586 | return text | |||
|
1587 | i = 0 | |||
|
1588 | while i < num_lines: | |||
|
1589 | pos = text.rfind('\n', None, pos) | |||
|
1590 | if pos == -1: | |||
|
1591 | pos = None | |||
|
1592 | break | |||
|
1593 | i += 1 | |||
|
1594 | if return_count: | |||
|
1595 | return text[pos:], i | |||
|
1596 | else: | |||
|
1597 | return text[pos:] | |||
|
1598 | ||||
|
1599 | def _get_last_lines_from_list(self, text_list, num_lines): | |||
|
1600 | """ Return the list of text clipped to last specified lines. | |||
|
1601 | """ | |||
|
1602 | ret = [] | |||
|
1603 | lines_pending = num_lines | |||
|
1604 | for text in reversed(text_list): | |||
|
1605 | text, lines_added = self._get_last_lines(text, lines_pending, | |||
|
1606 | return_count=True) | |||
|
1607 | ret.append(text) | |||
|
1608 | lines_pending -= lines_added | |||
|
1609 | if lines_pending <= 0: | |||
|
1610 | break | |||
|
1611 | return ret[::-1] | |||
|
1612 | ||||
1543 | def _get_prompt_cursor(self): |
|
1613 | def _get_prompt_cursor(self): | |
1544 | """ Convenience method that returns a cursor for the prompt position. |
|
1614 | """ Convenience method that returns a cursor for the prompt position. | |
1545 | """ |
|
1615 | """ | |
@@ -1644,10 +1714,29 b' class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):' | |||||
1644 | cursor.endEditBlock() |
|
1714 | cursor.endEditBlock() | |
1645 | return text |
|
1715 | return text | |
1646 |
|
1716 | |||
1647 | def _insert_plain_text(self, cursor, text): |
|
1717 | def _insert_plain_text(self, cursor, text, flush=False): | |
1648 | """ Inserts plain text using the specified cursor, processing ANSI codes |
|
1718 | """ Inserts plain text using the specified cursor, processing ANSI codes | |
1649 | if enabled. |
|
1719 | if enabled. | |
1650 | """ |
|
1720 | """ | |
|
1721 | # maximumBlockCount() can be different from self.buffer_size in | |||
|
1722 | # case input prompt is active. | |||
|
1723 | buffer_size = self._control.document().maximumBlockCount() | |||
|
1724 | ||||
|
1725 | if self._executing and not flush and \ | |||
|
1726 | self._pending_text_flush_interval.isActive(): | |||
|
1727 | self._pending_insert_text.append(text) | |||
|
1728 | if buffer_size > 0: | |||
|
1729 | self._pending_insert_text = self._get_last_lines_from_list( | |||
|
1730 | self._pending_insert_text, buffer_size) | |||
|
1731 | return | |||
|
1732 | ||||
|
1733 | if self._executing and not self._pending_text_flush_interval.isActive(): | |||
|
1734 | self._pending_text_flush_interval.start() | |||
|
1735 | ||||
|
1736 | # Clip the text to last `buffer_size` lines. | |||
|
1737 | if buffer_size > 0: | |||
|
1738 | text = self._get_last_lines(text, buffer_size) | |||
|
1739 | ||||
1651 | cursor.beginEditBlock() |
|
1740 | cursor.beginEditBlock() | |
1652 | if self.ansi_codes: |
|
1741 | if self.ansi_codes: | |
1653 | for substring in self._ansi_processor.split_string(text): |
|
1742 | for substring in self._ansi_processor.split_string(text): |
General Comments 0
You need to be logged in to leave comments.
Login now