Labels can rotate with line

This commit is contained in:
Luke Campagnola 2016-02-21 00:17:17 -08:00
parent a8510c3354
commit 069a5bfeea
4 changed files with 67 additions and 99 deletions

View File

@ -17,12 +17,14 @@ win.resize(1000,600)
pg.setConfigOptions(antialias=True)
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))
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)
inf1 = pg.InfiniteLine(movable=True, angle=90, text='x={value:0.2f}',
textOpts={'position':0.2, 'color': (200,200,100), 'fill': (200,200,200,50)})
inf2 = pg.InfiniteLine(movable=True, angle=0, pen=(0, 0, 200), bounds = [-2, 2], hoverPen=(0,200,0), text='y={value:0.2f}mm',
textOpts={'color': (200,0,0), 'movable': True, 'fill': 0.5})
inf3 = pg.InfiniteLine(movable=True, angle=45, text='diagonal', textOpts={'rotateAxis': [1, 0]})
inf1.setPos([2,2])
inf1.setTextLocation(position=0.75)
inf2.setTextLocation(shift=0.8)
#inf1.setTextLocation(position=0.75)
#inf2.setTextLocation(shift=0.8)
p1.addItem(inf1)
p1.addItem(inf2)
p1.addItem(inf3)

View File

@ -23,7 +23,7 @@ plot.setWindowTitle('pyqtgraph example: text')
curve = plot.plot(x,y) ## add a single curve
## Create text object, use HTML tags to specify color/size
text = pg.TextItem(html='<div style="text-align: center"><span style="color: #FFF;">This is the</span><br><span style="color: #FF0; font-size: 16pt;">PEAK</span></div>', anchor=(-0.3,1.3), border='w', fill=(0, 0, 255, 100))
text = pg.TextItem(html='<div style="text-align: center"><span style="color: #FFF;">This is the</span><br><span style="color: #FF0; font-size: 16pt;">PEAK</span></div>', anchor=(-0.3,0.5), angle=45, border='w', fill=(0, 0, 255, 100))
plot.addItem(text)
text.setPos(0, y.max())

View File

