console_text_edit.py
Go to the documentation of this file.
00001 # Software License Agreement (BSD License)
00002 #
00003 # Copyright (c) 2012, Dorian Scholz
00004 # All rights reserved.
00005 #
00006 # Redistribution and use in source and binary forms, with or without
00007 # modification, are permitted provided that the following conditions
00008 # are met:
00009 #
00010 #  * Redistributions of source code must retain the above copyright
00011 #    notice, this list of conditions and the following disclaimer.
00012 #  * Redistributions in binary form must reproduce the above
00013 #    copyright notice, this list of conditions and the following
00014 #    disclaimer in the documentation and/or other materials provided
00015 #    with the distribution.
00016 #  * Neither the name of Willow Garage, Inc. nor the names of its
00017 #    contributors may be used to endorse or promote products derived
00018 #    from this software without specific prior written permission.
00019 #
00020 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00021 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00022 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00023 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00024 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00025 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00026 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00027 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00028 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00029 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00030 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00031 # POSSIBILITY OF SUCH DAMAGE.
00032 
00033 import sys
00034 
00035 from python_qt_binding.QtCore import Qt
00036 from python_qt_binding.QtGui import QFont, QTextEdit
00037 
00038 
00039 class ConsoleTextEdit(QTextEdit):
00040     _color_stdout = Qt.blue
00041     _color_stderr = Qt.red
00042     _color_stdin = Qt.black
00043     _multi_line_char = '\\'
00044     _multi_line_indent = '    '
00045     _prompt = ('$ ', '  ')  # prompt for single and multi line
00046 
00047     class TextEditColoredWriter:
00048         def __init__(self, text_edit, color):
00049             self._text_edit = text_edit
00050             self._color = color
00051 
00052         def write(self, line):
00053             old_color = self._text_edit.textColor()
00054             self._text_edit.setTextColor(self._color)
00055             self._text_edit.insertPlainText(line)
00056             self._text_edit.setTextColor(old_color)
00057             self._text_edit.ensureCursorVisible()
00058 
00059     def __init__(self, parent=None):
00060         super(ConsoleTextEdit, self).__init__(parent)
00061         self.setFont(QFont('Mono'))
00062 
00063         self._multi_line = False
00064         self._multi_line_level = 0
00065         self._command = ''
00066         self._history = []
00067         self._history_index = -1
00068 
00069         # init colored writers
00070         self._stdout = self.TextEditColoredWriter(self, self._color_stdout)
00071         self._stderr = self.TextEditColoredWriter(self, self._color_stderr)
00072         self._comment_writer = self.TextEditColoredWriter(self, self._color_stdin)
00073 
00074     def print_message(self, msg):
00075         self._clear_current_line(clear_prompt=True)
00076         self._comment_writer.write(msg + '\n')
00077         self._add_prompt()
00078 
00079     def _add_prompt(self):
00080         self._comment_writer.write(self._prompt[self._multi_line] + self._multi_line_indent * self._multi_line_level)
00081 
00082     def _clear_current_line(self, clear_prompt=False):
00083         # block being current row
00084         prompt_length = len(self._prompt[self._multi_line])
00085         if clear_prompt:
00086             prompt_length = 0
00087         length = len(self.document().lastBlock().text()[prompt_length:])
00088         if length == 0:
00089             return None
00090         else:
00091             # should have a better way of doing this but I can't find it
00092             for _ in xrange(length):
00093                 self.textCursor().deletePreviousChar()
00094         return True
00095 
00096     def _move_in_history(self, delta):
00097         # used when using the arrow keys to scroll through _history
00098         self._clear_current_line()
00099         if -1 <= self._history_index + delta < len(self._history):
00100             self._history_index += delta
00101         if self._history_index >= 0:
00102             self.insertPlainText(self._history[self._history_index])
00103         return True
00104 
00105     def _exec_code(self, code):
00106         raise NotImplementedError
00107 
00108     def _exec_with_captured_output(self, code):
00109         old_out, old_err = sys.stdout, sys.stderr
00110         sys.stdout, sys.stderr = self._stdout, self._stderr
00111         self._exec_code(code)
00112         sys.stdout, sys.stderr = old_out, old_err
00113 
00114     def keyPressEvent(self, event):
00115         prompt_length = len(self._prompt[self._multi_line])
00116         block_length = self.document().lastBlock().length()
00117         document_length = self.document().characterCount()
00118         line_start = document_length - block_length
00119         prompt_position = line_start + prompt_length
00120 
00121         # only handle keys if cursor is in the last line
00122         if self.textCursor().position() >= prompt_position:
00123             if event.key() == Qt.Key_Down:
00124                 if self._history_index == len(self._history):
00125                     self._history_index -= 1
00126                 self._move_in_history(-1)
00127                 return None
00128 
00129             if event.key() == Qt.Key_Up:
00130                 self._move_in_history(1)
00131                 return None
00132 
00133             if event.key() in [Qt.Key_Backspace]:
00134                 # don't allow cursor to delete into prompt
00135                 if self.textCursor().positionInBlock() == prompt_length and not self.textCursor().hasSelection():
00136                     return None
00137 
00138             if event.key() in [Qt.Key_Return, Qt.Key_Enter]:
00139                 # set cursor to end of line to avoid line splitting
00140                 cursor = self.textCursor()
00141                 cursor.setPosition(document_length - 1)
00142                 self.setTextCursor(cursor)
00143 
00144                 self._history_index = -1
00145                 line = str(self.document().lastBlock().text())[prompt_length:].rstrip()  # remove prompt and trailing spaces
00146 
00147                 self.insertPlainText('\n')
00148                 if len(line) > 0:
00149                     if line[-1] == self._multi_line_char:
00150                         self._multi_line = True
00151                         self._multi_line_level += 1
00152                     self._history.insert(0, line)
00153 
00154                     if self._multi_line:  # multi line command
00155                         self._command += line + '\n'
00156 
00157                     else:  # single line command
00158                         self._exec_with_captured_output(line)
00159                         self._command = ''
00160 
00161                 else:  # new line was is empty
00162 
00163                     if self._multi_line:  # multi line done
00164                         self._exec_with_captured_output(self._command)
00165                         self._command = ''
00166                         self._multi_line = False
00167                         self._multi_line_level = 0
00168 
00169                 self._add_prompt()
00170                 return None
00171 
00172         # allow all other key events
00173         super(ConsoleTextEdit, self).keyPressEvent(event)
00174 
00175         # fix cursor position to be after the prompt, if the cursor is in the last line
00176         if line_start <= self.textCursor().position() < prompt_position:
00177             cursor = self.textCursor()
00178             cursor.setPosition(prompt_position)
00179             self.setTextCursor(cursor)


qt_gui_py_common
Author(s): Dorian Scholz
autogenerated on Mon Oct 6 2014 03:57:55