diff --git a/examples/ROIExamples.py b/examples/ROIExamples.py index 97e137d9..044e0141 100644 --- a/examples/ROIExamples.py +++ b/examples/ROIExamples.py @@ -1,13 +1,10 @@ -#!/usr/bin/python -i # -*- coding: utf-8 -*- -## Add path to library (just for examples; you do not need this) -import sys, os -sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..')) - +import initExample ## Add path to library (just for examples; you do not need this) +import pyqtgraph as pg from pyqtgraph.Qt import QtCore, QtGui import numpy as np -import pyqtgraph as pg + ## Create image to display arr = np.ones((100, 100), dtype=float) @@ -128,100 +125,10 @@ img4.setParentItem(r4) -#v = w.addViewBox(colspan=2) - -#v.invertY(True) ## Images usually have their Y-axis pointing downward -#v.setAspectLocked(True) -### Create image items, add to scene and set position -#im1 = pg.ImageItem(arr) -#im2 = pg.ImageItem(arr) -#v.addItem(im1) -#v.addItem(im2) -#im2.moveBy(110, 20) -#v.setRange(QtCore.QRectF(0, 0, 200, 120)) - -#im3 = pg.ImageItem() -#v2 = w.addViewBox(1,0) -#v2.addItem(im3) -#v2.setRange(QtCore.QRectF(0, 0, 60, 60)) -#v2.invertY(True) -#v2.setAspectLocked(True) -##im3.moveBy(0, 130) -#im3.setZValue(10) - -#im4 = pg.ImageItem() -#v3 = w.addViewBox(1,1) -#v3.addItem(im4) -#v3.setRange(QtCore.QRectF(0, 0, 60, 60)) -#v3.invertY(True) -#v3.setAspectLocked(True) -##im4.moveBy(110, 130) -#im4.setZValue(10) - -### create the plot -#pi1 = w.addPlot(2,0, colspan=2) -##pi1 = pg.PlotItem() -##s.addItem(pi1) -##pi1.scale(0.5, 0.5) -##pi1.setGeometry(0, 170, 300, 100) - -#lastRoi = None - -#def updateRoi(roi): - #global im1, im2, im3, im4, arr, lastRoi - #if roi is None: - #return - #lastRoi = roi - #arr1 = roi.getArrayRegion(im1.image, img=im1) - #im3.setImage(arr1) - #arr2 = roi.getArrayRegion(im2.image, img=im2) - #im4.setImage(arr2) - #updateRoiPlot(roi, arr1) - -#def updateRoiPlot(roi, data=None): - #if data is None: - #data = roi.getArrayRegion(im1.image, img=im1) - #if data is not None: - #roi.curve.setData(data.mean(axis=1)) - - -### Create a variety of different ROI types -#rois = [] -#rois.append(pg.TestROI([0, 0], [20, 20], maxBounds=QtCore.QRectF(-10, -10, 230, 140), pen=(0,9))) -#rois.append(pg.LineROI([0, 0], [20, 20], width=5, pen=(1,9))) -#rois.append(pg.MultiLineROI([[0, 50], [50, 60], [60, 30]], width=5, pen=(2,9))) -#rois.append(pg.EllipseROI([110, 10], [30, 20], pen=(3,9))) -#rois.append(pg.CircleROI([110, 50], [20, 20], pen=(4,9))) -#rois.append(pg.LineSegmentROI([[110, 50], [20, 20]], pen=(5,9))) -#rois.append(pg.PolyLineROI([[110, 60], [20, 30], [50, 10]], pen=(6,9))) -##rois.append(pg.PolygonROI([[2,0], [2.1,0], [2,.1]], pen=(5,9))) -##rois.append(SpiralROI([20,30], [1,1], pen=mkPen(0))) - -### Add each ROI to the scene and link its data to a plot curve with the same color -#for r in rois: - #v.addItem(r) - #c = pi1.plot(pen=r.pen) - #r.curve = c - #r.sigRegionChanged.connect(updateRoi) - -#def updateImage(): - #global im1, arr, lastRoi - #r = abs(np.random.normal(loc=0, scale=(arr.max()-arr.min())*0.1, size=arr.shape)) - #im1.updateImage(arr + r) - #updateRoi(lastRoi) - #for r in rois: - #updateRoiPlot(r) - -### Rapidly update one of the images with random noise -#t = QtCore.QTimer() -#t.timeout.connect(updateImage) -#t.start(50) - - - -## Start Qt event loop unless running in interactive mode. -if sys.flags.interactive != 1: - app.exec_() +## Start Qt event loop unless running in interactive mode or using pyside. +import sys +if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): + QtGui.QApplication.instance().exec_() diff --git a/graphicsItems/AxisItem.py b/graphicsItems/AxisItem.py index 384b0fd0..fea281f3 100644 --- a/graphicsItems/AxisItem.py +++ b/graphicsItems/AxisItem.py @@ -210,7 +210,7 @@ class AxisItem(GraphicsWidget): if scale is None: #if self.drawLabel: ## If there is a label, then we are free to rescale the values if self.label.isVisible(): - d = self.range[1] - self.range[0] + #d = self.range[1] - self.range[0] #(scale, prefix) = fn.siScale(d / 2.) (scale, prefix) = fn.siScale(max(abs(self.range[0]), abs(self.range[1]))) if self.labelUnits == '' and prefix in ['k', 'm']: ## If we are not showing units, wait until 1e6 before scaling. @@ -230,7 +230,7 @@ class AxisItem(GraphicsWidget): def setRange(self, mn, mx): """Set the range of values displayed by the axis. Usually this is handled automatically by linking the axis to a ViewBox with :func:`linkToView `""" - if mn in [np.nan, np.inf, -np.inf] or mx in [np.nan, np.inf, -np.inf]: + if any(np.isinf((mn, mx))) or any(np.isnan((mn, mx))): raise Exception("Not setting range to [%s, %s]" % (str(mn), str(mx))) self.range = [mn, mx] if self.autoScale: @@ -259,7 +259,10 @@ class AxisItem(GraphicsWidget): view.sigXRangeChanged.connect(self.linkedViewChanged) def linkedViewChanged(self, view, newRange): - self.setRange(*newRange) + if self.orientation in ['right', 'left'] and view.yInverted(): + self.setRange(*newRange[::-1]) + else: + self.setRange(*newRange) def boundingRect(self): linkedView = self.linkedView() @@ -365,12 +368,14 @@ class AxisItem(GraphicsWidget): By default, this method calls tickSpacing to determine the correct tick locations. This is a good method to override in subclasses. """ + minVal, maxVal = sorted((minVal, maxVal)) + if self.logMode: return self.logTickValues(minVal, maxVal, size) ticks = [] tickLevels = self.tickSpacing(minVal, maxVal, size) - allValues = [] + allValues = np.array([]) for i in range(len(tickLevels)): spacing, offset = tickLevels[i] @@ -380,8 +385,11 @@ class AxisItem(GraphicsWidget): ## determine number of ticks num = int((maxVal-start) / spacing) + 1 values = np.arange(num) * spacing + start - values = filter(lambda x: x not in allValues, values) ## remove any ticks that were present in higher levels - allValues.extend(values) + ## remove any ticks that were present in higher levels + ## we assume here that if the difference between a tick value and a previously seen tick value + ## is less than spacing/100, then they are 'equal' and we can ignore the new tick. + values = filter(lambda x: all(np.abs(allValues-x) > spacing*0.01), values) + allValues = np.concatenate([allValues, values]) ticks.append((spacing, values)) return ticks diff --git a/graphicsItems/GraphicsLayout.py b/graphicsItems/GraphicsLayout.py index 3b1d652f..491ff303 100644 --- a/graphicsItems/GraphicsLayout.py +++ b/graphicsItems/GraphicsLayout.py @@ -1,6 +1,10 @@ from pyqtgraph.Qt import QtGui, QtCore import pyqtgraph.functions as fn from .GraphicsWidget import GraphicsWidget +## Must be imported at the end to avoid cyclic-dependency hell: +from .ViewBox import ViewBox +from .PlotItem import PlotItem +from .LabelItem import LabelItem __all__ = ['GraphicsLayout'] class GraphicsLayout(GraphicsWidget): @@ -34,7 +38,7 @@ class GraphicsLayout(GraphicsWidget): return self.currentCol-colspan def nextCol(self, *args, **kargs): - """Advance to next column for automatic item placement""" + """Alias of nextColumn""" return self.nextColumn(*args, **kargs) def addPlot(self, row=None, col=None, rowspan=1, colspan=1, **kargs): @@ -131,7 +135,3 @@ class GraphicsLayout(GraphicsWidget): self.removeItem(i) -## Must be imported at the end to avoid cyclic-dependency hell: -from .ViewBox import ViewBox -from .PlotItem import PlotItem -from .LabelItem import LabelItem \ No newline at end of file diff --git a/graphicsItems/PlotDataItem.py b/graphicsItems/PlotDataItem.py index 5f6f4ea6..33ebd726 100644 --- a/graphicsItems/PlotDataItem.py +++ b/graphicsItems/PlotDataItem.py @@ -371,7 +371,6 @@ class PlotDataItem(GraphicsObject): #self.curves.append(curve) if scatterArgs['symbol'] is not None: - print scatterArgs self.scatter.setData(x=x, y=y, **scatterArgs) self.scatter.show() else: diff --git a/graphicsItems/ScatterPlotItem.py b/graphicsItems/ScatterPlotItem.py index 4a468b97..e73653b7 100644 --- a/graphicsItems/ScatterPlotItem.py +++ b/graphicsItems/ScatterPlotItem.py @@ -330,7 +330,14 @@ class ScatterPlotItem(GraphicsObject): if isinstance(data, np.ndarray) or isinstance(data, list): if len(data) != len(dataSet): raise Exception("Length of meta data does not match number of points (%d != %d)" % (len(data), len(dataSet))) - dataSet['data'] = data + + ## Bug: If data is a numpy record array, then items from that array must be copied to dataSet one at a time. + ## (otherwise they are converted to tuples and thus lose their field names. + if isinstance(data, np.ndarray) and len(data.dtype.fields) > 1: + for i, rec in enumerate(data): + dataSet['data'][i] = rec + else: + dataSet['data'] = data def setPxMode(self, mode, update=True): if self.opts['pxMode'] == mode: diff --git a/widgets/CheckTable.py b/widgets/CheckTable.py index 3322113a..345c8c6f 100644 --- a/widgets/CheckTable.py +++ b/widgets/CheckTable.py @@ -47,9 +47,9 @@ class CheckTable(QtGui.QWidget): check.row = name self.layout.addWidget(check, row, col) checks.append(check) - col += 1 if name in self.oldRows: - check.setChecked(self.oldRows[name]) + check.setChecked(self.oldRows[name][col]) + col += 1 #QtCore.QObject.connect(check, QtCore.SIGNAL('stateChanged(int)'), self.checkChanged) check.stateChanged.connect(self.checkChanged) self.rowNames.append(name) diff --git a/widgets/GraphicsLayoutWidget.py b/widgets/GraphicsLayoutWidget.py index 7c93c32d..1e667278 100644 --- a/widgets/GraphicsLayoutWidget.py +++ b/widgets/GraphicsLayoutWidget.py @@ -7,6 +7,6 @@ class GraphicsLayoutWidget(GraphicsView): def __init__(self, parent=None, **kargs): GraphicsView.__init__(self, parent) self.ci = GraphicsLayout(**kargs) - for n in ['nextRow', 'nextCol', 'nextColumn', 'addPlot', 'addViewBox', 'addItem', 'getItem', 'addLabel', 'addLayout']: + for n in ['nextRow', 'nextCol', 'nextColumn', 'addPlot', 'addViewBox', 'addItem', 'getItem', 'addLabel', 'addLayout', 'addLabel', 'addViewBox', 'removeItem', 'itemIndex', 'clear']: setattr(self, n, getattr(self.ci, n)) self.setCentralItem(self.ci)