Added interactive console widget:
- GUI interactive python prompt, useful in a variety of situations where python -i or ipython don't work very well. - Command history (requires a little help finding storage for this) - Exception catching, allowing the user to inspect local variables at any level within a stack trace (currently, this is done _without_ blocking the application)
This commit is contained in:
parent
a7d2118a99
commit
c7a78642fd
61
console/CmdInput.py
Normal file
61
console/CmdInput.py
Normal file
@ -0,0 +1,61 @@
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
class CmdInput(QtGui.QLineEdit):
|
||||
|
||||
sigExecuteCmd = QtCore.Signal(object)
|
||||
|
||||
def __init__(self, parent):
|
||||
QtGui.QLineEdit.__init__(self, parent)
|
||||
self.history = [""]
|
||||
self.ptr = 0
|
||||
#self.lastCmd = None
|
||||
#self.setMultiline(False)
|
||||
|
||||
def keyPressEvent(self, ev):
|
||||
#print "press:", ev.key(), QtCore.Qt.Key_Up, QtCore.Qt.Key_Down, QtCore.Qt.Key_Enter
|
||||
if ev.key() == QtCore.Qt.Key_Up and self.ptr < len(self.history) - 1:
|
||||
self.setHistory(self.ptr+1)
|
||||
ev.accept()
|
||||
return
|
||||
elif ev.key() == QtCore.Qt.Key_Down and self.ptr > 0:
|
||||
self.setHistory(self.ptr-1)
|
||||
ev.accept()
|
||||
return
|
||||
elif ev.key() == QtCore.Qt.Key_Return:
|
||||
self.execCmd()
|
||||
else:
|
||||
QtGui.QLineEdit.keyPressEvent(self, ev)
|
||||
self.history[0] = unicode(self.text())
|
||||
|
||||
def execCmd(self):
|
||||
cmd = unicode(self.text())
|
||||
if len(self.history) == 1 or cmd != self.history[1]:
|
||||
self.history.insert(1, cmd)
|
||||
#self.lastCmd = cmd
|
||||
self.history[0] = ""
|
||||
self.setHistory(0)
|
||||
self.sigExecuteCmd.emit(cmd)
|
||||
|
||||
def setHistory(self, num):
|
||||
self.ptr = num
|
||||
self.setText(self.history[self.ptr])
|
||||
|
||||
#def setMultiline(self, m):
|
||||
#height = QtGui.QFontMetrics(self.font()).lineSpacing()
|
||||
#if m:
|
||||
#self.setFixedHeight(height*5)
|
||||
#else:
|
||||
#self.setFixedHeight(height+15)
|
||||
#self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||
#self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||
|
||||
|
||||
#def sizeHint(self):
|
||||
#hint = QtGui.QPlainTextEdit.sizeHint(self)
|
||||
#height = QtGui.QFontMetrics(self.font()).lineSpacing()
|
||||
#hint.setHeight(height)
|
||||
#return hint
|
||||
|
||||
|
||||
|
||||
|
290
console/Console.py
Normal file
290
console/Console.py
Normal file
@ -0,0 +1,290 @@
|
||||
|
||||
from pyqtgraph.Qt import QtCore, QtGui
|
||||
import sys, re, os, time, traceback
|
||||
import pyqtgraph as pg
|
||||
import template
|
||||
import pyqtgraph.exceptionHandling as exceptionHandling
|
||||
import pickle
|
||||
|
||||
EDITOR = "pykate {fileName}:{lineNum}"
|
||||
|
||||
class ConsoleWidget(QtGui.QWidget):
|
||||
"""
|
||||
Widget displaying console output and accepting command input.
|
||||
Implements:
|
||||
|
||||
- eval python expressions / exec python statements
|
||||
- storable history of commands
|
||||
- exception handling allowing commands to be interpreted in the context of any level in the exception stack frame
|
||||
|
||||
Why not just use python in an interactive shell (or ipython) ? There are a few reasons:
|
||||
|
||||
- pyside does not yet allow Qt event processing and interactive shell at the same time
|
||||
- on some systems, typing in the console _blocks_ the qt event loop until the user presses enter. This can
|
||||
be baffling and frustrating to users since it would appear the program has frozen.
|
||||
- some terminals (eg windows cmd.exe) have notoriously unfriendly interfaces
|
||||
- ability to add extra features like exception stack introspection
|
||||
- ability to have multiple interactive prompts for remotely generated processes
|
||||
"""
|
||||
|
||||
def __init__(self, parent=None, namespace=None, historyFile=None, text=None, editor=None):
|
||||
"""
|
||||
============ ============================================================================
|
||||
Arguments:
|
||||
namespace dictionary containing the initial variables present in the default namespace
|
||||
historyFile optional file for storing command history
|
||||
text initial text to display in the console window
|
||||
editor optional string for invoking code editor (called when stack trace entries are
|
||||
double-clicked). May contain {fileName} and {lineNum} format keys. Example::
|
||||
|
||||
editorCommand --loadfile {fileName} --gotoline {lineNum}
|
||||
============ =============================================================================
|
||||
"""
|
||||
QtGui.QWidget.__init__(self, parent)
|
||||
if namespace is None:
|
||||
namespace = {}
|
||||
self.localNamespace = namespace
|
||||
self.editor = editor
|
||||
self.multiline = None
|
||||
self.inCmd = False
|
||||
|
||||
self.ui = template.Ui_Form()
|
||||
self.ui.setupUi(self)
|
||||
self.output = self.ui.output
|
||||
self.input = self.ui.input
|
||||
self.input.setFocus()
|
||||
|
||||
if text is not None:
|
||||
self.output.setPlainText(text)
|
||||
|
||||
self.historyFile = historyFile
|
||||
|
||||
history = self.loadHistory()
|
||||
if history is not None:
|
||||
self.input.history = [""] + history
|
||||
self.ui.historyList.addItems(history[::-1])
|
||||
self.ui.historyList.hide()
|
||||
self.ui.exceptionGroup.hide()
|
||||
|
||||
self.input.sigExecuteCmd.connect(self.runCmd)
|
||||
self.ui.historyBtn.toggled.connect(self.ui.historyList.setVisible)
|
||||
self.ui.historyList.itemClicked.connect(self.cmdSelected)
|
||||
self.ui.historyList.itemDoubleClicked.connect(self.cmdDblClicked)
|
||||
self.ui.exceptionBtn.toggled.connect(self.ui.exceptionGroup.setVisible)
|
||||
|
||||
self.ui.catchAllExceptionsBtn.toggled.connect(self.catchAllToggled)
|
||||
self.ui.catchNextExceptionBtn.toggled.connect(self.catchNextToggled)
|
||||
self.ui.clearExceptionBtn.clicked.connect(self.clearExceptionClicked)
|
||||
self.ui.exceptionStackList.itemClicked.connect(self.stackItemClicked)
|
||||
self.ui.exceptionStackList.itemDoubleClicked.connect(self.stackItemDblClicked)
|
||||
|
||||
self.exceptionHandlerRunning = False
|
||||
self.currentTraceback = None
|
||||
|
||||
def loadHistory(self):
|
||||
"""Return the list of previously-invoked command strings (or None)."""
|
||||
if self.historyFile is not None:
|
||||
return pickle.load(open(self.historyFile, 'rb'))
|
||||
|
||||
def saveHistory(self, history):
|
||||
"""Store the list of previously-invoked command strings."""
|
||||
if self.historyFile is not None:
|
||||
pickle.dump(open(self.historyFile, 'wb'), history)
|
||||
|
||||
def runCmd(self, cmd):
|
||||
#cmd = str(self.input.lastCmd)
|
||||
self.stdout = sys.stdout
|
||||
self.stderr = sys.stderr
|
||||
encCmd = re.sub(r'>', '>', re.sub(r'<', '<', cmd))
|
||||
encCmd = re.sub(r' ', ' ', encCmd)
|
||||
|
||||
self.ui.historyList.addItem(cmd)
|
||||
self.saveHistory(self.input.history[1:100])
|
||||
|
||||
try:
|
||||
sys.stdout = self
|
||||
sys.stderr = self
|
||||
if self.multiline is not None:
|
||||
self.write("<br><b>%s</b>\n"%encCmd, html=True)
|
||||
self.execMulti(cmd)
|
||||
else:
|
||||
self.write("<br><div style='background-color: #CCF'><b>%s</b>\n"%encCmd, html=True)
|
||||
self.inCmd = True
|
||||
self.execSingle(cmd)
|
||||
|
||||
if not self.inCmd:
|
||||
self.write("</div>\n", html=True)
|
||||
|
||||
finally:
|
||||
sys.stdout = self.stdout
|
||||
sys.stderr = self.stderr
|
||||
|
||||
sb = self.output.verticalScrollBar()
|
||||
sb.setValue(sb.maximum())
|
||||
sb = self.ui.historyList.verticalScrollBar()
|
||||
sb.setValue(sb.maximum())
|
||||
|
||||
def globals(self):
|
||||
frame = self.currentFrame()
|
||||
if frame is not None and self.ui.runSelectedFrameCheck.isChecked():
|
||||
return self.currentFrame().tb_frame.f_globals
|
||||
else:
|
||||
return globals()
|
||||
|
||||
def locals(self):
|
||||
frame = self.currentFrame()
|
||||
if frame is not None and self.ui.runSelectedFrameCheck.isChecked():
|
||||
return self.currentFrame().tb_frame.f_locals
|
||||
else:
|
||||
return self.localNamespace
|
||||
|
||||
def currentFrame(self):
|
||||
## Return the currently selected exception stack frame (or None if there is no exception)
|
||||
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
|
||||
|
||||
def execSingle(self, cmd):
|
||||
try:
|
||||
output = eval(cmd, self.globals(), self.locals())
|
||||
self.write(repr(output) + '\n')
|
||||
except SyntaxError:
|
||||
try:
|
||||
exec(cmd, self.globals(), self.locals())
|
||||
except SyntaxError as exc:
|
||||
if 'unexpected EOF' in exc.msg:
|
||||
self.multiline = cmd
|
||||
else:
|
||||
self.displayException()
|
||||
except:
|
||||
self.displayException()
|
||||
except:
|
||||
self.displayException()
|
||||
|
||||
|
||||
def execMulti(self, nextLine):
|
||||
self.stdout.write(nextLine+"\n")
|
||||
if nextLine.strip() != '':
|
||||
self.multiline += "\n" + nextLine
|
||||
return
|
||||
else:
|
||||
cmd = self.multiline
|
||||
|
||||
try:
|
||||
output = eval(cmd, self.globals(), self.locals())
|
||||
self.write(str(output) + '\n')
|
||||
self.multiline = None
|
||||
except SyntaxError:
|
||||
try:
|
||||
exec(cmd, self.globals(), self.locals())
|
||||
self.multiline = None
|
||||
except SyntaxError as exc:
|
||||
if 'unexpected EOF' in exc.msg:
|
||||
self.multiline = cmd
|
||||
else:
|
||||
self.displayException()
|
||||
self.multiline = None
|
||||
except:
|
||||
self.displayException()
|
||||
self.multiline = None
|
||||
except:
|
||||
self.displayException()
|
||||
self.multiline = None
|
||||
|
||||
def write(self, strn, html=False):
|
||||
self.output.moveCursor(QtGui.QTextCursor.End)
|
||||
if html:
|
||||
self.output.textCursor().insertHtml(strn)
|
||||
else:
|
||||
if self.inCmd:
|
||||
self.inCmd = False
|
||||
self.output.textCursor().insertHtml("</div><br><div style='font-weight: normal; background-color: #FFF;'>")
|
||||
#self.stdout.write("</div><br><div style='font-weight: normal; background-color: #FFF;'>")
|
||||
self.output.insertPlainText(strn)
|
||||
#self.stdout.write(strn)
|
||||
|
||||
def displayException(self):
|
||||
tb = traceback.format_exc()
|
||||
lines = []
|
||||
indent = 4
|
||||
prefix = ''
|
||||
for l in tb.split('\n'):
|
||||
lines.append(" "*indent + prefix + l)
|
||||
self.write('\n'.join(lines))
|
||||
|
||||
def cmdSelected(self, item):
|
||||
index = -(self.ui.historyList.row(item)+1)
|
||||
self.input.setHistory(index)
|
||||
self.input.setFocus()
|
||||
|
||||
def cmdDblClicked(self, item):
|
||||
index = -(self.ui.historyList.row(item)+1)
|
||||
self.input.setHistory(index)
|
||||
self.input.execCmd()
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
def catchAllToggled(self, b):
|
||||
if b:
|
||||
self.ui.catchNextExceptionBtn.setChecked(False)
|
||||
exceptionHandling.register(self.allExceptionsHandler)
|
||||
else:
|
||||
exceptionHandling.unregister(self.allExceptionsHandler)
|
||||
|
||||
def catchNextToggled(self, b):
|
||||
if b:
|
||||
self.ui.catchAllExceptionsBtn.setChecked(False)
|
||||
exceptionHandling.register(self.nextExceptionHandler)
|
||||
else:
|
||||
exceptionHandling.unregister(self.nextExceptionHandler)
|
||||
|
||||
|
||||
def clearExceptionClicked(self):
|
||||
self.currentTraceback = None
|
||||
self.ui.exceptionInfoLabel.setText("[No current exception]")
|
||||
self.ui.exceptionStackList.clear()
|
||||
self.ui.clearExceptionBtn.setEnabled(False)
|
||||
|
||||
def stackItemClicked(self, item):
|
||||
pass
|
||||
|
||||
def stackItemDblClicked(self, item):
|
||||
global EDITOR
|
||||
tb = self.currentFrame()
|
||||
lineNum = tb.tb_lineno
|
||||
fileName = tb.tb_frame.f_code.co_filename
|
||||
os.system(EDITOR.format(fileName=fileName, lineNum=lineNum))
|
||||
|
||||
|
||||
def allExceptionsHandler(self, *args):
|
||||
self.exceptionHandler(*args)
|
||||
|
||||
def nextExceptionHandler(self, *args):
|
||||
self.ui.catchNextExceptionBtn.setChecked(False)
|
||||
self.exceptionHandler(*args)
|
||||
|
||||
def exceptionHandler(self, excType, exc, tb):
|
||||
self.ui.clearExceptionBtn.setEnabled(True)
|
||||
self.currentTraceback = tb
|
||||
|
||||
excMessage = ''.join(traceback.format_exception_only(excType, exc))
|
||||
self.ui.exceptionInfoLabel.setText(excMessage)
|
||||
self.ui.exceptionStackList.clear()
|
||||
for index, line in enumerate(traceback.extract_tb(tb)):
|
||||
self.ui.exceptionStackList.addItem('File "%s", line %s, in %s()\n %s' % line)
|
||||
|
||||
|
||||
def quit(self):
|
||||
if self.exceptionHandlerRunning:
|
||||
self.exitHandler = True
|
||||
try:
|
||||
exceptionHandling.unregister(self.exceptionHandler)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
1
console/__init__.py
Normal file
1
console/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from Console import ConsoleWidget
|
104
console/template.py
Normal file
104
console/template.py
Normal file
@ -0,0 +1,104 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file './lib/modules/Console/template.ui'
|
||||
#
|
||||
# Created: Wed Jun 6 12:46:30 2012
|
||||
# by: PyQt4 UI code generator 4.9.1
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
try:
|
||||
_fromUtf8 = QtCore.QString.fromUtf8
|
||||
except AttributeError:
|
||||
_fromUtf8 = lambda s: s
|
||||
|
||||
class Ui_Form(object):
|
||||
def setupUi(self, Form):
|
||||
Form.setObjectName(_fromUtf8("Form"))
|
||||
Form.resize(710, 497)
|
||||
self.gridLayout = QtGui.QGridLayout(Form)
|
||||
self.gridLayout.setMargin(0)
|
||||
self.gridLayout.setSpacing(0)
|
||||
self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
|
||||
self.splitter = QtGui.QSplitter(Form)
|
||||
self.splitter.setOrientation(QtCore.Qt.Vertical)
|
||||
self.splitter.setObjectName(_fromUtf8("splitter"))
|
||||
self.layoutWidget = QtGui.QWidget(self.splitter)
|
||||
self.layoutWidget.setObjectName(_fromUtf8("layoutWidget"))
|
||||
self.verticalLayout = QtGui.QVBoxLayout(self.layoutWidget)
|
||||
self.verticalLayout.setMargin(0)
|
||||
self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
|
||||
self.output = QtGui.QPlainTextEdit(self.layoutWidget)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily(_fromUtf8("Monospace"))
|
||||
self.output.setFont(font)
|
||||
self.output.setReadOnly(True)
|
||||
self.output.setObjectName(_fromUtf8("output"))
|
||||
self.verticalLayout.addWidget(self.output)
|
||||
self.horizontalLayout = QtGui.QHBoxLayout()
|
||||
self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
|
||||
self.input = CmdInput(self.layoutWidget)
|
||||
self.input.setObjectName(_fromUtf8("input"))
|
||||
self.horizontalLayout.addWidget(self.input)
|
||||
self.historyBtn = QtGui.QPushButton(self.layoutWidget)
|
||||
self.historyBtn.setCheckable(True)
|
||||
self.historyBtn.setObjectName(_fromUtf8("historyBtn"))
|
||||
self.horizontalLayout.addWidget(self.historyBtn)
|
||||
self.exceptionBtn = QtGui.QPushButton(self.layoutWidget)
|
||||
self.exceptionBtn.setCheckable(True)
|
||||
self.exceptionBtn.setObjectName(_fromUtf8("exceptionBtn"))
|
||||
self.horizontalLayout.addWidget(self.exceptionBtn)
|
||||
self.verticalLayout.addLayout(self.horizontalLayout)
|
||||
self.historyList = QtGui.QListWidget(self.splitter)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily(_fromUtf8("Monospace"))
|
||||
self.historyList.setFont(font)
|
||||
self.historyList.setObjectName(_fromUtf8("historyList"))
|
||||
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.setObjectName(_fromUtf8("gridLayout_2"))
|
||||
self.catchNextExceptionBtn = QtGui.QPushButton(self.exceptionGroup)
|
||||
self.catchNextExceptionBtn.setCheckable(True)
|
||||
self.catchNextExceptionBtn.setObjectName(_fromUtf8("catchNextExceptionBtn"))
|
||||
self.gridLayout_2.addWidget(self.catchNextExceptionBtn, 0, 0, 1, 1)
|
||||
self.exceptionStackList = QtGui.QListWidget(self.exceptionGroup)
|
||||
self.exceptionStackList.setAlternatingRowColors(True)
|
||||
self.exceptionStackList.setObjectName(_fromUtf8("exceptionStackList"))
|
||||
self.gridLayout_2.addWidget(self.exceptionStackList, 2, 0, 1, 3)
|
||||
self.catchAllExceptionsBtn = QtGui.QPushButton(self.exceptionGroup)
|
||||
self.catchAllExceptionsBtn.setCheckable(True)
|
||||
self.catchAllExceptionsBtn.setObjectName(_fromUtf8("catchAllExceptionsBtn"))
|
||||
self.gridLayout_2.addWidget(self.catchAllExceptionsBtn, 0, 1, 1, 1)
|
||||
self.clearExceptionBtn = QtGui.QPushButton(self.exceptionGroup)
|
||||
self.clearExceptionBtn.setEnabled(False)
|
||||
self.clearExceptionBtn.setObjectName(_fromUtf8("clearExceptionBtn"))
|
||||
self.gridLayout_2.addWidget(self.clearExceptionBtn, 0, 2, 1, 1)
|
||||
self.runSelectedFrameCheck = QtGui.QCheckBox(self.exceptionGroup)
|
||||
self.runSelectedFrameCheck.setChecked(True)
|
||||
self.runSelectedFrameCheck.setObjectName(_fromUtf8("runSelectedFrameCheck"))
|
||||
self.gridLayout_2.addWidget(self.runSelectedFrameCheck, 3, 0, 1, 3)
|
||||
self.exceptionInfoLabel = QtGui.QLabel(self.exceptionGroup)
|
||||
self.exceptionInfoLabel.setObjectName(_fromUtf8("exceptionInfoLabel"))
|
||||
self.gridLayout_2.addWidget(self.exceptionInfoLabel, 1, 0, 1, 3)
|
||||
self.gridLayout.addWidget(self.splitter, 0, 0, 1, 1)
|
||||
|
||||
self.retranslateUi(Form)
|
||||
QtCore.QMetaObject.connectSlotsByName(Form)
|
||||
|
||||
def retranslateUi(self, Form):
|
||||
Form.setWindowTitle(QtGui.QApplication.translate("Form", "Form", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.historyBtn.setText(QtGui.QApplication.translate("Form", "History..", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.exceptionBtn.setText(QtGui.QApplication.translate("Form", "Exceptions..", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.exceptionGroup.setTitle(QtGui.QApplication.translate("Form", "Exception Handling", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.catchNextExceptionBtn.setText(QtGui.QApplication.translate("Form", "Catch Next Exception", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.catchAllExceptionsBtn.setText(QtGui.QApplication.translate("Form", "Catch All Exceptions", None, QtGui.QApplication.UnicodeUTF8))
|
||||
self.clearExceptionBtn.setText(QtGui.QApplication.translate("Form", "Clear Exception", 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))
|
||||
|
||||
from CmdInput import CmdInput
|
161
console/template.ui
Normal file
161
console/template.ui
Normal file
@ -0,0 +1,161 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>710</width>
|
||||
<height>497</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="output">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Monospace</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="CmdInput" name="input"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="historyBtn">
|
||||
<property name="text">
|
||||
<string>History..</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="exceptionBtn">
|
||||
<property name="text">
|
||||
<string>Exceptions..</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QListWidget" name="historyList">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Monospace</family>
|
||||
</font>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QGroupBox" name="exceptionGroup">
|
||||
<property name="title">
|
||||
<string>Exception Handling</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QPushButton" name="catchNextExceptionBtn">
|
||||
<property name="text">
|
||||
<string>Catch Next Exception</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="3">
|
||||
<widget class="QListWidget" name="exceptionStackList">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="catchAllExceptionsBtn">
|
||||
<property name="text">
|
||||
<string>Catch All Exceptions</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QPushButton" name="clearExceptionBtn">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Clear Exception</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="3">
|
||||
<widget class="QCheckBox" name="runSelectedFrameCheck">
|
||||
<property name="text">
|
||||
<string>Run commands in selected stack frame</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="3">
|
||||
<widget class="QLabel" name="exceptionInfoLabel">
|
||||
<property name="text">
|
||||
<string>Exception Info</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>CmdInput</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>CmdInput</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
28
examples/ConsoleWidget.py
Normal file
28
examples/ConsoleWidget.py
Normal file
@ -0,0 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import initExample ## Add path to library (just for examples; you do not need this)
|
||||
|
||||
import pyqtgraph as pg
|
||||
from pyqtgraph.Qt import QtCore, QtGui
|
||||
import numpy as np
|
||||
import pyqtgraph.console
|
||||
|
||||
app = pg.mkQApp()
|
||||
|
||||
## build an initial namespace for console commands to be executed in (this is optional;
|
||||
## the user can always import these modules manually)
|
||||
namespace = {'pg': pg, 'np': np}
|
||||
|
||||
## initial text to display in the console
|
||||
text = """
|
||||
This is an interactive python console. The numpy and pyqtgraph modules have already been imported
|
||||
as 'np' and 'pg'.
|
||||
|
||||
Go, play.
|
||||
"""
|
||||
c = pyqtgraph.console.ConsoleWidget(namespace=namespace, text=text)
|
||||
c.show()
|
||||
|
||||
## Start Qt event loop unless running in interactive mode or using pyside.
|
||||
import sys
|
||||
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
|
||||
QtGui.QApplication.instance().exec_()
|
90
exceptionHandling.py
Normal file
90
exceptionHandling.py
Normal file
@ -0,0 +1,90 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""This module installs a wrapper around sys.excepthook which allows multiple
|
||||
new exception handlers to be registered.
|
||||
|
||||
Optionally, the wrapper also stops exceptions from causing long-term storage
|
||||
of local stack frames. This has two major effects:
|
||||
- Unhandled exceptions will no longer cause memory leaks
|
||||
(If an exception occurs while a lot of data is present on the stack,
|
||||
such as when loading large files, the data would ordinarily be kept
|
||||
until the next exception occurs. We would rather release this memory
|
||||
as soon as possible.)
|
||||
- Some debuggers may have a hard time handling uncaught exceptions
|
||||
|
||||
The module also provides a callback mechanism allowing others to respond
|
||||
to exceptions.
|
||||
"""
|
||||
|
||||
import sys, time
|
||||
#from lib.Manager import logMsg
|
||||
import traceback
|
||||
#from log import *
|
||||
|
||||
#logging = False
|
||||
|
||||
callbacks = []
|
||||
clear_tracebacks = False
|
||||
|
||||
def register(fn):
|
||||
"""
|
||||
Register a callable to be invoked when there is an unhandled exception.
|
||||
The callback will be passed the output of sys.exc_info(): (exception type, exception, traceback)
|
||||
Multiple callbacks will be invoked in the order they were registered.
|
||||
"""
|
||||
callbacks.append(fn)
|
||||
|
||||
def unregister(fn):
|
||||
"""Unregister a previously registered callback."""
|
||||
callbacks.remove(fn)
|
||||
|
||||
def setTracebackClearing(clear=True):
|
||||
"""
|
||||
Enable or disable traceback clearing.
|
||||
By default, clearing is disabled and Python will indefinitely store unhandled exception stack traces.
|
||||
This function is provided since Python's default behavior can cause unexpected retention of
|
||||
large memory-consuming objects.
|
||||
"""
|
||||
global clear_tracebacks
|
||||
clear_tracebacks = clear
|
||||
|
||||
class ExceptionHandler:
|
||||
def __call__(self, *args):
|
||||
## call original exception handler first (prints exception)
|
||||
global original_excepthook, callbacks, clear_tracebacks
|
||||
print "=====", time.strftime("%Y.%m.%d %H:%m:%S", time.localtime(time.time())), "====="
|
||||
ret = original_excepthook(*args)
|
||||
|
||||
for cb in callbacks:
|
||||
try:
|
||||
cb(*args)
|
||||
except:
|
||||
print " --------------------------------------------------------------"
|
||||
print " Error occurred during exception callback", cb
|
||||
print " --------------------------------------------------------------"
|
||||
traceback.print_exception(*sys.exc_info())
|
||||
|
||||
|
||||
## Clear long-term storage of last traceback to prevent memory-hogging.
|
||||
## (If an exception occurs while a lot of data is present on the stack,
|
||||
## such as when loading large files, the data would ordinarily be kept
|
||||
## until the next exception occurs. We would rather release this memory
|
||||
## as soon as possible.)
|
||||
if clear_tracebacks is True:
|
||||
sys.last_traceback = None
|
||||
|
||||
def implements(self, interface=None):
|
||||
## this just makes it easy for us to detect whether an ExceptionHook is already installed.
|
||||
if interface is None:
|
||||
return ['ExceptionHandler']
|
||||
else:
|
||||
return interface == 'ExceptionHandler'
|
||||
|
||||
|
||||
|
||||
## replace built-in excepthook only if this has not already been done
|
||||
if not (hasattr(sys.excepthook, 'implements') and sys.excepthook.implements('ExceptionHandler')):
|
||||
original_excepthook = sys.excepthook
|
||||
sys.excepthook = ExceptionHandler()
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user