console_text_edit.py
Go to the documentation of this file.
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2012, Dorian Scholz
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32 
33 import sys
34 
35 from python_qt_binding.QtCore import Qt
36 from python_qt_binding.QtGui import QFont
37 from python_qt_binding.QtWidgets import QTextEdit
38 
39 
40 class ConsoleTextEdit(QTextEdit):
41  _color_stdout = Qt.blue
42  _color_stderr = Qt.red
43  _color_stdin = Qt.black
44  _multi_line_char = '\\'
45  _multi_line_indent = ' '
46  _prompt = ('$ ', ' ') # prompt for single and multi line
47 
49 
50  def __init__(self, text_edit, color):
51  self._text_edit = text_edit
52  self._color = color
53 
54  def write(self, line):
55  old_color = self._text_edit.textColor()
56  self._text_edit.setTextColor(self._color)
57  self._text_edit.insertPlainText(line)
58  self._text_edit.setTextColor(old_color)
59  self._text_edit.ensureCursorVisible()
60 
61  def __init__(self, parent=None):
62  super(ConsoleTextEdit, self).__init__(parent)
63  self.setFont(QFont('Mono'))
64 
65  self._multi_line = False
67  self._command = ''
68  self._history = []
69  self._history_index = -1
70 
71  # init colored writers
75 
76  def print_message(self, msg):
77  self._clear_current_line(clear_prompt=True)
78  self._comment_writer.write(msg + '\n')
79  self._add_prompt()
80 
81  def _add_prompt(self):
82  self._comment_writer.write(
84 
85  def _clear_current_line(self, clear_prompt=False):
86  # block being current row
87  prompt_length = len(self._prompt[self._multi_line])
88  if clear_prompt:
89  prompt_length = 0
90  length = len(self.document().lastBlock().text()[prompt_length:])
91  if length == 0:
92  return None
93  else:
94  # should have a better way of doing this but I can't find it
95  for _ in range(length):
96  self.textCursor().deletePreviousChar()
97  return True
98 
99  def _move_in_history(self, delta):
100  # used when using the arrow keys to scroll through _history
101  self._clear_current_line()
102  if -1 <= self._history_index + delta < len(self._history):
103  self._history_index += delta
104  if self._history_index >= 0:
105  self.insertPlainText(self._history[self._history_index])
106  return True
107 
108  def _exec_code(self, code):
109  raise NotImplementedError
110 
111  def _exec_with_captured_output(self, code):
112  old_out, old_err = sys.stdout, sys.stderr
113  sys.stdout, sys.stderr = self._stdout, self._stderr
114  self._exec_code(code)
115  sys.stdout, sys.stderr = old_out, old_err
116 
117  def keyPressEvent(self, event):
118  prompt_length = len(self._prompt[self._multi_line])
119  block_length = self.document().lastBlock().length()
120  document_length = self.document().characterCount()
121  line_start = document_length - block_length
122  prompt_position = line_start + prompt_length
123 
124  # only handle keys if cursor is in the last line
125  if self.textCursor().position() >= prompt_position:
126  if event.key() == Qt.Key_Down:
127  if self._history_index == len(self._history):
128  self._history_index -= 1
129  self._move_in_history(-1)
130  return None
131 
132  if event.key() == Qt.Key_Up:
133  self._move_in_history(1)
134  return None
135 
136  if event.key() in [Qt.Key_Backspace]:
137  # don't allow cursor to delete into prompt
138  if (self.textCursor().positionInBlock() == prompt_length and
139  not self.textCursor().hasSelection()):
140  return None
141 
142  if event.key() in [Qt.Key_Return, Qt.Key_Enter]:
143  # set cursor to end of line to avoid line splitting
144  cursor = self.textCursor()
145  cursor.setPosition(document_length - 1)
146  self.setTextCursor(cursor)
147 
148  self._history_index = -1
149  line = str(self.document().lastBlock().text())[
150  prompt_length:].rstrip() # remove prompt and trailing spaces
151 
152  self.insertPlainText('\n')
153  if len(line) > 0:
154  if line[-1] == self._multi_line_char:
155  self._multi_line = True
156  self._multi_line_level += 1
157  self._history.insert(0, line)
158 
159  if self._multi_line: # multi line command
160  self._command += line + '\n'
161 
162  else: # single line command
163  self._exec_with_captured_output(line)
164  self._command = ''
165 
166  else: # new line was is empty
167 
168  if self._multi_line: # multi line done
170  self._command = ''
171  self._multi_line = False
172  self._multi_line_level = 0
173 
174  self._add_prompt()
175  return None
176 
177  # allow all other key events
178  super(ConsoleTextEdit, self).keyPressEvent(event)
179 
180  # fix cursor position to be after the prompt, if the cursor is in the last line
181  if line_start <= self.textCursor().position() < prompt_position:
182  cursor = self.textCursor()
183  cursor.setPosition(prompt_position)
184  self.setTextCursor(cursor)


qt_gui_py_common
Author(s): Dorian Scholz
autogenerated on Tue Apr 13 2021 03:03:15