Updates merged from acq4:
- disabled opengl (performance issues in Qt 4.7) - numerous caching changes (disabled deviceCoordinateCache due to performance issues) - speed up loading large images in ImageView - bugfixes from Ingo Breßler - Transform rotation bugfix - Added debug module - Major performance enhancements for scatterplot, fixed point clicking issues ** API change for scatterplot click signals - Drawing on ImageItem is working well now - PlotItem downsampling no longer uses scipy.signal.resample (this was creating artifacts) - Fixed ViewBox behavior when aspect-locked
This commit is contained in:
parent
6783f4fa26
commit
4d846e2aad
149
GraphicsView.py
149
GraphicsView.py
@ -10,8 +10,8 @@ from PyQt4 import QtCore, QtGui, QtOpenGL, QtSvg
|
||||
#import time
|
||||
from Point import *
|
||||
#from vector import *
|
||||
import sys
|
||||
#import debug
|
||||
import sys, os
|
||||
import debug
|
||||
|
||||
class GraphicsView(QtGui.QGraphicsView):
|
||||
|
||||
@ -19,8 +19,9 @@ class GraphicsView(QtGui.QGraphicsView):
|
||||
sigMouseReleased = QtCore.Signal(object)
|
||||
sigSceneMouseMoved = QtCore.Signal(object)
|
||||
#sigRegionChanged = QtCore.Signal(object)
|
||||
lastFileDir = None
|
||||
|
||||
def __init__(self, parent=None, useOpenGL=True):
|
||||
def __init__(self, parent=None, useOpenGL=False):
|
||||
"""Re-implementation of QGraphicsView that removes scrollbars and allows unambiguous control of the
|
||||
viewed coordinate range. Also automatically creates a QGraphicsScene and a central QGraphicsWidget
|
||||
that is automatically scaled to the full view geometry.
|
||||
@ -38,34 +39,27 @@ class GraphicsView(QtGui.QGraphicsView):
|
||||
useOpenGL = False
|
||||
self.useOpenGL(useOpenGL)
|
||||
|
||||
palette = QtGui.QPalette()
|
||||
self.setCacheMode(self.CacheBackground)
|
||||
|
||||
brush = QtGui.QBrush(QtGui.QColor(0,0,0))
|
||||
brush.setStyle(QtCore.Qt.SolidPattern)
|
||||
palette.setBrush(QtGui.QPalette.Active,QtGui.QPalette.Base,brush)
|
||||
brush = QtGui.QBrush(QtGui.QColor(0,0,0))
|
||||
brush.setStyle(QtCore.Qt.SolidPattern)
|
||||
palette.setBrush(QtGui.QPalette.Inactive,QtGui.QPalette.Base,brush)
|
||||
brush = QtGui.QBrush(QtGui.QColor(244,244,244))
|
||||
brush.setStyle(QtCore.Qt.SolidPattern)
|
||||
palette.setBrush(QtGui.QPalette.Disabled,QtGui.QPalette.Base,brush)
|
||||
self.setPalette(palette)
|
||||
#self.setProperty("cursor",QtCore.QVariant(QtCore.Qt.ArrowCursor))
|
||||
self.setBackgroundBrush(brush)
|
||||
|
||||
self.setFocusPolicy(QtCore.Qt.StrongFocus)
|
||||
self.setFrameShape(QtGui.QFrame.NoFrame)
|
||||
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||
self.setTransformationAnchor(QtGui.QGraphicsView.NoAnchor)
|
||||
self.setResizeAnchor(QtGui.QGraphicsView.AnchorViewCenter)
|
||||
#self.setResizeAnchor(QtGui.QGraphicsView.NoAnchor)
|
||||
self.setViewportUpdateMode(QtGui.QGraphicsView.SmartViewportUpdate)
|
||||
self.setSceneRect(QtCore.QRectF(-1e10, -1e10, 2e10, 2e10))
|
||||
#self.setSceneRect(1, 1, 0, 0) ## Set an empty (but non-zero) scene rect so that the view doesn't try to automatically update for us.
|
||||
#self.setInteractive(False)
|
||||
self.setViewportUpdateMode(QtGui.QGraphicsView.MinimalViewportUpdate)
|
||||
|
||||
|
||||
#self.setSceneRect(QtCore.QRectF(-1e10, -1e10, 2e10, 2e10))
|
||||
|
||||
self.lockedViewports = []
|
||||
self.lastMousePos = None
|
||||
#self.setMouseTracking(False)
|
||||
self.aspectLocked = False
|
||||
self.yInverted = True
|
||||
#self.yInverted = True
|
||||
self.range = QtCore.QRectF(0, 0, 1, 1)
|
||||
self.autoPixelRange = True
|
||||
self.currentItem = None
|
||||
@ -86,7 +80,7 @@ class GraphicsView(QtGui.QGraphicsView):
|
||||
self.clickAccepted = False
|
||||
|
||||
#def paintEvent(self, *args):
|
||||
#prof = debug.Profiler('GraphicsView.paintEvent '+str(id(self)), disabled=True)
|
||||
#prof = debug.Profiler('GraphicsView.paintEvent '+str(id(self)), disabled=False)
|
||||
#QtGui.QGraphicsView.paintEvent(self, *args)
|
||||
#prof.finish()
|
||||
|
||||
@ -97,6 +91,7 @@ class GraphicsView(QtGui.QGraphicsView):
|
||||
self.currentItem = None
|
||||
self.sceneObj = None
|
||||
self.closed = True
|
||||
self.setViewport(None)
|
||||
|
||||
def useOpenGL(self, b=True):
|
||||
if b:
|
||||
@ -116,6 +111,7 @@ class GraphicsView(QtGui.QGraphicsView):
|
||||
self.scene().removeItem(self.centralWidget)
|
||||
self.centralWidget = item
|
||||
self.sceneObj.addItem(item)
|
||||
self.resizeEvent(None)
|
||||
|
||||
def addItem(self, *args):
|
||||
return self.scene().addItem(*args)
|
||||
@ -140,37 +136,43 @@ class GraphicsView(QtGui.QGraphicsView):
|
||||
self.updateMatrix()
|
||||
|
||||
def updateMatrix(self, propagate=True):
|
||||
#print "udpateMatrix:"
|
||||
translate = Point(self.range.center())
|
||||
if self.range.width() == 0 or self.range.height() == 0:
|
||||
return
|
||||
scale = Point(self.size().width()/self.range.width(), self.size().height()/self.range.height())
|
||||
|
||||
m = QtGui.QTransform()
|
||||
|
||||
## First center the viewport at 0
|
||||
self.resetMatrix()
|
||||
center = self.viewportTransform().inverted()[0].map(Point(self.width()/2., self.height()/2.))
|
||||
if self.yInverted:
|
||||
m.translate(center.x(), center.y())
|
||||
#print " inverted; translate", center.x(), center.y()
|
||||
else:
|
||||
m.translate(center.x(), -center.y())
|
||||
#print " not inverted; translate", center.x(), -center.y()
|
||||
|
||||
## Now scale and translate properly
|
||||
self.setSceneRect(self.range)
|
||||
if self.aspectLocked:
|
||||
scale = Point(scale.min())
|
||||
if not self.yInverted:
|
||||
scale = scale * Point(1, -1)
|
||||
m.scale(scale[0], scale[1])
|
||||
#print " scale:", scale
|
||||
st = translate
|
||||
m.translate(-st[0], -st[1])
|
||||
#print " translate:", st
|
||||
self.setTransform(m)
|
||||
self.currentScale = scale
|
||||
#self.emit(QtCore.SIGNAL('viewChanged'), self.range)
|
||||
self.fitInView(self.range, QtCore.Qt.KeepAspectRatio)
|
||||
else:
|
||||
self.fitInView(self.range, QtCore.Qt.IgnoreAspectRatio)
|
||||
|
||||
##print "udpateMatrix:"
|
||||
#translate = Point(self.range.center())
|
||||
#if self.range.width() == 0 or self.range.height() == 0:
|
||||
#return
|
||||
#scale = Point(self.size().width()/self.range.width(), self.size().height()/self.range.height())
|
||||
|
||||
#m = QtGui.QTransform()
|
||||
|
||||
### First center the viewport at 0
|
||||
#self.resetMatrix()
|
||||
#center = self.viewportTransform().inverted()[0].map(Point(self.width()/2., self.height()/2.))
|
||||
#if self.yInverted:
|
||||
#m.translate(center.x(), center.y())
|
||||
##print " inverted; translate", center.x(), center.y()
|
||||
#else:
|
||||
#m.translate(center.x(), -center.y())
|
||||
##print " not inverted; translate", center.x(), -center.y()
|
||||
|
||||
### Now scale and translate properly
|
||||
#if self.aspectLocked:
|
||||
#scale = Point(scale.min())
|
||||
#if not self.yInverted:
|
||||
#scale = scale * Point(1, -1)
|
||||
#m.scale(scale[0], scale[1])
|
||||
##print " scale:", scale
|
||||
#st = translate
|
||||
#m.translate(-st[0], -st[1])
|
||||
##print " translate:", st
|
||||
#self.setTransform(m)
|
||||
#self.currentScale = scale
|
||||
##self.emit(QtCore.SIGNAL('viewChanged'), self.range)
|
||||
self.sigRangeChanged.emit(self, self.range)
|
||||
|
||||
if propagate:
|
||||
@ -251,11 +253,11 @@ class GraphicsView(QtGui.QGraphicsView):
|
||||
r1.setBottom(r.bottom())
|
||||
self.setRange(r1, padding=[0, padding], propagate=False)
|
||||
|
||||
def invertY(self, invert=True):
|
||||
#if self.yInverted != invert:
|
||||
#self.scale[1] *= -1.
|
||||
self.yInverted = invert
|
||||
self.updateMatrix()
|
||||
#def invertY(self, invert=True):
|
||||
##if self.yInverted != invert:
|
||||
##self.scale[1] *= -1.
|
||||
#self.yInverted = invert
|
||||
#self.updateMatrix()
|
||||
|
||||
|
||||
def wheelEvent(self, ev):
|
||||
@ -347,7 +349,7 @@ class GraphicsView(QtGui.QGraphicsView):
|
||||
def mouseMoveEvent(self, ev):
|
||||
if self.lastMousePos is None:
|
||||
self.lastMousePos = Point(ev.pos())
|
||||
delta = Point(ev.pos()) - self.lastMousePos
|
||||
delta = Point(ev.pos() - self.lastMousePos)
|
||||
self.lastMousePos = Point(ev.pos())
|
||||
|
||||
QtGui.QGraphicsView.mouseMoveEvent(self, ev)
|
||||
@ -371,7 +373,8 @@ class GraphicsView(QtGui.QGraphicsView):
|
||||
self.sigRangeChanged.emit(self, self.range)
|
||||
|
||||
elif ev.buttons() in [QtCore.Qt.MidButton, QtCore.Qt.LeftButton]: ## Allow panning by left or mid button.
|
||||
tr = -delta / self.currentScale
|
||||
px = self.pixelSize()
|
||||
tr = -delta * px
|
||||
|
||||
self.translate(tr[0], tr[1])
|
||||
#self.emit(QtCore.SIGNAL('regionChanged(QRectF)'), self.range)
|
||||
@ -386,11 +389,28 @@ class GraphicsView(QtGui.QGraphicsView):
|
||||
#self.currentItem.mouseMoveEvent(pev)
|
||||
|
||||
|
||||
def pixelSize(self):
|
||||
"""Return vector with the length and width of one view pixel in scene coordinates"""
|
||||
p0 = Point(0,0)
|
||||
p1 = Point(1,1)
|
||||
tr = self.transform().inverted()[0]
|
||||
p01 = tr.map(p0)
|
||||
p11 = tr.map(p1)
|
||||
return Point(p11 - p01)
|
||||
|
||||
|
||||
def writeSvg(self, fileName=None):
|
||||
if fileName is None:
|
||||
fileName = str(QtGui.QFileDialog.getSaveFileName())
|
||||
self.fileDialog = QtGui.QFileDialog()
|
||||
self.fileDialog.setFileMode(QtGui.QFileDialog.AnyFile)
|
||||
self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
|
||||
if GraphicsView.lastFileDir is not None:
|
||||
self.fileDialog.setDirectory(GraphicsView.lastFileDir)
|
||||
self.fileDialog.show()
|
||||
self.fileDialog.fileSelected.connect(self.writeSvg)
|
||||
return
|
||||
fileName = str(fileName)
|
||||
GraphicsView.lastFileDir = os.path.split(fileName)[0]
|
||||
self.svg = QtSvg.QSvgGenerator()
|
||||
self.svg.setFileName(fileName)
|
||||
self.svg.setSize(self.size())
|
||||
@ -400,7 +420,16 @@ class GraphicsView(QtGui.QGraphicsView):
|
||||
|
||||
def writeImage(self, fileName=None):
|
||||
if fileName is None:
|
||||
fileName = str(QtGui.QFileDialog.getSaveFileName())
|
||||
self.fileDialog = QtGui.QFileDialog()
|
||||
self.fileDialog.setFileMode(QtGui.QFileDialog.AnyFile)
|
||||
self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
|
||||
if GraphicsView.lastFileDir is not None:
|
||||
self.fileDialog.setDirectory(GraphicsView.lastFileDir)
|
||||
self.fileDialog.show()
|
||||
self.fileDialog.fileSelected.connect(self.writePng)
|
||||
return
|
||||
fileName = str(fileName)
|
||||
GraphicsView.lastFileDir = os.path.split(fileName)[0]
|
||||
self.png = QtGui.QImage(self.size(), QtGui.QImage.Format_ARGB32)
|
||||
painter = QtGui.QPainter(self.png)
|
||||
rh = self.renderHints()
|
||||
|
39
ImageView.py
39
ImageView.py
@ -21,6 +21,7 @@ import sys
|
||||
#from numpy import ndarray
|
||||
import ptime
|
||||
import numpy as np
|
||||
import debug
|
||||
|
||||
from SignalProxy import proxyConnect
|
||||
|
||||
@ -53,7 +54,7 @@ class ImageView(QtGui.QWidget):
|
||||
self.ui.graphicsView.enableMouse(True)
|
||||
self.ui.graphicsView.autoPixelRange = False
|
||||
self.ui.graphicsView.setAspectLocked(True)
|
||||
self.ui.graphicsView.invertY()
|
||||
#self.ui.graphicsView.invertY()
|
||||
self.ui.graphicsView.enableMouse()
|
||||
|
||||
self.ticks = [t[0] for t in self.ui.gradientWidget.listTicks()]
|
||||
@ -209,12 +210,13 @@ class ImageView(QtGui.QWidget):
|
||||
key = self.keysPressed.keys()[0]
|
||||
if key == QtCore.Qt.Key_Right:
|
||||
self.play(20)
|
||||
self.lastPlayTime = ptime.time() + 0.2 ## 2ms wait before start
|
||||
self.jumpFrames(1)
|
||||
self.lastPlayTime = ptime.time() + 0.2 ## 2ms wait before start
|
||||
## This happens *after* jumpFrames, since it might take longer than 2ms
|
||||
elif key == QtCore.Qt.Key_Left:
|
||||
self.play(-20)
|
||||
self.lastPlayTime = ptime.time() + 0.2
|
||||
self.jumpFrames(-1)
|
||||
self.lastPlayTime = ptime.time() + 0.2
|
||||
elif key == QtCore.Qt.Key_Up:
|
||||
self.play(-100)
|
||||
elif key == QtCore.Qt.Key_Down:
|
||||
@ -340,6 +342,8 @@ class ImageView(QtGui.QWidget):
|
||||
axes: {'t':0, 'x':1, 'y':2, 'c':3}; Dictionary indicating the interpretation for each axis.
|
||||
This is only needed to override the default guess.
|
||||
"""
|
||||
prof = debug.Profiler('ImageView.setImage', disabled=True)
|
||||
|
||||
if not isinstance(img, np.ndarray):
|
||||
raise Exception("Image must be specified as ndarray.")
|
||||
self.image = img
|
||||
@ -356,6 +360,7 @@ class ImageView(QtGui.QWidget):
|
||||
#self.ui.timeSlider.setValue(0)
|
||||
#self.ui.normStartSlider.setValue(0)
|
||||
#self.ui.timeSlider.setMaximum(img.shape[0]-1)
|
||||
prof.mark('1')
|
||||
|
||||
if axes is None:
|
||||
if img.ndim == 2:
|
||||
@ -380,18 +385,23 @@ class ImageView(QtGui.QWidget):
|
||||
|
||||
for x in ['t', 'x', 'y', 'c']:
|
||||
self.axes[x] = self.axes.get(x, None)
|
||||
prof.mark('2')
|
||||
|
||||
self.imageDisp = None
|
||||
if autoLevels:
|
||||
|
||||
|
||||
if levels is None and autoLevels:
|
||||
self.autoLevels()
|
||||
if levels is not None:
|
||||
if levels is not None: ## this does nothing since getProcessedImage sets these values again.
|
||||
self.levelMax = levels[1]
|
||||
self.levelMin = levels[0]
|
||||
prof.mark('3')
|
||||
|
||||
self.currentIndex = 0
|
||||
self.updateImage()
|
||||
if self.ui.roiBtn.isChecked():
|
||||
self.roiChanged()
|
||||
prof.mark('4')
|
||||
|
||||
|
||||
if self.axes['t'] is not None:
|
||||
@ -412,17 +422,20 @@ class ImageView(QtGui.QWidget):
|
||||
s.setBounds([start, stop])
|
||||
#else:
|
||||
#self.ui.roiPlot.hide()
|
||||
prof.mark('5')
|
||||
|
||||
self.imageItem.resetTransform()
|
||||
if scale is not None:
|
||||
self.imageItem.scale(*scale)
|
||||
if scale is not None:
|
||||
if pos is not None:
|
||||
self.imageItem.setPos(*pos)
|
||||
prof.mark('6')
|
||||
|
||||
if autoRange:
|
||||
self.autoRange()
|
||||
self.roiClicked()
|
||||
|
||||
prof.mark('7')
|
||||
prof.finish()
|
||||
|
||||
def autoLevels(self):
|
||||
image = self.getProcessedImage()
|
||||
@ -445,10 +458,18 @@ class ImageView(QtGui.QWidget):
|
||||
if self.imageDisp is None:
|
||||
image = self.normalize(self.image)
|
||||
self.imageDisp = image
|
||||
self.levelMax = float(image.max())
|
||||
self.levelMin = float(image.min())
|
||||
self.levelMin, self.levelMax = map(float, ImageView.quickMinMax(self.imageDisp))
|
||||
return self.imageDisp
|
||||
|
||||
@staticmethod
|
||||
def quickMinMax(data):
|
||||
while data.size > 1e6:
|
||||
ax = np.argmax(data.shape)
|
||||
sl = [slice(None)] * data.ndim
|
||||
sl[ax] = slice(None, None, 2)
|
||||
data = data[sl]
|
||||
return data.min(), data.max()
|
||||
|
||||
def normalize(self, image):
|
||||
|
||||
if self.ui.normOffRadio.isChecked():
|
||||
|
@ -1,38 +1,44 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'ImageViewTemplate.ui'
|
||||
# Form implementation generated from reading ui file './lib/util/pyqtgraph/ImageViewTemplate.ui'
|
||||
#
|
||||
# Created: Sat Jul 17 13:05:44 2010
|
||||
# by: PyQt4 UI code generator 4.5.4
|
||||
# Created: Wed May 18 20:44:20 2011
|
||||
# by: PyQt4 UI code generator 4.8.3
|
||||
#
|
||||
# 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("Form")
|
||||
Form.setObjectName(_fromUtf8("Form"))
|
||||
Form.resize(726, 588)
|
||||
self.verticalLayout = QtGui.QVBoxLayout(Form)
|
||||
self.verticalLayout.setSpacing(0)
|
||||
self.verticalLayout.setMargin(0)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
|
||||
self.splitter = QtGui.QSplitter(Form)
|
||||
self.splitter.setOrientation(QtCore.Qt.Vertical)
|
||||
self.splitter.setObjectName("splitter")
|
||||
self.splitter.setObjectName(_fromUtf8("splitter"))
|
||||
self.layoutWidget = QtGui.QWidget(self.splitter)
|
||||
self.layoutWidget.setObjectName("layoutWidget")
|
||||
self.layoutWidget.setObjectName(_fromUtf8("layoutWidget"))
|
||||
self.gridLayout = QtGui.QGridLayout(self.layoutWidget)
|
||||
self.gridLayout.setMargin(0)
|
||||
self.gridLayout.setSpacing(0)
|
||||
self.gridLayout.setObjectName("gridLayout")
|
||||
self.gridLayout.setMargin(0)
|
||||
self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
|
||||
self.graphicsView = GraphicsView(self.layoutWidget)
|
||||
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
|
||||
sizePolicy.setHorizontalStretch(10)
|
||||
sizePolicy.setVerticalStretch(10)
|
||||
sizePolicy.setHeightForWidth(self.graphicsView.sizePolicy().hasHeightForWidth())
|
||||
self.graphicsView.setSizePolicy(sizePolicy)
|
||||
self.graphicsView.setObjectName("graphicsView")
|
||||
self.graphicsView.setObjectName(_fromUtf8("graphicsView"))
|
||||
self.gridLayout.addWidget(self.graphicsView, 1, 0, 3, 1)
|
||||
self.roiBtn = QtGui.QPushButton(self.layoutWidget)
|
||||
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed)
|
||||
@ -42,7 +48,7 @@ class Ui_Form(object):
|
||||
self.roiBtn.setSizePolicy(sizePolicy)
|
||||
self.roiBtn.setMaximumSize(QtCore.QSize(30, 16777215))
|
||||
self.roiBtn.setCheckable(True)
|
||||
self.roiBtn.setObjectName("roiBtn")
|
||||
self.roiBtn.setObjectName(_fromUtf8("roiBtn"))
|
||||
self.gridLayout.addWidget(self.roiBtn, 3, 3, 1, 1)
|
||||
self.gradientWidget = GradientWidget(self.layoutWidget)
|
||||
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding)
|
||||
@ -50,7 +56,7 @@ class Ui_Form(object):
|
||||
sizePolicy.setVerticalStretch(100)
|
||||
sizePolicy.setHeightForWidth(self.gradientWidget.sizePolicy().hasHeightForWidth())
|
||||
self.gradientWidget.setSizePolicy(sizePolicy)
|
||||
self.gradientWidget.setObjectName("gradientWidget")
|
||||
self.gradientWidget.setObjectName(_fromUtf8("gradientWidget"))
|
||||
self.gridLayout.addWidget(self.gradientWidget, 1, 3, 1, 1)
|
||||
self.normBtn = QtGui.QPushButton(self.layoutWidget)
|
||||
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed)
|
||||
@ -60,75 +66,75 @@ class Ui_Form(object):
|
||||
self.normBtn.setSizePolicy(sizePolicy)
|
||||
self.normBtn.setMaximumSize(QtCore.QSize(30, 16777215))
|
||||
self.normBtn.setCheckable(True)
|
||||
self.normBtn.setObjectName("normBtn")
|
||||
self.normBtn.setObjectName(_fromUtf8("normBtn"))
|
||||
self.gridLayout.addWidget(self.normBtn, 2, 3, 1, 1)
|
||||
self.normGroup = QtGui.QGroupBox(self.layoutWidget)
|
||||
self.normGroup.setObjectName("normGroup")
|
||||
self.normGroup.setObjectName(_fromUtf8("normGroup"))
|
||||
self.gridLayout_2 = QtGui.QGridLayout(self.normGroup)
|
||||
self.gridLayout_2.setMargin(0)
|
||||
self.gridLayout_2.setSpacing(0)
|
||||
self.gridLayout_2.setObjectName("gridLayout_2")
|
||||
self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2"))
|
||||
self.normSubtractRadio = QtGui.QRadioButton(self.normGroup)
|
||||
self.normSubtractRadio.setObjectName("normSubtractRadio")
|
||||
self.normSubtractRadio.setObjectName(_fromUtf8("normSubtractRadio"))
|
||||
self.gridLayout_2.addWidget(self.normSubtractRadio, 0, 2, 1, 1)
|
||||
self.normDivideRadio = QtGui.QRadioButton(self.normGroup)
|
||||
self.normDivideRadio.setChecked(False)
|
||||
self.normDivideRadio.setObjectName("normDivideRadio")
|
||||
self.normDivideRadio.setObjectName(_fromUtf8("normDivideRadio"))
|
||||
self.gridLayout_2.addWidget(self.normDivideRadio, 0, 1, 1, 1)
|
||||
self.label_5 = QtGui.QLabel(self.normGroup)
|
||||
font = QtGui.QFont()
|
||||
font.setWeight(75)
|
||||
font.setBold(True)
|
||||
self.label_5.setFont(font)
|
||||
self.label_5.setObjectName("label_5")
|
||||
self.label_5.setObjectName(_fromUtf8("label_5"))
|
||||
self.gridLayout_2.addWidget(self.label_5, 0, 0, 1, 1)
|
||||
self.label_3 = QtGui.QLabel(self.normGroup)
|
||||
font = QtGui.QFont()
|
||||
font.setWeight(75)
|
||||
font.setBold(True)
|
||||
self.label_3.setFont(font)
|
||||
self.label_3.setObjectName("label_3")
|
||||
self.label_3.setObjectName(_fromUtf8("label_3"))
|
||||
self.gridLayout_2.addWidget(self.label_3, 1, 0, 1, 1)
|
||||
self.label_4 = QtGui.QLabel(self.normGroup)
|
||||
font = QtGui.QFont()
|
||||
font.setWeight(75)
|
||||
font.setBold(True)
|
||||
self.label_4.setFont(font)
|
||||
self.label_4.setObjectName("label_4")
|
||||
self.label_4.setObjectName(_fromUtf8("label_4"))
|
||||
self.gridLayout_2.addWidget(self.label_4, 2, 0, 1, 1)
|
||||
self.normROICheck = QtGui.QCheckBox(self.normGroup)
|
||||
self.normROICheck.setObjectName("normROICheck")
|
||||
self.normROICheck.setObjectName(_fromUtf8("normROICheck"))
|
||||
self.gridLayout_2.addWidget(self.normROICheck, 1, 1, 1, 1)
|
||||
self.normXBlurSpin = QtGui.QDoubleSpinBox(self.normGroup)
|
||||
self.normXBlurSpin.setObjectName("normXBlurSpin")
|
||||
self.normXBlurSpin.setObjectName(_fromUtf8("normXBlurSpin"))
|
||||
self.gridLayout_2.addWidget(self.normXBlurSpin, 2, 2, 1, 1)
|
||||
self.label_8 = QtGui.QLabel(self.normGroup)
|
||||
self.label_8.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
|
||||
self.label_8.setObjectName("label_8")
|
||||
self.label_8.setObjectName(_fromUtf8("label_8"))
|
||||
self.gridLayout_2.addWidget(self.label_8, 2, 1, 1, 1)
|
||||
self.label_9 = QtGui.QLabel(self.normGroup)
|
||||
self.label_9.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
|
||||
self.label_9.setObjectName("label_9")
|
||||
self.label_9.setObjectName(_fromUtf8("label_9"))
|
||||
self.gridLayout_2.addWidget(self.label_9, 2, 3, 1, 1)
|
||||
self.normYBlurSpin = QtGui.QDoubleSpinBox(self.normGroup)
|
||||
self.normYBlurSpin.setObjectName("normYBlurSpin")
|
||||
self.normYBlurSpin.setObjectName(_fromUtf8("normYBlurSpin"))
|
||||
self.gridLayout_2.addWidget(self.normYBlurSpin, 2, 4, 1, 1)
|
||||
self.label_10 = QtGui.QLabel(self.normGroup)
|
||||
self.label_10.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
|
||||
self.label_10.setObjectName("label_10")
|
||||
self.label_10.setObjectName(_fromUtf8("label_10"))
|
||||
self.gridLayout_2.addWidget(self.label_10, 2, 5, 1, 1)
|
||||
self.normOffRadio = QtGui.QRadioButton(self.normGroup)
|
||||
self.normOffRadio.setChecked(True)
|
||||
self.normOffRadio.setObjectName("normOffRadio")
|
||||
self.normOffRadio.setObjectName(_fromUtf8("normOffRadio"))
|
||||
self.gridLayout_2.addWidget(self.normOffRadio, 0, 3, 1, 1)
|
||||
self.normTimeRangeCheck = QtGui.QCheckBox(self.normGroup)
|
||||
self.normTimeRangeCheck.setObjectName("normTimeRangeCheck")
|
||||
self.normTimeRangeCheck.setObjectName(_fromUtf8("normTimeRangeCheck"))
|
||||
self.gridLayout_2.addWidget(self.normTimeRangeCheck, 1, 3, 1, 1)
|
||||
self.normFrameCheck = QtGui.QCheckBox(self.normGroup)
|
||||
self.normFrameCheck.setObjectName("normFrameCheck")
|
||||
self.normFrameCheck.setObjectName(_fromUtf8("normFrameCheck"))
|
||||
self.gridLayout_2.addWidget(self.normFrameCheck, 1, 2, 1, 1)
|
||||
self.normTBlurSpin = QtGui.QDoubleSpinBox(self.normGroup)
|
||||
self.normTBlurSpin.setObjectName("normTBlurSpin")
|
||||
self.normTBlurSpin.setObjectName(_fromUtf8("normTBlurSpin"))
|
||||
self.gridLayout_2.addWidget(self.normTBlurSpin, 2, 6, 1, 1)
|
||||
self.gridLayout.addWidget(self.normGroup, 0, 0, 1, 4)
|
||||
self.roiPlot = PlotWidget(self.splitter)
|
||||
@ -138,7 +144,7 @@ class Ui_Form(object):
|
||||
sizePolicy.setHeightForWidth(self.roiPlot.sizePolicy().hasHeightForWidth())
|
||||
self.roiPlot.setSizePolicy(sizePolicy)
|
||||
self.roiPlot.setMinimumSize(QtCore.QSize(0, 40))
|
||||
self.roiPlot.setObjectName("roiPlot")
|
||||
self.roiPlot.setObjectName(_fromUtf8("roiPlot"))
|
||||
self.verticalLayout.addWidget(self.splitter)
|
||||
|
||||
self.retranslateUi(Form)
|
||||
|
40
PlotItem.py
40
PlotItem.py
@ -285,7 +285,7 @@ class PlotItem(QtGui.QGraphicsWidget):
|
||||
|
||||
def close(self):
|
||||
#print "delete", self
|
||||
## All this crap is needed to avoid PySide trouble.
|
||||
## Most of this crap is needed to avoid PySide trouble.
|
||||
## The problem seems to be whenever scene.clear() leads to deletion of widgets (either through proxies or qgraphicswidgets)
|
||||
## the solution is to manually remove all widgets before scene.clear() is called
|
||||
if self.ctrlMenu is None: ## already shut down
|
||||
@ -305,8 +305,10 @@ class PlotItem(QtGui.QGraphicsWidget):
|
||||
self.scales = None
|
||||
self.scene().removeItem(self.vb)
|
||||
self.vb = None
|
||||
for i in range(self.layout.count()):
|
||||
self.layout.removeAt(i)
|
||||
|
||||
## causes invalid index errors:
|
||||
#for i in range(self.layout.count()):
|
||||
#self.layout.removeAt(i)
|
||||
|
||||
for p in self.proxies:
|
||||
try:
|
||||
@ -708,6 +710,11 @@ class PlotItem(QtGui.QGraphicsWidget):
|
||||
|
||||
return curve
|
||||
|
||||
def scatterPlot(self, *args, **kargs):
|
||||
sp = ScatterPlotItem(*args, **kargs)
|
||||
self.addDataItem(sp)
|
||||
return sp
|
||||
|
||||
def addDataItem(self, item):
|
||||
self.addItem(item)
|
||||
self.dataItems.append(item)
|
||||
@ -1075,9 +1082,9 @@ class PlotItem(QtGui.QGraphicsWidget):
|
||||
mode = False
|
||||
return mode
|
||||
|
||||
#def wheelEvent(self, ev):
|
||||
# disables panning the whole scene by mousewheel
|
||||
#ev.accept()
|
||||
def wheelEvent(self, ev):
|
||||
# disables default panning the whole scene by mousewheel
|
||||
ev.accept()
|
||||
|
||||
def resizeEvent(self, ev):
|
||||
if self.ctrlBtn is None: ## already closed down
|
||||
@ -1168,16 +1175,21 @@ class PlotItem(QtGui.QGraphicsWidget):
|
||||
return c
|
||||
|
||||
def saveSvgClicked(self):
|
||||
self.fileDialog = QtGui.QFileDialog()
|
||||
fileName = QtGui.QFileDialog.getSaveFileName()
|
||||
self.writeSvg(fileName)
|
||||
|
||||
## QFileDialog seems to be broken under OSX
|
||||
#self.fileDialog = QtGui.QFileDialog()
|
||||
##if PlotItem.lastFileDir is not None:
|
||||
##self.fileDialog.setDirectory(PlotItem.lastFileDir)
|
||||
#self.fileDialog.setFileMode(QtGui.QFileDialog.AnyFile)
|
||||
#self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
|
||||
#if PlotItem.lastFileDir is not None:
|
||||
#self.fileDialog.setDirectory(PlotItem.lastFileDir)
|
||||
self.fileDialog.setFileMode(QtGui.QFileDialog.AnyFile)
|
||||
self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
|
||||
if PlotItem.lastFileDir is not None:
|
||||
self.fileDialog.setDirectory(PlotItem.lastFileDir)
|
||||
self.fileDialog.show()
|
||||
#QtCore.QObject.connect(self.fileDialog, QtCore.SIGNAL('fileSelected(const QString)'), self.writeSvg)
|
||||
self.fileDialog.fileSelected.connect(self.writeSvg)
|
||||
|
||||
#self.fileDialog.show()
|
||||
##QtCore.QObject.connect(self.fileDialog, QtCore.SIGNAL('fileSelected(const QString)'), self.writeSvg)
|
||||
#self.fileDialog.fileSelected.connect(self.writeSvg)
|
||||
|
||||
#def svgFileSelected(self, fileName):
|
||||
##PlotWidget.lastFileDir = os.path.split(fileName)[0]
|
||||
|
@ -11,7 +11,7 @@ import exceptions
|
||||
|
||||
class PlotWidget(GraphicsView):
|
||||
|
||||
sigRangeChanged = QtCore.Signal(object, object)
|
||||
#sigRangeChanged = QtCore.Signal(object, object) ## already defined in GraphicsView
|
||||
|
||||
"""Widget implementing a graphicsView with a single PlotItem inside."""
|
||||
def __init__(self, parent=None, **kargs):
|
||||
|
9
Point.py
9
Point.py
@ -88,9 +88,12 @@ class Point(QtCore.QPointF):
|
||||
|
||||
def _math_(self, op, x):
|
||||
#print "point math:", op
|
||||
try:
|
||||
return Point(getattr(QtCore.QPointF, op)(self, x))
|
||||
except:
|
||||
#try:
|
||||
#fn = getattr(QtCore.QPointF, op)
|
||||
#pt = fn(self, x)
|
||||
#print fn, pt, self, x
|
||||
#return Point(pt)
|
||||
#except AttributeError:
|
||||
x = Point(x)
|
||||
return Point(getattr(self[0], op)(x[0]), getattr(self[1], op)(x[1]))
|
||||
|
||||
|
67
Transform.py
67
Transform.py
@ -6,7 +6,8 @@ import numpy as np
|
||||
class Transform(QtGui.QTransform):
|
||||
"""Transform that can always be represented as a combination of 3 matrices: scale * rotate * translate
|
||||
|
||||
This transform always has 0 shear."""
|
||||
This transform always has 0 shear.
|
||||
"""
|
||||
def __init__(self, init=None):
|
||||
QtGui.QTransform.__init__(self)
|
||||
self.reset()
|
||||
@ -41,7 +42,8 @@ class Transform(QtGui.QTransform):
|
||||
|
||||
## detect flipped axes
|
||||
if dp2.angle(dp3) > 0:
|
||||
da = 180
|
||||
#da = 180
|
||||
da = 0
|
||||
sy = -1.0
|
||||
else:
|
||||
da = 0
|
||||
@ -141,13 +143,37 @@ if __name__ == '__main__':
|
||||
win.setCentralWidget(cw)
|
||||
s = QtGui.QGraphicsScene()
|
||||
cw.setScene(s)
|
||||
win.resize(600,600)
|
||||
cw.enableMouse()
|
||||
cw.setRange(QtCore.QRectF(-100., -100., 200., 200.))
|
||||
|
||||
b = QtGui.QGraphicsRectItem(-5, -5, 10, 10)
|
||||
b.setPen(QtGui.QPen(mkPen('y')))
|
||||
t1 = QtGui.QGraphicsTextItem()
|
||||
t1.setHtml('<span style="color: #F00">R</span>')
|
||||
s.addItem(b)
|
||||
s.addItem(t1)
|
||||
class Item(QtGui.QGraphicsItem):
|
||||
def __init__(self):
|
||||
QtGui.QGraphicsItem.__init__(self)
|
||||
self.b = QtGui.QGraphicsRectItem(20, 20, 20, 20, self)
|
||||
self.b.setPen(QtGui.QPen(mkPen('y')))
|
||||
self.t1 = QtGui.QGraphicsTextItem(self)
|
||||
self.t1.setHtml('<span style="color: #F00">R</span>')
|
||||
self.t1.translate(20, 20)
|
||||
self.l1 = QtGui.QGraphicsLineItem(10, 0, -10, 0, self)
|
||||
self.l2 = QtGui.QGraphicsLineItem(0, 10, 0, -10, self)
|
||||
self.l1.setPen(QtGui.QPen(mkPen('y')))
|
||||
self.l2.setPen(QtGui.QPen(mkPen('y')))
|
||||
def boundingRect(self):
|
||||
return QtCore.QRectF()
|
||||
def paint(self, *args):
|
||||
pass
|
||||
|
||||
#s.addItem(b)
|
||||
#s.addItem(t1)
|
||||
item = Item()
|
||||
s.addItem(item)
|
||||
l1 = QtGui.QGraphicsLineItem(10, 0, -10, 0)
|
||||
l2 = QtGui.QGraphicsLineItem(0, 10, 0, -10)
|
||||
l1.setPen(QtGui.QPen(mkPen('r')))
|
||||
l2.setPen(QtGui.QPen(mkPen('r')))
|
||||
s.addItem(l1)
|
||||
s.addItem(l2)
|
||||
|
||||
tr1 = Transform()
|
||||
tr2 = Transform()
|
||||
@ -172,19 +198,26 @@ if __name__ == '__main__':
|
||||
tr4.rotate(30)
|
||||
print "tr1 * tr4 = ", tr1*tr4
|
||||
|
||||
w1 = widgets.TestROI((0,0), (50, 50))
|
||||
w2 = widgets.TestROI((0,0), (150, 150))
|
||||
w1 = widgets.TestROI((19,19), (22, 22), invertible=True)
|
||||
#w2 = widgets.TestROI((0,0), (150, 150))
|
||||
w1.setZValue(10)
|
||||
s.addItem(w1)
|
||||
s.addItem(w2)
|
||||
#s.addItem(w2)
|
||||
w1Base = w1.getState()
|
||||
w2Base = w2.getState()
|
||||
#w2Base = w2.getState()
|
||||
def update():
|
||||
tr1 = w1.getGlobalTransform(w1Base)
|
||||
tr2 = w2.getGlobalTransform(w2Base)
|
||||
t1.setTransform(tr1 * tr2)
|
||||
w1.setState(w1Base)
|
||||
w1.applyGlobalTransform(tr2)
|
||||
#tr2 = w2.getGlobalTransform(w2Base)
|
||||
item.setTransform(tr1)
|
||||
|
||||
#def update2():
|
||||
#tr1 = w1.getGlobalTransform(w1Base)
|
||||
#tr2 = w2.getGlobalTransform(w2Base)
|
||||
#t1.setTransform(tr1)
|
||||
#w1.setState(w1Base)
|
||||
#w1.applyGlobalTransform(tr2)
|
||||
|
||||
w1.sigRegionChanged.connect(update)
|
||||
w2.sigRegionChanged.connect(update)
|
||||
#w2.sigRegionChanged.connect(update2)
|
||||
|
||||
|
882
debug.py
Normal file
882
debug.py
Normal file
@ -0,0 +1,882 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
debug.py - Functions to aid in debugging
|
||||
Copyright 2010 Luke Campagnola
|
||||
Distributed under MIT/X11 license. See license.txt for more infomation.
|
||||
"""
|
||||
|
||||
import sys, traceback, time, gc, re, types, weakref, inspect, os, cProfile
|
||||
import ptime
|
||||
from numpy import ndarray
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
__ftraceDepth = 0
|
||||
def ftrace(func):
|
||||
"""Decorator used for marking the beginning and end of function calls.
|
||||
Automatically indents nested calls.
|
||||
"""
|
||||
def w(*args, **kargs):
|
||||
global __ftraceDepth
|
||||
pfx = " " * __ftraceDepth
|
||||
print pfx + func.__name__ + " start"
|
||||
__ftraceDepth += 1
|
||||
try:
|
||||
rv = func(*args, **kargs)
|
||||
finally:
|
||||
__ftraceDepth -= 1
|
||||
print pfx + func.__name__ + " done"
|
||||
return rv
|
||||
return w
|
||||
|
||||
def getExc(indent=4, prefix='| '):
|
||||
tb = traceback.format_exc()
|
||||
lines = []
|
||||
for l in tb.split('\n'):
|
||||
lines.append(" "*indent + prefix + l)
|
||||
return '\n'.join(lines)
|
||||
|
||||
def printExc(msg='', indent=4, prefix='|'):
|
||||
"""Print an error message followed by an indented exception backtrace
|
||||
(This function is intended to be called within except: blocks)"""
|
||||
exc = getExc(indent, prefix + ' ')
|
||||
print "[%s] %s\n" % (time.strftime("%H:%M:%S"), msg)
|
||||
print " "*indent + prefix + '='*30 + '>>'
|
||||
print exc
|
||||
print " "*indent + prefix + '='*30 + '<<'
|
||||
|
||||
def printTrace(msg='', indent=4, prefix='|'):
|
||||
"""Print an error message followed by an indented stack trace"""
|
||||
trace = backtrace(1)
|
||||
#exc = getExc(indent, prefix + ' ')
|
||||
print "[%s] %s\n" % (time.strftime("%H:%M:%S"), msg)
|
||||
print " "*indent + prefix + '='*30 + '>>'
|
||||
for line in trace.split('\n'):
|
||||
print " "*indent + prefix + " " + line
|
||||
print " "*indent + prefix + '='*30 + '<<'
|
||||
|
||||
|
||||
def backtrace(skip=0):
|
||||
return ''.join(traceback.format_stack()[:-(skip+1)])
|
||||
|
||||
|
||||
def listObjs(regex='Q', typ=None):
|
||||
"""List all objects managed by python gc with class name matching regex.
|
||||
Finds 'Q...' classes by default."""
|
||||
if typ is not None:
|
||||
return [x for x in gc.get_objects() if isinstance(x, typ)]
|
||||
else:
|
||||
return [x for x in gc.get_objects() if re.match(regex, type(x).__name__)]
|
||||
|
||||
|
||||
|
||||
def findRefPath(startObj, endObj, maxLen=8, restart=True, seen={}, path=None, ignore=None):
|
||||
"""Determine all paths of object references from startObj to endObj"""
|
||||
refs = []
|
||||
if path is None:
|
||||
path = [endObj]
|
||||
if ignore is None:
|
||||
ignore = {}
|
||||
ignore[id(sys._getframe())] = None
|
||||
ignore[id(path)] = None
|
||||
ignore[id(seen)] = None
|
||||
prefix = " "*(8-maxLen)
|
||||
#print prefix + str(map(type, path))
|
||||
prefix += " "
|
||||
if restart:
|
||||
#gc.collect()
|
||||
seen.clear()
|
||||
gc.collect()
|
||||
newRefs = [r for r in gc.get_referrers(endObj) if id(r) not in ignore]
|
||||
ignore[id(newRefs)] = None
|
||||
#fo = allFrameObjs()
|
||||
#newRefs = []
|
||||
#for r in gc.get_referrers(endObj):
|
||||
#try:
|
||||
#if r not in fo:
|
||||
#newRefs.append(r)
|
||||
#except:
|
||||
#newRefs.append(r)
|
||||
|
||||
for r in newRefs:
|
||||
#print prefix+"->"+str(type(r))
|
||||
if type(r).__name__ in ['frame', 'function', 'listiterator']:
|
||||
#print prefix+" FRAME"
|
||||
continue
|
||||
try:
|
||||
if any([r is x for x in path]):
|
||||
#print prefix+" LOOP", objChainString([r]+path)
|
||||
continue
|
||||
except:
|
||||
print r
|
||||
print path
|
||||
raise
|
||||
if r is startObj:
|
||||
refs.append([r])
|
||||
print refPathString([startObj]+path)
|
||||
continue
|
||||
if maxLen == 0:
|
||||
#print prefix+" END:", objChainString([r]+path)
|
||||
continue
|
||||
## See if we have already searched this node.
|
||||
## If not, recurse.
|
||||
tree = None
|
||||
try:
|
||||
cache = seen[id(r)]
|
||||
if cache[0] >= maxLen:
|
||||
tree = cache[1]
|
||||
for p in tree:
|
||||
print refPathString(p+path)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
ignore[id(tree)] = None
|
||||
if tree is None:
|
||||
tree = findRefPath(startObj, r, maxLen-1, restart=False, path=[r]+path, ignore=ignore)
|
||||
seen[id(r)] = [maxLen, tree]
|
||||
## integrate any returned results
|
||||
if len(tree) == 0:
|
||||
#print prefix+" EMPTY TREE"
|
||||
continue
|
||||
else:
|
||||
for p in tree:
|
||||
refs.append(p+[r])
|
||||
#seen[id(r)] = [maxLen, refs]
|
||||
return refs
|
||||
|
||||
|
||||
def objString(obj):
|
||||
"""Return a short but descriptive string for any object"""
|
||||
try:
|
||||
if type(obj) in [int, long, float]:
|
||||
return str(obj)
|
||||
elif isinstance(obj, dict):
|
||||
if len(obj) > 5:
|
||||
return "<dict {%s,...}>" % (",".join(obj.keys()[:5]))
|
||||
else:
|
||||
return "<dict {%s}>" % (",".join(obj.keys()))
|
||||
elif isinstance(obj, basestring):
|
||||
if len(obj) > 50:
|
||||
return '"%s..."' % obj[:50]
|
||||
else:
|
||||
return obj[:]
|
||||
elif isinstance(obj, ndarray):
|
||||
return "<ndarray %s %s>" % (str(obj.dtype), str(obj.shape))
|
||||
elif hasattr(obj, '__len__'):
|
||||
if len(obj) > 5:
|
||||
return "<%s [%s,...]>" % (type(obj).__name__, ",".join([type(o).__name__ for o in obj[:5]]))
|
||||
else:
|
||||
return "<%s [%s]>" % (type(obj).__name__, ",".join([type(o).__name__ for o in obj]))
|
||||
else:
|
||||
return "<%s %s>" % (type(obj).__name__, obj.__class__.__name__)
|
||||
except:
|
||||
return str(type(obj))
|
||||
|
||||
def refPathString(chain):
|
||||
"""Given a list of adjacent objects in a reference path, print the 'natural' path
|
||||
names (ie, attribute names, keys, and indexes) that follow from one object to the next ."""
|
||||
s = objString(chain[0])
|
||||
i = 0
|
||||
while i < len(chain)-1:
|
||||
#print " -> ", i
|
||||
i += 1
|
||||
o1 = chain[i-1]
|
||||
o2 = chain[i]
|
||||
cont = False
|
||||
if isinstance(o1, list) or isinstance(o1, tuple):
|
||||
if any([o2 is x for x in o1]):
|
||||
s += "[%d]" % o1.index(o2)
|
||||
continue
|
||||
#print " not list"
|
||||
if isinstance(o2, dict) and hasattr(o1, '__dict__') and o2 == o1.__dict__:
|
||||
i += 1
|
||||
if i >= len(chain):
|
||||
s += ".__dict__"
|
||||
continue
|
||||
o3 = chain[i]
|
||||
for k in o2:
|
||||
if o2[k] is o3:
|
||||
s += '.%s' % k
|
||||
cont = True
|
||||
continue
|
||||
#print " not __dict__"
|
||||
if isinstance(o1, dict):
|
||||
try:
|
||||
if o2 in o1:
|
||||
s += "[key:%s]" % objString(o2)
|
||||
continue
|
||||
except TypeError:
|
||||
pass
|
||||
for k in o1:
|
||||
if o1[k] is o2:
|
||||
s += "[%s]" % objString(k)
|
||||
cont = True
|
||||
continue
|
||||
#print " not dict"
|
||||
#for k in dir(o1): ## Not safe to request attributes like this.
|
||||
#if getattr(o1, k) is o2:
|
||||
#s += ".%s" % k
|
||||
#cont = True
|
||||
#continue
|
||||
#print " not attr"
|
||||
if cont:
|
||||
continue
|
||||
s += " ? "
|
||||
sys.stdout.flush()
|
||||
return s
|
||||
|
||||
|
||||
def objectSize(obj, ignore=None, verbose=False, depth=0, recursive=False):
|
||||
"""Guess how much memory an object is using"""
|
||||
ignoreTypes = [types.MethodType, types.UnboundMethodType, types.BuiltinMethodType, types.FunctionType, types.BuiltinFunctionType]
|
||||
ignoreRegex = re.compile('(method-wrapper|Flag|ItemChange|Option|Mode)')
|
||||
|
||||
|
||||
if ignore is None:
|
||||
ignore = {}
|
||||
|
||||
indent = ' '*depth
|
||||
|
||||
try:
|
||||
hash(obj)
|
||||
hsh = obj
|
||||
except:
|
||||
hsh = "%s:%d" % (str(type(obj)), id(obj))
|
||||
|
||||
if hsh in ignore:
|
||||
return 0
|
||||
ignore[hsh] = 1
|
||||
|
||||
try:
|
||||
size = sys.getsizeof(obj)
|
||||
except TypeError:
|
||||
size = 0
|
||||
|
||||
if isinstance(obj, ndarray):
|
||||
try:
|
||||
size += len(obj.data)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
if recursive:
|
||||
if type(obj) in [list, tuple]:
|
||||
if verbose:
|
||||
print indent+"list:"
|
||||
for o in obj:
|
||||
s = objectSize(o, ignore=ignore, verbose=verbose, depth=depth+1)
|
||||
if verbose:
|
||||
print indent+' +', s
|
||||
size += s
|
||||
elif isinstance(obj, dict):
|
||||
if verbose:
|
||||
print indent+"list:"
|
||||
for k in obj:
|
||||
s = objectSize(obj[k], ignore=ignore, verbose=verbose, depth=depth+1)
|
||||
if verbose:
|
||||
print indent+' +', k, s
|
||||
size += s
|
||||
#elif isinstance(obj, QtCore.QObject):
|
||||
#try:
|
||||
#childs = obj.children()
|
||||
#if verbose:
|
||||
#print indent+"Qt children:"
|
||||
#for ch in childs:
|
||||
#s = objectSize(obj, ignore=ignore, verbose=verbose, depth=depth+1)
|
||||
#size += s
|
||||
#if verbose:
|
||||
#print indent + ' +', ch.objectName(), s
|
||||
|
||||
#except:
|
||||
#pass
|
||||
#if isinstance(obj, types.InstanceType):
|
||||
gc.collect()
|
||||
if verbose:
|
||||
print indent+'attrs:'
|
||||
for k in dir(obj):
|
||||
if k in ['__dict__']:
|
||||
continue
|
||||
o = getattr(obj, k)
|
||||
if type(o) in ignoreTypes:
|
||||
continue
|
||||
strtyp = str(type(o))
|
||||
if ignoreRegex.search(strtyp):
|
||||
continue
|
||||
#if isinstance(o, types.ObjectType) and strtyp == "<type 'method-wrapper'>":
|
||||
#continue
|
||||
|
||||
#if verbose:
|
||||
#print indent, k, '?'
|
||||
refs = [r for r in gc.get_referrers(o) if type(r) != types.FrameType]
|
||||
if len(refs) == 1:
|
||||
s = objectSize(o, ignore=ignore, verbose=verbose, depth=depth+1)
|
||||
size += s
|
||||
if verbose:
|
||||
print indent + " +", k, s
|
||||
#else:
|
||||
#if verbose:
|
||||
#print indent + ' -', k, len(refs)
|
||||
return size
|
||||
|
||||
class GarbageWatcher:
|
||||
"""
|
||||
Convenient dictionary for holding weak references to objects.
|
||||
Mainly used to check whether the objects have been collect yet or not.
|
||||
|
||||
Example:
|
||||
gw = GarbageWatcher()
|
||||
gw['objName'] = obj
|
||||
gw['objName2'] = obj2
|
||||
gw.check()
|
||||
|
||||
|
||||
"""
|
||||
def __init__(self):
|
||||
self.objs = weakref.WeakValueDictionary()
|
||||
self.allNames = []
|
||||
|
||||
def add(self, obj, name):
|
||||
self.objs[name] = obj
|
||||
self.allNames.append(name)
|
||||
|
||||
def __setitem__(self, name, obj):
|
||||
self.add(obj, name)
|
||||
|
||||
def check(self):
|
||||
"""Print a list of all watched objects and whether they have been collected."""
|
||||
gc.collect()
|
||||
dead = self.allNames[:]
|
||||
alive = []
|
||||
for k in self.objs:
|
||||
dead.remove(k)
|
||||
alive.append(k)
|
||||
print "Deleted objects:", dead
|
||||
print "Live objects:", alive
|
||||
|
||||
def __getitem__(self, item):
|
||||
return self.objs[item]
|
||||
|
||||
|
||||
class Profiler:
|
||||
"""Simple profiler allowing measurement of multiple time intervals.
|
||||
|
||||
Example:
|
||||
prof = Profiler('Function')
|
||||
... do stuff ...
|
||||
prof.mark('did stuff')
|
||||
... do other stuff ...
|
||||
prof.mark('did other stuff')
|
||||
prof.finish()
|
||||
"""
|
||||
depth = 0
|
||||
|
||||
def __init__(self, msg="Profiler", disabled=False):
|
||||
self.depth = Profiler.depth
|
||||
Profiler.depth += 1
|
||||
|
||||
self.disabled = disabled
|
||||
if disabled:
|
||||
return
|
||||
self.t0 = ptime.time()
|
||||
self.t1 = self.t0
|
||||
self.msg = " "*self.depth + msg
|
||||
print self.msg, ">>> Started"
|
||||
|
||||
def mark(self, msg=''):
|
||||
if self.disabled:
|
||||
return
|
||||
t1 = ptime.time()
|
||||
print " "+self.msg, msg, "%gms" % ((t1-self.t1)*1000)
|
||||
self.t1 = t1
|
||||
|
||||
def finish(self):
|
||||
if self.disabled:
|
||||
return
|
||||
t1 = ptime.time()
|
||||
print self.msg, '<<< Finished, total time:', "%gms" % ((t1-self.t0)*1000)
|
||||
|
||||
def __del__(self):
|
||||
Profiler.depth -= 1
|
||||
|
||||
|
||||
def profile(code, name='profile_run', sort='cumulative', num=30):
|
||||
"""Common-use for cProfile"""
|
||||
cProfile.run(code, name)
|
||||
stats = pstats.Stats(name)
|
||||
stats.sort_stats(sort)
|
||||
stats.print_stats(num)
|
||||
return stats
|
||||
|
||||
|
||||
|
||||
#### Code for listing (nearly) all objects in the known universe
|
||||
#### http://utcc.utoronto.ca/~cks/space/blog/python/GetAllObjects
|
||||
# Recursively expand slist's objects
|
||||
# into olist, using seen to track
|
||||
# already processed objects.
|
||||
def _getr(slist, olist, first=True):
|
||||
i = 0
|
||||
for e in slist:
|
||||
|
||||
oid = id(e)
|
||||
typ = type(e)
|
||||
if oid in olist or typ is int or typ is long: ## or e in olist: ## since we're excluding all ints, there is no longer a need to check for olist keys
|
||||
continue
|
||||
olist[oid] = e
|
||||
if first and (i%1000) == 0:
|
||||
gc.collect()
|
||||
tl = gc.get_referents(e)
|
||||
if tl:
|
||||
_getr(tl, olist, first=False)
|
||||
i += 1
|
||||
# The public function.
|
||||
def get_all_objects():
|
||||
"""Return a list of all live Python objects (excluding int and long), not including the list itself."""
|
||||
gc.collect()
|
||||
gcl = gc.get_objects()
|
||||
olist = {}
|
||||
_getr(gcl, olist)
|
||||
|
||||
del olist[id(olist)]
|
||||
del olist[id(gcl)]
|
||||
del olist[id(sys._getframe())]
|
||||
return olist
|
||||
|
||||
|
||||
def lookup(oid, objects=None):
|
||||
"""Return an object given its ID, if it exists."""
|
||||
if objects is None:
|
||||
objects = get_all_objects()
|
||||
return objects[oid]
|
||||
|
||||
|
||||
|
||||
|
||||
class ObjTracker:
|
||||
"""
|
||||
Tracks all objects under the sun, reporting the changes between snapshots: what objects are created, deleted, and persistent.
|
||||
This class is very useful for tracking memory leaks. The class goes to great (but not heroic) lengths to avoid tracking
|
||||
its own internal objects.
|
||||
|
||||
Example:
|
||||
ot = ObjTracker() # takes snapshot of currently existing objects
|
||||
... do stuff ...
|
||||
ot.diff() # prints lists of objects created and deleted since ot was initialized
|
||||
... do stuff ...
|
||||
ot.diff() # prints lists of objects created and deleted since last call to ot.diff()
|
||||
# also prints list of items that were created since initialization AND have not been deleted yet
|
||||
# (if done correctly, this list can tell you about objects that were leaked)
|
||||
|
||||
arrays = ot.findPersistent('ndarray') ## returns all objects matching 'ndarray' (string match, not instance checking)
|
||||
## that were considered persistent when the last diff() was run
|
||||
|
||||
describeObj(arrays[0]) ## See if we can determine who has references to this array
|
||||
"""
|
||||
|
||||
|
||||
allObjs = {} ## keep track of all objects created and stored within class instances
|
||||
allObjs[id(allObjs)] = None
|
||||
|
||||
def __init__(self):
|
||||
self.startRefs = {} ## list of objects that exist when the tracker is initialized {oid: weakref}
|
||||
## (If it is not possible to weakref the object, then the value is None)
|
||||
self.startCount = {}
|
||||
self.newRefs = {} ## list of objects that have been created since initialization
|
||||
self.persistentRefs = {} ## list of objects considered 'persistent' when the last diff() was called
|
||||
self.objTypes = {}
|
||||
|
||||
ObjTracker.allObjs[id(self)] = None
|
||||
self.objs = [self.__dict__, self.startRefs, self.startCount, self.newRefs, self.persistentRefs, self.objTypes]
|
||||
self.objs.append(self.objs)
|
||||
for v in self.objs:
|
||||
ObjTracker.allObjs[id(v)] = None
|
||||
|
||||
self.start()
|
||||
|
||||
def findNew(self, regex):
|
||||
"""Return all objects matching regex that were considered 'new' when the last diff() was run."""
|
||||
return self.findTypes(self.newRefs, regex)
|
||||
|
||||
def findPersistent(self, regex):
|
||||
"""Return all objects matching regex that were considered 'persistent' when the last diff() was run."""
|
||||
return self.findTypes(self.persistentRefs, regex)
|
||||
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
Remember the current set of objects as the comparison for all future calls to diff()
|
||||
Called automatically on init, but can be called manually as well.
|
||||
"""
|
||||
refs, count, objs = self.collect()
|
||||
for r in self.startRefs:
|
||||
self.forgetRef(self.startRefs[r])
|
||||
self.startRefs.clear()
|
||||
self.startRefs.update(refs)
|
||||
for r in refs:
|
||||
self.rememberRef(r)
|
||||
self.startCount.clear()
|
||||
self.startCount.update(count)
|
||||
#self.newRefs.clear()
|
||||
#self.newRefs.update(refs)
|
||||
|
||||
def diff(self, **kargs):
|
||||
"""
|
||||
Compute all differences between the current object set and the reference set.
|
||||
Print a set of reports for created, deleted, and persistent objects
|
||||
"""
|
||||
refs, count, objs = self.collect() ## refs contains the list of ALL objects
|
||||
|
||||
## Which refs have disappeared since call to start() (these are only displayed once, then forgotten.)
|
||||
delRefs = {}
|
||||
for i in self.startRefs.keys():
|
||||
if i not in refs:
|
||||
delRefs[i] = self.startRefs[i]
|
||||
del self.startRefs[i]
|
||||
self.forgetRef(delRefs[i])
|
||||
for i in self.newRefs.keys():
|
||||
if i not in refs:
|
||||
delRefs[i] = self.newRefs[i]
|
||||
del self.newRefs[i]
|
||||
self.forgetRef(delRefs[i])
|
||||
#print "deleted:", len(delRefs)
|
||||
|
||||
## Which refs have appeared since call to start() or diff()
|
||||
persistentRefs = {} ## created since start(), but before last diff()
|
||||
createRefs = {} ## created since last diff()
|
||||
for o in refs:
|
||||
if o not in self.startRefs:
|
||||
if o not in self.newRefs:
|
||||
createRefs[o] = refs[o] ## object has been created since last diff()
|
||||
else:
|
||||
persistentRefs[o] = refs[o] ## object has been created since start(), but before last diff() (persistent)
|
||||
#print "new:", len(newRefs)
|
||||
|
||||
## self.newRefs holds the entire set of objects created since start()
|
||||
for r in self.newRefs:
|
||||
self.forgetRef(self.newRefs[r])
|
||||
self.newRefs.clear()
|
||||
self.newRefs.update(persistentRefs)
|
||||
self.newRefs.update(createRefs)
|
||||
for r in self.newRefs:
|
||||
self.rememberRef(self.newRefs[r])
|
||||
#print "created:", len(createRefs)
|
||||
|
||||
## self.persistentRefs holds all objects considered persistent.
|
||||
self.persistentRefs.clear()
|
||||
self.persistentRefs.update(persistentRefs)
|
||||
|
||||
|
||||
print "----------- Count changes since start: ----------"
|
||||
c1 = count.copy()
|
||||
for k in self.startCount:
|
||||
c1[k] = c1.get(k, 0) - self.startCount[k]
|
||||
typs = c1.keys()
|
||||
typs.sort(lambda a,b: cmp(c1[a], c1[b]))
|
||||
for t in typs:
|
||||
if c1[t] == 0:
|
||||
continue
|
||||
num = "%d" % c1[t]
|
||||
print " " + num + " "*(10-len(num)) + str(t)
|
||||
|
||||
print "----------- %d Deleted since last diff: ------------" % len(delRefs)
|
||||
self.report(delRefs, objs, **kargs)
|
||||
print "----------- %d Created since last diff: ------------" % len(createRefs)
|
||||
self.report(createRefs, objs, **kargs)
|
||||
print "----------- %d Created since start (persistent): ------------" % len(persistentRefs)
|
||||
self.report(persistentRefs, objs, **kargs)
|
||||
|
||||
|
||||
def __del__(self):
|
||||
self.startRefs.clear()
|
||||
self.startCount.clear()
|
||||
self.newRefs.clear()
|
||||
self.persistentRefs.clear()
|
||||
|
||||
del ObjTracker.allObjs[id(self)]
|
||||
for v in self.objs:
|
||||
del ObjTracker.allObjs[id(v)]
|
||||
|
||||
@classmethod
|
||||
def isObjVar(cls, o):
|
||||
return type(o) is cls or id(o) in cls.allObjs
|
||||
|
||||
def collect(self):
|
||||
print "Collecting list of all objects..."
|
||||
gc.collect()
|
||||
objs = get_all_objects()
|
||||
frame = sys._getframe()
|
||||
del objs[id(frame)] ## ignore the current frame
|
||||
del objs[id(frame.f_code)]
|
||||
|
||||
ignoreTypes = [int, long]
|
||||
refs = {}
|
||||
count = {}
|
||||
for k in objs:
|
||||
o = objs[k]
|
||||
typ = type(o)
|
||||
oid = id(o)
|
||||
if ObjTracker.isObjVar(o) or typ in ignoreTypes:
|
||||
continue
|
||||
|
||||
try:
|
||||
ref = weakref.ref(obj)
|
||||
except:
|
||||
ref = None
|
||||
refs[oid] = ref
|
||||
typ = type(o)
|
||||
typStr = typeStr(o)
|
||||
self.objTypes[oid] = typStr
|
||||
ObjTracker.allObjs[id(typStr)] = None
|
||||
count[typ] = count.get(typ, 0) + 1
|
||||
|
||||
print "All objects: %d Tracked objects: %d" % (len(objs), len(refs))
|
||||
return refs, count, objs
|
||||
|
||||
def forgetRef(self, ref):
|
||||
if ref is not None:
|
||||
del ObjTracker.allObjs[id(ref)]
|
||||
|
||||
def rememberRef(self, ref):
|
||||
## Record the address of the weakref object so it is not included in future object counts.
|
||||
if ref is not None:
|
||||
ObjTracker.allObjs[id(ref)] = None
|
||||
|
||||
|
||||
def lookup(self, oid, ref, objs=None):
|
||||
if ref is None or ref() is None:
|
||||
try:
|
||||
obj = lookup(oid, objects=objs)
|
||||
except:
|
||||
obj = None
|
||||
else:
|
||||
obj = ref()
|
||||
return obj
|
||||
|
||||
|
||||
def report(self, refs, allobjs=None, showIDs=False):
|
||||
if allobjs is None:
|
||||
allobjs = get_all_objects()
|
||||
|
||||
count = {}
|
||||
rev = {}
|
||||
for oid in refs:
|
||||
obj = self.lookup(oid, refs[oid], allobjs)
|
||||
if obj is None:
|
||||
typ = "[del] " + self.objTypes[oid]
|
||||
else:
|
||||
typ = typeStr(obj)
|
||||
if typ not in rev:
|
||||
rev[typ] = []
|
||||
rev[typ].append(oid)
|
||||
c = count.get(typ, [0,0])
|
||||
count[typ] = [c[0]+1, c[1]+objectSize(obj)]
|
||||
typs = count.keys()
|
||||
typs.sort(lambda a,b: cmp(count[a][1], count[b][1]))
|
||||
|
||||
for t in typs:
|
||||
line = " %d\t%d\t%s" % (count[t][0], count[t][1], t)
|
||||
if showIDs:
|
||||
line += "\t"+",".join(map(str,rev[t]))
|
||||
print line
|
||||
|
||||
def findTypes(self, refs, regex):
|
||||
allObjs = get_all_objects()
|
||||
ids = {}
|
||||
objs = []
|
||||
r = re.compile(regex)
|
||||
for k in refs:
|
||||
if r.search(self.objTypes[k]):
|
||||
objs.append(self.lookup(k, refs[k], allObjs))
|
||||
return objs
|
||||
|
||||
|
||||
|
||||
|
||||
def describeObj(obj, depth=4, path=None, ignore=None):
|
||||
"""
|
||||
Trace all reference paths backward, printing a list of different ways this object can be accessed.
|
||||
Attempts to answer the question "who has a reference to this object"
|
||||
"""
|
||||
if path is None:
|
||||
path = [obj]
|
||||
if ignore is None:
|
||||
ignore = {} ## holds IDs of objects used within the function.
|
||||
ignore[id(sys._getframe())] = None
|
||||
ignore[id(path)] = None
|
||||
gc.collect()
|
||||
refs = gc.get_referrers(obj)
|
||||
ignore[id(refs)] = None
|
||||
printed=False
|
||||
for ref in refs:
|
||||
if id(ref) in ignore:
|
||||
continue
|
||||
if id(ref) in map(id, path):
|
||||
print "Cyclic reference: " + refPathString([ref]+path)
|
||||
printed = True
|
||||
continue
|
||||
newPath = [ref]+path
|
||||
if len(newPath) >= depth:
|
||||
refStr = refPathString(newPath)
|
||||
if '[_]' not in refStr: ## ignore '_' references generated by the interactive shell
|
||||
print refStr
|
||||
printed = True
|
||||
else:
|
||||
describeObj(ref, depth, newPath, ignore)
|
||||
printed = True
|
||||
if not printed:
|
||||
print "Dead end: " + refPathString(path)
|
||||
|
||||
|
||||
|
||||
def typeStr(obj):
|
||||
"""Create a more useful type string by making <instance> types report their class."""
|
||||
typ = type(obj)
|
||||
if typ == types.InstanceType:
|
||||
return "<instance of %s>" % obj.__class__.__name__
|
||||
else:
|
||||
return str(typ)
|
||||
|
||||
def searchRefs(obj, *args):
|
||||
"""Pseudo-interactive function for tracing references backward.
|
||||
Arguments:
|
||||
obj: The initial object from which to start searching
|
||||
args: A set of string or int arguments.
|
||||
each integer selects one of obj's referrers to be the new 'obj'
|
||||
each string indicates an action to take on the current 'obj':
|
||||
t: print the types of obj's referrers
|
||||
l: print the lengths of obj's referrers (if they have __len__)
|
||||
i: print the IDs of obj's referrers
|
||||
o: print obj
|
||||
ro: return obj
|
||||
rr: return list of obj's referrers
|
||||
|
||||
Examples:
|
||||
searchRefs(obj, 't') ## Print types of all objects referring to obj
|
||||
searchRefs(obj, 't', 0, 't') ## ..then select the first referrer and print the types of its referrers
|
||||
searchRefs(obj, 't', 0, 't', 'l') ## ..also print lengths of the last set of referrers
|
||||
searchRefs(obj, 0, 1, 'ro') ## Select index 0 from obj's referrer, then select index 1 from the next set of referrers, then return that object
|
||||
|
||||
"""
|
||||
ignore = {id(sys._getframe()): None}
|
||||
gc.collect()
|
||||
refs = gc.get_referrers(obj)
|
||||
ignore[id(refs)] = None
|
||||
refs = [r for r in refs if id(r) not in ignore]
|
||||
for a in args:
|
||||
|
||||
#fo = allFrameObjs()
|
||||
#refs = [r for r in refs if r not in fo]
|
||||
|
||||
if type(a) is int:
|
||||
obj = refs[a]
|
||||
gc.collect()
|
||||
refs = gc.get_referrers(obj)
|
||||
ignore[id(refs)] = None
|
||||
refs = [r for r in refs if id(r) not in ignore]
|
||||
elif a == 't':
|
||||
print map(typeStr, refs)
|
||||
elif a == 'i':
|
||||
print map(id, refs)
|
||||
elif a == 'l':
|
||||
def slen(o):
|
||||
if hasattr(o, '__len__'):
|
||||
return len(o)
|
||||
else:
|
||||
return None
|
||||
print map(slen, refs)
|
||||
elif a == 'o':
|
||||
print obj
|
||||
elif a == 'ro':
|
||||
return obj
|
||||
elif a == 'rr':
|
||||
return refs
|
||||
|
||||
def allFrameObjs():
|
||||
"""Return list of frame objects in current stack. Useful if you want to ignore these objects in refernece searches"""
|
||||
f = sys._getframe()
|
||||
objs = []
|
||||
while f is not None:
|
||||
objs.append(f)
|
||||
objs.append(f.f_code)
|
||||
#objs.append(f.f_locals)
|
||||
#objs.append(f.f_globals)
|
||||
#objs.append(f.f_builtins)
|
||||
f = f.f_back
|
||||
return objs
|
||||
|
||||
|
||||
def findObj(regex):
|
||||
"""Return a list of objects whose typeStr matches regex"""
|
||||
allObjs = get_all_objects()
|
||||
objs = []
|
||||
r = re.compile(regex)
|
||||
for i in allObjs:
|
||||
obj = allObjs[i]
|
||||
if r.search(typeStr(obj)):
|
||||
objs.append(obj)
|
||||
return objs
|
||||
|
||||
|
||||
|
||||
def listRedundantModules():
|
||||
"""List modules that have been imported more than once via different paths."""
|
||||
mods = {}
|
||||
for name, mod in sys.modules.iteritems():
|
||||
if not hasattr(mod, '__file__'):
|
||||
continue
|
||||
mfile = os.path.abspath(mod.__file__)
|
||||
if mfile[-1] == 'c':
|
||||
mfile = mfile[:-1]
|
||||
if mfile in mods:
|
||||
print "module at %s has 2 names: %s, %s" % (mfile, name, mods[mfile])
|
||||
else:
|
||||
mods[mfile] = name
|
||||
|
||||
|
||||
def walkQObjectTree(obj, counts=None, verbose=False, depth=0):
|
||||
"""
|
||||
Walk through a tree of QObjects, doing nothing to them.
|
||||
The purpose of this function is to find dead objects and generate a crash
|
||||
immediately rather than stumbling upon them later.
|
||||
Prints a count of the objects encountered, for fun. (or is it?)
|
||||
"""
|
||||
|
||||
if verbose:
|
||||
print " "*depth + typeStr(obj)
|
||||
report = False
|
||||
if counts is None:
|
||||
counts = {}
|
||||
report = True
|
||||
typ = str(type(obj))
|
||||
try:
|
||||
counts[typ] += 1
|
||||
except KeyError:
|
||||
counts[typ] = 1
|
||||
for child in obj.children():
|
||||
walkQObjectTree(child, counts, verbose, depth+1)
|
||||
|
||||
return counts
|
||||
|
||||
QObjCache = {}
|
||||
def qObjectReport(verbose=False):
|
||||
"""Generate a report counting all QObjects and their types"""
|
||||
global qObjCache
|
||||
count = {}
|
||||
for obj in findObj('PyQt'):
|
||||
if isinstance(obj, QtCore.QObject):
|
||||
oid = id(obj)
|
||||
if oid not in QObjCache:
|
||||
QObjCache[oid] = typeStr(obj) + " " + obj.objectName()
|
||||
try:
|
||||
QObjCache[oid] += " " + obj.parent().objectName()
|
||||
QObjCache[oid] += " " + obj.text()
|
||||
except:
|
||||
pass
|
||||
print "check obj", oid, unicode(QObjCache[oid])
|
||||
if obj.parent() is None:
|
||||
walkQObjectTree(obj, count, verbose)
|
||||
|
||||
typs = count.keys()
|
||||
typs.sort()
|
||||
for t in typs:
|
||||
print count[t], "\t", t
|
||||
|
@ -11,6 +11,7 @@ import pyqtgraph as pg
|
||||
|
||||
app = QtGui.QApplication([])
|
||||
mw = QtGui.QMainWindow()
|
||||
mw.resize(800,800)
|
||||
|
||||
p = pg.PlotWidget()
|
||||
mw.setCentralWidget(p)
|
||||
|
@ -12,6 +12,7 @@ app = QtGui.QApplication([])
|
||||
|
||||
## Create window with GraphicsView widget
|
||||
win = QtGui.QMainWindow()
|
||||
win.resize(800,800)
|
||||
view = pg.GraphicsView()
|
||||
#view.useOpenGL(True)
|
||||
win.setCentralWidget(view)
|
||||
@ -41,11 +42,26 @@ def updateData():
|
||||
img.updateImage(data[i])
|
||||
i = (i+1) % data.shape[0]
|
||||
|
||||
QtCore.QTimer.singleShot(20, updateData)
|
||||
|
||||
|
||||
# update image data every 20ms (or so)
|
||||
t = QtCore.QTimer()
|
||||
t.timeout.connect(updateData)
|
||||
t.start(20)
|
||||
#t = QtCore.QTimer()
|
||||
#t.timeout.connect(updateData)
|
||||
#t.start(20)
|
||||
updateData()
|
||||
|
||||
|
||||
def doWork():
|
||||
while True:
|
||||
x = '.'.join(['%f'%i for i in range(100)]) ## some work for the thread to do
|
||||
if time is None: ## main thread has started cleaning up, bail out now
|
||||
break
|
||||
time.sleep(1e-3)
|
||||
|
||||
import thread
|
||||
thread.start_new_thread(doWork, ())
|
||||
|
||||
|
||||
## Start Qt event loop unless running in interactive mode.
|
||||
if sys.flags.interactive != 1:
|
||||
|
@ -13,6 +13,7 @@ app = QtGui.QApplication([])
|
||||
|
||||
## Create window with ImageView widget
|
||||
win = QtGui.QMainWindow()
|
||||
win.resize(800,800)
|
||||
imv = pg.ImageView()
|
||||
win.setCentralWidget(imv)
|
||||
win.show()
|
||||
|
@ -18,6 +18,7 @@ except:
|
||||
|
||||
app = QtGui.QApplication([])
|
||||
mw = QtGui.QMainWindow()
|
||||
mw.resize(800,800)
|
||||
pw = MultiPlotWidget()
|
||||
mw.setCentralWidget(pw)
|
||||
mw.show()
|
||||
|
@ -9,8 +9,10 @@ from PyQt4 import QtGui, QtCore
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
|
||||
#QtGui.QApplication.setGraphicsSystem('raster')
|
||||
app = QtGui.QApplication([])
|
||||
mw = QtGui.QMainWindow()
|
||||
mw.resize(800,800)
|
||||
cw = QtGui.QWidget()
|
||||
mw.setCentralWidget(cw)
|
||||
l = QtGui.QVBoxLayout()
|
||||
|
@ -12,8 +12,9 @@ import pyqtgraph as pg
|
||||
## create GUI
|
||||
app = QtGui.QApplication([])
|
||||
w = QtGui.QMainWindow()
|
||||
w.resize(800,800)
|
||||
v = pg.GraphicsView()
|
||||
v.invertY(True) ## Images usually have their Y-axis pointing downward
|
||||
#v.invertY(True) ## Images usually have their Y-axis pointing downward
|
||||
v.setAspectLocked(True)
|
||||
v.enableMouse(True)
|
||||
v.autoPixelScale = False
|
||||
|
@ -12,6 +12,7 @@ app = QtGui.QApplication([])
|
||||
|
||||
## Create window with GraphicsView widget
|
||||
win = QtGui.QMainWindow()
|
||||
win.resize(800,800)
|
||||
view = pg.GraphicsView()
|
||||
#view.useOpenGL(True)
|
||||
win.setCentralWidget(view)
|
||||
|
@ -7,18 +7,74 @@ from PyQt4 import QtGui, QtCore
|
||||
import pyqtgraph as pg
|
||||
import numpy as np
|
||||
|
||||
#QtGui.QApplication.setGraphicsSystem('raster')
|
||||
app = QtGui.QApplication([])
|
||||
|
||||
mw = QtGui.QMainWindow()
|
||||
cw = pg.PlotWidget()
|
||||
mw.resize(800,800)
|
||||
cw = QtGui.QWidget()
|
||||
layout = QtGui.QGridLayout()
|
||||
cw.setLayout(layout)
|
||||
mw.setCentralWidget(cw)
|
||||
|
||||
w1 = pg.PlotWidget()
|
||||
layout.addWidget(w1, 0,0)
|
||||
|
||||
w2 = pg.PlotWidget()
|
||||
layout.addWidget(w2, 1,0)
|
||||
|
||||
w3 = pg.GraphicsView()
|
||||
w3.enableMouse()
|
||||
w3.aspectLocked = True
|
||||
layout.addWidget(w3, 0,1)
|
||||
|
||||
w4 = pg.PlotWidget()
|
||||
#vb = pg.ViewBox()
|
||||
#w4.setCentralItem(vb)
|
||||
layout.addWidget(w4, 1,1)
|
||||
|
||||
mw.show()
|
||||
|
||||
s1 = pg.ScatterPlotItem(size=10, pen=QtGui.QPen(QtCore.Qt.NoPen), brush=QtGui.QBrush(QtGui.QColor(255, 255, 255, 20)))
|
||||
pos = np.random.normal(size=(2,3000))
|
||||
spots = [{'pos': pos[:,i]} for i in range(3000)]
|
||||
s1.addPoints(spots)
|
||||
|
||||
cw.addDataItem(s1)
|
||||
n = 3000
|
||||
s1 = pg.ScatterPlotItem(size=10, pen=QtGui.QPen(QtCore.Qt.NoPen), brush=QtGui.QBrush(QtGui.QColor(255, 255, 255, 20)))
|
||||
pos = np.random.normal(size=(2,n), scale=1e-5)
|
||||
spots = [{'pos': pos[:,i], 'data': 1} for i in range(n)] + [{'pos': [0,0], 'data': 1}]
|
||||
s1.addPoints(spots)
|
||||
w1.addDataItem(s1)
|
||||
|
||||
def clicked(plot, points):
|
||||
print "clicked points", points
|
||||
|
||||
s1.sigClicked.connect(clicked)
|
||||
|
||||
|
||||
s2 = pg.ScatterPlotItem(pxMode=False)
|
||||
spots2 = []
|
||||
for i in range(10):
|
||||
for j in range(10):
|
||||
spots2.append({'pos': (1e-6*i, 1e-6*j), 'size': 1e-6, 'brush':pg.intColor(i*10+j, 100)})
|
||||
s2.addPoints(spots2)
|
||||
w2.addDataItem(s2)
|
||||
|
||||
s2.sigClicked.connect(clicked)
|
||||
|
||||
|
||||
s3 = pg.ScatterPlotItem(size=10, pen=pg.mkPen('w'), pxMode=True)
|
||||
pos = np.random.normal(size=(2,3000), scale=1e-5)
|
||||
spots = [{'pos': pos[:,i], 'data': 1, 'brush':pg.intColor(i, 3000)} for i in range(3000)]
|
||||
s3.addPoints(spots)
|
||||
w3.addItem(s3)
|
||||
w3.setRange(s3.boundingRect())
|
||||
s3.sigClicked.connect(clicked)
|
||||
|
||||
|
||||
s4 = pg.ScatterPlotItem(identical=True, size=10, pen=QtGui.QPen(QtCore.Qt.NoPen), brush=QtGui.QBrush(QtGui.QColor(255, 255, 255, 20)))
|
||||
#pos = np.random.normal(size=(2,n), scale=1e-5)
|
||||
#spots = [{'pos': pos[:,i], 'data': 1} for i in range(n)] + [{'pos': [0,0], 'data': 1}]
|
||||
s4.addPoints(spots)
|
||||
w4.addDataItem(s4)
|
||||
|
||||
|
||||
## Start Qt event loop unless running in interactive mode.
|
||||
if sys.flags.interactive != 1:
|
||||
|
12
functions.py
12
functions.py
@ -17,7 +17,7 @@ colorAbbrev = {
|
||||
}
|
||||
|
||||
|
||||
from PyQt4 import QtGui
|
||||
from PyQt4 import QtGui, QtCore
|
||||
import numpy as np
|
||||
import scipy.ndimage
|
||||
|
||||
@ -40,20 +40,26 @@ def siScale(x, minVal=1e-25):
|
||||
return (p, pref)
|
||||
|
||||
def mkBrush(color):
|
||||
if isinstance(color, QtGui.QBrush):
|
||||
return color
|
||||
return QtGui.QBrush(mkColor(color))
|
||||
|
||||
def mkPen(arg=None, color=None, width=1, style=None, cosmetic=True, hsv=None, ):
|
||||
def mkPen(arg='default', color=None, width=1, style=None, cosmetic=True, hsv=None, ):
|
||||
"""Convenience function for making pens. Examples:
|
||||
mkPen(color)
|
||||
mkPen(color, width=2)
|
||||
mkPen(cosmetic=False, width=4.5, color='r')
|
||||
mkPen({'color': "FF0", width: 2})
|
||||
mkPen(None) (no pen)
|
||||
"""
|
||||
if isinstance(arg, dict):
|
||||
return mkPen(**arg)
|
||||
elif arg is not None:
|
||||
elif arg != 'default':
|
||||
if isinstance(arg, QtGui.QPen):
|
||||
return arg
|
||||
elif arg is None:
|
||||
style = QtCore.Qt.NoPen
|
||||
else:
|
||||
color = arg
|
||||
|
||||
if color is None:
|
||||
|
831
graphicsItems.py
831
graphicsItems.py
File diff suppressed because it is too large
Load Diff
@ -113,6 +113,7 @@ class ImageWindow(ImageView):
|
||||
def __init__(self, *args, **kargs):
|
||||
mkQApp()
|
||||
self.win = QtGui.QMainWindow()
|
||||
self.win.resize(800,600)
|
||||
if 'title' in kargs:
|
||||
self.win.setWindowTitle(kargs['title'])
|
||||
del kargs['title']
|
||||
|
@ -1,242 +1,247 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'plotConfigTemplate.ui'
|
||||
# Form implementation generated from reading ui file './lib/util/pyqtgraph/plotConfigTemplate.ui'
|
||||
#
|
||||
# Created: Sat Jul 17 00:28:43 2010
|
||||
# by: PyQt4 UI code generator 4.7.2
|
||||
# Created: Wed May 18 20:44:20 2011
|
||||
# by: PyQt4 UI code generator 4.8.3
|
||||
#
|
||||
# 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("Form")
|
||||
Form.setObjectName(_fromUtf8("Form"))
|
||||
Form.resize(250, 340)
|
||||
Form.setMaximumSize(QtCore.QSize(250, 350))
|
||||
self.gridLayout_3 = QtGui.QGridLayout(Form)
|
||||
self.gridLayout_3.setMargin(0)
|
||||
self.gridLayout_3.setSpacing(0)
|
||||
self.gridLayout_3.setObjectName("gridLayout_3")
|
||||
self.gridLayout_3.setObjectName(_fromUtf8("gridLayout_3"))
|
||||
self.tabWidget = QtGui.QTabWidget(Form)
|
||||
self.tabWidget.setMaximumSize(QtCore.QSize(16777215, 16777215))
|
||||
self.tabWidget.setObjectName("tabWidget")
|
||||
self.tabWidget.setObjectName(_fromUtf8("tabWidget"))
|
||||
self.tab = QtGui.QWidget()
|
||||
self.tab.setObjectName("tab")
|
||||
self.tab.setObjectName(_fromUtf8("tab"))
|
||||
self.verticalLayout = QtGui.QVBoxLayout(self.tab)
|
||||
self.verticalLayout.setSpacing(0)
|
||||
self.verticalLayout.setMargin(0)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
|
||||
self.groupBox = QtGui.QGroupBox(self.tab)
|
||||
self.groupBox.setObjectName("groupBox")
|
||||
self.groupBox.setObjectName(_fromUtf8("groupBox"))
|
||||
self.gridLayout = QtGui.QGridLayout(self.groupBox)
|
||||
self.gridLayout.setMargin(0)
|
||||
self.gridLayout.setSpacing(0)
|
||||
self.gridLayout.setObjectName("gridLayout")
|
||||
self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
|
||||
self.xManualRadio = QtGui.QRadioButton(self.groupBox)
|
||||
self.xManualRadio.setObjectName("xManualRadio")
|
||||
self.xManualRadio.setObjectName(_fromUtf8("xManualRadio"))
|
||||
self.gridLayout.addWidget(self.xManualRadio, 0, 0, 1, 1)
|
||||
self.xMinText = QtGui.QLineEdit(self.groupBox)
|
||||
self.xMinText.setObjectName("xMinText")
|
||||
self.xMinText.setObjectName(_fromUtf8("xMinText"))
|
||||
self.gridLayout.addWidget(self.xMinText, 0, 1, 1, 1)
|
||||
self.xMaxText = QtGui.QLineEdit(self.groupBox)
|
||||
self.xMaxText.setObjectName("xMaxText")
|
||||
self.xMaxText.setObjectName(_fromUtf8("xMaxText"))
|
||||
self.gridLayout.addWidget(self.xMaxText, 0, 2, 1, 1)
|
||||
self.xAutoRadio = QtGui.QRadioButton(self.groupBox)
|
||||
self.xAutoRadio.setChecked(True)
|
||||
self.xAutoRadio.setObjectName("xAutoRadio")
|
||||
self.xAutoRadio.setObjectName(_fromUtf8("xAutoRadio"))
|
||||
self.gridLayout.addWidget(self.xAutoRadio, 1, 0, 1, 1)
|
||||
self.xAutoPercentSpin = QtGui.QSpinBox(self.groupBox)
|
||||
self.xAutoPercentSpin.setEnabled(True)
|
||||
self.xAutoPercentSpin.setMinimum(1)
|
||||
self.xAutoPercentSpin.setMaximum(100)
|
||||
self.xAutoPercentSpin.setSingleStep(1)
|
||||
self.xAutoPercentSpin.setProperty("value", 100)
|
||||
self.xAutoPercentSpin.setObjectName("xAutoPercentSpin")
|
||||
self.xAutoPercentSpin.setProperty(_fromUtf8("value"), 100)
|
||||
self.xAutoPercentSpin.setObjectName(_fromUtf8("xAutoPercentSpin"))
|
||||
self.gridLayout.addWidget(self.xAutoPercentSpin, 1, 1, 1, 2)
|
||||
self.xLinkCombo = QtGui.QComboBox(self.groupBox)
|
||||
self.xLinkCombo.setObjectName("xLinkCombo")
|
||||
self.xLinkCombo.setObjectName(_fromUtf8("xLinkCombo"))
|
||||
self.gridLayout.addWidget(self.xLinkCombo, 2, 1, 1, 2)
|
||||
self.xMouseCheck = QtGui.QCheckBox(self.groupBox)
|
||||
self.xMouseCheck.setChecked(True)
|
||||
self.xMouseCheck.setObjectName("xMouseCheck")
|
||||
self.xMouseCheck.setObjectName(_fromUtf8("xMouseCheck"))
|
||||
self.gridLayout.addWidget(self.xMouseCheck, 3, 1, 1, 1)
|
||||
self.xLogCheck = QtGui.QCheckBox(self.groupBox)
|
||||
self.xLogCheck.setObjectName("xLogCheck")
|
||||
self.xLogCheck.setObjectName(_fromUtf8("xLogCheck"))
|
||||
self.gridLayout.addWidget(self.xLogCheck, 3, 0, 1, 1)
|
||||
self.label = QtGui.QLabel(self.groupBox)
|
||||
self.label.setObjectName("label")
|
||||
self.label.setObjectName(_fromUtf8("label"))
|
||||
self.gridLayout.addWidget(self.label, 2, 0, 1, 1)
|
||||
self.verticalLayout.addWidget(self.groupBox)
|
||||
self.groupBox_2 = QtGui.QGroupBox(self.tab)
|
||||
self.groupBox_2.setObjectName("groupBox_2")
|
||||
self.groupBox_2.setObjectName(_fromUtf8("groupBox_2"))
|
||||
self.gridLayout_2 = QtGui.QGridLayout(self.groupBox_2)
|
||||
self.gridLayout_2.setMargin(0)
|
||||
self.gridLayout_2.setSpacing(0)
|
||||
self.gridLayout_2.setObjectName("gridLayout_2")
|
||||
self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2"))
|
||||
self.yManualRadio = QtGui.QRadioButton(self.groupBox_2)
|
||||
self.yManualRadio.setObjectName("yManualRadio")
|
||||
self.yManualRadio.setObjectName(_fromUtf8("yManualRadio"))
|
||||
self.gridLayout_2.addWidget(self.yManualRadio, 0, 0, 1, 1)
|
||||
self.yMinText = QtGui.QLineEdit(self.groupBox_2)
|
||||
self.yMinText.setObjectName("yMinText")
|
||||
self.yMinText.setObjectName(_fromUtf8("yMinText"))
|
||||
self.gridLayout_2.addWidget(self.yMinText, 0, 1, 1, 1)
|
||||
self.yMaxText = QtGui.QLineEdit(self.groupBox_2)
|
||||
self.yMaxText.setObjectName("yMaxText")
|
||||
self.yMaxText.setObjectName(_fromUtf8("yMaxText"))
|
||||
self.gridLayout_2.addWidget(self.yMaxText, 0, 2, 1, 1)
|
||||
self.yAutoRadio = QtGui.QRadioButton(self.groupBox_2)
|
||||
self.yAutoRadio.setChecked(True)
|
||||
self.yAutoRadio.setObjectName("yAutoRadio")
|
||||
self.yAutoRadio.setObjectName(_fromUtf8("yAutoRadio"))
|
||||
self.gridLayout_2.addWidget(self.yAutoRadio, 1, 0, 1, 1)
|
||||
self.yAutoPercentSpin = QtGui.QSpinBox(self.groupBox_2)
|
||||
self.yAutoPercentSpin.setEnabled(True)
|
||||
self.yAutoPercentSpin.setMinimum(1)
|
||||
self.yAutoPercentSpin.setMaximum(100)
|
||||
self.yAutoPercentSpin.setSingleStep(1)
|
||||
self.yAutoPercentSpin.setProperty("value", 100)
|
||||
self.yAutoPercentSpin.setObjectName("yAutoPercentSpin")
|
||||
self.yAutoPercentSpin.setProperty(_fromUtf8("value"), 100)
|
||||
self.yAutoPercentSpin.setObjectName(_fromUtf8("yAutoPercentSpin"))
|
||||
self.gridLayout_2.addWidget(self.yAutoPercentSpin, 1, 1, 1, 2)
|
||||
self.yLinkCombo = QtGui.QComboBox(self.groupBox_2)
|
||||
self.yLinkCombo.setObjectName("yLinkCombo")
|
||||
self.yLinkCombo.setObjectName(_fromUtf8("yLinkCombo"))
|
||||
self.gridLayout_2.addWidget(self.yLinkCombo, 2, 1, 1, 2)
|
||||
self.yMouseCheck = QtGui.QCheckBox(self.groupBox_2)
|
||||
self.yMouseCheck.setChecked(True)
|
||||
self.yMouseCheck.setObjectName("yMouseCheck")
|
||||
self.yMouseCheck.setObjectName(_fromUtf8("yMouseCheck"))
|
||||
self.gridLayout_2.addWidget(self.yMouseCheck, 3, 1, 1, 1)
|
||||
self.yLogCheck = QtGui.QCheckBox(self.groupBox_2)
|
||||
self.yLogCheck.setObjectName("yLogCheck")
|
||||
self.yLogCheck.setObjectName(_fromUtf8("yLogCheck"))
|
||||
self.gridLayout_2.addWidget(self.yLogCheck, 3, 0, 1, 1)
|
||||
self.label_2 = QtGui.QLabel(self.groupBox_2)
|
||||
self.label_2.setObjectName("label_2")
|
||||
self.label_2.setObjectName(_fromUtf8("label_2"))
|
||||
self.gridLayout_2.addWidget(self.label_2, 2, 0, 1, 1)
|
||||
self.verticalLayout.addWidget(self.groupBox_2)
|
||||
self.tabWidget.addTab(self.tab, "")
|
||||
self.tabWidget.addTab(self.tab, _fromUtf8(""))
|
||||
self.tab_2 = QtGui.QWidget()
|
||||
self.tab_2.setObjectName("tab_2")
|
||||
self.tab_2.setObjectName(_fromUtf8("tab_2"))
|
||||
self.verticalLayout_2 = QtGui.QVBoxLayout(self.tab_2)
|
||||
self.verticalLayout_2.setSpacing(0)
|
||||
self.verticalLayout_2.setMargin(0)
|
||||
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
||||
self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2"))
|
||||
self.powerSpectrumGroup = QtGui.QGroupBox(self.tab_2)
|
||||
self.powerSpectrumGroup.setCheckable(True)
|
||||
self.powerSpectrumGroup.setChecked(False)
|
||||
self.powerSpectrumGroup.setObjectName("powerSpectrumGroup")
|
||||
self.powerSpectrumGroup.setObjectName(_fromUtf8("powerSpectrumGroup"))
|
||||
self.verticalLayout_2.addWidget(self.powerSpectrumGroup)
|
||||
self.decimateGroup = QtGui.QGroupBox(self.tab_2)
|
||||
self.decimateGroup.setCheckable(True)
|
||||
self.decimateGroup.setObjectName("decimateGroup")
|
||||
self.decimateGroup.setObjectName(_fromUtf8("decimateGroup"))
|
||||
self.gridLayout_4 = QtGui.QGridLayout(self.decimateGroup)
|
||||
self.gridLayout_4.setMargin(0)
|
||||
self.gridLayout_4.setSpacing(0)
|
||||
self.gridLayout_4.setObjectName("gridLayout_4")
|
||||
self.gridLayout_4.setObjectName(_fromUtf8("gridLayout_4"))
|
||||
self.manualDecimateRadio = QtGui.QRadioButton(self.decimateGroup)
|
||||
self.manualDecimateRadio.setChecked(True)
|
||||
self.manualDecimateRadio.setObjectName("manualDecimateRadio")
|
||||
self.manualDecimateRadio.setObjectName(_fromUtf8("manualDecimateRadio"))
|
||||
self.gridLayout_4.addWidget(self.manualDecimateRadio, 0, 0, 1, 1)
|
||||
self.downsampleSpin = QtGui.QSpinBox(self.decimateGroup)
|
||||
self.downsampleSpin.setMinimum(1)
|
||||
self.downsampleSpin.setMaximum(100000)
|
||||
self.downsampleSpin.setProperty("value", 1)
|
||||
self.downsampleSpin.setObjectName("downsampleSpin")
|
||||
self.downsampleSpin.setProperty(_fromUtf8("value"), 1)
|
||||
self.downsampleSpin.setObjectName(_fromUtf8("downsampleSpin"))
|
||||
self.gridLayout_4.addWidget(self.downsampleSpin, 0, 1, 1, 1)
|
||||
self.autoDecimateRadio = QtGui.QRadioButton(self.decimateGroup)
|
||||
self.autoDecimateRadio.setChecked(False)
|
||||
self.autoDecimateRadio.setObjectName("autoDecimateRadio")
|
||||
self.autoDecimateRadio.setObjectName(_fromUtf8("autoDecimateRadio"))
|
||||
self.gridLayout_4.addWidget(self.autoDecimateRadio, 1, 0, 1, 1)
|
||||
self.maxTracesCheck = QtGui.QCheckBox(self.decimateGroup)
|
||||
self.maxTracesCheck.setObjectName("maxTracesCheck")
|
||||
self.maxTracesCheck.setObjectName(_fromUtf8("maxTracesCheck"))
|
||||
self.gridLayout_4.addWidget(self.maxTracesCheck, 2, 0, 1, 1)
|
||||
self.maxTracesSpin = QtGui.QSpinBox(self.decimateGroup)
|
||||
self.maxTracesSpin.setObjectName("maxTracesSpin")
|
||||
self.maxTracesSpin.setObjectName(_fromUtf8("maxTracesSpin"))
|
||||
self.gridLayout_4.addWidget(self.maxTracesSpin, 2, 1, 1, 1)
|
||||
self.forgetTracesCheck = QtGui.QCheckBox(self.decimateGroup)
|
||||
self.forgetTracesCheck.setObjectName("forgetTracesCheck")
|
||||
self.forgetTracesCheck.setObjectName(_fromUtf8("forgetTracesCheck"))
|
||||
self.gridLayout_4.addWidget(self.forgetTracesCheck, 3, 0, 1, 2)
|
||||
self.verticalLayout_2.addWidget(self.decimateGroup)
|
||||
self.averageGroup = QtGui.QGroupBox(self.tab_2)
|
||||
self.averageGroup.setCheckable(True)
|
||||
self.averageGroup.setChecked(False)
|
||||
self.averageGroup.setObjectName("averageGroup")
|
||||
self.averageGroup.setObjectName(_fromUtf8("averageGroup"))
|
||||
self.gridLayout_5 = QtGui.QGridLayout(self.averageGroup)
|
||||
self.gridLayout_5.setMargin(0)
|
||||
self.gridLayout_5.setSpacing(0)
|
||||
self.gridLayout_5.setObjectName("gridLayout_5")
|
||||
self.gridLayout_5.setObjectName(_fromUtf8("gridLayout_5"))
|
||||
self.avgParamList = QtGui.QListWidget(self.averageGroup)
|
||||
self.avgParamList.setObjectName("avgParamList")
|
||||
self.avgParamList.setObjectName(_fromUtf8("avgParamList"))
|
||||
self.gridLayout_5.addWidget(self.avgParamList, 0, 0, 1, 1)
|
||||
self.verticalLayout_2.addWidget(self.averageGroup)
|
||||
self.tabWidget.addTab(self.tab_2, "")
|
||||
self.tabWidget.addTab(self.tab_2, _fromUtf8(""))
|
||||
self.tab_3 = QtGui.QWidget()
|
||||
self.tab_3.setObjectName("tab_3")
|
||||
self.tab_3.setObjectName(_fromUtf8("tab_3"))
|
||||
self.verticalLayout_3 = QtGui.QVBoxLayout(self.tab_3)
|
||||
self.verticalLayout_3.setObjectName("verticalLayout_3")
|
||||
self.verticalLayout_3.setObjectName(_fromUtf8("verticalLayout_3"))
|
||||
self.alphaGroup = QtGui.QGroupBox(self.tab_3)
|
||||
self.alphaGroup.setCheckable(True)
|
||||
self.alphaGroup.setObjectName("alphaGroup")
|
||||
self.alphaGroup.setObjectName(_fromUtf8("alphaGroup"))
|
||||
self.horizontalLayout = QtGui.QHBoxLayout(self.alphaGroup)
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
|
||||
self.autoAlphaCheck = QtGui.QCheckBox(self.alphaGroup)
|
||||
self.autoAlphaCheck.setChecked(False)
|
||||
self.autoAlphaCheck.setObjectName("autoAlphaCheck")
|
||||
self.autoAlphaCheck.setObjectName(_fromUtf8("autoAlphaCheck"))
|
||||
self.horizontalLayout.addWidget(self.autoAlphaCheck)
|
||||
self.alphaSlider = QtGui.QSlider(self.alphaGroup)
|
||||
self.alphaSlider.setMaximum(1000)
|
||||
self.alphaSlider.setProperty("value", 1000)
|
||||
self.alphaSlider.setProperty(_fromUtf8("value"), 1000)
|
||||
self.alphaSlider.setOrientation(QtCore.Qt.Horizontal)
|
||||
self.alphaSlider.setObjectName("alphaSlider")
|
||||
self.alphaSlider.setObjectName(_fromUtf8("alphaSlider"))
|
||||
self.horizontalLayout.addWidget(self.alphaSlider)
|
||||
self.verticalLayout_3.addWidget(self.alphaGroup)
|
||||
self.gridGroup = QtGui.QGroupBox(self.tab_3)
|
||||
self.gridGroup.setCheckable(True)
|
||||
self.gridGroup.setChecked(False)
|
||||
self.gridGroup.setObjectName("gridGroup")
|
||||
self.gridGroup.setObjectName(_fromUtf8("gridGroup"))
|
||||
self.verticalLayout_4 = QtGui.QVBoxLayout(self.gridGroup)
|
||||
self.verticalLayout_4.setObjectName("verticalLayout_4")
|
||||
self.verticalLayout_4.setObjectName(_fromUtf8("verticalLayout_4"))
|
||||
self.gridAlphaSlider = QtGui.QSlider(self.gridGroup)
|
||||
self.gridAlphaSlider.setMaximum(255)
|
||||
self.gridAlphaSlider.setProperty("value", 70)
|
||||
self.gridAlphaSlider.setProperty(_fromUtf8("value"), 70)
|
||||
self.gridAlphaSlider.setOrientation(QtCore.Qt.Horizontal)
|
||||
self.gridAlphaSlider.setObjectName("gridAlphaSlider")
|
||||
self.gridAlphaSlider.setObjectName(_fromUtf8("gridAlphaSlider"))
|
||||
self.verticalLayout_4.addWidget(self.gridAlphaSlider)
|
||||
self.verticalLayout_3.addWidget(self.gridGroup)
|
||||
self.pointsGroup = QtGui.QGroupBox(self.tab_3)
|
||||
self.pointsGroup.setCheckable(True)
|
||||
self.pointsGroup.setObjectName("pointsGroup")
|
||||
self.pointsGroup.setObjectName(_fromUtf8("pointsGroup"))
|
||||
self.verticalLayout_5 = QtGui.QVBoxLayout(self.pointsGroup)
|
||||
self.verticalLayout_5.setObjectName("verticalLayout_5")
|
||||
self.verticalLayout_5.setObjectName(_fromUtf8("verticalLayout_5"))
|
||||
self.autoPointsCheck = QtGui.QCheckBox(self.pointsGroup)
|
||||
self.autoPointsCheck.setChecked(True)
|
||||
self.autoPointsCheck.setObjectName("autoPointsCheck")
|
||||
self.autoPointsCheck.setObjectName(_fromUtf8("autoPointsCheck"))
|
||||
self.verticalLayout_5.addWidget(self.autoPointsCheck)
|
||||
self.verticalLayout_3.addWidget(self.pointsGroup)
|
||||
spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
|
||||
self.verticalLayout_3.addItem(spacerItem)
|
||||
self.tabWidget.addTab(self.tab_3, "")
|
||||
self.tabWidget.addTab(self.tab_3, _fromUtf8(""))
|
||||
self.tab_4 = QtGui.QWidget()
|
||||
self.tab_4.setObjectName("tab_4")
|
||||
self.tab_4.setObjectName(_fromUtf8("tab_4"))
|
||||
self.gridLayout_7 = QtGui.QGridLayout(self.tab_4)
|
||||
self.gridLayout_7.setObjectName("gridLayout_7")
|
||||
self.gridLayout_7.setObjectName(_fromUtf8("gridLayout_7"))
|
||||
spacerItem1 = QtGui.QSpacerItem(59, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
|
||||
self.gridLayout_7.addItem(spacerItem1, 0, 0, 1, 1)
|
||||
self.gridLayout_6 = QtGui.QGridLayout()
|
||||
self.gridLayout_6.setObjectName("gridLayout_6")
|
||||
self.gridLayout_6.setObjectName(_fromUtf8("gridLayout_6"))
|
||||
self.saveSvgBtn = QtGui.QPushButton(self.tab_4)
|
||||
self.saveSvgBtn.setObjectName("saveSvgBtn")
|
||||
self.saveSvgBtn.setObjectName(_fromUtf8("saveSvgBtn"))
|
||||
self.gridLayout_6.addWidget(self.saveSvgBtn, 0, 0, 1, 1)
|
||||
self.saveImgBtn = QtGui.QPushButton(self.tab_4)
|
||||
self.saveImgBtn.setObjectName("saveImgBtn")
|
||||
self.saveImgBtn.setObjectName(_fromUtf8("saveImgBtn"))
|
||||
self.gridLayout_6.addWidget(self.saveImgBtn, 1, 0, 1, 1)
|
||||
self.saveMaBtn = QtGui.QPushButton(self.tab_4)
|
||||
self.saveMaBtn.setObjectName("saveMaBtn")
|
||||
self.saveMaBtn.setObjectName(_fromUtf8("saveMaBtn"))
|
||||
self.gridLayout_6.addWidget(self.saveMaBtn, 2, 0, 1, 1)
|
||||
self.saveCsvBtn = QtGui.QPushButton(self.tab_4)
|
||||
self.saveCsvBtn.setObjectName("saveCsvBtn")
|
||||
self.saveCsvBtn.setObjectName(_fromUtf8("saveCsvBtn"))
|
||||
self.gridLayout_6.addWidget(self.saveCsvBtn, 3, 0, 1, 1)
|
||||
self.gridLayout_7.addLayout(self.gridLayout_6, 0, 1, 1, 1)
|
||||
spacerItem2 = QtGui.QSpacerItem(59, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
|
||||
self.gridLayout_7.addItem(spacerItem2, 0, 2, 1, 1)
|
||||
spacerItem3 = QtGui.QSpacerItem(20, 211, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
|
||||
self.gridLayout_7.addItem(spacerItem3, 1, 1, 1, 1)
|
||||
self.tabWidget.addTab(self.tab_4, "")
|
||||
self.tabWidget.addTab(self.tab_4, _fromUtf8(""))
|
||||
self.gridLayout_3.addWidget(self.tabWidget, 0, 0, 1, 1)
|
||||
|
||||
self.retranslateUi(Form)
|
||||
|
26
widgets.py
26
widgets.py
@ -487,7 +487,7 @@ class ROI(QtGui.QGraphicsObject):
|
||||
if lp1.length() == 0 or lp0.length() == 0:
|
||||
return
|
||||
|
||||
ang = newState['angle'] + lp0.angle(lp1)
|
||||
ang = newState['angle'] - lp0.angle(lp1)
|
||||
if ang is None:
|
||||
return
|
||||
if self.rotateSnap or (modifiers & QtCore.Qt.ControlModifier):
|
||||
@ -504,7 +504,7 @@ class ROI(QtGui.QGraphicsObject):
|
||||
c1 = c * newState['size']
|
||||
tr = QtGui.QTransform()
|
||||
#tr.rotate(-ang * 180. / np.pi)
|
||||
tr.rotate(-ang)
|
||||
tr.rotate(ang)
|
||||
|
||||
cc = self.mapToParent(cs) - (tr.map(c1) + self.state['pos'])
|
||||
newState['angle'] = ang
|
||||
@ -665,9 +665,20 @@ class ROI(QtGui.QGraphicsObject):
|
||||
|
||||
origin = self.mapToItem(img, QtCore.QPointF(0, 0))
|
||||
|
||||
## vx and vy point in the directions of the slice axes, but must be scaled properly
|
||||
vx = self.mapToItem(img, QtCore.QPointF(1, 0)) - origin
|
||||
vy = self.mapToItem(img, QtCore.QPointF(0, 1)) - origin
|
||||
vectors = ((vx.x(), vx.y()), (vy.x(), vy.y()))
|
||||
|
||||
lvx = np.sqrt(vx.x()**2 + vx.y()**2)
|
||||
lvy = np.sqrt(vy.x()**2 + vy.y()**2)
|
||||
pxLen = img.width() / data.shape[axes[0]]
|
||||
sx = pxLen / lvx
|
||||
sy = pxLen / lvy
|
||||
|
||||
vectors = ((vx.x()*sx, vx.y()*sx), (vy.x()*sy, vy.y()*sy))
|
||||
shape = self.state['size']
|
||||
shape = [abs(shape[0]/sx), abs(shape[1]/sy)]
|
||||
|
||||
origin = (origin.x(), origin.y())
|
||||
|
||||
#print "shape", shape, "vectors", vectors, "origin", origin
|
||||
@ -812,16 +823,9 @@ class ROI(QtGui.QGraphicsObject):
|
||||
|
||||
def applyGlobalTransform(self, tr):
|
||||
st = self.getState()
|
||||
|
||||
st['scale'] = st['size']
|
||||
st = Transform(st)
|
||||
#trans = QtGui.QTransform()
|
||||
#trans.translate(*translate)
|
||||
#trans.rotate(-rotate)
|
||||
|
||||
#x2, y2 = trans.map(*st['pos'])
|
||||
|
||||
#self.setAngle(st['angle']+rotate*np.pi/180.)
|
||||
#self.setPos([x2, y2])
|
||||
st = (st * tr).saveState()
|
||||
st['size'] = st['scale']
|
||||
self.setState(st)
|
||||
|
Loading…
Reference in New Issue
Block a user