source: orange/Orange/OrangeCanvas/application/outputview.py @ 11270:487bf7bfdcf6

Revision 11270:487bf7bfdcf6, 8.6 KB checked in by Ales Erjavec <ales.erjavec@…>, 15 months ago (diff)

Fixed default output dock size when first shown/undocked.

Line 
1"""
2"""
3import sys
4import traceback
5
6from functools import wraps
7from PyQt4.QtGui import (
8    QWidget, QPlainTextEdit, QVBoxLayout, QTextCursor, QTextCharFormat,
9    QFont, QSizePolicy
10)
11
12from PyQt4.QtCore import Qt, QObject, QEvent, QCoreApplication, QThread, QSize
13from PyQt4.QtCore import pyqtSignal as Signal
14
15
16class TerminalView(QPlainTextEdit):
17    def __init__(self, *args, **kwargs):
18        QPlainTextEdit.__init__(self, *args, **kwargs)
19        self.setFrameStyle(QPlainTextEdit.NoFrame)
20        self.setTextInteractionFlags(Qt.TextBrowserInteraction)
21        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
22        self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
23
24        font = self.font()
25        font.setStyleHint(QFont.Monospace)
26        font.setFamily("Monaco")
27        font.setPointSize(12)
28        self.setFont(font)
29
30    def sizeHint(self):
31        metrics = self.fontMetrics()
32        width = metrics.boundingRect("_" * 81).width()
33        height = metrics.lineSpacing()
34        scroll_width = self.verticalScrollBar().width()
35        size = QSize(width + scroll_width, height * 25)
36        return size
37
38
39class OutputView(QWidget):
40    def __init__(self, parent=None, **kwargs):
41        QWidget.__init__(self, parent, **kwargs)
42
43        self.__lines = 5000
44
45        self.setLayout(QVBoxLayout())
46        self.layout().setContentsMargins(0, 0, 0, 0)
47
48        self.__text = TerminalView()
49
50        self.__currentCharFormat = self.__text.currentCharFormat()
51
52        self.layout().addWidget(self.__text)
53
54    def setMaximumLines(self, lines):
55        """
56        Set the maximum number of lines to keep displayed.
57        """
58        if self.__lines != lines:
59            self.__lines = lines
60            self.__text.setMaximumBlockCount(lines)
61
62    def maximumLines(self):
63        """
64        Return the maximum number of lines in the display.
65        """
66        return self.__lines
67
68    def clear(self):
69        """
70        Clear the displayed text.
71        """
72        self.__text.clear()
73
74    def setCurrentCharFormat(self, charformat):
75        """Set the QTextCharFormat to be used when writing.
76        """
77        if self.__currentCharFormat != charformat:
78            self.__currentCharFormat = charformat
79
80    def currentCharFormat(self):
81        return self.__currentCharFormat
82
83    def toPlainText(self):
84        """
85        Return the full contents of the output view.
86        """
87        return self.__text.toPlainText()
88
89    # A file like interface.
90    def write(self, string):
91        self.__text.moveCursor(QTextCursor.End, QTextCursor.MoveAnchor)
92        self.__text.setCurrentCharFormat(self.__currentCharFormat)
93
94        self.__text.insertPlainText(string)
95
96    def writelines(self, lines):
97        self.write("".join(lines))
98
99    def flush(self):
100        QCoreApplication.flush()
101
102    def writeWithFormat(self, string, charformat):
103        self.__text.moveCursor(QTextCursor.End, QTextCursor.MoveAnchor)
104        self.__text.setCurrentCharFormat(charformat)
105        self.__text.insertPlainText(string)
106
107    def writelinesWithFormat(self, lines, charformat):
108        self.writeWithFormat("".join(lines), charformat)
109
110    def formated(self, color=None, background=None, weight=None,
111                 italic=None, underline=None, font=None):
112        """
113        Return a formated file like object proxy.
114        """
115        charformat = update_char_format(
116            self.currentCharFormat(), color, background, weight,
117            italic, underline, font
118        )
119        return formater(self, charformat)
120
121
122def update_char_format(baseformat, color=None, background=None, weight=None,
123                       italic=None, underline=None, font=None):
124    """
125    Return a copy of `baseformat` :class:`QTextCharFormat` with
126    updated color, weight, background and font properties.
127
128    """
129    charformat = QTextCharFormat(baseformat)
130
131    if color is not None:
132        charformat.setForeground(color)
133
134    if background is not None:
135        charformat.setBackground(background)
136
137    if font is not None:
138        charformat.setFont(font)
139    else:
140        font = update_font(baseformat.font(), weight, italic, underline)
141        charformat.setFont(font)
142
143    return charformat
144
145
146def update_font(basefont, weight=None, italic=None, underline=None,
147                pixelSize=None, pointSize=None):
148    """
149    Return a copy of `basefont` :class:`QFont` with updated properties.
150    """
151    font = QFont(basefont)
152
153    if weight is not None:
154        font.setWeight(weight)
155
156    if italic is not None:
157        font.setItalic(italic)
158
159    if underline is not None:
160        font.setUnderline(underline)
161
162    if pixelSize is not None:
163        font.setPixelSize(pixelSize)
164
165    if pointSize is not None:
166        font.setPointSize(pointSize)
167
168    return font
169
170
171class formater(object):
172    def __init__(self, outputview, charformat):
173        self.outputview = outputview
174        self.charformat = charformat
175
176    def write(self, string):
177        self.outputview.writeWithFormat(string, self.charformat)
178
179    def writelines(self, lines):
180        self.outputview.writelines(lines, self.charformat)
181
182    def flush(self):
183        self.outputview.flush()
184
185    def formated(self, color=None, background=None, weight=None,
186                 italic=None, underline=None, font=None):
187        charformat = update_char_format(self.charformat, color, background,
188                                        weight, italic, underline, font)
189        return formater(self.outputview, charformat)
190
191    def __enter__(self):
192        return self
193
194    def __exit__(self, *args):
195        self.outputview = None
196        self.charformat = None
197
198
199class QueuedCallEvent(QEvent):
200    QueuedCall = QEvent.registerEventType()
201
202    def __init__(self, function, args, kwargs):
203        QEvent.__init__(self, QueuedCallEvent.QueuedCall)
204        self.function = function
205        self.args = args
206        self.kwargs = kwargs
207        self._result = None
208        self._exc_info = None
209        self._state = 0
210
211    def call(self):
212        try:
213            self._result = self.function(*self.args, **self.kwargs)
214            self._state = 1
215        except Exception, ex:
216            self._exc_info = (type(ex), ex.args, None)
217            raise
218
219    def result(self):
220        if self._state == 1:
221            return self._result
222        elif self._exc_info:
223            raise self._exc_info[0], self._exc_info[1]
224        else:
225            # Should this block, add timeout?
226            raise RuntimeError("Result not yet ready")
227
228    def isready(self):
229        return self._state == 1 or self._exc_info
230
231
232def queued(method):
233    """
234    Run method from the event queue.
235    """
236    @wraps(method)
237    def delay_method_call(self, *args, **kwargs):
238        event = QueuedCallEvent(method.__get__(self), args, kwargs)
239        QCoreApplication.postEvent(self, event)
240
241    return delay_method_call
242
243
244def queued_blocking(method):
245    """
246    Run method from the event queue and wait until the event is processed.
247    Return the call's return value.
248
249    """
250    @wraps(method)
251    def delay_method_call(self, *args, **kwargs):
252        event = QueuedCallEvent(method, (self,) + args, kwargs)
253        QCoreApplication.postEvent(self, event)
254        QCoreApplication.sendPostedEvents()
255        return event.result()
256
257    return delay_method_call
258
259
260class TextStream(QObject):
261    stream = Signal(basestring)
262    flushed = Signal()
263
264    def __init__(self, parent=None):
265        QObject.__init__(self, parent)
266
267    @queued
268    def write(self, string):
269        self.stream.emit(string)
270
271    @queued
272    def writelines(self, lines):
273        self.stream.emit("".join(lines))
274
275    @queued_blocking
276    def flush(self):
277        self.flushed.emit()
278
279    def customEvent(self, event):
280        if event.type() == QueuedCallEvent.QueuedCall:
281            event.call()
282            event.accept()
283
284
285class ExceptHook(QObject):
286    handledException = Signal()
287
288    def __init__(self, parent=None, stream=None):
289        QObject.__init__(self, parent)
290        self.stream = stream
291
292    def __call__(self, exc_type, exc_value, tb):
293        text = traceback.format_exception(exc_type, exc_value, tb)
294        separator = "-" * 80 + "\n"
295        if QThread.currentThread() != QCoreApplication.instance().thread():
296            header = exc_type.__name__ + " (in non GUI thread)"
297        else:
298            header = exc_type.__name__
299
300        header_fmt = "%%%is\n"
301        if tb:
302            header += (header_fmt % (80 - len(header))) % text[0].strip()
303            del text[0]
304        else:
305            header
306
307        if self.stream is None:
308            stream = sys.stderr
309        else:
310            stream = self.stream
311
312        stream.writelines([separator, header] + text)
313
314        self.handledException.emit()
Note: See TracBrowser for help on using the repository browser.