diff --git a/GraphicsView.py b/GraphicsView.py
index 2895d09b..9846d0c4 100644
--- a/GraphicsView.py
+++ b/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()
diff --git a/ImageView.py b/ImageView.py
index ce7a80be..3c293964 100644
--- a/ImageView.py
+++ b/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():
diff --git a/ImageViewTemplate.py b/ImageViewTemplate.py
index 6e2cdc9f..fe283a74 100644
--- a/ImageViewTemplate.py
+++ b/ImageViewTemplate.py
@@ -1,167 +1,173 @@
-# -*- coding: utf-8 -*-
-
-# Form implementation generated from reading ui file 'ImageViewTemplate.ui'
-#
-# Created: Sat Jul 17 13:05:44 2010
-# by: PyQt4 UI code generator 4.5.4
-#
-# WARNING! All changes made in this file will be lost!
-
-from PyQt4 import QtCore, QtGui
-
-class Ui_Form(object):
- def setupUi(self, Form):
- Form.setObjectName("Form")
- Form.resize(726, 588)
- self.verticalLayout = QtGui.QVBoxLayout(Form)
- self.verticalLayout.setSpacing(0)
- self.verticalLayout.setMargin(0)
- self.verticalLayout.setObjectName("verticalLayout")
- self.splitter = QtGui.QSplitter(Form)
- self.splitter.setOrientation(QtCore.Qt.Vertical)
- self.splitter.setObjectName("splitter")
- self.layoutWidget = QtGui.QWidget(self.splitter)
- self.layoutWidget.setObjectName("layoutWidget")
- self.gridLayout = QtGui.QGridLayout(self.layoutWidget)
- self.gridLayout.setMargin(0)
- self.gridLayout.setSpacing(0)
- self.gridLayout.setObjectName("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.gridLayout.addWidget(self.graphicsView, 1, 0, 3, 1)
- self.roiBtn = QtGui.QPushButton(self.layoutWidget)
- sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(1)
- sizePolicy.setHeightForWidth(self.roiBtn.sizePolicy().hasHeightForWidth())
- self.roiBtn.setSizePolicy(sizePolicy)
- self.roiBtn.setMaximumSize(QtCore.QSize(30, 16777215))
- self.roiBtn.setCheckable(True)
- self.roiBtn.setObjectName("roiBtn")
- self.gridLayout.addWidget(self.roiBtn, 3, 3, 1, 1)
- self.gradientWidget = GradientWidget(self.layoutWidget)
- sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(100)
- sizePolicy.setHeightForWidth(self.gradientWidget.sizePolicy().hasHeightForWidth())
- self.gradientWidget.setSizePolicy(sizePolicy)
- self.gradientWidget.setObjectName("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)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(1)
- sizePolicy.setHeightForWidth(self.normBtn.sizePolicy().hasHeightForWidth())
- self.normBtn.setSizePolicy(sizePolicy)
- self.normBtn.setMaximumSize(QtCore.QSize(30, 16777215))
- self.normBtn.setCheckable(True)
- self.normBtn.setObjectName("normBtn")
- self.gridLayout.addWidget(self.normBtn, 2, 3, 1, 1)
- self.normGroup = QtGui.QGroupBox(self.layoutWidget)
- self.normGroup.setObjectName("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.normSubtractRadio = QtGui.QRadioButton(self.normGroup)
- self.normSubtractRadio.setObjectName("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.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.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.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.gridLayout_2.addWidget(self.label_4, 2, 0, 1, 1)
- self.normROICheck = QtGui.QCheckBox(self.normGroup)
- self.normROICheck.setObjectName("normROICheck")
- self.gridLayout_2.addWidget(self.normROICheck, 1, 1, 1, 1)
- self.normXBlurSpin = QtGui.QDoubleSpinBox(self.normGroup)
- self.normXBlurSpin.setObjectName("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.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.gridLayout_2.addWidget(self.label_9, 2, 3, 1, 1)
- self.normYBlurSpin = QtGui.QDoubleSpinBox(self.normGroup)
- self.normYBlurSpin.setObjectName("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.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.gridLayout_2.addWidget(self.normOffRadio, 0, 3, 1, 1)
- self.normTimeRangeCheck = QtGui.QCheckBox(self.normGroup)
- self.normTimeRangeCheck.setObjectName("normTimeRangeCheck")
- self.gridLayout_2.addWidget(self.normTimeRangeCheck, 1, 3, 1, 1)
- self.normFrameCheck = QtGui.QCheckBox(self.normGroup)
- self.normFrameCheck.setObjectName("normFrameCheck")
- self.gridLayout_2.addWidget(self.normFrameCheck, 1, 2, 1, 1)
- self.normTBlurSpin = QtGui.QDoubleSpinBox(self.normGroup)
- self.normTBlurSpin.setObjectName("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)
- sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.roiPlot.sizePolicy().hasHeightForWidth())
- self.roiPlot.setSizePolicy(sizePolicy)
- self.roiPlot.setMinimumSize(QtCore.QSize(0, 40))
- self.roiPlot.setObjectName("roiPlot")
- self.verticalLayout.addWidget(self.splitter)
-
- self.retranslateUi(Form)
- QtCore.QMetaObject.connectSlotsByName(Form)
-
- def retranslateUi(self, Form):
- Form.setWindowTitle(QtGui.QApplication.translate("Form", "Form", None, QtGui.QApplication.UnicodeUTF8))
- self.roiBtn.setText(QtGui.QApplication.translate("Form", "R", None, QtGui.QApplication.UnicodeUTF8))
- self.normBtn.setText(QtGui.QApplication.translate("Form", "N", None, QtGui.QApplication.UnicodeUTF8))
- self.normGroup.setTitle(QtGui.QApplication.translate("Form", "Normalization", None, QtGui.QApplication.UnicodeUTF8))
- self.normSubtractRadio.setText(QtGui.QApplication.translate("Form", "Subtract", None, QtGui.QApplication.UnicodeUTF8))
- self.normDivideRadio.setText(QtGui.QApplication.translate("Form", "Divide", None, QtGui.QApplication.UnicodeUTF8))
- self.label_5.setText(QtGui.QApplication.translate("Form", "Operation:", None, QtGui.QApplication.UnicodeUTF8))
- self.label_3.setText(QtGui.QApplication.translate("Form", "Mean:", None, QtGui.QApplication.UnicodeUTF8))
- self.label_4.setText(QtGui.QApplication.translate("Form", "Blur:", None, QtGui.QApplication.UnicodeUTF8))
- self.normROICheck.setText(QtGui.QApplication.translate("Form", "ROI", None, QtGui.QApplication.UnicodeUTF8))
- self.label_8.setText(QtGui.QApplication.translate("Form", "X", None, QtGui.QApplication.UnicodeUTF8))
- self.label_9.setText(QtGui.QApplication.translate("Form", "Y", None, QtGui.QApplication.UnicodeUTF8))
- self.label_10.setText(QtGui.QApplication.translate("Form", "T", None, QtGui.QApplication.UnicodeUTF8))
- self.normOffRadio.setText(QtGui.QApplication.translate("Form", "Off", None, QtGui.QApplication.UnicodeUTF8))
- self.normTimeRangeCheck.setText(QtGui.QApplication.translate("Form", "Time range", None, QtGui.QApplication.UnicodeUTF8))
- self.normFrameCheck.setText(QtGui.QApplication.translate("Form", "Frame", None, QtGui.QApplication.UnicodeUTF8))
-
-from GraphicsView import GraphicsView
-from pyqtgraph.GradientWidget import GradientWidget
-from PlotWidget import PlotWidget
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file './lib/util/pyqtgraph/ImageViewTemplate.ui'
+#
+# 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(_fromUtf8("Form"))
+ Form.resize(726, 588)
+ self.verticalLayout = QtGui.QVBoxLayout(Form)
+ self.verticalLayout.setSpacing(0)
+ self.verticalLayout.setMargin(0)
+ self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
+ 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.gridLayout = QtGui.QGridLayout(self.layoutWidget)
+ self.gridLayout.setMargin(0)
+ self.gridLayout.setSpacing(0)
+ 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(_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)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(1)
+ sizePolicy.setHeightForWidth(self.roiBtn.sizePolicy().hasHeightForWidth())
+ self.roiBtn.setSizePolicy(sizePolicy)
+ self.roiBtn.setMaximumSize(QtCore.QSize(30, 16777215))
+ self.roiBtn.setCheckable(True)
+ 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)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(100)
+ sizePolicy.setHeightForWidth(self.gradientWidget.sizePolicy().hasHeightForWidth())
+ self.gradientWidget.setSizePolicy(sizePolicy)
+ 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)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(1)
+ sizePolicy.setHeightForWidth(self.normBtn.sizePolicy().hasHeightForWidth())
+ self.normBtn.setSizePolicy(sizePolicy)
+ self.normBtn.setMaximumSize(QtCore.QSize(30, 16777215))
+ self.normBtn.setCheckable(True)
+ self.normBtn.setObjectName(_fromUtf8("normBtn"))
+ self.gridLayout.addWidget(self.normBtn, 2, 3, 1, 1)
+ self.normGroup = QtGui.QGroupBox(self.layoutWidget)
+ 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(_fromUtf8("gridLayout_2"))
+ self.normSubtractRadio = QtGui.QRadioButton(self.normGroup)
+ 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(_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(_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(_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(_fromUtf8("label_4"))
+ self.gridLayout_2.addWidget(self.label_4, 2, 0, 1, 1)
+ self.normROICheck = QtGui.QCheckBox(self.normGroup)
+ self.normROICheck.setObjectName(_fromUtf8("normROICheck"))
+ self.gridLayout_2.addWidget(self.normROICheck, 1, 1, 1, 1)
+ self.normXBlurSpin = QtGui.QDoubleSpinBox(self.normGroup)
+ 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(_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(_fromUtf8("label_9"))
+ self.gridLayout_2.addWidget(self.label_9, 2, 3, 1, 1)
+ self.normYBlurSpin = QtGui.QDoubleSpinBox(self.normGroup)
+ 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(_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(_fromUtf8("normOffRadio"))
+ self.gridLayout_2.addWidget(self.normOffRadio, 0, 3, 1, 1)
+ self.normTimeRangeCheck = QtGui.QCheckBox(self.normGroup)
+ self.normTimeRangeCheck.setObjectName(_fromUtf8("normTimeRangeCheck"))
+ self.gridLayout_2.addWidget(self.normTimeRangeCheck, 1, 3, 1, 1)
+ self.normFrameCheck = QtGui.QCheckBox(self.normGroup)
+ self.normFrameCheck.setObjectName(_fromUtf8("normFrameCheck"))
+ self.gridLayout_2.addWidget(self.normFrameCheck, 1, 2, 1, 1)
+ self.normTBlurSpin = QtGui.QDoubleSpinBox(self.normGroup)
+ 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)
+ sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.roiPlot.sizePolicy().hasHeightForWidth())
+ self.roiPlot.setSizePolicy(sizePolicy)
+ self.roiPlot.setMinimumSize(QtCore.QSize(0, 40))
+ self.roiPlot.setObjectName(_fromUtf8("roiPlot"))
+ self.verticalLayout.addWidget(self.splitter)
+
+ self.retranslateUi(Form)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+
+ def retranslateUi(self, Form):
+ Form.setWindowTitle(QtGui.QApplication.translate("Form", "Form", None, QtGui.QApplication.UnicodeUTF8))
+ self.roiBtn.setText(QtGui.QApplication.translate("Form", "R", None, QtGui.QApplication.UnicodeUTF8))
+ self.normBtn.setText(QtGui.QApplication.translate("Form", "N", None, QtGui.QApplication.UnicodeUTF8))
+ self.normGroup.setTitle(QtGui.QApplication.translate("Form", "Normalization", None, QtGui.QApplication.UnicodeUTF8))
+ self.normSubtractRadio.setText(QtGui.QApplication.translate("Form", "Subtract", None, QtGui.QApplication.UnicodeUTF8))
+ self.normDivideRadio.setText(QtGui.QApplication.translate("Form", "Divide", None, QtGui.QApplication.UnicodeUTF8))
+ self.label_5.setText(QtGui.QApplication.translate("Form", "Operation:", None, QtGui.QApplication.UnicodeUTF8))
+ self.label_3.setText(QtGui.QApplication.translate("Form", "Mean:", None, QtGui.QApplication.UnicodeUTF8))
+ self.label_4.setText(QtGui.QApplication.translate("Form", "Blur:", None, QtGui.QApplication.UnicodeUTF8))
+ self.normROICheck.setText(QtGui.QApplication.translate("Form", "ROI", None, QtGui.QApplication.UnicodeUTF8))
+ self.label_8.setText(QtGui.QApplication.translate("Form", "X", None, QtGui.QApplication.UnicodeUTF8))
+ self.label_9.setText(QtGui.QApplication.translate("Form", "Y", None, QtGui.QApplication.UnicodeUTF8))
+ self.label_10.setText(QtGui.QApplication.translate("Form", "T", None, QtGui.QApplication.UnicodeUTF8))
+ self.normOffRadio.setText(QtGui.QApplication.translate("Form", "Off", None, QtGui.QApplication.UnicodeUTF8))
+ self.normTimeRangeCheck.setText(QtGui.QApplication.translate("Form", "Time range", None, QtGui.QApplication.UnicodeUTF8))
+ self.normFrameCheck.setText(QtGui.QApplication.translate("Form", "Frame", None, QtGui.QApplication.UnicodeUTF8))
+
+from GraphicsView import GraphicsView
+from pyqtgraph.GradientWidget import GradientWidget
+from PlotWidget import PlotWidget
diff --git a/PlotItem.py b/PlotItem.py
index 347e0c12..d53d2bfc 100644
--- a/PlotItem.py
+++ b/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]
diff --git a/PlotWidget.py b/PlotWidget.py
index c95547e7..1254b963 100644
--- a/PlotWidget.py
+++ b/PlotWidget.py
@@ -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):
diff --git a/Point.py b/Point.py
index 48f122da..b98dfad0 100644
--- a/Point.py
+++ b/Point.py
@@ -88,11 +88,14 @@ class Point(QtCore.QPointF):
def _math_(self, op, x):
#print "point math:", op
- try:
- return Point(getattr(QtCore.QPointF, op)(self, x))
- except:
- x = Point(x)
- return Point(getattr(self[0], op)(x[0]), getattr(self[1], op)(x[1]))
+ #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]))
def length(self):
"""Returns the vector length of this Point."""
diff --git a/Transform.py b/Transform.py
index 0529b89c..9938a190 100644
--- a/Transform.py
+++ b/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('R')
- 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('R')
+ 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)
\ No newline at end of file
diff --git a/debug.py b/debug.py
new file mode 100644
index 00000000..2a2157db
--- /dev/null
+++ b/debug.py
@@ -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 "" % (",".join(obj.keys()[:5]))
+ else:
+ return "" % (",".join(obj.keys()))
+ elif isinstance(obj, basestring):
+ if len(obj) > 50:
+ return '"%s..."' % obj[:50]
+ else:
+ return obj[:]
+ elif isinstance(obj, ndarray):
+ return "" % (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 == "":
+ #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 types report their class."""
+ typ = type(obj)
+ if typ == types.InstanceType:
+ return "" % 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
+
diff --git a/examples/test_Arrow.py b/examples/test_Arrow.py
index 07c12f30..7d0c4aad 100755
--- a/examples/test_Arrow.py
+++ b/examples/test_Arrow.py
@@ -11,6 +11,7 @@ import pyqtgraph as pg
app = QtGui.QApplication([])
mw = QtGui.QMainWindow()
+mw.resize(800,800)
p = pg.PlotWidget()
mw.setCentralWidget(p)
diff --git a/examples/test_ImageItem.py b/examples/test_ImageItem.py
index c86eb278..f48f0f51 100755
--- a/examples/test_ImageItem.py
+++ b/examples/test_ImageItem.py
@@ -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)
@@ -40,12 +41,27 @@ def updateData():
## Display the data
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:
diff --git a/examples/test_ImageView.py b/examples/test_ImageView.py
index 32868698..fd1fd8fe 100755
--- a/examples/test_ImageView.py
+++ b/examples/test_ImageView.py
@@ -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()
diff --git a/examples/test_MultiPlotWidget.py b/examples/test_MultiPlotWidget.py
index 621ca12b..4c72275b 100755
--- a/examples/test_MultiPlotWidget.py
+++ b/examples/test_MultiPlotWidget.py
@@ -18,6 +18,7 @@ except:
app = QtGui.QApplication([])
mw = QtGui.QMainWindow()
+mw.resize(800,800)
pw = MultiPlotWidget()
mw.setCentralWidget(pw)
mw.show()
diff --git a/examples/test_PlotWidget.py b/examples/test_PlotWidget.py
index 70a8310d..2b2ef496 100755
--- a/examples/test_PlotWidget.py
+++ b/examples/test_PlotWidget.py
@@ -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()
diff --git a/examples/test_ROItypes.py b/examples/test_ROItypes.py
index 76623541..f080e0b4 100755
--- a/examples/test_ROItypes.py
+++ b/examples/test_ROItypes.py
@@ -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
diff --git a/examples/test_draw.py b/examples/test_draw.py
index 9b7e111f..b40932ba 100755
--- a/examples/test_draw.py
+++ b/examples/test_draw.py
@@ -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)
diff --git a/examples/test_scatterPlot.py b/examples/test_scatterPlot.py
index 329e07f1..e8d91eea 100755
--- a/examples/test_scatterPlot.py
+++ b/examples/test_scatterPlot.py
@@ -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:
diff --git a/functions.py b/functions.py
index cdb2c9fc..e5b6a41b 100644
--- a/functions.py
+++ b/functions.py
@@ -17,7 +17,7 @@ colorAbbrev = {
}
-from PyQt4 import QtGui
+from PyQt4 import QtGui, QtCore
import numpy as np
import scipy.ndimage
@@ -40,21 +40,27 @@ 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
- color = arg
+ elif arg is None:
+ style = QtCore.Qt.NoPen
+ else:
+ color = arg
if color is None:
color = mkColor(200, 200, 200)
diff --git a/graphicsItems.py b/graphicsItems.py
index be3dc028..63de57e0 100644
--- a/graphicsItems.py
+++ b/graphicsItems.py
@@ -21,14 +21,14 @@ try:
except:
pass
from scipy.fftpack import fft
-from scipy.signal import resample
+#from scipy.signal import resample
import scipy.stats
#from metaarray import MetaArray
from Point import *
from functions import *
import types, sys, struct
import weakref
-#import debug
+import debug
#from debug import *
## QGraphicsObject didn't appear until 4.6; this is for compatibility with 4.5
@@ -191,12 +191,13 @@ class ImageItem(QtGui.QGraphicsObject):
else:
useWeave = False
- def __init__(self, image=None, copy=True, parent=None, border=None, *args):
+ def __init__(self, image=None, copy=True, parent=None, border=None, mode=None, *args):
#QObjectWorkaround.__init__(self)
QtGui.QGraphicsObject.__init__(self)
#self.pixmapItem = QtGui.QGraphicsPixmapItem(self)
self.qimage = QtGui.QImage()
self.pixmap = None
+ self.paintMode = mode
#self.useWeave = True
self.blackLevel = None
self.whiteLevel = None
@@ -213,7 +214,11 @@ class ImageItem(QtGui.QGraphicsObject):
if image is not None:
self.updateImage(image, copy, autoRange=True)
#self.setCacheMode(QtGui.QGraphicsItem.DeviceCoordinateCache)
-
+
+ def setCompositionMode(self, mode):
+ self.paintMode = mode
+ self.update()
+
def setAlpha(self, alpha):
self.alpha = alpha
self.updateImage()
@@ -257,6 +262,8 @@ class ImageItem(QtGui.QGraphicsObject):
return self.whiteLevel, self.blackLevel
def updateImage(self, image=None, copy=True, autoRange=False, clipMask=None, white=None, black=None, axes=None):
+ prof = debug.Profiler('ImageItem.updateImage 0x%x' %id(self), disabled=True)
+ #debug.printTrace()
if axes is None:
axh = {'x': 0, 'y': 1, 'c': 2}
else:
@@ -280,6 +287,7 @@ class ImageItem(QtGui.QGraphicsObject):
else:
self.image = image.view(np.ndarray)
#print " image max:", self.image.max(), "min:", self.image.min()
+ prof.mark('1')
# Determine scale factors
if autoRange or self.blackLevel is None:
@@ -296,41 +304,49 @@ class ImageItem(QtGui.QGraphicsObject):
else:
scale = 0.
+ prof.mark('2')
## Recolor and convert to 8 bit per channel
# Try using weave, then fall back to python
shape = self.image.shape
black = float(self.blackLevel)
- try:
- if not ImageItem.useWeave:
- raise Exception('Skipping weave compile')
- sim = np.ascontiguousarray(self.image)
- sim.shape = sim.size
- im = np.empty(sim.shape, dtype=np.ubyte)
- n = im.size
+ white = float(self.whiteLevel)
+
+ if black == 0 and white == 255 and self.image.dtype == np.ubyte:
+ im = self.image
- code = """
- for( int i=0; i 255.0 )
- a = 255.0;
- else if( a < 0.0 )
- a = 0.0;
- im(i) = a;
- }
- """
-
- weave.inline(code, ['sim', 'im', 'n', 'black', 'scale'], type_converters=converters.blitz, compiler = 'gcc')
- sim.shape = shape
- im.shape = shape
- except:
- if ImageItem.useWeave:
- ImageItem.useWeave = False
- #sys.excepthook(*sys.exc_info())
- #print "=============================================================================="
- print "Weave compile failed, falling back to slower version."
- self.image.shape = shape
- im = ((self.image - black) * scale).clip(0.,255.).astype(np.ubyte)
+ else:
+ try:
+ if not ImageItem.useWeave:
+ raise Exception('Skipping weave compile')
+ sim = np.ascontiguousarray(self.image)
+ sim.shape = sim.size
+ im = np.empty(sim.shape, dtype=np.ubyte)
+ n = im.size
+
+ code = """
+ for( int i=0; i 255.0 )
+ a = 255.0;
+ else if( a < 0.0 )
+ a = 0.0;
+ im(i) = a;
+ }
+ """
+
+ weave.inline(code, ['sim', 'im', 'n', 'black', 'scale'], type_converters=converters.blitz, compiler = 'gcc')
+ sim.shape = shape
+ im.shape = shape
+ except:
+ if ImageItem.useWeave:
+ ImageItem.useWeave = False
+ #sys.excepthook(*sys.exc_info())
+ #print "=============================================================================="
+ print "Weave compile failed, falling back to slower version."
+ self.image.shape = shape
+ im = ((self.image - black) * scale).clip(0.,255.).astype(np.ubyte)
+ prof.mark('3')
try:
im1 = np.empty((im.shape[axh['y']], im.shape[axh['x']], 4), dtype=np.ubyte)
@@ -338,6 +354,7 @@ class ImageItem(QtGui.QGraphicsObject):
print im.shape, axh
raise
alpha = np.clip(int(255 * self.alpha), 0, 255)
+ prof.mark('4')
# Fill image
if im.ndim == 2:
im2 = im.transpose(axh['y'], axh['x'])
@@ -363,33 +380,40 @@ class ImageItem(QtGui.QGraphicsObject):
raise Exception("Image must be 2 or 3 dimensions")
#self.im1 = im1
# Display image
-
+ prof.mark('5')
if self.clipLevel is not None or clipMask is not None:
- if clipMask is not None:
- mask = clipMask.transpose()
- else:
- mask = (self.image < self.clipLevel).transpose()
- im1[..., 0][mask] *= 0.5
- im1[..., 1][mask] *= 0.5
- im1[..., 2][mask] = 255
+ if clipMask is not None:
+ mask = clipMask.transpose()
+ else:
+ mask = (self.image < self.clipLevel).transpose()
+ im1[..., 0][mask] *= 0.5
+ im1[..., 1][mask] *= 0.5
+ im1[..., 2][mask] = 255
+ prof.mark('6')
#print "Final image:", im1.dtype, im1.min(), im1.max(), im1.shape
self.ims = im1.tostring() ## Must be held in memory here because qImage won't do it for us :(
+ prof.mark('7')
qimage = QtGui.QImage(buffer(self.ims), im1.shape[1], im1.shape[0], QtGui.QImage.Format_ARGB32)
+ prof.mark('8')
self.pixmap = QtGui.QPixmap.fromImage(qimage)
+ prof.mark('9')
##del self.ims
#self.pixmapItem.setPixmap(self.pixmap)
self.update()
+ prof.mark('10')
if gotNewData:
#self.emit(QtCore.SIGNAL('imageChanged'))
self.sigImageChanged.emit()
+
+ prof.finish()
def getPixmap(self):
return self.pixmap.copy()
def getHistogram(self, bins=500, step=3):
- """returns an x and y arrays containing the histogram values for the current image.
+ """returns x and y arrays containing the histogram values for the current image.
The step argument causes pixels to be skipped when computing the histogram to save time."""
stepData = self.image[::step, ::step]
hist = np.histogram(stepData, bins=bins)
@@ -397,7 +421,7 @@ class ImageItem(QtGui.QGraphicsObject):
def mousePressEvent(self, ev):
if self.drawKernel is not None and ev.button() == QtCore.Qt.LeftButton:
- self.drawAt(ev.pos())
+ self.drawAt(ev.pos(), ev)
ev.accept()
else:
ev.ignore()
@@ -405,24 +429,80 @@ class ImageItem(QtGui.QGraphicsObject):
def mouseMoveEvent(self, ev):
#print "mouse move", ev.pos()
if self.drawKernel is not None:
- self.drawAt(ev.pos())
+ self.drawAt(ev.pos(), ev)
def mouseReleaseEvent(self, ev):
pass
- def drawAt(self, pos):
- self.image[int(pos.x()), int(pos.y())] += 1
- self.updateImage()
+ def tabletEvent(self, ev):
+ print ev.device()
+ print ev.pointerType()
+ print ev.pressure()
+
+ def drawAt(self, pos, ev=None):
+ pos = [int(pos.x()), int(pos.y())]
+ dk = self.drawKernel
+ kc = self.drawKernelCenter
+ sx = [0,dk.shape[0]]
+ sy = [0,dk.shape[1]]
+ tx = [pos[0] - kc[0], pos[0] - kc[0]+ dk.shape[0]]
+ ty = [pos[1] - kc[1], pos[1] - kc[1]+ dk.shape[1]]
- def setDrawKernel(self, kernel=None):
+ for i in [0,1]:
+ dx1 = -min(0, tx[i])
+ dx2 = min(0, self.image.shape[0]-tx[i])
+ tx[i] += dx1+dx2
+ sx[i] += dx1+dx2
+
+ dy1 = -min(0, ty[i])
+ dy2 = min(0, self.image.shape[1]-ty[i])
+ ty[i] += dy1+dy2
+ sy[i] += dy1+dy2
+
+ #print sx
+ #print sy
+ #print tx
+ #print ty
+ #print self.image.shape
+ #print self.image[tx[0]:tx[1], ty[0]:ty[1]].shape
+ #print dk[sx[0]:sx[1], sy[0]:sy[1]].shape
+ ts = (slice(tx[0],tx[1]), slice(ty[0],ty[1]))
+ ss = (slice(sx[0],sx[1]), slice(sy[0],sy[1]))
+ #src = dk[sx[0]:sx[1], sy[0]:sy[1]]
+ #mask = self.drawMask[sx[0]:sx[1], sy[0]:sy[1]]
+ mask = self.drawMask
+ src = dk
+ #print self.image[ts].shape, src.shape
+
+ if callable(self.drawMode):
+ self.drawMode(dk, self.image, mask, ss, ts, ev)
+ else:
+ mask = mask[ss]
+ src = src[ss]
+ if self.drawMode == 'set':
+ if mask is not None:
+ self.image[ts] = self.image[ts] * (1-mask) + src * mask
+ else:
+ self.image[ts] = src
+ elif self.drawMode == 'add':
+ self.image[ts] += src
+ else:
+ raise Exception("Unknown draw mode '%s'" % self.drawMode)
+ self.updateImage()
+
+ def setDrawKernel(self, kernel=None, mask=None, center=(0,0), mode='set'):
self.drawKernel = kernel
+ self.drawKernelCenter = center
+ self.drawMode = mode
+ self.drawMask = mask
def paint(self, p, *args):
#QtGui.QGraphicsPixmapItem.paint(self, p, *args)
if self.pixmap is None:
return
-
+ if self.paintMode is not None:
+ p.setCompositionMode(self.paintMode)
p.drawPixmap(self.boundingRect(), self.pixmap, QtCore.QRectF(0, 0, self.pixmap.width(), self.pixmap.height()))
if self.border is not None:
p.setPen(self.border)
@@ -492,7 +572,8 @@ class PlotCurveItem(GraphicsObject):
ds = self.opts['downsample']
if ds > 1:
x = x[::ds]
- y = resample(y[:len(x)*ds], len(x))
+ #y = resample(y[:len(x)*ds], len(x)) ## scipy.signal.resample causes nasty ringing
+ y = y[::ds]
if self.opts['spectrumMode']:
f = fft(y) / len(y)
y = abs(f[1:len(f)/2])
@@ -603,7 +684,7 @@ class PlotCurveItem(GraphicsObject):
self.updateData(y, x, copy)
def updateData(self, data, x=None, copy=False):
- #prof = debug.Profiler('PlotCurveItem.updateData', disabled=True)
+ prof = debug.Profiler('PlotCurveItem.updateData', disabled=True)
if isinstance(data, list):
data = np.array(data)
if isinstance(x, list):
@@ -630,7 +711,11 @@ class PlotCurveItem(GraphicsObject):
x = data[tuple(ind)]
elif data.ndim == 1:
y = data
- #prof.mark("data checks")
+ prof.mark("data checks")
+
+ self.setCacheMode(QtGui.QGraphicsItem.NoCache) ## Disabling and re-enabling the cache works around a bug in Qt 4.6 causing the cached results to display incorrectly
+ ## Test this bug with test_PlotWidget and zoom in on the animated plot
+
self.prepareGeometryChange()
if copy:
self.yData = y.copy()
@@ -641,7 +726,7 @@ class PlotCurveItem(GraphicsObject):
self.xData = x.copy()
else:
self.xData = x
- #prof.mark('copy')
+ prof.mark('copy')
if x is None:
self.xData = np.arange(0, self.yData.shape[0])
@@ -652,15 +737,19 @@ class PlotCurveItem(GraphicsObject):
self.path = None
self.xDisp = self.yDisp = None
- #prof.mark('set')
+ prof.mark('set')
self.update()
- #prof.mark('update')
+ prof.mark('update')
#self.emit(QtCore.SIGNAL('plotChanged'), self)
self.sigPlotChanged.emit(self)
- #prof.mark('emit')
+ prof.mark('emit')
#prof.finish()
+ #self.setCacheMode(QtGui.QGraphicsItem.DeviceCoordinateCache)
+ prof.mark('set cache mode')
+ prof.finish()
def generatePath(self, x, y):
+ prof = debug.Profiler('PlotCurveItem.generatePath', disabled=True)
path = QtGui.QPainterPath()
## Create all vertices in path. The method used below creates a binary format so that all
@@ -680,31 +769,31 @@ class PlotCurveItem(GraphicsObject):
##
## All values are big endian--pack using struct.pack('>d') or struct.pack('>i')
- #prof = debug.Profiler('PlotCurveItem.generatePath', disabled=True)
-
n = x.shape[0]
# create empty array, pad with extra space on either end
arr = np.empty(n+2, dtype=[('x', '>f8'), ('y', '>f8'), ('c', '>i4')])
- #prof.mark('create empty')
# write first two integers
+ prof.mark('allocate empty')
arr.data[12:20] = struct.pack('>ii', n, 0)
+ prof.mark('pack header')
# Fill array with vertex values
arr[1:-1]['x'] = x
arr[1:-1]['y'] = y
arr[1:-1]['c'] = 1
- #prof.mark('fill array')
+ prof.mark('fill array')
# write last 0
lastInd = 20*(n+1)
arr.data[lastInd:lastInd+4] = struct.pack('>i', 0)
-
+ prof.mark('footer')
# create datastream object and stream into path
buf = QtCore.QByteArray(arr.data[12:lastInd+4]) # I think one unnecessary copy happens here
- #prof.mark('create buffer')
+ prof.mark('create buffer')
ds = QtCore.QDataStream(buf)
- #prof.mark('create dataStream')
+ prof.mark('create datastream')
ds >> path
- #prof.mark('load path')
- #prof.finish()
+ prof.mark('load')
+
+ prof.finish()
return path
def boundingRect(self):
@@ -729,7 +818,7 @@ class PlotCurveItem(GraphicsObject):
return QtCore.QRectF(xmin, ymin, xmax-xmin, ymax-ymin)
def paint(self, p, opt, widget):
- #prof = debug.Profiler('PlotCurveItem.paint '+str(id(self)), disabled=True)
+ prof = debug.Profiler('PlotCurveItem.paint '+str(id(self)), disabled=True)
if self.xData is None:
return
#if self.opts['spectrumMode']:
@@ -741,7 +830,7 @@ class PlotCurveItem(GraphicsObject):
if self.path is None:
self.path = self.generatePath(*self.getData())
path = self.path
- #prof.mark('generate path')
+ prof.mark('generate path')
if self.shadow is not None:
sp = QtGui.QPen(self.shadow)
@@ -763,9 +852,9 @@ class PlotCurveItem(GraphicsObject):
p.drawPath(path)
p.setPen(cp)
p.drawPath(path)
- #prof.mark('drawPath')
+ prof.mark('drawPath')
- #prof.finish()
+ prof.finish()
#p.setPen(QtGui.QPen(QtGui.QColor(255,0,0)))
#p.drawRect(self.boundingRect())
@@ -822,10 +911,10 @@ class CurvePoint(QtGui.QGraphicsObject):
self.setIndex(index)
def setPos(self, pos):
- self.setProperty('position', pos)
+ self.setProperty('position', float(pos))## cannot use numpy types here, MUST be python float.
def setIndex(self, index):
- self.setProperty('index', index)
+ self.setProperty('index', int(index)) ## cannot use numpy types here, MUST be python int.
def event(self, ev):
if not isinstance(ev, QtCore.QDynamicPropertyChangeEvent) or self.curve() is None:
@@ -840,7 +929,7 @@ class CurvePoint(QtGui.QGraphicsObject):
(x, y) = self.curve().getData()
if index is None:
- #print self.property('position').toDouble()[0], self.property('position').typeName()
+ #print ev.propertyName(), self.property('position').toDouble()[0], self.property('position').typeName()
index = (len(x)-1) * clip(self.property('position').toDouble()[0], 0.0, 1.0)
if index != int(index): ## interpolate floating-point values
@@ -947,28 +1036,54 @@ class CurveArrow(CurvePoint):
-class ScatterPlotItem(QtGui.QGraphicsWidget):
+class ScatterPlotItem(GraphicsObject):
- sigPointClicked = QtCore.Signal(object, object)
+ #sigPointClicked = QtCore.Signal(object, object)
+ sigClicked = QtCore.Signal(object, object) ## self, points
- def __init__(self, spots=None, pxMode=True, pen=None, brush=None, size=5):
- QtGui.QGraphicsWidget.__init__(self)
+ def __init__(self, spots=None, x=None, y=None, pxMode=True, pen='default', brush='default', size=5, identical=False, data=None):
+ """
+ Arguments:
+ spots: list of dicts. Each dict specifies parameters for a single spot.
+ x,y: array of x,y values. Alternatively, specify spots['pos'] = (x,y)
+ pxMode: If True, spots are always the same size regardless of scaling
+ identical: If True, all spots are forced to look identical.
+ This can result in performance enhancement."""
+ GraphicsObject.__init__(self)
self.spots = []
self.range = [[0,0], [0,0]]
+ self.identical = identical
+ self._spotPixmap = None
- if brush is None:
- brush = QtGui.QBrush(QtGui.QColor(100, 100, 150))
- self.brush = brush
+ if brush == 'default':
+ self.brush = QtGui.QBrush(QtGui.QColor(100, 100, 150))
+ else:
+ self.brush = mkBrush(brush)
- if pen is None:
- pen = QtGui.QPen(QtGui.QColor(200, 200, 200))
- self.pen = pen
+ if pen == 'default':
+ self.pen = QtGui.QPen(QtGui.QColor(200, 200, 200))
+ else:
+ self.pen = mkPen(pen)
self.size = size
self.pxMode = pxMode
- if spots is not None:
- self.setPoints(spots)
+ if spots is not None or x is not None:
+ self.setPoints(spots, x, y, data)
+
+ #self.optimize = optimize
+ #if optimize:
+ #self.spotImage = QtGui.QImage(size, size, QtGui.QImage.Format_ARGB32_Premultiplied)
+ #self.spotImage.fill(0)
+ #p = QtGui.QPainter(self.spotImage)
+ #p.setRenderHint(p.Antialiasing)
+ #p.setBrush(brush)
+ #p.setPen(pen)
+ #p.drawEllipse(0, 0, size, size)
+ #p.end()
+ #self.optimizePixmap = QtGui.QPixmap(self.spotImage)
+ #self.optimizeFragments = []
+ #self.setFlags(self.flags() | self.ItemIgnoresTransformations)
def setPxMode(self, mode):
self.pxMode = mode
@@ -985,15 +1100,28 @@ class ScatterPlotItem(QtGui.QGraphicsWidget):
def getRange(self, ax, percent):
return self.range[ax]
- def setPoints(self, spots):
+ def setPoints(self, spots=None, x=None, y=None, data=None):
self.clear()
self.range = [[0,0],[0,0]]
- self.addPoints(spots)
+ self.addPoints(spots, x, y, data)
- def addPoints(self, spots):
+ def addPoints(self, spots=None, x=None, y=None, data=None):
xmn = ymn = xmx = ymx = None
- for s in spots:
- pos = Point(s['pos'])
+ if spots is not None:
+ n = len(spots)
+ else:
+ n = len(x)
+
+ for i in range(n):
+ if spots is not None:
+ s = spots[i]
+ pos = Point(s['pos'])
+ else:
+ s = {}
+ pos = Point(x[i], y[i])
+ if data is not None:
+ s['data'] = data[i]
+
size = s.get('size', self.size)
if self.pxMode:
psize = 0
@@ -1013,17 +1141,40 @@ class ScatterPlotItem(QtGui.QGraphicsWidget):
brush = s.get('brush', self.brush)
pen = s.get('pen', self.pen)
pen.setCosmetic(True)
- data = s.get('data', None)
- item = self.mkSpot(pos, size, self.pxMode, brush, pen, data)
+ data2 = s.get('data', None)
+ item = self.mkSpot(pos, size, self.pxMode, brush, pen, data2, index=len(self.spots))
self.spots.append(item)
+ #if self.optimize:
+ #item.hide()
+ #frag = QtGui.QPainter.PixmapFragment.create(pos, QtCore.QRectF(0, 0, size, size))
+ #self.optimizeFragments.append(frag)
self.range = [[xmn, xmx], [ymn, ymx]]
+ #def paint(self, p, *args):
+ #if not self.optimize:
+ #return
+ ##p.setClipRegion(self.boundingRect())
+ #p.drawPixmapFragments(self.optimizeFragments, self.optimizePixmap)
- def mkSpot(self, pos, size, pxMode, brush, pen, data):
- item = SpotItem(size, pxMode, brush, pen, data)
+ def paint(self, *args):
+ pass
+
+ def spotPixmap(self):
+ if not self.identical:
+ return None
+ if self._spotPixmap is None:
+ self._spotPixmap = PixmapSpotItem.makeSpotImage(self.size, self.pen, self.brush)
+ return self._spotPixmap
+
+ def mkSpot(self, pos, size, pxMode, brush, pen, data, index=None):
+ if pxMode:
+ img = self.spotPixmap()
+ item = PixmapSpotItem(size, brush, pen, data, image=img, index=index)
+ else:
+ item = SpotItem(size, pxMode, brush, pen, data, index=index)
item.setParentItem(self)
item.setPos(pos)
- item.sigClicked.connect(self.pointClicked)
+ #item.sigClicked.connect(self.pointClicked)
return item
def boundingRect(self):
@@ -1031,32 +1182,92 @@ class ScatterPlotItem(QtGui.QGraphicsWidget):
if xmn is None or xmx is None or ymn is None or ymx is None:
return QtCore.QRectF()
return QtCore.QRectF(xmn, ymn, xmx-xmn, ymx-ymn)
+ return QtCore.QRectF(xmn-1, ymn-1, xmx-xmn+2, ymx-ymn+2)
- def paint(self, p, *args):
- pass
-
- def pointClicked(self, point):
- self.sigPointClicked.emit(self, point)
+ #def pointClicked(self, point):
+ #self.sigPointClicked.emit(self, point)
def points(self):
return self.spots[:]
-class SpotItem(QtGui.QGraphicsWidget):
- sigClicked = QtCore.Signal(object)
+ def pointsAt(self, pos):
+ x = pos.x()
+ y = pos.y()
+ pw = self.pixelWidth()
+ ph = self.pixelHeight()
+ pts = []
+ for s in self.spots:
+ sp = s.pos()
+ ss = s.size
+ sx = sp.x()
+ sy = sp.y()
+ s2x = s2y = ss * 0.5
+ if self.pxMode:
+ s2x *= pw
+ s2y *= ph
+ if x > sx-s2x and x < sx+s2x and y > sy-s2y and y < sy+s2y:
+ pts.append(s)
+ #print "HIT:", x, y, sx, sy, s2x, s2y
+ #else:
+ #print "No hit:", (x, y), (sx, sy)
+ #print " ", (sx-s2x, sy-s2y), (sx+s2x, sy+s2y)
+ pts.sort(lambda a,b: cmp(b.zValue(), a.zValue()))
+ return pts
+
+
+ def mousePressEvent(self, ev):
+ QtGui.QGraphicsItem.mousePressEvent(self, ev)
+ if ev.button() == QtCore.Qt.LeftButton:
+ pts = self.pointsAt(ev.pos())
+ if len(pts) > 0:
+ self.mouseMoved = False
+ self.ptsClicked = pts
+ ev.accept()
+ else:
+ #print "no spots"
+ ev.ignore()
+ else:
+ ev.ignore()
+
+ def mouseMoveEvent(self, ev):
+ QtGui.QGraphicsItem.mouseMoveEvent(self, ev)
+ self.mouseMoved = True
+ pass
- def __init__(self, size, pxMode, brush, pen, data):
+ def mouseReleaseEvent(self, ev):
+ QtGui.QGraphicsItem.mouseReleaseEvent(self, ev)
+ if not self.mouseMoved:
+ self.sigClicked.emit(self, self.ptsClicked)
+
+
+class SpotItem(QtGui.QGraphicsWidget):
+ #sigClicked = QtCore.Signal(object)
+
+ def __init__(self, size, pxMode, brush, pen, data, index=None):
QtGui.QGraphicsWidget.__init__(self)
- if pxMode:
- self.setCacheMode(self.DeviceCoordinateCache)
- self.setFlags(self.flags() | self.ItemIgnoresTransformations)
- #self.setCacheMode(self.DeviceCoordinateCache) ## causes crash on linux
+ self.pxMode = pxMode
+
self.pen = pen
self.brush = brush
- self.path = QtGui.QPainterPath()
self.size = size
+ self.index = index
#s2 = size/2.
+ self.path = QtGui.QPainterPath()
self.path.addEllipse(QtCore.QRectF(-0.5, -0.5, 1, 1))
- self.scale(size, size)
+ if pxMode:
+ #self.setCacheMode(self.DeviceCoordinateCache) ## broken.
+ self.setFlags(self.flags() | self.ItemIgnoresTransformations)
+ self.spotImage = QtGui.QImage(size, size, QtGui.QImage.Format_ARGB32_Premultiplied)
+ self.spotImage.fill(0)
+ p = QtGui.QPainter(self.spotImage)
+ p.setRenderHint(p.Antialiasing)
+ p.setBrush(brush)
+ p.setPen(pen)
+ p.drawEllipse(0, 0, size, size)
+ p.end()
+ self.pixmap = QtGui.QPixmap(self.spotImage)
+ else:
+ self.scale(size, size)
self.data = data
def setBrush(self, brush):
@@ -1074,31 +1285,104 @@ class SpotItem(QtGui.QGraphicsWidget):
return self.path
def paint(self, p, *opts):
- p.setPen(self.pen)
- p.setBrush(self.brush)
- p.drawPath(self.path)
-
- def mousePressEvent(self, ev):
- QtGui.QGraphicsItem.mousePressEvent(self, ev)
- if ev.button() == QtCore.Qt.LeftButton:
- self.mouseMoved = False
- ev.accept()
+ if self.pxMode:
+ p.drawPixmap(QtCore.QPoint(int(-0.5*self.size), int(-0.5*self.size)), self.pixmap)
else:
- ev.ignore()
+ p.setPen(self.pen)
+ p.setBrush(self.brush)
+ p.drawPath(self.path)
+
+ #def mousePressEvent(self, ev):
+ #QtGui.QGraphicsItem.mousePressEvent(self, ev)
+ #if ev.button() == QtCore.Qt.LeftButton:
+ #self.mouseMoved = False
+ #ev.accept()
+ #else:
+ #ev.ignore()
- def mouseMoveEvent(self, ev):
- QtGui.QGraphicsItem.mouseMoveEvent(self, ev)
- self.mouseMoved = True
- pass
+ #def mouseMoveEvent(self, ev):
+ #QtGui.QGraphicsItem.mouseMoveEvent(self, ev)
+ #self.mouseMoved = True
+ #pass
- def mouseReleaseEvent(self, ev):
- QtGui.QGraphicsItem.mouseReleaseEvent(self, ev)
- if not self.mouseMoved:
- self.sigClicked.emit(self)
+ #def mouseReleaseEvent(self, ev):
+ #QtGui.QGraphicsItem.mouseReleaseEvent(self, ev)
+ #if not self.mouseMoved:
+ #self.sigClicked.emit(self)
+
+class PixmapSpotItem(QtGui.QGraphicsItem):
+ #sigClicked = QtCore.Signal(object)
+
+ def __init__(self, size, brush, pen, data, image=None, index=None):
+ """This class draws a scale-invariant image centered at 0,0.
+ If no image is specified, then an antialiased circle is constructed instead.
+ It should be quite fast, but large spots will use a lot of memory."""
+
+ QtGui.QGraphicsItem.__init__(self)
+ self.pen = pen
+ self.brush = brush
+ self.size = size
+ self.index = index
+ self.setFlags(self.flags() | self.ItemIgnoresTransformations | self.ItemHasNoContents)
+ if image is None:
+ self.image = self.makeSpotImage(self.size, self.pen, self.brush)
+ else:
+ self.image = image
+ self.pixmap = QtGui.QPixmap(self.image)
+ #self.setPixmap(self.pixmap)
+ self.data = data
+ self.pi = QtGui.QGraphicsPixmapItem(self.pixmap, self)
+ self.pi.setPos(-0.5*size, -0.5*size)
+
+ #self.translate(-0.5, -0.5)
+ def boundingRect(self):
+ return self.pi.boundingRect()
+
+ @staticmethod
+ def makeSpotImage(size, pen, brush):
+ img = QtGui.QImage(size+2, size+2, QtGui.QImage.Format_ARGB32_Premultiplied)
+ img.fill(0)
+ p = QtGui.QPainter(img)
+ try:
+ p.setRenderHint(p.Antialiasing)
+ p.setBrush(brush)
+ p.setPen(pen)
+ p.drawEllipse(1, 1, size, size)
+ finally:
+ p.end() ## failure to end a painter properly causes crash.
+ return img
+
+ #def paint(self, p, *args):
+ #p.setCompositionMode(p.CompositionMode_Plus)
+ #QtGui.QGraphicsPixmapItem.paint(self, p, *args)
+
+ #def setBrush(self, brush):
+ #self.brush = mkBrush(brush)
+ #self.update()
+
+ #def setPen(self, pen):
+ #self.pen = mkPen(pen)
+ #self.update()
+
+ #def boundingRect(self):
+ #return self.path.boundingRect()
+
+ #def shape(self):
+ #return self.path
+
+ #def paint(self, p, *opts):
+ #if self.pxMode:
+ #p.drawPixmap(QtCore.QPoint(int(-0.5*self.size), int(-0.5*self.size)), self.pixmap)
+ #else:
+ #p.setPen(self.pen)
+ #p.setBrush(self.brush)
+ #p.drawPath(self.path)
+
+
class ROIPlotItem(PlotCurveItem):
"""Plot curve that monitors an ROI and image for changes to automatically replot."""
@@ -1190,12 +1474,16 @@ class UIGraphicsItem(GraphicsObject):
pass
-
+class DebugText(QtGui.QGraphicsTextItem):
+ def paint(self, *args):
+ p = debug.Profiler("DebugText.paint", disabled=True)
+ QtGui.QGraphicsTextItem.paint(self, *args)
+ p.finish()
class LabelItem(QtGui.QGraphicsWidget):
def __init__(self, text, parent=None, **args):
QtGui.QGraphicsWidget.__init__(self, parent)
- self.item = QtGui.QGraphicsTextItem(self)
+ self.item = DebugText(self)
self.opts = args
if 'color' not in args:
self.opts['color'] = 'CCC'
@@ -1319,7 +1607,7 @@ class ScaleItem(QtGui.QGraphicsWidget):
self.showLabel(False)
self.grid = False
-
+ self.setCacheMode(self.DeviceCoordinateCache)
def close(self):
self.scene().removeItem(self.label)
@@ -1478,6 +1766,7 @@ class ScaleItem(QtGui.QGraphicsWidget):
return self.mapRectFromParent(self.geometry()) | self.mapRectFromScene(self.linkedView().mapRectToScene(self.linkedView().boundingRect()))
def paint(self, p, opt, widget):
+ prof = debug.Profiler("ScaleItem.paint", disabled=True)
p.setPen(self.pen)
#bounds = self.boundingRect()
@@ -1538,10 +1827,14 @@ class ScaleItem(QtGui.QGraphicsWidget):
xs = -bounds.height() / dif
else:
xs = bounds.width() / dif
+
+ prof.mark('init')
tickPositions = set() # remembers positions of previously drawn ticks
- ## draw ticks and text
+ ## draw ticks and generate list of texts to draw
+ ## (to improve performance, we do not interleave line and text drawing, since this causes unnecessary pipeline switching)
## draw three different intervals, long ticks first
+ texts = []
for i in reversed([i1, i1+1, i1+2]):
if i > len(intervals):
continue
@@ -1614,33 +1907,14 @@ class ScaleItem(QtGui.QGraphicsWidget):
rect = QtCore.QRectF(x-100, tickStop+self.tickLength, 200, height)
p.setPen(QtGui.QPen(QtGui.QColor(100, 100, 100)))
- p.drawText(rect, textFlags, vstr)
- #p.drawRect(rect)
-
- ## Draw label
- #if self.drawLabel:
- #height = self.size().height()
- #width = self.size().width()
- #if self.orientation == 'left':
- #p.translate(0, height)
- #p.rotate(-90)
- #rect = QtCore.QRectF(0, 0, height, self.textHeight)
- #textFlags = QtCore.Qt.AlignCenter|QtCore.Qt.AlignTop
- #elif self.orientation == 'right':
- #p.rotate(10)
- #rect = QtCore.QRectF(0, 0, height, width)
- #textFlags = QtCore.Qt.AlignCenter|QtCore.Qt.AlignBottom
- ##rect = QtCore.QRectF(tickStart+self.tickLength, x-(height/2), 100-self.tickLength, height)
- #elif self.orientation == 'top':
- #rect = QtCore.QRectF(0, 0, width, height)
- #textFlags = QtCore.Qt.AlignCenter|QtCore.Qt.AlignTop
- ##rect = QtCore.QRectF(x-100, tickStart-self.tickLength-height, 200, height)
- #elif self.orientation == 'bottom':
- #rect = QtCore.QRectF(0, 0, width, height)
- #textFlags = QtCore.Qt.AlignCenter|QtCore.Qt.AlignBottom
- ##rect = QtCore.QRectF(x-100, tickStart+self.tickLength, 200, height)
- #p.drawText(rect, textFlags, self.labelString())
- ##p.drawRect(rect)
+ #p.drawText(rect, textFlags, vstr)
+ texts.append((rect, textFlags, vstr))
+
+ prof.mark('draw ticks')
+ for args in texts:
+ p.drawText(*args)
+ prof.mark('draw text')
+ prof.finish()
def show(self):
@@ -1675,15 +1949,21 @@ class ViewBox(QtGui.QGraphicsWidget):
sigRangeChanged = QtCore.Signal(object, object)
"""Box that allows internal scaling/panning of children by mouse drag. Not compatible with GraphicsView having the same functionality."""
- def __init__(self, parent=None):
+ def __init__(self, parent=None, border=None):
QtGui.QGraphicsWidget.__init__(self, parent)
#self.gView = view
#self.showGrid = showGrid
- self.range = [[0,1], [0,1]] ## child coord. range visible [[xmin, xmax], [ymin, ymax]]
+
+ ## separating targetRange and viewRange allows the view to be resized
+ ## while keeping all previously viewed contents visible
+ self.targetRange = [[0,1], [0,1]] ## child coord. range visible [[xmin, xmax], [ymin, ymax]]
+ self.viewRange = [[0,1], [0,1]] ## actual range viewed
+
self.wheelScaleFactor = -1.0 / 8.0
self.aspectLocked = False
self.setFlag(QtGui.QGraphicsItem.ItemClipsChildrenToShape)
#self.setFlag(QtGui.QGraphicsItem.ItemClipsToShape)
+ #self.setCacheMode(QtGui.QGraphicsItem.DeviceCoordinateCache)
#self.childGroup = QtGui.QGraphicsItemGroup(self)
self.childGroup = ItemGroup(self)
@@ -1695,7 +1975,7 @@ class ViewBox(QtGui.QGraphicsWidget):
#self.picture = None
self.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding))
- self.drawFrame = False
+ self.border = border
self.mouseEnabled = [True, True]
@@ -1718,66 +1998,57 @@ class ViewBox(QtGui.QGraphicsWidget):
def viewRect(self):
try:
- return QtCore.QRectF(self.range[0][0], self.range[1][0], self.range[0][1]-self.range[0][0], self.range[1][1] - self.range[1][0])
+ vr0 = self.viewRange[0]
+ vr1 = self.viewRange[1]
+ return QtCore.QRectF(vr0[0], vr1[0], vr0[1]-vr0[0], vr1[1] - vr1[0])
except:
- print "make qrectf failed:", self.range
+ print "make qrectf failed:", self.viewRange
+ raise
+
+ def targetRect(self):
+ """Return the region which has been requested to be visible.
+ (this is not necessarily the same as the region that is *actually* visible)"""
+ try:
+ tr0 = self.targetRange[0]
+ tr1 = self.targetRange[1]
+ return QtCore.QRectF(tr0[0], tr1[0], tr0[1]-tr0[0], tr1[1] - tr1[0])
+ except:
+ print "make qrectf failed:", self.targetRange
raise
- def updateMatrix(self):
- #print "udpateMatrix:"
- #print " range:", self.range
- vr = self.viewRect()
- translate = Point(vr.center())
- bounds = self.boundingRect()
- #print " bounds:", bounds
- if vr.height() == 0 or vr.width() == 0:
- return
- scale = Point(bounds.width()/vr.width(), bounds.height()/vr.height())
- #print " scale:", scale
- m = QtGui.QTransform()
-
- ## First center the viewport at 0
- self.childGroup.resetTransform()
- center = self.transform().inverted()[0].map(bounds.center())
- #print " transform to center:", center
- 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.childGroup.setTransform(m)
- self.currentScale = scale
-
def invertY(self, b=True):
self.yInverted = b
self.updateMatrix()
+ def setAspectLocked(self, lock=True, ratio=1):
+ """If the aspect ratio is locked, view scaling is always forced to be isotropic.
+ By default, the ratio is set to 1; x and y both have the same scaling.
+ This ratio can be overridden (width/height), or use None to lock in the current ratio.
+ """
+ if not lock:
+ self.aspectLocked = False
+ else:
+ vr = self.viewRect()
+ currentRatio = vr.width() / vr.height()
+ if ratio is None:
+ ratio = currentRatio
+ self.aspectLocked = ratio
+ if ratio != currentRatio: ## If this would change the current range, do that now
+ #self.setRange(0, self.viewRange[0][0], self.viewRange[0][1])
+ self.updateMatrix()
+
def childTransform(self):
m = self.childGroup.transform()
m1 = QtGui.QTransform()
m1.translate(self.childGroup.pos().x(), self.childGroup.pos().y())
return m*m1
- def setAspectLocked(self, s):
- self.aspectLocked = s
def viewScale(self):
- pr = self.range
+ vr = self.viewRect()
#print "viewScale:", self.range
- xd = pr[0][1] - pr[0][0]
- yd = pr[1][1] - pr[1][0]
+ xd = vr.width()
+ yd = vr.height()
if xd == 0 or yd == 0:
print "Warning: 0 range in view:", xd, yd
return np.array([1,1])
@@ -1789,41 +2060,54 @@ class ViewBox(QtGui.QGraphicsWidget):
return scale
def scaleBy(self, s, center=None):
+ """Scale by s around given center point (or center of view)"""
#print "scaleBy", s, center
- xr, yr = self.range
+ #if self.aspectLocked:
+ #s[0] = s[1]
+ scale = Point(s)
+ if self.aspectLocked is not False:
+ scale[0] = self.aspectLocked * scale[1]
+
+
+ #xr, yr = self.range
+ vr = self.viewRect()
if center is None:
- xc = (xr[1] + xr[0]) * 0.5
- yc = (yr[1] + yr[0]) * 0.5
+ center = Point(vr.center())
+ #xc = (xr[1] + xr[0]) * 0.5
+ #yc = (yr[1] + yr[0]) * 0.5
else:
- (xc, yc) = center
-
- x1 = xc + (xr[0]-xc) * s[0]
- x2 = xc + (xr[1]-xc) * s[0]
- y1 = yc + (yr[0]-yc) * s[1]
- y2 = yc + (yr[1]-yc) * s[1]
+ center = Point(center)
+ #(xc, yc) = center
+ #x1 = xc + (xr[0]-xc) * s[0]
+ #x2 = xc + (xr[1]-xc) * s[0]
+ #y1 = yc + (yr[0]-yc) * s[1]
+ #y2 = yc + (yr[1]-yc) * s[1]
+ tl = center + (vr.topLeft()-center) * scale
+ br = center + (vr.bottomRight()-center) * scale
+
#print xr, xc, s, (xr[0]-xc) * s[0], (xr[1]-xc) * s[0]
#print [[x1, x2], [y1, y2]]
-
-
- self.setXRange(x1, x2, update=False, padding=0)
- self.setYRange(y1, y2, padding=0)
+ #if not self.aspectLocked:
+ #self.setXRange(x1, x2, update=False, padding=0)
+ #self.setYRange(y1, y2, padding=0)
#print self.range
+ self.setRange(QtCore.QRectF(tl, br), padding=0)
+
def translateBy(self, t, viewCoords=False):
t = t.astype(np.float)
#print "translate:", t, self.viewScale()
if viewCoords: ## scale from pixels
t /= self.viewScale()
- xr, yr = self.range
- #self.setAxisScale(self.xBottom, xr[0] + t[0], xr[1] + t[0])
- #self.setAxisScale(self.yLeft, yr[0] + t[1], yr[1] + t[1])
+ #xr, yr = self.range
+
+ vr = self.viewRect()
#print xr, yr, t
- self.setXRange(xr[0] + t[0], xr[1] + t[0], update=False, padding=0)
- self.setYRange(yr[0] + t[1], yr[1] + t[1], padding=0)
- #self.replot(autoRange=False)
- #self.updateMatrix()
+ #self.setXRange(xr[0] + t[0], xr[1] + t[0], update=False, padding=0)
+ #self.setYRange(yr[0] + t[1], yr[1] + t[1], padding=0)
+ self.setRange(vr.translated(Point(t)), padding=0)
def wheelEvent(self, ev, axis=None):
mask = np.array(self.mouseEnabled, dtype=np.float)
@@ -1835,7 +2119,8 @@ class ViewBox(QtGui.QGraphicsWidget):
# scale 'around' mouse cursor position
center = Point(self.childGroup.transform().inverted()[0].map(ev.pos()))
self.scaleBy(s, center)
- self.emit(QtCore.SIGNAL('rangeChangedManually'), self.mouseEnabled)
+ #self.emit(QtCore.SIGNAL('rangeChangedManually'), self.mouseEnabled)
+ self.sigRangeChangedManually.emit(self.mouseEnabled)
ev.accept()
def mouseMoveEvent(self, ev):
@@ -1858,6 +2143,8 @@ class ViewBox(QtGui.QGraphicsWidget):
self.sigRangeChangedManually.emit(self.mouseEnabled)
ev.accept()
elif ev.buttons() & QtCore.Qt.RightButton:
+ if self.aspectLocked is not False:
+ mask[0] = 0
dif = ev.screenPos() - ev.lastScreenPos()
dif = np.array([dif.x(), dif.y()])
dif[0] *= -1
@@ -1887,81 +2174,145 @@ class ViewBox(QtGui.QGraphicsWidget):
self.mousePos = pos
ev.accept()
- def setRange(self, ax, min, max, padding=0.02, update=True):
- if ax == 0:
- self.setXRange(min, max, update=update, padding=padding)
+ def setRange(self, ax, min=None, max=None, padding=0.02, update=True):
+ if isinstance(ax, QtCore.QRectF):
+ changes = {0: [ax.left(), ax.right()], 1: [ax.top(), ax.bottom()]}
+ #if self.aspectLocked is not False:
+ #sbr = self.boundingRect()
+ #if sbr.width() == 0 or (ax.height()/ax.width()) > (sbr.height()/sbr.width()):
+ #chax = 0
+ #else:
+ #chax = 1
+
+
+
+
+ elif ax in [1,0]:
+ changes = {ax: [min,max]}
+ #if self.aspectLocked is not False:
+ #ax2 = 1 - ax
+ #ratio = self.aspectLocked
+ #r2 = self.range[ax2]
+ #d = ratio * (max-min) * 0.5
+ #c = (self.range[ax2][1] + self.range[ax2][0]) * 0.5
+ #changes[ax2] = [c-d, c+d]
+
else:
- self.setYRange(min, max, update=update, padding=padding)
+ print ax
+ raise Exception("argument 'ax' must be 0, 1, or QRectF.")
+
+
+ changed = [False, False]
+ for ax, range in changes.iteritems():
+ min, max = range
+ if min == max: ## If we requested 0 range, try to preserve previous scale. Otherwise just pick an arbitrary scale.
+ dy = self.viewRange[ax][1] - self.viewRange[ax][0]
+ if dy == 0:
+ dy = 1
+ min -= dy*0.5
+ max += dy*0.5
+ padding = 0.0
+ if any(np.isnan([min, max])) or any(np.isinf([min, max])):
+ raise Exception("Not setting range [%s, %s]" % (str(min), str(max)))
+
+ p = (max-min) * padding
+ min -= p
+ max += p
+
+ if self.targetRange[ax] != [min, max]:
+ self.targetRange[ax] = [min, max]
+ changed[ax] = True
+
+ if update:
+ self.updateMatrix(changed)
+
+
+
def setYRange(self, min, max, update=True, padding=0.02):
- #print "setYRange:", min, max
- if min == max: ## If we requested no range, try to preserve previous scale. Otherwise just pick an arbitrary scale.
- dy = self.range[1][1] - self.range[1][0]
- if dy == 0:
- dy = 1
- min -= dy*0.5
- max += dy*0.5
- #raise Exception("Tried to set range with 0 width.")
- if any(np.isnan([min, max])) or any(np.isinf([min, max])):
- raise Exception("Not setting range [%s, %s]" % (str(min), str(max)))
-
- padding = (max-min) * padding
- min -= padding
- max += padding
- if self.range[1] != [min, max]:
- #self.setAxisScale(self.yLeft, min, max)
- self.range[1] = [min, max]
- #self.ctrl.yMinText.setText('%g' % min)
- #self.ctrl.yMaxText.setText('%g' % max)
- #self.emit(QtCore.SIGNAL('yRangeChanged'), self, (min, max))
- self.sigYRangeChanged.emit(self, (min, max))
- #self.emit(QtCore.SIGNAL('viewChanged'), self)
- self.sigRangeChanged.emit(self, self.range)
- if update:
- self.updateMatrix()
+ self.setRange(1, min, max, update=update, padding=padding)
def setXRange(self, min, max, update=True, padding=0.02):
- #print "setXRange:", min, max
- if min == max:
- dx = self.range[0][1] - self.range[0][0]
- if dx == 0:
- dx = 1
- min -= dx*0.5
- max += dx*0.5
- #print "Warning: Tried to set range with 0 width."
- #raise Exception("Tried to set range with 0 width.")
- if any(np.isnan([min, max])) or any(np.isinf([min, max])):
- raise Exception("Not setting range [%s, %s]" % (str(min), str(max)))
- padding = (max-min) * padding
- min -= padding
- max += padding
- if self.range[0] != [min, max]:
- #self.setAxisScale(self.xBottom, min, max)
- self.range[0] = [min, max]
- #self.ctrl.xMinText.setText('%g' % min)
- #self.ctrl.xMaxText.setText('%g' % max)
- #self.emit(QtCore.SIGNAL('xRangeChanged'), self, (min, max))
- self.sigXRangeChanged.emit(self, (min, max))
- #self.emit(QtCore.SIGNAL('viewChanged'), self)
- self.sigRangeChanged.emit(self, self.range)
- if update:
- self.updateMatrix()
+ self.setRange(0, min, max, update=update, padding=padding)
def autoRange(self, padding=0.02):
br = self.childGroup.childrenBoundingRect()
- #print br
- #px = br.width() * padding
- #py = br.height() * padding
- self.setXRange(br.left(), br.right(), padding=padding, update=False)
- self.setYRange(br.top(), br.bottom(), padding=padding)
+ self.setRange(br, padding=padding)
+
+
+ def updateMatrix(self, changed=None):
+ if changed is None:
+ changed = [False, False]
+ #print "udpateMatrix:"
+ #print " range:", self.range
+ tr = self.targetRect()
+ bounds = self.boundingRect()
+ ## set viewRect, given targetRect and possibly aspect ratio constraint
+ if self.aspectLocked is False or bounds.height() == 0:
+ self.viewRange = [self.targetRange[0][:], self.targetRange[1][:]]
+ else:
+ viewRatio = bounds.width() / bounds.height()
+ targetRatio = self.aspectLocked * tr.width() / tr.height()
+ if targetRatio > viewRatio:
+ ## target is wider than view
+ dy = 0.5 * (tr.width() / (self.aspectLocked * viewRatio) - tr.height())
+ if dy != 0:
+ changed[1] = True
+ self.viewRange = [self.targetRange[0][:], [self.targetRange[1][0] - dy, self.targetRange[1][1] + dy]]
+ else:
+ dx = 0.5 * (tr.height() * viewRatio * self.aspectLocked - tr.width())
+ if dx != 0:
+ changed[0] = True
+ self.viewRange = [[self.targetRange[0][0] - dx, self.targetRange[0][1] + dx], self.targetRange[1][:]]
+
+
+ vr = self.viewRect()
+ translate = Point(vr.center())
+ #print " bounds:", bounds
+ if vr.height() == 0 or vr.width() == 0:
+ return
+ scale = Point(bounds.width()/vr.width(), bounds.height()/vr.height())
+ #print " scale:", scale
+ m = QtGui.QTransform()
+
+ ## First center the viewport at 0
+ self.childGroup.resetTransform()
+ center = self.transform().inverted()[0].map(bounds.center())
+ #print " transform to center:", center
+ 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 not self.yInverted:
+ scale = scale * Point(1, -1)
+ m.scale(scale[0], scale[1])
+ st = translate
+ m.translate(-st[0], -st[1])
+ self.childGroup.setTransform(m)
+ self.currentScale = scale
+
+
+ if changed[0]:
+ self.sigXRangeChanged.emit(self, tuple(self.viewRange[0]))
+ if changed[1]:
+ self.sigYRangeChanged.emit(self, tuple(self.viewRange[1]))
+ if any(changed):
+ self.sigRangeChanged.emit(self, self.viewRange)
+
+
+
def boundingRect(self):
return QtCore.QRectF(0, 0, self.size().width(), self.size().height())
def paint(self, p, opt, widget):
- if self.drawFrame:
+ if self.border is not None:
bounds = self.boundingRect()
- p.setPen(QtGui.QPen(QtGui.QColor(100, 100, 100)))
+ p.setPen(self.border)
#p.fillRect(bounds, QtGui.QColor(0, 0, 0))
p.drawRect(bounds)
@@ -2429,7 +2780,7 @@ class GridItem(UIGraphicsItem):
#print "no pic, draw.."
self.generatePicture()
p.drawPicture(0, 0, self.picture)
- #print "draw"
+ #print "drawing Grid."
def generatePicture(self):
@@ -2465,9 +2816,15 @@ class GridItem(UIGraphicsItem):
c = np.clip(3.*(ppl-3), 0., 30.)
linePen = QtGui.QPen(QtGui.QColor(255, 255, 255, c))
textPen = QtGui.QPen(QtGui.QColor(255, 255, 255, c*2))
-
+ #linePen.setCosmetic(True)
+ #linePen.setWidth(1)
bx = (ax+1) % 2
for x in range(0, int(nl[ax])):
+ linePen.setCosmetic(False)
+ if ax == 0:
+ linePen.setWidthF(self.pixelHeight())
+ else:
+ linePen.setWidthF(self.pixelWidth())
p.setPen(linePen)
p1 = np.array([0.,0.])
p2 = np.array([0.,0.])
diff --git a/graphicsWindows.py b/graphicsWindows.py
index 4f51f052..8b8e8678 100644
--- a/graphicsWindows.py
+++ b/graphicsWindows.py
@@ -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']
diff --git a/plotConfigTemplate.py b/plotConfigTemplate.py
index 5d30654a..e0063b14 100644
--- a/plotConfigTemplate.py
+++ b/plotConfigTemplate.py
@@ -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)
diff --git a/widgets.py b/widgets.py
index 6f36fe22..8516fefc 100644
--- a/widgets.py
+++ b/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)