@ -31,9 +31,7 @@ class InfiniteLine(GraphicsObject):
sigPositionChanged = QtCore.Signal(object)
def __init__(self, pos=None, angle=90, pen=None, movable=False, bounds=None,
hoverPen=None, label=False, textColor=None, textFill=None,
textPosition=[0.05, 0.5], textFormat="{:.3f}", draggableLabel=False,
suffix=None, name='InfiniteLine'):
hoverPen=None, text=None, textOpts=None, name=None):
"""
=============== ==================================================================
**Arguments:**
@ -49,21 +47,12 @@ class InfiniteLine(GraphicsObject):
Default pen is red.
bounds Optional [min, max] bounding values. Bounds are only valid if the
line is vertical or horizontal.
label if True, a label is displayed next to the line to indicate its
location in data coordinates
textColor color of the label. Can be any argument fn.mkColor can understand.
textFill A brush to use when filling within the border of the text.
textPosition list of float (0-1) that defines when the precise location of the
label. The first float governs the location of the label in the
direction of the line, whereas the second one governs the shift
of the label from one side of the line to the other in the
orthogonal direction.
textFormat Any new python 3 str.format() format.
draggableLabel Bool. If True, the user can relocate the label during the dragging.
If set to True, the first entry of textPosition is no longer
useful.
suffix If not None, corresponds to the unit to show next to the label
name name of the item
text Text to be displayed in a label attached to the line, or
None to show no label (default is None). May optionally
include formatting strings to display the line value.
textOpts A dict of keyword arguments to use when constructing the
text label. See :class:`InfLineLabel`.
name Name of the item
=============== ==================================================================
"""
@ -79,18 +68,10 @@ class InfiniteLine(GraphicsObject):
self.p = [0, 0]
self.setAngle(angle)
if textColor is None:
textColor = (200, 200, 100)
self.textColor = textColor
self.textFill = textFill
self.textPosition = textPosition
self.suffix = suffix
self.textItem = InfLineLabel(self, fill=textFill)
self.textItem.setParentItem(self)
self.setDraggableLabel(draggableLabel)
self.showLabel(label)
if text is not None:
textOpts = {} if textOpts is None else textOpts
self.textItem = InfLineLabel(self, text=text, **textOpts)
self.textItem.setParentItem(self)
self.anchorLeft = (1., 0.5)
self.anchorRight = (0., 0.5)
@ -110,8 +91,6 @@ class InfiniteLine(GraphicsObject):
self.setHoverPen(hoverPen)
self.currentPen = self.pen
self.format = textFormat
self._name = name
# Cache complex value for drawing speed-up (#PR267)
@ -308,46 +287,7 @@ class InfiniteLine(GraphicsObject):
(eg, the view range has changed or the view was resized)
"""
self._invalidateCache()
self.textItem.updatePosition()
def showLabel(self, state):
"""
Display or not the label indicating the location of the line in data
coordinates.
============== ======================================================
**Arguments:**
state If True, the label is shown. Otherwise, it is hidden.
============== ======================================================
"""
self.textItem.setVisible(state)
def setTextLocation(self, position=0.05, shift=0.5):
"""
Set the parameters that defines the location of the label on the axis.
The position *parameter* governs the location of the label in the
direction of the line, whereas the *shift* governs the shift of the
label from one side of the line to the other in the orthogonal
direction.
============== ======================================================
**Arguments:**
position float (range of value = [0-1])
shift float (range of value = [0-1]).
============== ======================================================
"""
self.textItem.orthoPos = position
self.textItem.shiftPos = shift
self.textItem.updatePosition()
def setDraggableLabel(self, state):
"""
Set the state of the label regarding its behaviour during the dragging
of the line. If True, then the location of the label change during the
dragging of the line.
"""
self.textItem.setMovable(state)
def setName(self, name):
self._name = name
@ -356,13 +296,21 @@ class InfiniteLine(GraphicsObject):
class InfLineLabel(TextItem):
# a text label that attaches itself to an InfiniteLine
def __init__(self, line, **kwds):
"""
A TextItem that attaches itself to an InfiniteLine.
This class extends TextItem with the following features:
* Automatically positions adjacent to the line at a fixed position along
the line and within the view box.
* Automatically reformats text when the line value has changed.
* Can optionally be dragged to change its location along the line.
"""
def __init__(self, line, text="", movable=False, position=0.5, **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.movable = movable
self.orthoPos = position # text will always be placed on the line at a position relative to view bounds
self.format = text
self.line.sigPositionChanged.connect(self.valueChanged)
TextItem.__init__(self, **kwds)
self.valueChanged()
@ -412,7 +360,7 @@ class InfLineLabel(TextItem):
return
rel = self._posToRel(ev.pos())
self.orthoPos = self._startPosition + rel - self._cursorOffset
self.orthoPos = np.clip(self._startPosition + rel - self._cursorOffset, 0, 1)
self.updatePosition()
if ev.isFinish():
self._moving = False
@ -427,6 +375,10 @@ class InfLineLabel(TextItem):
if not ev.isExit() and self.movable:
ev.acceptDrags(QtCore.Qt.LeftButton)
def viewTransformChanged(self):
self.updatePosition()
TextItem.viewTransformChanged(self)
def _posToRel(self, pos):
# convert local position to relative position along line between view bounds
view = self.getViewBox()

View File

@ -1,13 +1,16 @@
import numpy as np
from ..Qt import QtCore, QtGui
from ..Point import Point
from .UIGraphicsItem import *
from .. import functions as fn
from .GraphicsObject import GraphicsObject
class TextItem(UIGraphicsItem):
class TextItem(GraphicsObject):
"""
GraphicsItem displaying unscaled text (the text will always appear normal even inside a scaled ViewBox).
"""
def __init__(self, text='', color=(200,200,200), html=None, anchor=(0,0), border=None, fill=None, angle=0):
def __init__(self, text='', color=(200,200,200), html=None, anchor=(0,0),
border=None, fill=None, angle=0, rotateAxis=None):
"""
============== =================================================================================
**Arguments:**
@ -20,16 +23,19 @@ class TextItem(UIGraphicsItem):
sets the lower-right corner.
*border* A pen to use when drawing the border
*fill* A brush to use when filling within the border
*angle* Angle in degrees to rotate text. Default is 0; text will be displayed upright.
*rotateAxis* If None, then a text angle of 0 always points along the +x axis of the scene.
If a QPointF or (x,y) sequence is given, then it represents a vector direction
in the parent's coordinate system that the 0-degree line will be aligned to. This
Allows text to follow both the position and orientation of its parent while still
discarding any scale and shear factors.
============== =================================================================================
"""
## not working yet
#*angle* Angle in degrees to rotate text (note that the rotation assigned in this item's
#transformation will be ignored)
self.anchor = Point(anchor)
self.rotateAxis = None if rotateAxis is None else Point(rotateAxis)
#self.angle = 0
UIGraphicsItem.__init__(self)
GraphicsObject.__init__(self)
self.textItem = QtGui.QGraphicsTextItem()
self.textItem.setParentItem(self)
self._lastTransform = None
@ -101,9 +107,8 @@ class TextItem(UIGraphicsItem):
self.updateText()
def setAngle(self, angle):
self.textItem.resetTransform()
self.textItem.rotate(angle)
self.updateText()
self.angle = angle
self.updateTransform()
def updateText(self):
# update text position to obey anchor
@ -120,9 +125,6 @@ class TextItem(UIGraphicsItem):
#s = self._exportOpts['resolutionScale']
#self.textItem.scale(s, s)
def viewRangeChanged(self):
self.updateText()
def boundingRect(self):
return self.textItem.mapToParent(self.textItem.boundingRect()).boundingRect()
@ -160,7 +162,19 @@ class TextItem(UIGraphicsItem):
t = pt.inverted()[0]
# reset translation
t.setMatrix(t.m11(), t.m12(), t.m13(), t.m21(), t.m22(), t.m23(), 0, 0, t.m33())
# apply rotation
angle = -self.angle
if self.rotateAxis is not None:
d = pt.map(self.rotateAxis) - pt.map(Point(0, 0))
a = np.arctan2(d.y(), d.x()) * 180 / np.pi
angle += a
t.rotate(angle)
self.setTransform(t)
self._lastTransform = pt
self.updateText()