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