diff --git a/pyqtgraph/console/Console.py b/pyqtgraph/console/Console.py index ed4b7f08..23ae93d5 100644 --- a/pyqtgraph/console/Console.py +++ b/pyqtgraph/console/Console.py @@ -53,6 +53,7 @@ class ConsoleWidget(QtGui.QWidget): self.editor = editor self.multiline = None self.inCmd = False + self.frames = [] # stack frames to access when an item in the stack list is selected self.ui = template.Ui_Form() self.ui.setupUi(self) @@ -133,14 +134,14 @@ class ConsoleWidget(QtGui.QWidget): def globals(self): frame = self.currentFrame() if frame is not None and self.ui.runSelectedFrameCheck.isChecked(): - return self.currentFrame().tb_frame.f_globals + return self.currentFrame().f_globals else: return self.localNamespace def locals(self): frame = self.currentFrame() if frame is not None and self.ui.runSelectedFrameCheck.isChecked(): - return self.currentFrame().tb_frame.f_locals + return self.currentFrame().f_locals else: return self.localNamespace @@ -149,10 +150,7 @@ class ConsoleWidget(QtGui.QWidget): if self.currentTraceback is None: return None index = self.ui.exceptionStackList.currentRow() - tb = self.currentTraceback - for i in range(index): - tb = tb.tb_next - return tb + return self.frames[index] def execSingle(self, cmd): try: @@ -171,7 +169,6 @@ class ConsoleWidget(QtGui.QWidget): except: self.displayException() - def execMulti(self, nextLine): #self.stdout.write(nextLine+"\n") if nextLine.strip() != '': @@ -202,6 +199,10 @@ class ConsoleWidget(QtGui.QWidget): self.multiline = None def write(self, strn, html=False): + isGuiThread = QtCore.QThread.currentThread() == QtCore.QCoreApplication.instance().thread() + if not isGuiThread: + self.stdout.write(strn) + return self.output.moveCursor(QtGui.QTextCursor.End) if html: self.output.textCursor().insertHtml(strn) @@ -293,14 +294,6 @@ class ConsoleWidget(QtGui.QWidget): fileName = tb.tb_frame.f_code.co_filename subprocess.Popen(self.editor.format(fileName=fileName, lineNum=lineNum), shell=True) - - #def allExceptionsHandler(self, *args): - #self.exceptionHandler(*args) - - #def nextExceptionHandler(self, *args): - #self.ui.catchNextExceptionBtn.setChecked(False) - #self.exceptionHandler(*args) - def updateSysTrace(self): ## Install or uninstall sys.settrace handler @@ -319,7 +312,7 @@ class ConsoleWidget(QtGui.QWidget): else: sys.settrace(self.systrace) - def exceptionHandler(self, excType, exc, tb): + def exceptionHandler(self, excType, exc, tb, systrace=False): if self.ui.catchNextExceptionBtn.isChecked(): self.ui.catchNextExceptionBtn.setChecked(False) elif not self.ui.catchAllExceptionsBtn.isChecked(): @@ -330,13 +323,60 @@ class ConsoleWidget(QtGui.QWidget): excMessage = ''.join(traceback.format_exception_only(excType, exc)) self.ui.exceptionInfoLabel.setText(excMessage) + + if systrace: + # exceptions caught using systrace don't need the usual + # call stack + traceback handling + self.setStack(sys._getframe().f_back.f_back) + else: + self.setStack(frame=sys._getframe().f_back, tb=tb) + + def setStack(self, frame=None, tb=None): + """Display a call stack and exception traceback. + + This allows the user to probe the contents of any frame in the given stack. + + *frame* may either be a Frame instance or None, in which case the current + frame is retrieved from ``sys._getframe()``. + + If *tb* is provided then the frames in the traceback will be appended to + the end of the stack list. If *tb* is None, then sys.exc_info() will + be checked instead. + """ + if frame is None: + frame = sys._getframe().f_back + + if tb is None: + tb = sys.exc_info()[2] + self.ui.exceptionStackList.clear() + self.frames = [] + + # Build stack up to this point + for index, line in enumerate(traceback.extract_stack(frame)): + self.ui.exceptionStackList.addItem('File "%s", line %s, in %s()\n %s' % line) + while frame is not None: + self.frames.insert(0, frame) + frame = frame.f_back + + if tb is None: + return + + self.ui.exceptionStackList.addItem('-- exception caught here: --') + item = self.ui.exceptionStackList.item(self.ui.exceptionStackList.count()-1) + item.setBackground(QtGui.QBrush(QtGui.QColor(200, 200, 200))) + self.frames.append(None) + + # And finish the rest of the stack up to the exception for index, line in enumerate(traceback.extract_tb(tb)): self.ui.exceptionStackList.addItem('File "%s", line %s, in %s()\n %s' % line) - + while tb is not None: + self.frames.append(tb.tb_frame) + tb = tb.tb_next + def systrace(self, frame, event, arg): if event == 'exception' and self.checkException(*arg): - self.exceptionHandler(*arg) + self.exceptionHandler(*arg, systrace=True) return self.systrace def checkException(self, excType, exc, tb): diff --git a/pyqtgraph/console/template.ui b/pyqtgraph/console/template.ui index 1a672c5e..1dd752d9 100644 --- a/pyqtgraph/console/template.ui +++ b/pyqtgraph/console/template.ui @@ -86,7 +86,10 @@ 0 - + + 2 + + 0 @@ -95,7 +98,7 @@ false - Clear Exception + Clear Stack @@ -149,7 +152,7 @@ - Exception Info + Stack Trace diff --git a/pyqtgraph/console/template_pyqt.py b/pyqtgraph/console/template_pyqt.py index 354fb1d6..e5fc4619 100644 --- a/pyqtgraph/console/template_pyqt.py +++ b/pyqtgraph/console/template_pyqt.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'template.ui' # -# Created: Fri May 02 18:55:28 2014 +# Created: Wed Apr 08 16:28:53 2015 # by: PyQt4 UI code generator 4.10.4 # # WARNING! All changes made in this file will be lost! @@ -68,8 +68,9 @@ class Ui_Form(object): self.exceptionGroup = QtGui.QGroupBox(self.splitter) self.exceptionGroup.setObjectName(_fromUtf8("exceptionGroup")) self.gridLayout_2 = QtGui.QGridLayout(self.exceptionGroup) - self.gridLayout_2.setSpacing(0) self.gridLayout_2.setContentsMargins(-1, 0, -1, 0) + self.gridLayout_2.setHorizontalSpacing(2) + self.gridLayout_2.setVerticalSpacing(0) self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2")) self.clearExceptionBtn = QtGui.QPushButton(self.exceptionGroup) self.clearExceptionBtn.setEnabled(False) @@ -116,12 +117,12 @@ class Ui_Form(object): self.historyBtn.setText(_translate("Form", "History..", None)) self.exceptionBtn.setText(_translate("Form", "Exceptions..", None)) self.exceptionGroup.setTitle(_translate("Form", "Exception Handling", None)) - self.clearExceptionBtn.setText(_translate("Form", "Clear Exception", None)) + self.clearExceptionBtn.setText(_translate("Form", "Clear Stack", None)) self.catchAllExceptionsBtn.setText(_translate("Form", "Show All Exceptions", None)) self.catchNextExceptionBtn.setText(_translate("Form", "Show Next Exception", None)) self.onlyUncaughtCheck.setText(_translate("Form", "Only Uncaught Exceptions", None)) self.runSelectedFrameCheck.setText(_translate("Form", "Run commands in selected stack frame", None)) - self.exceptionInfoLabel.setText(_translate("Form", "Exception Info", None)) + self.exceptionInfoLabel.setText(_translate("Form", "Stack Trace", None)) self.label.setText(_translate("Form", "Filter (regex):", None)) from .CmdInput import CmdInput diff --git a/pyqtgraph/console/template_pyside.py b/pyqtgraph/console/template_pyside.py index 2db8ed95..36065afd 100644 --- a/pyqtgraph/console/template_pyside.py +++ b/pyqtgraph/console/template_pyside.py @@ -100,7 +100,7 @@ class Ui_Form(object): self.catchNextExceptionBtn.setText(QtGui.QApplication.translate("Form", "Show Next Exception", None, QtGui.QApplication.UnicodeUTF8)) self.onlyUncaughtCheck.setText(QtGui.QApplication.translate("Form", "Only Uncaught Exceptions", None, QtGui.QApplication.UnicodeUTF8)) self.runSelectedFrameCheck.setText(QtGui.QApplication.translate("Form", "Run commands in selected stack frame", None, QtGui.QApplication.UnicodeUTF8)) - self.exceptionInfoLabel.setText(QtGui.QApplication.translate("Form", "Exception Info", None, QtGui.QApplication.UnicodeUTF8)) - self.clearExceptionBtn.setText(QtGui.QApplication.translate("Form", "Clear Exception", None, QtGui.QApplication.UnicodeUTF8)) + self.exceptionInfoLabel.setText(QtGui.QApplication.translate("Form", "Stack Trace", None, QtGui.QApplication.UnicodeUTF8)) + self.clearExceptionBtn.setText(QtGui.QApplication.translate("Form", "Clear Stack", None, QtGui.QApplication.UnicodeUTF8)) from .CmdInput import CmdInput