Added inflinelabel class, label dragging and position update works.
Update to TextItem to allow mouse interaction
This commit is contained in:
parent
010cda004b
commit
5172b782b5
@ -18,7 +18,7 @@ pg.setConfigOptions(antialias=True)
|
|||||||
|
|
||||||
p1 = win.addPlot(title="Plot Items example", y=np.random.normal(size=100))
|
p1 = win.addPlot(title="Plot Items example", y=np.random.normal(size=100))
|
||||||
inf1 = pg.InfiniteLine(movable=True, angle=90, label=True, textPosition=[0.5, 0.2], textColor=(200,200,100), textFill=(200,200,200,50))
|
inf1 = pg.InfiniteLine(movable=True, angle=90, label=True, textPosition=[0.5, 0.2], textColor=(200,200,100), textFill=(200,200,200,50))
|
||||||
inf2 = pg.InfiniteLine(movable=True, angle=0, label=True, pen=(0, 0, 200), textColor=(200,0,0), bounds = [-2, 2], suffix="mm", hoverPen=(0,200,0), draggableLabel=True)
|
inf2 = pg.InfiniteLine(movable=True, angle=0, label=True, pen=(0, 0, 200), textColor=(200,0,0), bounds = [-2, 2], suffix="mm", hoverPen=(0,200,0), draggableLabel=True, textFill=0.5)
|
||||||
inf3 = pg.InfiniteLine(movable=True, angle=45)
|
inf3 = pg.InfiniteLine(movable=True, angle=45)
|
||||||
inf1.setPos([2,2])
|
inf1.setPos([2,2])
|
||||||
inf1.setTextLocation(position=0.75)
|
inf1.setTextLocation(position=0.75)
|
||||||
|
@ -84,14 +84,13 @@ class InfiniteLine(GraphicsObject):
|
|||||||
self.textColor = textColor
|
self.textColor = textColor
|
||||||
self.textFill = textFill
|
self.textFill = textFill
|
||||||
self.textPosition = textPosition
|
self.textPosition = textPosition
|
||||||
self.draggableLabel = draggableLabel
|
|
||||||
self.suffix = suffix
|
self.suffix = suffix
|
||||||
|
|
||||||
if (self.angle == 0 or self.angle == 90) and label:
|
|
||||||
self.textItem = TextItem(fill=textFill)
|
self.textItem = InfLineLabel(self, fill=textFill)
|
||||||
self.textItem.setParentItem(self)
|
self.textItem.setParentItem(self)
|
||||||
else:
|
self.setDraggableLabel(draggableLabel)
|
||||||
self.textItem = None
|
self.showLabel(label)
|
||||||
|
|
||||||
self.anchorLeft = (1., 0.5)
|
self.anchorLeft = (1., 0.5)
|
||||||
self.anchorRight = (0., 0.5)
|
self.anchorRight = (0., 0.5)
|
||||||
@ -192,53 +191,8 @@ class InfiniteLine(GraphicsObject):
|
|||||||
if self.p != newPos:
|
if self.p != newPos:
|
||||||
self.p = newPos
|
self.p = newPos
|
||||||
self._invalidateCache()
|
self._invalidateCache()
|
||||||
|
|
||||||
if self.textItem is not None and isinstance(self.getViewBox(), ViewBox):
|
|
||||||
self.updateText()
|
|
||||||
else: # no label displayed or called just before being dragged for the first time
|
|
||||||
GraphicsObject.setPos(self, Point(self.p))
|
|
||||||
self.update()
|
|
||||||
self.sigPositionChanged.emit(self)
|
|
||||||
|
|
||||||
def updateText(self):
|
|
||||||
"""
|
|
||||||
Update the content displayed by the textItem. Called only if a textItem
|
|
||||||
is requested and if the item has already been added to a PlotItem.
|
|
||||||
"""
|
|
||||||
rangeX, rangeY = self.getViewBox().viewRange()
|
|
||||||
xmin, xmax = rangeX
|
|
||||||
ymin, ymax = rangeY
|
|
||||||
pos, shift = self.textPosition
|
|
||||||
if self.angle == 90: # vertical line
|
|
||||||
diffMin = self.value()-xmin
|
|
||||||
limInf = shift*(xmax-xmin)
|
|
||||||
if diffMin < limInf:
|
|
||||||
self.textItem.anchor = Point(self.anchorRight)
|
|
||||||
else:
|
|
||||||
self.textItem.anchor = Point(self.anchorLeft)
|
|
||||||
fmt = " x = " + self.format
|
|
||||||
if self.suffix is not None:
|
|
||||||
fmt = fmt + self.suffix
|
|
||||||
self.textItem.setText(fmt.format(self.value()), color=self.textColor)
|
|
||||||
posY = ymin+pos*(ymax-ymin)
|
|
||||||
self._exactPos = Point(self.value(), posY)
|
|
||||||
elif self.angle == 0: # horizontal line
|
|
||||||
diffMin = self.value()-ymin
|
|
||||||
limInf = shift*(ymax-ymin)
|
|
||||||
if diffMin < limInf:
|
|
||||||
self.textItem.anchor = Point(self.anchorUp)
|
|
||||||
else:
|
|
||||||
self.textItem.anchor = Point(self.anchorDown)
|
|
||||||
fmt = " y = " + self.format
|
|
||||||
if self.suffix is not None:
|
|
||||||
fmt = fmt + self.suffix
|
|
||||||
self.textItem.setText(fmt.format(self.value()), color=self.textColor)
|
|
||||||
posX = xmin+pos*(xmax-xmin)
|
|
||||||
self._exactPos = Point(posX, self.value())
|
|
||||||
if self.draggableLabel:
|
|
||||||
GraphicsObject.setPos(self, Point(self.p))
|
GraphicsObject.setPos(self, Point(self.p))
|
||||||
else: # precise location needed
|
self.sigPositionChanged.emit(self)
|
||||||
GraphicsObject.setPos(self, self._exactPos)
|
|
||||||
|
|
||||||
def getXPos(self):
|
def getXPos(self):
|
||||||
return self.p[0]
|
return self.p[0]
|
||||||
@ -354,8 +308,7 @@ class InfiniteLine(GraphicsObject):
|
|||||||
(eg, the view range has changed or the view was resized)
|
(eg, the view range has changed or the view was resized)
|
||||||
"""
|
"""
|
||||||
self._invalidateCache()
|
self._invalidateCache()
|
||||||
if isinstance(self.getViewBox(), ViewBox) and self.textItem is not None:
|
self.textItem.updatePosition()
|
||||||
self.updateText()
|
|
||||||
|
|
||||||
def showLabel(self, state):
|
def showLabel(self, state):
|
||||||
"""
|
"""
|
||||||
@ -367,12 +320,7 @@ class InfiniteLine(GraphicsObject):
|
|||||||
state If True, the label is shown. Otherwise, it is hidden.
|
state If True, the label is shown. Otherwise, it is hidden.
|
||||||
============== ======================================================
|
============== ======================================================
|
||||||
"""
|
"""
|
||||||
if state:
|
self.textItem.setVisible(state)
|
||||||
self.textItem = TextItem(fill=self.textFill)
|
|
||||||
self.textItem.setParentItem(self)
|
|
||||||
self.viewTransformChanged()
|
|
||||||
else:
|
|
||||||
self.textItem = None
|
|
||||||
|
|
||||||
def setTextLocation(self, position=0.05, shift=0.5):
|
def setTextLocation(self, position=0.05, shift=0.5):
|
||||||
"""
|
"""
|
||||||
@ -388,10 +336,9 @@ class InfiniteLine(GraphicsObject):
|
|||||||
shift float (range of value = [0-1]).
|
shift float (range of value = [0-1]).
|
||||||
============== ======================================================
|
============== ======================================================
|
||||||
"""
|
"""
|
||||||
pos = np.clip(position, 0, 1)
|
self.textItem.orthoPos = position
|
||||||
shift = np.clip(shift, 0, 1)
|
self.textItem.shiftPos = shift
|
||||||
self.textPosition = [pos, shift]
|
self.textItem.updatePosition()
|
||||||
self.update()
|
|
||||||
|
|
||||||
def setDraggableLabel(self, state):
|
def setDraggableLabel(self, state):
|
||||||
"""
|
"""
|
||||||
@ -399,11 +346,93 @@ class InfiniteLine(GraphicsObject):
|
|||||||
of the line. If True, then the location of the label change during the
|
of the line. If True, then the location of the label change during the
|
||||||
dragging of the line.
|
dragging of the line.
|
||||||
"""
|
"""
|
||||||
self.draggableLabel = state
|
self.textItem.setMovable(state)
|
||||||
self.update()
|
|
||||||
|
|
||||||
def setName(self, name):
|
def setName(self, name):
|
||||||
self._name = name
|
self._name = name
|
||||||
|
|
||||||
def name(self):
|
def name(self):
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
|
|
||||||
|
class InfLineLabel(TextItem):
|
||||||
|
# a text label that attaches itself to an InfiniteLine
|
||||||
|
def __init__(self, line, **kwds):
|
||||||
|
self.line = line
|
||||||
|
self.movable = False
|
||||||
|
self.dragAxis = None # 0=x, 1=y
|
||||||
|
self.orthoPos = 0.5 # text will always be placed on the line at a position relative to view bounds
|
||||||
|
self.format = "{value}"
|
||||||
|
self.line.sigPositionChanged.connect(self.valueChanged)
|
||||||
|
TextItem.__init__(self, **kwds)
|
||||||
|
self.valueChanged()
|
||||||
|
|
||||||
|
def valueChanged(self):
|
||||||
|
if not self.isVisible():
|
||||||
|
return
|
||||||
|
value = self.line.value()
|
||||||
|
self.setText(self.format.format(value=value))
|
||||||
|
self.updatePosition()
|
||||||
|
|
||||||
|
def updatePosition(self):
|
||||||
|
view = self.getViewBox()
|
||||||
|
if not self.isVisible() or not isinstance(view, ViewBox):
|
||||||
|
# not in a viewbox, skip update
|
||||||
|
return
|
||||||
|
|
||||||
|
# 1. determine view extents along line axis
|
||||||
|
tr = view.childGroup.itemTransform(self.line)[0]
|
||||||
|
vr = tr.mapRect(view.viewRect())
|
||||||
|
pt1 = Point(vr.left(), 0)
|
||||||
|
pt2 = Point(vr.right(), 0)
|
||||||
|
|
||||||
|
# 2. pick relative point between extents and set position
|
||||||
|
pt = pt2 * self.orthoPos + pt1 * (1-self.orthoPos)
|
||||||
|
self.setPos(pt)
|
||||||
|
|
||||||
|
def setVisible(self, v):
|
||||||
|
TextItem.setVisible(self, v)
|
||||||
|
if v:
|
||||||
|
self.updateText()
|
||||||
|
self.updatePosition()
|
||||||
|
|
||||||
|
def setMovable(self, m):
|
||||||
|
self.movable = m
|
||||||
|
self.setAcceptHoverEvents(m)
|
||||||
|
|
||||||
|
def mouseDragEvent(self, ev):
|
||||||
|
if self.movable and ev.button() == QtCore.Qt.LeftButton:
|
||||||
|
if ev.isStart():
|
||||||
|
self._moving = True
|
||||||
|
self._cursorOffset = self._posToRel(ev.buttonDownPos())
|
||||||
|
self._startPosition = self.orthoPos
|
||||||
|
ev.accept()
|
||||||
|
|
||||||
|
if not self._moving:
|
||||||
|
return
|
||||||
|
|
||||||
|
rel = self._posToRel(ev.pos())
|
||||||
|
self.orthoPos = self._startPosition + rel - self._cursorOffset
|
||||||
|
self.updatePosition()
|
||||||
|
if ev.isFinish():
|
||||||
|
self._moving = False
|
||||||
|
|
||||||
|
def mouseClickEvent(self, ev):
|
||||||
|
if self.moving and ev.button() == QtCore.Qt.RightButton:
|
||||||
|
ev.accept()
|
||||||
|
self.orthoPos = self._startPosition
|
||||||
|
self.moving = False
|
||||||
|
|
||||||
|
def hoverEvent(self, ev):
|
||||||
|
if not ev.isExit() and self.movable:
|
||||||
|
ev.acceptDrags(QtCore.Qt.LeftButton)
|
||||||
|
|
||||||
|
def _posToRel(self, pos):
|
||||||
|
# convert local position to relative position along line between view bounds
|
||||||
|
view = self.getViewBox()
|
||||||
|
tr = view.childGroup.itemTransform(self.line)[0]
|
||||||
|
vr = tr.mapRect(view.viewRect())
|
||||||
|
pos = self.mapToParent(pos)
|
||||||
|
return (pos.x() - vr.left()) / vr.width()
|
||||||
|
|
||||||
|
|
@ -41,7 +41,7 @@ class TextItem(UIGraphicsItem):
|
|||||||
self.fill = fn.mkBrush(fill)
|
self.fill = fn.mkBrush(fill)
|
||||||
self.border = fn.mkPen(border)
|
self.border = fn.mkPen(border)
|
||||||
self.rotate(angle)
|
self.rotate(angle)
|
||||||
self.setFlag(self.ItemIgnoresTransformations) ## This is required to keep the text unscaled inside the viewport
|
#self.textItem.setFlag(self.ItemIgnoresTransformations) ## This is required to keep the text unscaled inside the viewport
|
||||||
|
|
||||||
def setText(self, text, color=(200,200,200)):
|
def setText(self, text, color=(200,200,200)):
|
||||||
"""
|
"""
|
||||||
@ -114,22 +114,10 @@ class TextItem(UIGraphicsItem):
|
|||||||
s = self._exportOpts['resolutionScale']
|
s = self._exportOpts['resolutionScale']
|
||||||
self.textItem.scale(s, s)
|
self.textItem.scale(s, s)
|
||||||
|
|
||||||
#br = self.textItem.mapRectToParent(self.textItem.boundingRect())
|
self.textItem.setTransform(self.sceneTransform().inverted()[0])
|
||||||
self.textItem.setPos(0,0)
|
self.textItem.setPos(0,0)
|
||||||
br = self.textItem.boundingRect()
|
self.textItem.setPos(-self.textItem.mapToParent(Point(0,0)))
|
||||||
apos = self.textItem.mapToParent(Point(br.width()*self.anchor.x(), br.height()*self.anchor.y()))
|
|
||||||
#print br, apos
|
|
||||||
self.textItem.setPos(-apos.x(), -apos.y())
|
|
||||||
|
|
||||||
#def textBoundingRect(self):
|
|
||||||
### return the bounds of the text box in device coordinates
|
|
||||||
#pos = self.mapToDevice(QtCore.QPointF(0,0))
|
|
||||||
#if pos is None:
|
|
||||||
#return None
|
|
||||||
#tbr = self.textItem.boundingRect()
|
|
||||||
#return QtCore.QRectF(pos.x() - tbr.width()*self.anchor.x(), pos.y() - tbr.height()*self.anchor.y(), tbr.width(), tbr.height())
|
|
||||||
|
|
||||||
|
|
||||||
def viewRangeChanged(self):
|
def viewRangeChanged(self):
|
||||||
self.updateText()
|
self.updateText()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user