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.QtGui import QTextEdit, QFont
00036 from python_qt_binding.QtCore import Qt
00037 
00038 class ConsoleTextEdit(QTextEdit):
00039     _color_stdout = Qt.blue
00040     _color_stderr = Qt.red
00041     _color_stdin = Qt.black
00042     _multi_line_char = '\\'
00043     _multi_line_indent = '    '
00044     _prompt = ('$ ', '  ') # prompt for single and multi line
00045     
00046     class TextEditColoredWriter:
00047         def __init__(self, text_edit, color):
00048             self._text_edit = text_edit
00049             self._color = color
00050             
00051         def write(self, line):
00052             old_color = self._text_edit.textColor()
00053             self._text_edit.setTextColor(self._color)
00054             self._text_edit.insertPlainText(line)
00055             self._text_edit.setTextColor(old_color)
00056             self._text_edit.ensureCursorVisible()
00057 
00058     def __init__(self, parent=None):
00059         super(ConsoleTextEdit, self).__init__(parent)
00060         self.setFont(QFont('Mono'))
00061 
00062         self._multi_line = False
00063         self._multi_line_level = 0
00064         self._command = ''
00065         self._history = []
00066         self._history_index = -1
00067         
00068         # init colored writers
00069         self._stdout = self.TextEditColoredWriter(self, self._color_stdout)
00070         self._stderr = self.TextEditColoredWriter(self, self._color_stderr)
00071         self._comment_writer = self.TextEditColoredWriter(self, self._color_stdin)
00072         
00073     def print_message(self, msg):
00074         self._clear_current_line(clear_prompt=True)
00075         self._comment_writer.write(msg + '\n')
00076         self._add_prompt()  
00077 
00078     def _add_prompt(self):
00079         self._comment_writer.write(self._prompt[self._multi_line] + self._multi_line_indent * self._multi_line_level)
00080 
00081     def _clear_current_line(self, clear_prompt=False):
00082         # block being current row
00083         prompt_length = len(self._prompt[self._multi_line])
00084         if clear_prompt:
00085             prompt_length = 0
00086         length = len(self.document().lastBlock().text()[prompt_length:])
00087         if length == 0:
00088             return None
00089         else:
00090             # should have a better way of doing this but I can't find it
00091             [self.textCursor().deletePreviousChar() for x in xrange(length)]
00092         return True
00093 
00094     def _move_in_history(self, delta):
00095         # used when using the arrow keys to scroll through _history
00096         self._clear_current_line()
00097         if -1 <= self._history_index + delta < len(self._history):
00098             self._history_index += delta
00099         if self._history_index >= 0:
00100             self.insertPlainText(self._history[self._history_index])
00101         return True
00102     
00103     def _exec_code(self, code):
00104         raise NotImplementedError
00105 
00106     def _exec_with_captured_output(self, code):
00107         old_out, old_err = sys.stdout, sys.stderr
00108         sys.stdout, sys.stderr = self._stdout, self._stderr
00109         self._exec_code(code)
00110         sys.stdout, sys.stderr = old_out, old_err
00111 
00112     def keyPressEvent(self, event):
00113         prompt_length = len(self._prompt[self._multi_line])
00114         block_length = self.document().lastBlock().length()
00115         document_length = self.document().characterCount()
00116         line_start = document_length - block_length
00117         prompt_position = line_start + prompt_length
00118 
00119         # only handle keys if cursor is in the last line
00120         if self.textCursor().position() >= prompt_position:
00121             if event.key() == Qt.Key_Down:
00122                 if self._history_index == len(self._history):
00123                     self._history_index -= 1
00124                 self._move_in_history(-1)
00125                 return None
00126     
00127             if event.key() == Qt.Key_Up:
00128                 self._move_in_history(1)
00129                 return None
00130     
00131             if event.key() in [Qt.Key_Backspace]:
00132                 # don't allow cursor to delete into prompt
00133                 if self.textCursor().positionInBlock() == prompt_length and not self.textCursor().hasSelection():
00134                     return None
00135     
00136             if event.key() in [Qt.Key_Return, Qt.Key_Enter]:
00137                 # set cursor to end of line to avoid line splitting
00138                 cursor = self.textCursor()
00139                 cursor.setPosition(document_length - 1)
00140                 self.setTextCursor(cursor)
00141     
00142                 self._history_index = -1
00143                 line = str(self.document().lastBlock().text())[prompt_length:].rstrip() # remove prompt and trailing spaces
00144     
00145                 self.insertPlainText('\n')
00146                 if len(line) > 0:
00147                     if line[-1] == self._multi_line_char:
00148                         self._multi_line = True
00149                         self._multi_line_level += 1
00150                     self._history.insert(0, line)
00151     
00152                     if self._multi_line: # multi line command
00153                         self._command += line + '\n'
00154                     
00155                     else: # single line command
00156                         self._exec_with_captured_output(line)
00157                         self._command = ''
00158                 
00159                 else: # new line was is empty
00160                     
00161                     if self._multi_line: #  multi line done
00162                         self._exec_with_captured_output(self._command)
00163                         self._command = ''
00164                         self._multi_line = False
00165                         self._multi_line_level = 0
00166     
00167                 self._add_prompt()
00168                 return None
00169         
00170         # allow all other key events
00171         super(ConsoleTextEdit, self).keyPressEvent(event)
00172 
00173         # fix cursor position to be after the prompt, if the cursor is in the last line
00174         if line_start <= self.textCursor().position() < prompt_position:
00175             cursor = self.textCursor()
00176             cursor.setPosition(prompt_position)
00177             self.setTextCursor(cursor)


qt_gui_py_common
Author(s): Dorian Scholz
autogenerated on Fri Jan 3 2014 11:44:10