source: orange/orange/OrangeCanvas/orngOutput.py @ 8042:ffcb93bc9028

Revision 8042:ffcb93bc9028, 8.4 KB checked in by markotoplak, 3 years ago (diff)

Hierarchical clustering: also catch RuntimeError when importing matplotlib (or the documentation could not be built on server).

Line 
1# Author: Gregor Leban (gregor.leban@fri.uni-lj.si)
2# Description:
3#     print system output and exceptions into a window. Enables copy/paste
4#
5from PyQt4.QtCore import *
6from PyQt4.QtGui import *
7import sys
8import string
9from time import localtime
10import traceback
11import os.path, os
12
13try:
14    __IPYTHON__  #We are running orange from ipython - it already has redirected sys.stdout
15    __DISABLE_OUTPUT__ = True
16except NameError:
17    __DISABLE_OUTPUT__ = False
18
19def thread_safe_queue(func):
20    """
21    """
22    from functools import wraps, partial
23    @wraps(func)
24    def safe_wrapper(self, *args, **kwargs):
25        if not hasattr(self, "_thread_safe_thread"): 
26            self._thread_safe_thread = self.thread()
27        if QThread.currentThread() is not self._thread_safe_thread:
28            print >> sys.stderr, "Calling", func, "with", args, kwargs, "from the wrong thread.", "Queuing the call!"
29            QMetaObject.invokeMethod(self, "queuedInvoke", Qt.QueuedConnection, Q_ARG("PyQt_PyObject", partial(safe_wrapper, self, *args, **kwargs)))
30        else:
31            return func(self, *args, **kwargs)
32           
33    return safe_wrapper
34
35class OutputWindow(QDialog):
36    def __init__(self, canvasDlg, *args):
37        QDialog.__init__(self, None, Qt.Window)
38        self.canvasDlg = canvasDlg
39
40        self.textOutput = QPlainTextEdit(self)
41        self.textOutput.setReadOnly(1)
42#        self.textOutput.zoomIn(1)
43
44        self.setLayout(QVBoxLayout())
45        self.layout().addWidget(self.textOutput)
46        self.layout().setMargin(2)
47        self.setWindowTitle("Output Window")
48        self.setWindowIcon(QIcon(canvasDlg.outputPix))
49
50        self.defaultExceptionHandler = sys.excepthook
51        self.defaultSysOutHandler = sys.stdout
52
53        self.logFile = open(os.path.join(canvasDlg.canvasSettingsDir, "outputLog.html"), "w") # create the log file
54        self.unfinishedText = ""
55
56        w = h = 500
57        if canvasDlg.settings.has_key("outputWindowPos"):
58            desktop = qApp.desktop()
59            deskH = desktop.availableGeometry(self).height()
60            deskW = desktop.availableGeometry(self).width()
61            w, h, x, y = canvasDlg.settings["outputWindowPos"]
62            if x >= 0 and y >= 0 and deskH >= y+h and deskW >= x+w: 
63                self.move(QPoint(x, y))
64            else: 
65                w = h = 500
66        self.resize(w, h)
67           
68        self.hide()
69
70    def stopCatching(self):
71        self.catchException(0)
72        self.catchOutput(0)
73
74    def showEvent(self, ce):
75        ce.accept()
76        QDialog.showEvent(self, ce)
77        settings = self.canvasDlg.settings
78        if settings.has_key("outputWindowPos"):
79            w, h, x, y = settings["outputWindowPos"]
80            self.move(QPoint(x, y))
81            self.resize(w, h)
82       
83    def hideEvent(self, ce):
84        self.canvasDlg.settings["outputWindowPos"] = (self.width(), self.height(), self.pos().x(), self.pos().y())
85        ce.accept()
86        QDialog.hideEvent(self, ce)
87               
88    def closeEvent(self,ce):
89        self.canvasDlg.settings["outputWindowPos"] = (self.width(), self.height(), self.pos().x(), self.pos().y())
90        if getattr(self.canvasDlg, "canvasIsClosing", 0):
91            self.catchException(0)
92            self.catchOutput(0)
93            ce.accept()
94            QDialog.closeEvent(self, ce)
95        else:
96            self.hide()
97
98    def catchException(self, catch):
99        if __DISABLE_OUTPUT__:
100            return
101        if catch: sys.excepthook = self.exceptionHandler
102        else:     sys.excepthook = self.defaultExceptionHandler
103
104    def catchOutput(self, catch):
105        if __DISABLE_OUTPUT__:
106            return 
107        if catch:    sys.stdout = self
108        else:         sys.stdout = self.defaultSysOutHandler
109
110    def clear(self):
111        self.textOutput.clear()
112
113    # print text produced by warning and error widget calls
114    def widgetEvents(self, text, eventVerbosity = 1):
115        if self.canvasDlg.settings["outputVerbosity"] >= eventVerbosity:
116            if text != None:
117                self.write(str(text))
118            self.canvasDlg.setStatusBarEvent(QString(text))
119
120    # simple printing of text called by print calls
121    @thread_safe_queue
122    def write(self, text):
123        Text = self.getSafeString(text)
124        Text = Text.replace("\n", "<br>\n")   # replace new line characters with <br> otherwise they don't get shown correctly in html output
125#        text = "<nobr>" + text + "</nobr>"
126
127        if self.canvasDlg.settings["focusOnCatchOutput"]:
128            self.canvasDlg.menuItemShowOutputWindow()
129
130        if self.canvasDlg.settings["writeLogFile"]:
131            #self.logFile.write(str(text) + "<br>\n")
132            self.logFile.write(Text)
133
134        cursor = QTextCursor(self.textOutput.textCursor())                # clear the current text selection so that
135        cursor.movePosition(QTextCursor.End, QTextCursor.MoveAnchor)      # the text will be appended to the end of the
136        self.textOutput.setTextCursor(cursor)                             # existing text
137#        if text == " ": self.textOutput.insertHtml("&nbsp;")
138#        else:           self.textOutput.insertHtml(Text)                                  # then append the text
139
140        self.textOutput.insertPlainText(text)
141       
142        cursor.movePosition(QTextCursor.End, QTextCursor.MoveAnchor)      # and then scroll down to the end of the text
143
144        self.textOutput.setTextCursor(cursor)
145
146#        self.textOutput.insertPlainText(text)
147
148        if Text[-1:] == "\n":
149            if self.canvasDlg.settings["printOutputInStatusBar"]:
150                self.canvasDlg.setStatusBarEvent(self.unfinishedText + text)
151            self.unfinishedText = ""
152        else:
153            self.unfinishedText += text
154
155    def writelines(self, lines):
156        for line in lines:
157            self.write(line)
158
159    def flush(self):
160        pass
161   
162    def getSafeString(self, s):
163        return str(s).replace("<", "&lt;").replace(">", "&gt;")
164
165    @thread_safe_queue
166    def exceptionHandler(self, type, value, tracebackInfo):
167        if self.canvasDlg.settings["focusOnCatchException"]:
168            self.canvasDlg.menuItemShowOutputWindow()
169
170        t = localtime()
171        text = "<nobr>Unhandled exception of type %s occured at %d:%02d:%02d:</nobr><br><nobr>Traceback:</nobr><br>\n" % ( self.getSafeString(type.__name__), t[3],t[4],t[5])
172
173        if self.canvasDlg.settings["printExceptionInStatusBar"]:
174            self.canvasDlg.setStatusBarEvent("Unhandled exception of type %s occured at %d:%02d:%02d. See output window for details." % ( str(type) , t[3],t[4],t[5]))
175
176        # TO DO:repair this code to shown full traceback. when 2 same errors occur, only the first one gets full traceback, the second one gets only 1 item
177        list = traceback.extract_tb(tracebackInfo, 10)
178        space = "&nbsp; "
179        totalSpace = space
180        for i in range(len(list)):
181            (file, line, funct, code) = list[i]
182            if code == None:
183                continue
184            (dir, filename) = os.path.split(file)
185            text += "<nobr>" + totalSpace + "File: <b>" + filename + "</b>, line %4d" %(line) + " in <b>%s</b></nobr><br>\n" % (self.getSafeString(funct))
186            text += "<nobr>" + totalSpace + "Code: " + code + "</nobr><br>\n"
187            totalSpace += space
188
189        lines = traceback.format_exception_only(type, value)
190        for line in lines[:-1]:
191            text += "<nobr>" + totalSpace + self.getSafeString(line) + "</nobr><br>\n"
192        text += "<nobr><b>" + totalSpace + self.getSafeString(lines[-1]) + "</b></nobr><br>\n"
193
194        cursor = QTextCursor(self.textOutput.textCursor())                # clear the current text selection so that
195        cursor.movePosition(QTextCursor.End, QTextCursor.MoveAnchor)      # the text will be appended to the end of the
196#        self.textOutput.setTextCursor(cursor)                             # existing text
197#        self.textOutput.insertHtml(text)                                  # then append the text
198        self.textOutput.appendHtml(text)
199        cursor.movePosition(QTextCursor.End, QTextCursor.MoveAnchor)      # and then scroll down to the end of the text
200        self.textOutput.setTextCursor(cursor)
201
202#        self.textOutput.appendPlainText(traceback.format_exc(10))
203
204        if self.canvasDlg.settings["writeLogFile"]:
205            self.logFile.write(str(text) + "<br>\n")
206           
207    @pyqtSignature("queuedInvoke(PyQt_PyObject)")
208    def queuedInvoke(self, func):
209        func()
Note: See TracBrowser for help on using the repository browser.