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.CircleROI([80, 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.PolyLineROI([[80, 60], [90, 30], [60, 40]], pen=(6,9), closed=True))
|
||||
|
||||
def update(roi):
|
||||
img1b.setImage(roi.getArrayRegion(arr, img1a), levels=(0, arr.max()))
|
||||
|
@ -314,10 +314,13 @@ class AxisItem(GraphicsWidget):
|
||||
view.sigResized.connect(self.linkedViewChanged)
|
||||
|
||||
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:
|
||||
newRange = view.viewRange()[1]
|
||||
self.setRange(*newRange[::-1])
|
||||
if view.yInverted():
|
||||
self.setRange(*newRange[::-1])
|
||||
else:
|
||||
self.setRange(*newRange)
|
||||
else:
|
||||
if newRange is None:
|
||||
newRange = view.viewRange()[0]
|
||||
@ -330,18 +333,12 @@ class AxisItem(GraphicsWidget):
|
||||
## extend rect if ticks go in negative direction
|
||||
## also extend to account for text that flows past the edges
|
||||
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)
|
||||
elif self.orientation == 'right':
|
||||
#rect.setLeft(rect.left() + min(0,self.tickLength))
|
||||
rect = rect.adjusted(min(0,self.tickLength), -15, 0, 15)
|
||||
elif self.orientation == 'top':
|
||||
#rect.setBottom(rect.bottom() - min(0,self.tickLength))
|
||||
rect = rect.adjusted(-15, 0, 15, -min(0,self.tickLength))
|
||||
elif self.orientation == 'bottom':
|
||||
#rect.setTop(rect.top() + min(0,self.tickLength))
|
||||
rect = rect.adjusted(-15, min(0,self.tickLength), 15, 0)
|
||||
return rect
|
||||
else:
|
||||
|
@ -249,7 +249,7 @@ class ImageItem(GraphicsObject):
|
||||
|
||||
def render(self):
|
||||
prof = debug.Profiler('ImageItem.render', disabled=True)
|
||||
if self.image is None:
|
||||
if self.image is None or self.image.size == 0:
|
||||
return
|
||||
if isinstance(self.lut, collections.Callable):
|
||||
lut = self.lut(self.image)
|
||||
@ -269,6 +269,8 @@ class ImageItem(GraphicsObject):
|
||||
return
|
||||
if self.qimage is None:
|
||||
self.render()
|
||||
if self.qimage is None:
|
||||
return
|
||||
prof.mark('render QImage')
|
||||
if self.paintMode is not None:
|
||||
p.setCompositionMode(self.paintMode)
|
||||
|
@ -112,7 +112,10 @@ class PlotCurveItem(GraphicsObject):
|
||||
if orthoRange is not None:
|
||||
mask = (d2 >= orthoRange[0]) * (d2 <= orthoRange[1])
|
||||
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
|
||||
if frac >= 1.0:
|
||||
|
@ -802,7 +802,11 @@ class ROI(GraphicsObject):
|
||||
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
|
||||
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"
|
||||
|
||||
## Determine shape of array along ROI axes
|
||||
@ -810,8 +814,11 @@ class ROI(GraphicsObject):
|
||||
#print " dshape", dShape
|
||||
|
||||
## 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
|
||||
#m = QtGui.QTransform()
|
||||
tr.scale(float(dShape[0]) / img.width(), float(dShape[1]) / img.height())
|
||||
@ -1737,11 +1744,34 @@ class PolyLineROI(ROI):
|
||||
|
||||
def shape(self):
|
||||
p = QtGui.QPainterPath()
|
||||
if len(self.handles) == 0:
|
||||
return p
|
||||
p.moveTo(self.handles[0]['item'].pos())
|
||||
for i in range(len(self.handles)):
|
||||
p.lineTo(self.handles[i]['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):
|
||||
@ -1845,8 +1875,8 @@ class SpiralROI(ROI):
|
||||
#for h in self.handles:
|
||||
#h['pos'] = h['item'].pos()/self.state['size'][0]
|
||||
|
||||
def stateChanged(self):
|
||||
ROI.stateChanged(self)
|
||||
def stateChanged(self, finish=True):
|
||||
ROI.stateChanged(self, finish=finish)
|
||||
if len(self.handles) > 1:
|
||||
self.path = QtGui.QPainterPath()
|
||||
h0 = Point(self.handles[0]['item'].pos()).length()
|
||||
|
@ -467,12 +467,32 @@ class ViewBox(GraphicsWidget):
|
||||
padding = 0.02
|
||||
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).
|
||||
*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:
|
||||
scale[0] = self.state['aspectLocked'] * scale[1]
|
||||
|
||||
@ -481,21 +501,37 @@ class ViewBox(GraphicsWidget):
|
||||
center = Point(vr.center())
|
||||
else:
|
||||
center = Point(center)
|
||||
|
||||
tl = center + (vr.topLeft()-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).
|
||||
"""
|
||||
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()
|
||||
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):
|
||||
"""
|
||||
@ -935,7 +971,10 @@ class ViewBox(GraphicsWidget):
|
||||
else:
|
||||
tr = dif*mask
|
||||
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'])
|
||||
elif ev.button() & QtCore.Qt.RightButton:
|
||||
#print "vb.rightDrag"
|
||||
@ -950,8 +989,11 @@ class ViewBox(GraphicsWidget):
|
||||
tr = self.childGroup.transform()
|
||||
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)))
|
||||
self.scaleBy(s, center)
|
||||
self.scaleBy(x=x, y=y, center=center)
|
||||
self.sigRangeChangedManually.emit(self.state['mouseEnabled'])
|
||||
|
||||
def keyPressEvent(self, ev):
|
||||
|
Loading…
Reference in New Issue
Block a user