Ignore:
Timestamp:
01/11/13 17:15:19 (15 months ago)
Author:
Ales Erjavec <ales.erjavec@…>
Branch:
default
Message:

Added colored/formated output for the stdout/err output, output thread safety.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • Orange/OrangeCanvas/application/outputview.py

    r11167 r11257  
    11""" 
    22""" 
    3  
     3from functools import wraps 
    44from PyQt4.QtGui import ( 
    5     QWidget, QPlainTextEdit, QVBoxLayout, QTextCursor 
     5    QWidget, QPlainTextEdit, QVBoxLayout, QTextCursor, QTextCharFormat, 
     6    QFont, QFontMetrics 
    67) 
    78 
    8 from PyQt4.QtCore import Qt 
    9  
    10  
    11 class OutputText(QWidget): 
     9from PyQt4.QtCore import Qt, QObject, QEvent, QCoreApplication 
     10from PyQt4.QtCore import pyqtSignal as Signal 
     11 
     12 
     13class OutputView(QWidget): 
    1214    def __init__(self, parent=None, **kwargs): 
    1315        QWidget.__init__(self, parent, **kwargs) 
     
    2527        self.__text.setFont(font) 
    2628 
     29        self.__currentCharFormat = self.__text.currentCharFormat() 
     30 
    2731        self.layout().addWidget(self.__text) 
    2832 
     33        metrics = QFontMetrics(font) 
     34        width = metrics.boundingRect("X").width() 
     35        self.resize(width * 80, width * 30) 
     36 
    2937    def setMaximumLines(self, lines): 
    30         """Set the maximum number of lines to keep displayed. 
     38        """ 
     39        Set the maximum number of lines to keep displayed. 
    3140        """ 
    3241        if self.__lines != lines: 
     
    3544 
    3645    def maximumLines(self): 
    37         """Return the maximum number of lines in the display. 
     46        """ 
     47        Return the maximum number of lines in the display. 
    3848        """ 
    3949        return self.__lines 
    4050 
    4151    def clear(self): 
    42         """Clear the displayed text. 
     52        """ 
     53        Clear the displayed text. 
    4354        """ 
    4455        self.__text.clear() 
    4556 
     57    def setCurrentCharFormat(self, charformat): 
     58        """Set the QTextCharFormat to be used when writing. 
     59        """ 
     60        if self.__currentCharFormat != charformat: 
     61            self.__currentCharFormat = charformat 
     62 
     63    def currentCharFormat(self): 
     64        return self.__currentCharFormat 
     65 
    4666    def toPlainText(self): 
    47         """Return the full contents of the output view. 
     67        """ 
     68        Return the full contents of the output view. 
    4869        """ 
    4970        return self.__text.toPlainText() 
     
    5273    def write(self, string): 
    5374        self.__text.moveCursor(QTextCursor.End, QTextCursor.MoveAnchor) 
     75        self.__text.setCurrentCharFormat(self.__currentCharFormat) 
     76 
    5477        self.__text.insertPlainText(string) 
    5578 
    5679    def writelines(self, lines): 
    57         for line in lines: 
    58             self.write(line) 
     80        self.write("".join(lines)) 
    5981 
    6082    def flush(self): 
    61         pass 
     83        QCoreApplication.flush() 
     84 
     85    def writeWithFormat(self, string, charformat): 
     86        self.__text.moveCursor(QTextCursor.End, QTextCursor.MoveAnchor) 
     87        self.__text.setCurrentCharFormat(charformat) 
     88        self.__text.insertPlainText(string) 
     89 
     90    def writelinesWithFormat(self, lines, charformat): 
     91        self.writeWithFormat("".join(lines), charformat) 
     92 
     93    def formated(self, color=None, background=None, weight=None, 
     94                 italic=None, underline=None, font=None): 
     95        """ 
     96        Return a formated file like object proxy. 
     97        """ 
     98        charformat = update_char_format( 
     99            self.currentCharFormat(), color, background, weight, 
     100            italic, underline, font 
     101        ) 
     102        return formater(self, charformat) 
     103 
     104 
     105def update_char_format(baseformat, color=None, background=None, weight=None, 
     106                       italic=None, underline=None, font=None): 
     107    """ 
     108    Return a copy of `baseformat` :class:`QTextCharFormat` with 
     109    updated color, weight, background and font properties. 
     110 
     111    """ 
     112    charformat = QTextCharFormat(baseformat) 
     113 
     114    if color is not None: 
     115        charformat.setForeground(color) 
     116 
     117    if background is not None: 
     118        charformat.setBackground(background) 
     119 
     120    if font is not None: 
     121        charformat.setFont(font) 
     122    else: 
     123        font = update_font(baseformat.font(), weight, italic, underline) 
     124        charformat.setFont(font) 
     125 
     126    return charformat 
     127 
     128 
     129def update_font(basefont, weight=None, italic=None, underline=None, 
     130                pixelSize=None, pointSize=None): 
     131    """ 
     132    Return a copy of `basefont` :class:`QFont` with updated properties. 
     133    """ 
     134    font = QFont(basefont) 
     135 
     136    if weight is not None: 
     137        font.setWeight(weight) 
     138 
     139    if italic is not None: 
     140        font.setItalic(italic) 
     141 
     142    if underline is not None: 
     143        font.setUnderline(underline) 
     144 
     145    if pixelSize is not None: 
     146        font.setPixelSize(pixelSize) 
     147 
     148    if pointSize is not None: 
     149        font.setPointSize(pointSize) 
     150 
     151    return font 
     152 
     153 
     154class formater(object): 
     155    def __init__(self, outputview, charformat): 
     156        self.outputview = outputview 
     157        self.charformat = charformat 
     158 
     159    def write(self, string): 
     160        self.outputview.writeWithFormat(string, self.charformat) 
     161 
     162    def writelines(self, lines): 
     163        self.outputview.writelines(lines, self.charformat) 
     164 
     165    def flush(self): 
     166        self.outputview.flush() 
     167 
     168    def formated(self, color=None, background=None, weight=None, 
     169                 italic=None, underline=None, font=None): 
     170        charformat = update_char_format(self.charformat, color, background, 
     171                                        weight, italic, underline, font) 
     172        return formater(self.outputview, charformat) 
     173 
     174    def __enter__(self): 
     175        return self 
     176 
     177    def __exit__(self, *args): 
     178        self.outputview = None 
     179        self.charformat = None 
     180 
     181 
     182class QueuedCallEvent(QEvent): 
     183    QueuedCall = QEvent.registerEventType() 
     184 
     185    def __init__(self, function, args, kwargs): 
     186        QEvent.__init__(self, QueuedCallEvent.QueuedCall) 
     187        self.function = function 
     188        self.args = args 
     189        self.kwargs = kwargs 
     190        self._result = None 
     191        self._exc_info = None 
     192        self._state = 0 
     193 
     194    def call(self): 
     195        try: 
     196            self._result = self.function(*self.args, **self.kwargs) 
     197            self._state = 1 
     198        except Exception, ex: 
     199            self._exc_info = (type(ex), ex.args, None) 
     200            raise 
     201 
     202    def result(self): 
     203        if self._state == 1: 
     204            return self._result 
     205        elif self._exc_info: 
     206            raise self._exc_info[0], self._exc_info[1] 
     207        else: 
     208            # Should this block, add timeout? 
     209            raise RuntimeError("Result not yet ready") 
     210 
     211    def isready(self): 
     212        return self._state == 1 or self._exc_info 
     213 
     214 
     215def queued(method): 
     216    """ 
     217    Run method from the event queue. 
     218    """ 
     219    @wraps(method) 
     220    def delay_method_call(self, *args, **kwargs): 
     221        event = QueuedCallEvent(method.__get__(self), args, kwargs) 
     222        QCoreApplication.postEvent(self, event) 
     223 
     224    return delay_method_call 
     225 
     226 
     227def queued_blocking(method): 
     228    """ 
     229    Run method from the event queue and wait until the event is processed. 
     230    Return the call's return value. 
     231 
     232    """ 
     233    @wraps(method) 
     234    def delay_method_call(self, *args, **kwargs): 
     235        event = QueuedCallEvent(method, args, kwargs) 
     236        QCoreApplication.postEvent(self, event) 
     237        QCoreApplication.flush() 
     238        return event.result() 
     239 
     240    return delay_method_call 
     241 
     242 
     243class TextStream(QObject): 
     244    stream = Signal(basestring) 
     245    flushed = Signal(basestring) 
     246 
     247    def __init__(self, parent=None): 
     248        QObject.__init__(self, parent) 
     249 
     250    @queued 
     251    def write(self, string): 
     252        self.stream.emit(string) 
     253 
     254    @queued 
     255    def writelines(self, lines): 
     256        self.stream.emit("".join(lines)) 
     257 
     258    @queued_blocking 
     259    def flush(self): 
     260        self.flushed.emit() 
     261 
     262    def customEvent(self, event): 
     263        if event.type() == QueuedCallEvent.QueuedCall: 
     264            event.call() 
     265            event.accept() 
Note: See TracChangeset for help on using the changeset viewer.