merged with inp
This commit is contained in:
commit
f029e7893e
67
examples/MultiplePlotAxes.py
Normal file
67
examples/MultiplePlotAxes.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Demonstrates a way to put multiple axes around a single plot.
|
||||||
|
|
||||||
|
(This will eventually become a built-in feature of PlotItem)
|
||||||
|
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
|
||||||
|
pg.mkQApp()
|
||||||
|
|
||||||
|
pw = pg.PlotWidget()
|
||||||
|
pw.show()
|
||||||
|
pw.setWindowTitle('pyqtgraph example: MultiplePlotAxes')
|
||||||
|
p1 = pw.plotItem
|
||||||
|
p1.setLabels(left='axis 1')
|
||||||
|
|
||||||
|
## create a new ViewBox, link the right axis to its coordinate system
|
||||||
|
p2 = pg.ViewBox()
|
||||||
|
p1.showAxis('right')
|
||||||
|
p1.scene().addItem(p2)
|
||||||
|
p1.getAxis('right').linkToView(p2)
|
||||||
|
p2.setXLink(p1)
|
||||||
|
p1.getAxis('right').setLabel('axis2', color='#0000ff')
|
||||||
|
|
||||||
|
## create third ViewBox.
|
||||||
|
## this time we need to create a new axis as well.
|
||||||
|
p3 = pg.ViewBox()
|
||||||
|
ax3 = pg.AxisItem('right')
|
||||||
|
p1.layout.addItem(ax3, 2, 3)
|
||||||
|
p1.scene().addItem(p3)
|
||||||
|
ax3.linkToView(p3)
|
||||||
|
p3.setXLink(p1)
|
||||||
|
ax3.setZValue(-10000)
|
||||||
|
ax3.setLabel('axis 3', color='#ff0000')
|
||||||
|
|
||||||
|
|
||||||
|
## Handle view resizing
|
||||||
|
def updateViews():
|
||||||
|
## view has resized; update auxiliary views to match
|
||||||
|
global p1, p2, p3
|
||||||
|
p2.setGeometry(p1.vb.sceneBoundingRect())
|
||||||
|
p3.setGeometry(p1.vb.sceneBoundingRect())
|
||||||
|
|
||||||
|
## need to re-update linked axes since this was called
|
||||||
|
## incorrectly while views had different shapes.
|
||||||
|
## (probably this should be handled in ViewBox.resizeEvent)
|
||||||
|
p2.linkedViewChanged(p1.vb, p2.XAxis)
|
||||||
|
p3.linkedViewChanged(p1.vb, p3.XAxis)
|
||||||
|
|
||||||
|
updateViews()
|
||||||
|
p1.vb.sigResized.connect(updateViews)
|
||||||
|
|
||||||
|
|
||||||
|
p1.plot([1,2,4,8,16,32])
|
||||||
|
p2.addItem(pg.PlotCurveItem([10,20,40,80,40,20], pen='b'))
|
||||||
|
p3.addItem(pg.PlotCurveItem([3200,1600,800,400,200,100], pen='r'))
|
||||||
|
|
||||||
|
## Start Qt event loop unless running in interactive mode or using pyside.
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
|
||||||
|
QtGui.QApplication.instance().exec_()
|
@ -56,7 +56,7 @@ rois.append(pg.MultiRectROI([[20, 90], [50, 60], [60, 90]], width=5, pen=(2,9)))
|
|||||||
rois.append(pg.EllipseROI([60, 10], [30, 20], pen=(3,9)))
|
rois.append(pg.EllipseROI([60, 10], [30, 20], pen=(3,9)))
|
||||||
rois.append(pg.CircleROI([80, 50], [20, 20], pen=(4,9)))
|
rois.append(pg.CircleROI([80, 50], [20, 20], pen=(4,9)))
|
||||||
#rois.append(pg.LineSegmentROI([[110, 50], [20, 20]], pen=(5,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.PolyLineROI([[80, 60], [90, 30], [60, 40]], pen=(6,9), closed=True))
|
||||||
|
|
||||||
def update(roi):
|
def update(roi):
|
||||||
img1b.setImage(roi.getArrayRegion(arr, img1a), levels=(0, arr.max()))
|
img1b.setImage(roi.getArrayRegion(arr, img1a), levels=(0, arr.max()))
|
||||||
|
@ -314,10 +314,13 @@ class AxisItem(GraphicsWidget):
|
|||||||
view.sigResized.connect(self.linkedViewChanged)
|
view.sigResized.connect(self.linkedViewChanged)
|
||||||
|
|
||||||
def linkedViewChanged(self, view, newRange=None):
|
def linkedViewChanged(self, view, newRange=None):
|
||||||
if self.orientation in ['right', 'left'] and view.yInverted():
|
if self.orientation in ['right', 'left']:
|
||||||
if newRange is None:
|
if newRange is None:
|
||||||
newRange = view.viewRange()[1]
|
newRange = view.viewRange()[1]
|
||||||
self.setRange(*newRange[::-1])
|
if view.yInverted():
|
||||||
|
self.setRange(*newRange[::-1])
|
||||||
|
else:
|
||||||
|
self.setRange(*newRange)
|
||||||
else:
|
else:
|
||||||
if newRange is None:
|
if newRange is None:
|
||||||
newRange = view.viewRange()[0]
|
newRange = view.viewRange()[0]
|
||||||
@ -330,18 +333,12 @@ class AxisItem(GraphicsWidget):
|
|||||||
## extend rect if ticks go in negative direction
|
## extend rect if ticks go in negative direction
|
||||||
## also extend to account for text that flows past the edges
|
## also extend to account for text that flows past the edges
|
||||||
if self.orientation == 'left':
|
if self.orientation == 'left':
|
||||||
#rect.setRight(rect.right() - min(0,self.tickLength))
|
|
||||||
#rect.setTop(rect.top() - 15)
|
|
||||||
#rect.setBottom(rect.bottom() + 15)
|
|
||||||
rect = rect.adjusted(0, -15, -min(0,self.tickLength), 15)
|
rect = rect.adjusted(0, -15, -min(0,self.tickLength), 15)
|
||||||
elif self.orientation == 'right':
|
elif self.orientation == 'right':
|
||||||
#rect.setLeft(rect.left() + min(0,self.tickLength))
|
|
||||||
rect = rect.adjusted(min(0,self.tickLength), -15, 0, 15)
|
rect = rect.adjusted(min(0,self.tickLength), -15, 0, 15)
|
||||||
elif self.orientation == 'top':
|
elif self.orientation == 'top':
|
||||||
#rect.setBottom(rect.bottom() - min(0,self.tickLength))
|
|
||||||
rect = rect.adjusted(-15, 0, 15, -min(0,self.tickLength))
|
rect = rect.adjusted(-15, 0, 15, -min(0,self.tickLength))
|
||||||
elif self.orientation == 'bottom':
|
elif self.orientation == 'bottom':
|
||||||
#rect.setTop(rect.top() + min(0,self.tickLength))
|
|
||||||
rect = rect.adjusted(-15, min(0,self.tickLength), 15, 0)
|
rect = rect.adjusted(-15, min(0,self.tickLength), 15, 0)
|
||||||
return rect
|
return rect
|
||||||
else:
|
else:
|
||||||
|
@ -249,7 +249,7 @@ class ImageItem(GraphicsObject):
|
|||||||
|
|
||||||
def render(self):
|
def render(self):
|
||||||
prof = debug.Profiler('ImageItem.render', disabled=True)
|
prof = debug.Profiler('ImageItem.render', disabled=True)
|
||||||
if self.image is None:
|
if self.image is None or self.image.size == 0:
|
||||||
return
|
return
|
||||||
if isinstance(self.lut, collections.Callable):
|
if isinstance(self.lut, collections.Callable):
|
||||||
lut = self.lut(self.image)
|
lut = self.lut(self.image)
|
||||||
@ -269,6 +269,8 @@ class ImageItem(GraphicsObject):
|
|||||||
return
|
return
|
||||||
if self.qimage is None:
|
if self.qimage is None:
|
||||||
self.render()
|
self.render()
|
||||||
|
if self.qimage is None:
|
||||||
|
return
|
||||||
prof.mark('render QImage')
|
prof.mark('render QImage')
|
||||||
if self.paintMode is not None:
|
if self.paintMode is not None:
|
||||||
p.setCompositionMode(self.paintMode)
|
p.setCompositionMode(self.paintMode)
|
||||||
|
@ -112,7 +112,10 @@ class PlotCurveItem(GraphicsObject):
|
|||||||
if orthoRange is not None:
|
if orthoRange is not None:
|
||||||
mask = (d2 >= orthoRange[0]) * (d2 <= orthoRange[1])
|
mask = (d2 >= orthoRange[0]) * (d2 <= orthoRange[1])
|
||||||
d = d[mask]
|
d = d[mask]
|
||||||
d2 = d2[mask]
|
#d2 = d2[mask]
|
||||||
|
|
||||||
|
if len(d) == 0:
|
||||||
|
return (None, None)
|
||||||
|
|
||||||
## Get min/max (or percentiles) of the requested data range
|
## Get min/max (or percentiles) of the requested data range
|
||||||
if frac >= 1.0:
|
if frac >= 1.0:
|
||||||
|
@ -802,7 +802,11 @@ class ROI(GraphicsObject):
|
|||||||
Also returns the transform which maps the ROI into data coordinates.
|
Also returns the transform which maps the ROI into data coordinates.
|
||||||
|
|
||||||
If returnSlice is set to False, the function returns a pair of tuples with the values that would have
|
If returnSlice is set to False, the function returns a pair of tuples with the values that would have
|
||||||
been used to generate the slice objects. ((ax0Start, ax0Stop), (ax1Start, ax1Stop))"""
|
been used to generate the slice objects. ((ax0Start, ax0Stop), (ax1Start, ax1Stop))
|
||||||
|
|
||||||
|
If the slice can not be computed (usually because the scene/transforms are not properly
|
||||||
|
constructed yet), then the method returns None.
|
||||||
|
"""
|
||||||
#print "getArraySlice"
|
#print "getArraySlice"
|
||||||
|
|
||||||
## Determine shape of array along ROI axes
|
## Determine shape of array along ROI axes
|
||||||
@ -810,8 +814,11 @@ class ROI(GraphicsObject):
|
|||||||
#print " dshape", dShape
|
#print " dshape", dShape
|
||||||
|
|
||||||
## Determine transform that maps ROI bounding box to image coordinates
|
## Determine transform that maps ROI bounding box to image coordinates
|
||||||
tr = self.sceneTransform() * fn.invertQTransform(img.sceneTransform())
|
try:
|
||||||
|
tr = self.sceneTransform() * fn.invertQTransform(img.sceneTransform())
|
||||||
|
except np.linalg.linalg.LinAlgError:
|
||||||
|
return None
|
||||||
|
|
||||||
## Modify transform to scale from image coords to data coords
|
## Modify transform to scale from image coords to data coords
|
||||||
#m = QtGui.QTransform()
|
#m = QtGui.QTransform()
|
||||||
tr.scale(float(dShape[0]) / img.width(), float(dShape[1]) / img.height())
|
tr.scale(float(dShape[0]) / img.width(), float(dShape[1]) / img.height())
|
||||||
@ -1737,11 +1744,34 @@ class PolyLineROI(ROI):
|
|||||||
|
|
||||||
def shape(self):
|
def shape(self):
|
||||||
p = QtGui.QPainterPath()
|
p = QtGui.QPainterPath()
|
||||||
|
if len(self.handles) == 0:
|
||||||
|
return p
|
||||||
p.moveTo(self.handles[0]['item'].pos())
|
p.moveTo(self.handles[0]['item'].pos())
|
||||||
for i in range(len(self.handles)):
|
for i in range(len(self.handles)):
|
||||||
p.lineTo(self.handles[i]['item'].pos())
|
p.lineTo(self.handles[i]['item'].pos())
|
||||||
p.lineTo(self.handles[0]['item'].pos())
|
p.lineTo(self.handles[0]['item'].pos())
|
||||||
return p
|
return p
|
||||||
|
|
||||||
|
def getArrayRegion(self, data, img, axes=(0,1), returnMappedCoords=False, **kwds):
|
||||||
|
sl = self.getArraySlice(data, img, axes=(0,1))
|
||||||
|
if sl is None:
|
||||||
|
return None
|
||||||
|
sliced = data[sl[0]]
|
||||||
|
im = QtGui.QImage(sliced.shape[axes[0]], sliced.shape[axes[1]], QtGui.QImage.Format_ARGB32)
|
||||||
|
im.fill(0x0)
|
||||||
|
p = QtGui.QPainter(im)
|
||||||
|
p.setPen(fn.mkPen(None))
|
||||||
|
p.setBrush(fn.mkBrush('w'))
|
||||||
|
p.setTransform(self.itemTransform(img)[0])
|
||||||
|
bounds = self.mapRectToItem(img, self.boundingRect())
|
||||||
|
p.translate(-bounds.left(), -bounds.top())
|
||||||
|
p.drawPath(self.shape())
|
||||||
|
p.end()
|
||||||
|
mask = fn.imageToArray(im)[:,:,0].astype(float) / 255.
|
||||||
|
shape = [1] * data.ndim
|
||||||
|
shape[axes[0]] = sliced.shape[axes[0]]
|
||||||
|
shape[axes[1]] = sliced.shape[axes[1]]
|
||||||
|
return sliced * mask
|
||||||
|
|
||||||
|
|
||||||
class LineSegmentROI(ROI):
|
class LineSegmentROI(ROI):
|
||||||
@ -1845,8 +1875,8 @@ class SpiralROI(ROI):
|
|||||||
#for h in self.handles:
|
#for h in self.handles:
|
||||||
#h['pos'] = h['item'].pos()/self.state['size'][0]
|
#h['pos'] = h['item'].pos()/self.state['size'][0]
|
||||||
|
|
||||||
def stateChanged(self):
|
def stateChanged(self, finish=True):
|
||||||
ROI.stateChanged(self)
|
ROI.stateChanged(self, finish=finish)
|
||||||
if len(self.handles) > 1:
|
if len(self.handles) > 1:
|
||||||
self.path = QtGui.QPainterPath()
|
self.path = QtGui.QPainterPath()
|
||||||
h0 = Point(self.handles[0]['item'].pos()).length()
|
h0 = Point(self.handles[0]['item'].pos()).length()
|
||||||
|
@ -467,12 +467,32 @@ class ViewBox(GraphicsWidget):
|
|||||||
padding = 0.02
|
padding = 0.02
|
||||||
return padding
|
return padding
|
||||||
|
|
||||||
def scaleBy(self, s, center=None):
|
def scaleBy(self, s=None, center=None, x=None, y=None):
|
||||||
"""
|
"""
|
||||||
Scale by *s* around given center point (or center of view).
|
Scale by *s* around given center point (or center of view).
|
||||||
*s* may be a Point or tuple (x, y)
|
*s* may be a Point or tuple (x, y).
|
||||||
|
|
||||||
|
Optionally, x or y may be specified individually. This allows the other
|
||||||
|
axis to be left unaffected (note that using a scale factor of 1.0 may
|
||||||
|
cause slight changes due to floating-point error).
|
||||||
"""
|
"""
|
||||||
scale = Point(s)
|
if s is not None:
|
||||||
|
scale = Point(s)
|
||||||
|
else:
|
||||||
|
scale = [x, y]
|
||||||
|
|
||||||
|
affect = [True, True]
|
||||||
|
if scale[0] is None and scale[1] is None:
|
||||||
|
return
|
||||||
|
elif scale[0] is None:
|
||||||
|
affect[0] = False
|
||||||
|
scale[0] = 1.0
|
||||||
|
elif scale[1] is None:
|
||||||
|
affect[1] = False
|
||||||
|
scale[1] = 1.0
|
||||||
|
|
||||||
|
scale = Point(scale)
|
||||||
|
|
||||||
if self.state['aspectLocked'] is not False:
|
if self.state['aspectLocked'] is not False:
|
||||||
scale[0] = self.state['aspectLocked'] * scale[1]
|
scale[0] = self.state['aspectLocked'] * scale[1]
|
||||||
|
|
||||||
@ -481,21 +501,37 @@ class ViewBox(GraphicsWidget):
|
|||||||
center = Point(vr.center())
|
center = Point(vr.center())
|
||||||
else:
|
else:
|
||||||
center = Point(center)
|
center = Point(center)
|
||||||
|
|
||||||
tl = center + (vr.topLeft()-center) * scale
|
tl = center + (vr.topLeft()-center) * scale
|
||||||
br = center + (vr.bottomRight()-center) * scale
|
br = center + (vr.bottomRight()-center) * scale
|
||||||
self.setRange(QtCore.QRectF(tl, br), padding=0)
|
|
||||||
|
|
||||||
def translateBy(self, t):
|
if not affect[0]:
|
||||||
|
self.setYRange(tl.y(), br.y(), padding=0)
|
||||||
|
elif not affect[1]:
|
||||||
|
self.setXRange(tl.x(), br.x(), padding=0)
|
||||||
|
else:
|
||||||
|
self.setRange(QtCore.QRectF(tl, br), padding=0)
|
||||||
|
|
||||||
|
def translateBy(self, t=None, x=None, y=None):
|
||||||
"""
|
"""
|
||||||
Translate the view by *t*, which may be a Point or tuple (x, y).
|
Translate the view by *t*, which may be a Point or tuple (x, y).
|
||||||
"""
|
|
||||||
t = Point(t)
|
|
||||||
#if viewCoords: ## scale from pixels
|
|
||||||
#o = self.mapToView(Point(0,0))
|
|
||||||
#t = self.mapToView(t) - o
|
|
||||||
|
|
||||||
|
Alternately, x or y may be specified independently, leaving the other
|
||||||
|
axis unchanged (note that using a translation of 0 may still cause
|
||||||
|
small changes due to floating-point error).
|
||||||
|
"""
|
||||||
vr = self.targetRect()
|
vr = self.targetRect()
|
||||||
self.setRange(vr.translated(t), padding=0)
|
if t is not None:
|
||||||
|
t = Point(t)
|
||||||
|
self.setRange(vr.translated(t), padding=0)
|
||||||
|
elif x is not None:
|
||||||
|
x1, x2 = vr.left()+x, vr.right()+x
|
||||||
|
self.setXRange(x1, x2, padding=0)
|
||||||
|
elif y is not None:
|
||||||
|
y1, y2 = vr.top()+y, vr.bottom()+y
|
||||||
|
self.setYRange(y1, y2, padding=0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def enableAutoRange(self, axis=None, enable=True):
|
def enableAutoRange(self, axis=None, enable=True):
|
||||||
"""
|
"""
|
||||||
@ -935,7 +971,10 @@ class ViewBox(GraphicsWidget):
|
|||||||
else:
|
else:
|
||||||
tr = dif*mask
|
tr = dif*mask
|
||||||
tr = self.mapToView(tr) - self.mapToView(Point(0,0))
|
tr = self.mapToView(tr) - self.mapToView(Point(0,0))
|
||||||
self.translateBy(tr)
|
x = tr.x() if mask[0] == 1 else None
|
||||||
|
y = tr.y() if mask[1] == 1 else None
|
||||||
|
|
||||||
|
self.translateBy(x=x, y=y)
|
||||||
self.sigRangeChangedManually.emit(self.state['mouseEnabled'])
|
self.sigRangeChangedManually.emit(self.state['mouseEnabled'])
|
||||||
elif ev.button() & QtCore.Qt.RightButton:
|
elif ev.button() & QtCore.Qt.RightButton:
|
||||||
#print "vb.rightDrag"
|
#print "vb.rightDrag"
|
||||||
@ -950,8 +989,11 @@ class ViewBox(GraphicsWidget):
|
|||||||
tr = self.childGroup.transform()
|
tr = self.childGroup.transform()
|
||||||
tr = fn.invertQTransform(tr)
|
tr = fn.invertQTransform(tr)
|
||||||
|
|
||||||
|
x = s[0] if mask[0] == 1 else None
|
||||||
|
y = s[1] if mask[1] == 1 else None
|
||||||
|
|
||||||
center = Point(tr.map(ev.buttonDownPos(QtCore.Qt.RightButton)))
|
center = Point(tr.map(ev.buttonDownPos(QtCore.Qt.RightButton)))
|
||||||
self.scaleBy(s, center)
|
self.scaleBy(x=x, y=y, center=center)
|
||||||
self.sigRangeChangedManually.emit(self.state['mouseEnabled'])
|
self.sigRangeChangedManually.emit(self.state['mouseEnabled'])
|
||||||
|
|
||||||
def keyPressEvent(self, ev):
|
def keyPressEvent(self, ev):
|
||||||
|
Loading…
Reference in New Issue
Block a user