source: orange/Orange/OrangeCanvas/application/outputview.py @ 11268:ab6d28c3472d

Revision 11268:ab6d28c3472d, 8.1 KB checked in by Ales Erjavec <ales.erjavec@…>, 15 months ago (diff)

Fixed blocking 'delay_method_call'.

QCoreApplication.flush processed only platform specific events.

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