Infiniteline enhancement
This commit is contained in:
parent
9e8c2082ed
commit
ce36ea4eb6
35
examples/plottingItems.py
Normal file
35
examples/plottingItems.py
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
This example demonstrates some of the plotting items available in pyqtgraph.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import initExample ## Add path to library (just for examples; you do not need this)
|
||||||
|
from pyqtgraph.Qt import QtGui, QtCore
|
||||||
|
import numpy as np
|
||||||
|
import pyqtgraph as pg
|
||||||
|
|
||||||
|
|
||||||
|
app = QtGui.QApplication([])
|
||||||
|
win = pg.GraphicsWindow(title="Plotting items examples")
|
||||||
|
win.resize(1000,600)
|
||||||
|
win.setWindowTitle('pyqtgraph example: plotting with items')
|
||||||
|
|
||||||
|
# Enable antialiasing for prettier plots
|
||||||
|
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, textColor=(200,200,100), textFill=(200,200,200,50))
|
||||||
|
inf2 = pg.InfiniteLine(movable=True, angle=0, label=True, pen=(0, 0, 200), bounds = [-2, 2], unit="mm", hoverPen=(0,200,0))
|
||||||
|
inf3 = pg.InfiniteLine(movable=True, angle=45)
|
||||||
|
inf1.setPos([2,2])
|
||||||
|
p1.addItem(inf1)
|
||||||
|
p1.addItem(inf2)
|
||||||
|
p1.addItem(inf3)
|
||||||
|
lr = pg.LinearRegionItem(values=[0, 10])
|
||||||
|
p1.addItem(lr)
|
||||||
|
|
||||||
|
## 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_()
|
|
@ -1,32 +1,73 @@
|
||||||
from ..Qt import QtGui, QtCore
|
from ..Qt import QtGui, QtCore
|
||||||
from ..Point import Point
|
from ..Point import Point
|
||||||
from .GraphicsObject import GraphicsObject
|
from .UIGraphicsItem import UIGraphicsItem
|
||||||
|
from .TextItem import TextItem
|
||||||
from .. import functions as fn
|
from .. import functions as fn
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import weakref
|
import weakref
|
||||||
|
import math
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['InfiniteLine']
|
__all__ = ['InfiniteLine']
|
||||||
class InfiniteLine(GraphicsObject):
|
|
||||||
|
|
||||||
|
def _calcLine(pos, angle, xmin, ymin, xmax, ymax):
|
||||||
"""
|
"""
|
||||||
**Bases:** :class:`GraphicsObject <pyqtgraph.GraphicsObject>`
|
Evaluate the location of the points that delimitates a line into a viewbox
|
||||||
|
described by x and y ranges. Depending on the angle value, pos can be a
|
||||||
|
float (if angle=0 and 90) or a list of float (x and y coordinates).
|
||||||
|
Could be possible to beautify this piece of code.
|
||||||
|
New in verson 0.9.11
|
||||||
|
"""
|
||||||
|
if angle == 0:
|
||||||
|
x1, y1, x2, y2 = xmin, pos, xmax, pos
|
||||||
|
elif angle == 90:
|
||||||
|
x1, y1, x2, y2 = pos, ymin, pos, ymax
|
||||||
|
else:
|
||||||
|
x0, y0 = pos
|
||||||
|
tana = math.tan(angle*math.pi/180)
|
||||||
|
y1 = tana*(xmin-x0) + y0
|
||||||
|
y2 = tana*(xmax-x0) + y0
|
||||||
|
if angle > 0:
|
||||||
|
y1 = max(y1, ymin)
|
||||||
|
y2 = min(y2, ymax)
|
||||||
|
else:
|
||||||
|
y1 = min(y1, ymax)
|
||||||
|
y2 = max(y2, ymin)
|
||||||
|
x1 = (y1-y0)/tana + x0
|
||||||
|
x2 = (y2-y0)/tana + x0
|
||||||
|
p1 = Point(x1, y1)
|
||||||
|
p2 = Point(x2, y2)
|
||||||
|
return p1, p2
|
||||||
|
|
||||||
|
|
||||||
|
class InfiniteLine(UIGraphicsItem):
|
||||||
|
"""
|
||||||
|
**Bases:** :class:`UIGraphicsItem <pyqtgraph.UIGraphicsItem>`
|
||||||
|
|
||||||
Displays a line of infinite length.
|
Displays a line of infinite length.
|
||||||
This line may be dragged to indicate a position in data coordinates.
|
This line may be dragged to indicate a position in data coordinates.
|
||||||
|
|
||||||
=============================== ===================================================
|
=============================== ===================================================
|
||||||
**Signals:**
|
**Signals:**
|
||||||
sigDragged(self)
|
sigDragged(self)
|
||||||
sigPositionChangeFinished(self)
|
sigPositionChangeFinished(self)
|
||||||
sigPositionChanged(self)
|
sigPositionChanged(self)
|
||||||
=============================== ===================================================
|
=============================== ===================================================
|
||||||
|
|
||||||
|
Major changes have been performed in this class since version 0.9.11. The
|
||||||
|
number of methods in the public API has been increased, but the already
|
||||||
|
existing methods can be used in the same way.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
sigDragged = QtCore.Signal(object)
|
sigDragged = QtCore.Signal(object)
|
||||||
sigPositionChangeFinished = QtCore.Signal(object)
|
sigPositionChangeFinished = QtCore.Signal(object)
|
||||||
sigPositionChanged = QtCore.Signal(object)
|
sigPositionChanged = QtCore.Signal(object)
|
||||||
|
|
||||||
def __init__(self, pos=None, angle=90, pen=None, movable=False, bounds=None):
|
def __init__(self, pos=None, angle=90, pen=None, movable=False, bounds=None,
|
||||||
|
hoverPen=None, label=False, textColor=None, textFill=None,
|
||||||
|
textLocation=0.05, textShift=0.5, textFormat="{:.3f}",
|
||||||
|
unit=None, name=None):
|
||||||
"""
|
"""
|
||||||
=============== ==================================================================
|
=============== ==================================================================
|
||||||
**Arguments:**
|
**Arguments:**
|
||||||
|
@ -37,79 +78,125 @@ class InfiniteLine(GraphicsObject):
|
||||||
for :func:`mkPen <pyqtgraph.mkPen>`. Default pen is transparent
|
for :func:`mkPen <pyqtgraph.mkPen>`. Default pen is transparent
|
||||||
yellow.
|
yellow.
|
||||||
movable If True, the line can be dragged to a new position by the user.
|
movable If True, the line can be dragged to a new position by the user.
|
||||||
|
hoverPen Pen to use when drawing line when hovering over it. Can be any
|
||||||
|
arguments that are valid for :func:`mkPen <pyqtgraph.mkPen>`.
|
||||||
|
Default pen is red.
|
||||||
bounds Optional [min, max] bounding values. Bounds are only valid if the
|
bounds Optional [min, max] bounding values. Bounds are only valid if the
|
||||||
line is vertical or horizontal.
|
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.
|
||||||
|
textLocation A float [0-1] that defines the location of the text.
|
||||||
|
textShift A float [0-1] that defines when the text shifts from one side to
|
||||||
|
another.
|
||||||
|
textFormat Any new python 3 str.format() format.
|
||||||
|
unit If not None, corresponds to the unit to show next to the label
|
||||||
|
name If not None, corresponds to the name of the object
|
||||||
=============== ==================================================================
|
=============== ==================================================================
|
||||||
"""
|
"""
|
||||||
|
|
||||||
GraphicsObject.__init__(self)
|
UIGraphicsItem.__init__(self)
|
||||||
|
|
||||||
if bounds is None: ## allowed value boundaries for orthogonal lines
|
if bounds is None: ## allowed value boundaries for orthogonal lines
|
||||||
self.maxRange = [None, None]
|
self.maxRange = [None, None]
|
||||||
else:
|
else:
|
||||||
self.maxRange = bounds
|
self.maxRange = bounds
|
||||||
self.moving = False
|
self.moving = False
|
||||||
self.setMovable(movable)
|
|
||||||
self.mouseHovering = False
|
self.mouseHovering = False
|
||||||
|
|
||||||
|
self.angle = ((angle+45) % 180) - 45
|
||||||
|
if textColor is None:
|
||||||
|
textColor = (200, 200, 200)
|
||||||
|
self.textColor = textColor
|
||||||
|
self.location = textLocation
|
||||||
|
self.shift = textShift
|
||||||
|
self.label = label
|
||||||
|
self.format = textFormat
|
||||||
|
self.unit = unit
|
||||||
|
self._name = name
|
||||||
|
|
||||||
|
self.anchorLeft = (1., 0.5)
|
||||||
|
self.anchorRight = (0., 0.5)
|
||||||
|
self.anchorUp = (0.5, 1.)
|
||||||
|
self.anchorDown = (0.5, 0.)
|
||||||
|
self.text = TextItem(fill=textFill)
|
||||||
|
self.text.setParentItem(self) # important
|
||||||
self.p = [0, 0]
|
self.p = [0, 0]
|
||||||
self.setAngle(angle)
|
|
||||||
|
if pen is None:
|
||||||
|
pen = (200, 200, 100)
|
||||||
|
|
||||||
|
self.setPen(pen)
|
||||||
|
|
||||||
|
if hoverPen is None:
|
||||||
|
self.setHoverPen(color=(255,0,0), width=self.pen.width())
|
||||||
|
else:
|
||||||
|
self.setHoverPen(hoverPen)
|
||||||
|
self.currentPen = self.pen
|
||||||
|
|
||||||
|
self.setMovable(movable)
|
||||||
|
|
||||||
if pos is None:
|
if pos is None:
|
||||||
pos = Point(0,0)
|
pos = Point(0,0)
|
||||||
self.setPos(pos)
|
self.setPos(pos)
|
||||||
|
|
||||||
if pen is None:
|
if (self.angle == 0 or self.angle == 90) and self.label:
|
||||||
pen = (200, 200, 100)
|
self.text.show()
|
||||||
|
else:
|
||||||
self.setPen(pen)
|
self.text.hide()
|
||||||
self.setHoverPen(color=(255,0,0), width=self.pen.width())
|
|
||||||
self.currentPen = self.pen
|
|
||||||
|
|
||||||
def setMovable(self, m):
|
def setMovable(self, m):
|
||||||
"""Set whether the line is movable by the user."""
|
"""Set whether the line is movable by the user."""
|
||||||
self.movable = m
|
self.movable = m
|
||||||
self.setAcceptHoverEvents(m)
|
self.setAcceptHoverEvents(m)
|
||||||
|
|
||||||
def setBounds(self, bounds):
|
def setBounds(self, bounds):
|
||||||
"""Set the (minimum, maximum) allowable values when dragging."""
|
"""Set the (minimum, maximum) allowable values when dragging."""
|
||||||
self.maxRange = bounds
|
self.maxRange = bounds
|
||||||
self.setValue(self.value())
|
self.setValue(self.value())
|
||||||
|
|
||||||
def setPen(self, *args, **kwargs):
|
def setPen(self, *args, **kwargs):
|
||||||
"""Set the pen for drawing the line. Allowable arguments are any that are valid
|
"""Set the pen for drawing the line. Allowable arguments are any that are valid
|
||||||
for :func:`mkPen <pyqtgraph.mkPen>`."""
|
for :func:`mkPen <pyqtgraph.mkPen>`."""
|
||||||
self.pen = fn.mkPen(*args, **kwargs)
|
self.pen = fn.mkPen(*args, **kwargs)
|
||||||
if not self.mouseHovering:
|
if not self.mouseHovering:
|
||||||
self.currentPen = self.pen
|
self.currentPen = self.pen
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def setHoverPen(self, *args, **kwargs):
|
def setHoverPen(self, *args, **kwargs):
|
||||||
"""Set the pen for drawing the line while the mouse hovers over it.
|
"""Set the pen for drawing the line while the mouse hovers over it.
|
||||||
Allowable arguments are any that are valid
|
Allowable arguments are any that are valid
|
||||||
for :func:`mkPen <pyqtgraph.mkPen>`.
|
for :func:`mkPen <pyqtgraph.mkPen>`.
|
||||||
|
|
||||||
If the line is not movable, then hovering is also disabled.
|
If the line is not movable, then hovering is also disabled.
|
||||||
|
|
||||||
Added in version 0.9.9."""
|
Added in version 0.9.9."""
|
||||||
self.hoverPen = fn.mkPen(*args, **kwargs)
|
self.hoverPen = fn.mkPen(*args, **kwargs)
|
||||||
if self.mouseHovering:
|
if self.mouseHovering:
|
||||||
self.currentPen = self.hoverPen
|
self.currentPen = self.hoverPen
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def setAngle(self, angle):
|
def setAngle(self, angle):
|
||||||
"""
|
"""
|
||||||
Takes angle argument in degrees.
|
Takes angle argument in degrees.
|
||||||
0 is horizontal; 90 is vertical.
|
0 is horizontal; 90 is vertical.
|
||||||
|
|
||||||
Note that the use of value() and setValue() changes if the line is
|
Note that the use of value() and setValue() changes if the line is
|
||||||
not vertical or horizontal.
|
not vertical or horizontal.
|
||||||
"""
|
"""
|
||||||
self.angle = ((angle+45) % 180) - 45 ## -45 <= angle < 135
|
self.angle = ((angle+45) % 180) - 45 ## -45 <= angle < 135
|
||||||
self.resetTransform()
|
# self.resetTransform() # no longer needed since version 0.9.11
|
||||||
self.rotate(self.angle)
|
# self.rotate(self.angle) # no longer needed since version 0.9.11
|
||||||
|
if (self.angle == 0 or self.angle == 90) and self.label:
|
||||||
|
self.text.show()
|
||||||
|
else:
|
||||||
|
self.text.hide()
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def setPos(self, pos):
|
def setPos(self, pos):
|
||||||
|
|
||||||
if type(pos) in [list, tuple]:
|
if type(pos) in [list, tuple]:
|
||||||
newPos = pos
|
newPos = pos
|
||||||
elif isinstance(pos, QtCore.QPointF):
|
elif isinstance(pos, QtCore.QPointF):
|
||||||
|
@ -121,10 +208,10 @@ class InfiniteLine(GraphicsObject):
|
||||||
newPos = [0, pos]
|
newPos = [0, pos]
|
||||||
else:
|
else:
|
||||||
raise Exception("Must specify 2D coordinate for non-orthogonal lines.")
|
raise Exception("Must specify 2D coordinate for non-orthogonal lines.")
|
||||||
|
|
||||||
## check bounds (only works for orthogonal lines)
|
## check bounds (only works for orthogonal lines)
|
||||||
if self.angle == 90:
|
if self.angle == 90:
|
||||||
if self.maxRange[0] is not None:
|
if self.maxRange[0] is not None:
|
||||||
newPos[0] = max(newPos[0], self.maxRange[0])
|
newPos[0] = max(newPos[0], self.maxRange[0])
|
||||||
if self.maxRange[1] is not None:
|
if self.maxRange[1] is not None:
|
||||||
newPos[0] = min(newPos[0], self.maxRange[1])
|
newPos[0] = min(newPos[0], self.maxRange[1])
|
||||||
|
@ -133,24 +220,24 @@ class InfiniteLine(GraphicsObject):
|
||||||
newPos[1] = max(newPos[1], self.maxRange[0])
|
newPos[1] = max(newPos[1], self.maxRange[0])
|
||||||
if self.maxRange[1] is not None:
|
if self.maxRange[1] is not None:
|
||||||
newPos[1] = min(newPos[1], self.maxRange[1])
|
newPos[1] = min(newPos[1], self.maxRange[1])
|
||||||
|
|
||||||
if self.p != newPos:
|
if self.p != newPos:
|
||||||
self.p = newPos
|
self.p = newPos
|
||||||
GraphicsObject.setPos(self, Point(self.p))
|
# UIGraphicsItem.setPos(self, Point(self.p)) # thanks Sylvain!
|
||||||
self.update()
|
self.update()
|
||||||
self.sigPositionChanged.emit(self)
|
self.sigPositionChanged.emit(self)
|
||||||
|
|
||||||
def getXPos(self):
|
def getXPos(self):
|
||||||
return self.p[0]
|
return self.p[0]
|
||||||
|
|
||||||
def getYPos(self):
|
def getYPos(self):
|
||||||
return self.p[1]
|
return self.p[1]
|
||||||
|
|
||||||
def getPos(self):
|
def getPos(self):
|
||||||
return self.p
|
return self.p
|
||||||
|
|
||||||
def value(self):
|
def value(self):
|
||||||
"""Return the value of the line. Will be a single number for horizontal and
|
"""Return the value of the line. Will be a single number for horizontal and
|
||||||
vertical lines, and a list of [x,y] values for diagonal lines."""
|
vertical lines, and a list of [x,y] values for diagonal lines."""
|
||||||
if self.angle%180 == 0:
|
if self.angle%180 == 0:
|
||||||
return self.getYPos()
|
return self.getYPos()
|
||||||
|
@ -158,10 +245,10 @@ class InfiniteLine(GraphicsObject):
|
||||||
return self.getXPos()
|
return self.getXPos()
|
||||||
else:
|
else:
|
||||||
return self.getPos()
|
return self.getPos()
|
||||||
|
|
||||||
def setValue(self, v):
|
def setValue(self, v):
|
||||||
"""Set the position of the line. If line is horizontal or vertical, v can be
|
"""Set the position of the line. If line is horizontal or vertical, v can be
|
||||||
a single value. Otherwise, a 2D coordinate must be specified (list, tuple and
|
a single value. Otherwise, a 2D coordinate must be specified (list, tuple and
|
||||||
QPointF are all acceptable)."""
|
QPointF are all acceptable)."""
|
||||||
self.setPos(v)
|
self.setPos(v)
|
||||||
|
|
||||||
|
@ -174,25 +261,59 @@ class InfiniteLine(GraphicsObject):
|
||||||
#else:
|
#else:
|
||||||
#print "ignore", change
|
#print "ignore", change
|
||||||
#return GraphicsObject.itemChange(self, change, val)
|
#return GraphicsObject.itemChange(self, change, val)
|
||||||
|
|
||||||
def boundingRect(self):
|
def boundingRect(self):
|
||||||
#br = UIGraphicsItem.boundingRect(self)
|
br = UIGraphicsItem.boundingRect(self) # directly in viewBox coordinates
|
||||||
br = self.viewRect()
|
# we need to limit the boundingRect to the appropriate value.
|
||||||
## add a 4-pixel radius around the line for mouse interaction.
|
val = self.value()
|
||||||
|
if self.angle == 0: # horizontal line
|
||||||
px = self.pixelLength(direction=Point(1,0), ortho=True) ## get pixel length orthogonal to the line
|
self._p1, self._p2 = _calcLine(val, 0, *br.getCoords())
|
||||||
if px is None:
|
px = self.pixelLength(direction=Point(1,0), ortho=True) ## get pixel length orthogonal to the line
|
||||||
px = 0
|
if px is None:
|
||||||
w = (max(4, self.pen.width()/2, self.hoverPen.width()/2)+1) * px
|
px = 0
|
||||||
br.setBottom(-w)
|
w = (max(4, self.pen.width()/2, self.hoverPen.width()/2)+1) * px
|
||||||
br.setTop(w)
|
o1, o2 = _calcLine(val-w, 0, *br.getCoords())
|
||||||
|
o3, o4 = _calcLine(val+w, 0, *br.getCoords())
|
||||||
|
elif self.angle == 90: # vertical line
|
||||||
|
self._p1, self._p2 = _calcLine(val, 90, *br.getCoords())
|
||||||
|
px = self.pixelLength(direction=Point(0,1), ortho=True) ## get pixel length orthogonal to the line
|
||||||
|
if px is None:
|
||||||
|
px = 0
|
||||||
|
w = (max(4, self.pen.width()/2, self.hoverPen.width()/2)+1) * px
|
||||||
|
o1, o2 = _calcLine(val-w, 90, *br.getCoords())
|
||||||
|
o3, o4 = _calcLine(val+w, 90, *br.getCoords())
|
||||||
|
else: # oblique line
|
||||||
|
self._p1, self._p2 = _calcLine(val, self.angle, *br.getCoords())
|
||||||
|
pxy = self.pixelLength(direction=Point(0,1), ortho=True)
|
||||||
|
if pxy is None:
|
||||||
|
pxy = 0
|
||||||
|
wy = (max(4, self.pen.width()/2, self.hoverPen.width()/2)+1) * pxy
|
||||||
|
pxx = self.pixelLength(direction=Point(1,0), ortho=True)
|
||||||
|
if pxx is None:
|
||||||
|
pxx = 0
|
||||||
|
wx = (max(4, self.pen.width()/2, self.hoverPen.width()/2)+1) * pxx
|
||||||
|
o1, o2 = _calcLine([val[0]-wy, val[1]-wx], self.angle, *br.getCoords())
|
||||||
|
o3, o4 = _calcLine([val[0]+wy, val[1]+wx], self.angle, *br.getCoords())
|
||||||
|
self._polygon = QtGui.QPolygonF([o1, o2, o4, o3])
|
||||||
|
br = self._polygon.boundingRect()
|
||||||
return br.normalized()
|
return br.normalized()
|
||||||
|
|
||||||
|
|
||||||
|
def shape(self):
|
||||||
|
# returns a QPainterPath. Needed when the item is non rectangular if
|
||||||
|
# accurate mouse click detection is required.
|
||||||
|
# New in version 0.9.11
|
||||||
|
qpp = QtGui.QPainterPath()
|
||||||
|
qpp.addPolygon(self._polygon)
|
||||||
|
return qpp
|
||||||
|
|
||||||
|
|
||||||
def paint(self, p, *args):
|
def paint(self, p, *args):
|
||||||
br = self.boundingRect()
|
br = self.boundingRect()
|
||||||
p.setPen(self.currentPen)
|
p.setPen(self.currentPen)
|
||||||
p.drawLine(Point(br.right(), 0), Point(br.left(), 0))
|
p.drawLine(self._p1, self._p2)
|
||||||
|
|
||||||
|
|
||||||
def dataBounds(self, axis, frac=1.0, orthoRange=None):
|
def dataBounds(self, axis, frac=1.0, orthoRange=None):
|
||||||
if axis == 0:
|
if axis == 0:
|
||||||
return None ## x axis should never be auto-scaled
|
return None ## x axis should never be auto-scaled
|
||||||
|
@ -203,19 +324,20 @@ class InfiniteLine(GraphicsObject):
|
||||||
if self.movable and ev.button() == QtCore.Qt.LeftButton:
|
if self.movable and ev.button() == QtCore.Qt.LeftButton:
|
||||||
if ev.isStart():
|
if ev.isStart():
|
||||||
self.moving = True
|
self.moving = True
|
||||||
self.cursorOffset = self.pos() - self.mapToParent(ev.buttonDownPos())
|
self.cursorOffset = self.value() - ev.buttonDownPos()
|
||||||
self.startPosition = self.pos()
|
self.startPosition = self.value()
|
||||||
ev.accept()
|
ev.accept()
|
||||||
|
|
||||||
if not self.moving:
|
if not self.moving:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.setPos(self.cursorOffset + self.mapToParent(ev.pos()))
|
self.setPos(self.cursorOffset + ev.pos())
|
||||||
|
self.prepareGeometryChange() # new in version 0.9.11
|
||||||
self.sigDragged.emit(self)
|
self.sigDragged.emit(self)
|
||||||
if ev.isFinish():
|
if ev.isFinish():
|
||||||
self.moving = False
|
self.moving = False
|
||||||
self.sigPositionChangeFinished.emit(self)
|
self.sigPositionChangeFinished.emit(self)
|
||||||
|
|
||||||
def mouseClickEvent(self, ev):
|
def mouseClickEvent(self, ev):
|
||||||
if self.moving and ev.button() == QtCore.Qt.RightButton:
|
if self.moving and ev.button() == QtCore.Qt.RightButton:
|
||||||
ev.accept()
|
ev.accept()
|
||||||
|
@ -240,3 +362,122 @@ class InfiniteLine(GraphicsObject):
|
||||||
else:
|
else:
|
||||||
self.currentPen = self.pen
|
self.currentPen = self.pen
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
# new in version 0.9.11
|
||||||
|
UIGraphicsItem.update(self)
|
||||||
|
br = UIGraphicsItem.boundingRect(self) # directly in viewBox coordinates
|
||||||
|
xmin, ymin, xmax, ymax = br.getCoords()
|
||||||
|
if self.angle == 90: # vertical line
|
||||||
|
diffX = xmax-xmin
|
||||||
|
diffMin = self.value()-xmin
|
||||||
|
limInf = self.shift*diffX
|
||||||
|
ypos = ymin+self.location*(ymax-ymin)
|
||||||
|
if diffMin < limInf:
|
||||||
|
self.text.anchor = Point(self.anchorRight)
|
||||||
|
else:
|
||||||
|
self.text.anchor = Point(self.anchorLeft)
|
||||||
|
fmt = " x = " + self.format
|
||||||
|
if self.unit is not None:
|
||||||
|
fmt = fmt + self.unit
|
||||||
|
self.text.setText(fmt.format(self.value()), color=self.textColor)
|
||||||
|
self.text.setPos(self.value(), ypos)
|
||||||
|
elif self.angle == 0: # horizontal line
|
||||||
|
diffY = ymax-ymin
|
||||||
|
diffMin = self.value()-ymin
|
||||||
|
limInf = self.shift*(ymax-ymin)
|
||||||
|
xpos = xmin+self.location*(xmax-xmin)
|
||||||
|
if diffMin < limInf:
|
||||||
|
self.text.anchor = Point(self.anchorUp)
|
||||||
|
else:
|
||||||
|
self.text.anchor = Point(self.anchorDown)
|
||||||
|
fmt = " y = " + self.format
|
||||||
|
if self.unit is not None:
|
||||||
|
fmt = fmt + self.unit
|
||||||
|
self.text.setText(fmt.format(self.value()), color=self.textColor)
|
||||||
|
self.text.setPos(xpos, self.value())
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
============== ==============================================
|
||||||
|
"""
|
||||||
|
if state:
|
||||||
|
self.text.show()
|
||||||
|
else:
|
||||||
|
self.text.hide()
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def setLocation(self, loc):
|
||||||
|
"""
|
||||||
|
Set the location of the textItem with respect to a specific axis. If the
|
||||||
|
line is vertical, the location is based on the normalized range of the
|
||||||
|
yaxis. Otherwise, it is based on the normalized range of the xaxis.
|
||||||
|
|
||||||
|
============== ==============================================
|
||||||
|
**Arguments:**
|
||||||
|
loc the normalized location of the textItem.
|
||||||
|
============== ==============================================
|
||||||
|
"""
|
||||||
|
if loc > 1.:
|
||||||
|
loc = 1.
|
||||||
|
if loc < 0.:
|
||||||
|
loc = 0.
|
||||||
|
self.location = loc
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def setShift(self, shift):
|
||||||
|
"""
|
||||||
|
Set the value with respect to the normalized range of the corresponding
|
||||||
|
axis where the location of the textItem shifts from one side to another.
|
||||||
|
|
||||||
|
============== ==============================================
|
||||||
|
**Arguments:**
|
||||||
|
shift the normalized shift value of the textItem.
|
||||||
|
============== ==============================================
|
||||||
|
"""
|
||||||
|
if shift > 1.:
|
||||||
|
shift = 1.
|
||||||
|
if shift < 0.:
|
||||||
|
shift = 0.
|
||||||
|
self.shift = shift
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def setFormat(self, format):
|
||||||
|
"""
|
||||||
|
Set the format of the label used to indicate the location of the line.
|
||||||
|
|
||||||
|
|
||||||
|
============== ==============================================
|
||||||
|
**Arguments:**
|
||||||
|
format Any format compatible with the new python
|
||||||
|
str.format() format style.
|
||||||
|
============== ==============================================
|
||||||
|
"""
|
||||||
|
self.format = format
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def setUnit(self, unit):
|
||||||
|
"""
|
||||||
|
Set the unit of the label used to indicate the location of the line.
|
||||||
|
|
||||||
|
|
||||||
|
============== ==============================================
|
||||||
|
**Arguments:**
|
||||||
|
unit Any string.
|
||||||
|
============== ==============================================
|
||||||
|
"""
|
||||||
|
self.unit = unit
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def setName(self, name):
|
||||||
|
self._name = name
|
||||||
|
|
||||||
|
def name(self):
|
||||||
|
return self._name
|
||||||
|
|
|
@ -9,10 +9,10 @@ __all__ = ['LinearRegionItem']
|
||||||
class LinearRegionItem(UIGraphicsItem):
|
class LinearRegionItem(UIGraphicsItem):
|
||||||
"""
|
"""
|
||||||
**Bases:** :class:`UIGraphicsItem <pyqtgraph.UIGraphicsItem>`
|
**Bases:** :class:`UIGraphicsItem <pyqtgraph.UIGraphicsItem>`
|
||||||
|
|
||||||
Used for marking a horizontal or vertical region in plots.
|
Used for marking a horizontal or vertical region in plots.
|
||||||
The region can be dragged and is bounded by lines which can be dragged individually.
|
The region can be dragged and is bounded by lines which can be dragged individually.
|
||||||
|
|
||||||
=============================== =============================================================================
|
=============================== =============================================================================
|
||||||
**Signals:**
|
**Signals:**
|
||||||
sigRegionChangeFinished(self) Emitted when the user has finished dragging the region (or one of its lines)
|
sigRegionChangeFinished(self) Emitted when the user has finished dragging the region (or one of its lines)
|
||||||
|
@ -21,15 +21,15 @@ class LinearRegionItem(UIGraphicsItem):
|
||||||
and when the region is changed programatically.
|
and when the region is changed programatically.
|
||||||
=============================== =============================================================================
|
=============================== =============================================================================
|
||||||
"""
|
"""
|
||||||
|
|
||||||
sigRegionChangeFinished = QtCore.Signal(object)
|
sigRegionChangeFinished = QtCore.Signal(object)
|
||||||
sigRegionChanged = QtCore.Signal(object)
|
sigRegionChanged = QtCore.Signal(object)
|
||||||
Vertical = 0
|
Vertical = 0
|
||||||
Horizontal = 1
|
Horizontal = 1
|
||||||
|
|
||||||
def __init__(self, values=[0,1], orientation=None, brush=None, movable=True, bounds=None):
|
def __init__(self, values=[0,1], orientation=None, brush=None, movable=True, bounds=None):
|
||||||
"""Create a new LinearRegionItem.
|
"""Create a new LinearRegionItem.
|
||||||
|
|
||||||
============== =====================================================================
|
============== =====================================================================
|
||||||
**Arguments:**
|
**Arguments:**
|
||||||
values A list of the positions of the lines in the region. These are not
|
values A list of the positions of the lines in the region. These are not
|
||||||
|
@ -44,7 +44,7 @@ class LinearRegionItem(UIGraphicsItem):
|
||||||
bounds Optional [min, max] bounding values for the region
|
bounds Optional [min, max] bounding values for the region
|
||||||
============== =====================================================================
|
============== =====================================================================
|
||||||
"""
|
"""
|
||||||
|
|
||||||
UIGraphicsItem.__init__(self)
|
UIGraphicsItem.__init__(self)
|
||||||
if orientation is None:
|
if orientation is None:
|
||||||
orientation = LinearRegionItem.Vertical
|
orientation = LinearRegionItem.Vertical
|
||||||
|
@ -53,30 +53,30 @@ class LinearRegionItem(UIGraphicsItem):
|
||||||
self.blockLineSignal = False
|
self.blockLineSignal = False
|
||||||
self.moving = False
|
self.moving = False
|
||||||
self.mouseHovering = False
|
self.mouseHovering = False
|
||||||
|
|
||||||
if orientation == LinearRegionItem.Horizontal:
|
if orientation == LinearRegionItem.Horizontal:
|
||||||
self.lines = [
|
self.lines = [
|
||||||
InfiniteLine(QtCore.QPointF(0, values[0]), 0, movable=movable, bounds=bounds),
|
InfiniteLine(QtCore.QPointF(0, values[0]), 0, movable=movable, bounds=bounds),
|
||||||
InfiniteLine(QtCore.QPointF(0, values[1]), 0, movable=movable, bounds=bounds)]
|
InfiniteLine(QtCore.QPointF(0, values[1]), 0, movable=movable, bounds=bounds)]
|
||||||
elif orientation == LinearRegionItem.Vertical:
|
elif orientation == LinearRegionItem.Vertical:
|
||||||
self.lines = [
|
self.lines = [
|
||||||
InfiniteLine(QtCore.QPointF(values[1], 0), 90, movable=movable, bounds=bounds),
|
InfiniteLine(QtCore.QPointF(values[1], 0), 90, movable=movable, bounds=bounds),
|
||||||
InfiniteLine(QtCore.QPointF(values[0], 0), 90, movable=movable, bounds=bounds)]
|
InfiniteLine(QtCore.QPointF(values[0], 0), 90, movable=movable, bounds=bounds)]
|
||||||
else:
|
else:
|
||||||
raise Exception('Orientation must be one of LinearRegionItem.Vertical or LinearRegionItem.Horizontal')
|
raise Exception('Orientation must be one of LinearRegionItem.Vertical or LinearRegionItem.Horizontal')
|
||||||
|
|
||||||
|
|
||||||
for l in self.lines:
|
for l in self.lines:
|
||||||
l.setParentItem(self)
|
l.setParentItem(self)
|
||||||
l.sigPositionChangeFinished.connect(self.lineMoveFinished)
|
l.sigPositionChangeFinished.connect(self.lineMoveFinished)
|
||||||
l.sigPositionChanged.connect(self.lineMoved)
|
l.sigPositionChanged.connect(self.lineMoved)
|
||||||
|
|
||||||
if brush is None:
|
if brush is None:
|
||||||
brush = QtGui.QBrush(QtGui.QColor(0, 0, 255, 50))
|
brush = QtGui.QBrush(QtGui.QColor(0, 0, 255, 50))
|
||||||
self.setBrush(brush)
|
self.setBrush(brush)
|
||||||
|
|
||||||
self.setMovable(movable)
|
self.setMovable(movable)
|
||||||
|
|
||||||
def getRegion(self):
|
def getRegion(self):
|
||||||
"""Return the values at the edges of the region."""
|
"""Return the values at the edges of the region."""
|
||||||
#if self.orientation[0] == 'h':
|
#if self.orientation[0] == 'h':
|
||||||
|
@ -88,7 +88,7 @@ class LinearRegionItem(UIGraphicsItem):
|
||||||
|
|
||||||
def setRegion(self, rgn):
|
def setRegion(self, rgn):
|
||||||
"""Set the values for the edges of the region.
|
"""Set the values for the edges of the region.
|
||||||
|
|
||||||
============== ==============================================
|
============== ==============================================
|
||||||
**Arguments:**
|
**Arguments:**
|
||||||
rgn A list or tuple of the lower and upper values.
|
rgn A list or tuple of the lower and upper values.
|
||||||
|
@ -114,14 +114,14 @@ class LinearRegionItem(UIGraphicsItem):
|
||||||
def setBounds(self, bounds):
|
def setBounds(self, bounds):
|
||||||
"""Optional [min, max] bounding values for the region. To have no bounds on the
|
"""Optional [min, max] bounding values for the region. To have no bounds on the
|
||||||
region use [None, None].
|
region use [None, None].
|
||||||
Does not affect the current position of the region unless it is outside the new bounds.
|
Does not affect the current position of the region unless it is outside the new bounds.
|
||||||
See :func:`setRegion <pyqtgraph.LinearRegionItem.setRegion>` to set the position
|
See :func:`setRegion <pyqtgraph.LinearRegionItem.setRegion>` to set the position
|
||||||
of the region."""
|
of the region."""
|
||||||
for l in self.lines:
|
for l in self.lines:
|
||||||
l.setBounds(bounds)
|
l.setBounds(bounds)
|
||||||
|
|
||||||
def setMovable(self, m):
|
def setMovable(self, m):
|
||||||
"""Set lines to be movable by the user, or not. If lines are movable, they will
|
"""Set lines to be movable by the user, or not. If lines are movable, they will
|
||||||
also accept HoverEvents."""
|
also accept HoverEvents."""
|
||||||
for l in self.lines:
|
for l in self.lines:
|
||||||
l.setMovable(m)
|
l.setMovable(m)
|
||||||
|
@ -138,7 +138,7 @@ class LinearRegionItem(UIGraphicsItem):
|
||||||
br.setTop(rng[0])
|
br.setTop(rng[0])
|
||||||
br.setBottom(rng[1])
|
br.setBottom(rng[1])
|
||||||
return br.normalized()
|
return br.normalized()
|
||||||
|
|
||||||
def paint(self, p, *args):
|
def paint(self, p, *args):
|
||||||
profiler = debug.Profiler()
|
profiler = debug.Profiler()
|
||||||
UIGraphicsItem.paint(self, p, *args)
|
UIGraphicsItem.paint(self, p, *args)
|
||||||
|
@ -158,12 +158,12 @@ class LinearRegionItem(UIGraphicsItem):
|
||||||
self.prepareGeometryChange()
|
self.prepareGeometryChange()
|
||||||
#self.emit(QtCore.SIGNAL('regionChanged'), self)
|
#self.emit(QtCore.SIGNAL('regionChanged'), self)
|
||||||
self.sigRegionChanged.emit(self)
|
self.sigRegionChanged.emit(self)
|
||||||
|
|
||||||
def lineMoveFinished(self):
|
def lineMoveFinished(self):
|
||||||
#self.emit(QtCore.SIGNAL('regionChangeFinished'), self)
|
#self.emit(QtCore.SIGNAL('regionChangeFinished'), self)
|
||||||
self.sigRegionChangeFinished.emit(self)
|
self.sigRegionChangeFinished.emit(self)
|
||||||
|
|
||||||
|
|
||||||
#def updateBounds(self):
|
#def updateBounds(self):
|
||||||
#vb = self.view().viewRect()
|
#vb = self.view().viewRect()
|
||||||
#vals = [self.lines[0].value(), self.lines[1].value()]
|
#vals = [self.lines[0].value(), self.lines[1].value()]
|
||||||
|
@ -176,7 +176,7 @@ class LinearRegionItem(UIGraphicsItem):
|
||||||
#if vb != self.bounds:
|
#if vb != self.bounds:
|
||||||
#self.bounds = vb
|
#self.bounds = vb
|
||||||
#self.rect.setRect(vb)
|
#self.rect.setRect(vb)
|
||||||
|
|
||||||
#def mousePressEvent(self, ev):
|
#def mousePressEvent(self, ev):
|
||||||
#if not self.movable:
|
#if not self.movable:
|
||||||
#ev.ignore()
|
#ev.ignore()
|
||||||
|
@ -188,11 +188,11 @@ class LinearRegionItem(UIGraphicsItem):
|
||||||
##self.pressDelta = self.mapToParent(ev.pos()) - QtCore.QPointF(*self.p)
|
##self.pressDelta = self.mapToParent(ev.pos()) - QtCore.QPointF(*self.p)
|
||||||
##else:
|
##else:
|
||||||
##ev.ignore()
|
##ev.ignore()
|
||||||
|
|
||||||
#def mouseReleaseEvent(self, ev):
|
#def mouseReleaseEvent(self, ev):
|
||||||
#for l in self.lines:
|
#for l in self.lines:
|
||||||
#l.mouseReleaseEvent(ev)
|
#l.mouseReleaseEvent(ev)
|
||||||
|
|
||||||
#def mouseMoveEvent(self, ev):
|
#def mouseMoveEvent(self, ev):
|
||||||
##print "move", ev.pos()
|
##print "move", ev.pos()
|
||||||
#if not self.movable:
|
#if not self.movable:
|
||||||
|
@ -208,16 +208,16 @@ class LinearRegionItem(UIGraphicsItem):
|
||||||
if not self.movable or int(ev.button() & QtCore.Qt.LeftButton) == 0:
|
if not self.movable or int(ev.button() & QtCore.Qt.LeftButton) == 0:
|
||||||
return
|
return
|
||||||
ev.accept()
|
ev.accept()
|
||||||
|
|
||||||
if ev.isStart():
|
if ev.isStart():
|
||||||
bdp = ev.buttonDownPos()
|
bdp = ev.buttonDownPos()
|
||||||
self.cursorOffsets = [l.pos() - bdp for l in self.lines]
|
self.cursorOffsets = [l.value() - bdp for l in self.lines]
|
||||||
self.startPositions = [l.pos() for l in self.lines]
|
self.startPositions = [l.value() for l in self.lines]
|
||||||
self.moving = True
|
self.moving = True
|
||||||
|
|
||||||
if not self.moving:
|
if not self.moving:
|
||||||
return
|
return
|
||||||
|
|
||||||
#delta = ev.pos() - ev.lastPos()
|
#delta = ev.pos() - ev.lastPos()
|
||||||
self.lines[0].blockSignals(True) # only want to update once
|
self.lines[0].blockSignals(True) # only want to update once
|
||||||
for i, l in enumerate(self.lines):
|
for i, l in enumerate(self.lines):
|
||||||
|
@ -226,13 +226,13 @@ class LinearRegionItem(UIGraphicsItem):
|
||||||
#l.mouseDragEvent(ev)
|
#l.mouseDragEvent(ev)
|
||||||
self.lines[0].blockSignals(False)
|
self.lines[0].blockSignals(False)
|
||||||
self.prepareGeometryChange()
|
self.prepareGeometryChange()
|
||||||
|
|
||||||
if ev.isFinish():
|
if ev.isFinish():
|
||||||
self.moving = False
|
self.moving = False
|
||||||
self.sigRegionChangeFinished.emit(self)
|
self.sigRegionChangeFinished.emit(self)
|
||||||
else:
|
else:
|
||||||
self.sigRegionChanged.emit(self)
|
self.sigRegionChanged.emit(self)
|
||||||
|
|
||||||
def mouseClickEvent(self, ev):
|
def mouseClickEvent(self, ev):
|
||||||
if self.moving and ev.button() == QtCore.Qt.RightButton:
|
if self.moving and ev.button() == QtCore.Qt.RightButton:
|
||||||
ev.accept()
|
ev.accept()
|
||||||
|
@ -248,7 +248,7 @@ class LinearRegionItem(UIGraphicsItem):
|
||||||
self.setMouseHover(True)
|
self.setMouseHover(True)
|
||||||
else:
|
else:
|
||||||
self.setMouseHover(False)
|
self.setMouseHover(False)
|
||||||
|
|
||||||
def setMouseHover(self, hover):
|
def setMouseHover(self, hover):
|
||||||
## Inform the item that the mouse is(not) hovering over it
|
## Inform the item that the mouse is(not) hovering over it
|
||||||
if self.mouseHovering == hover:
|
if self.mouseHovering == hover:
|
||||||
|
@ -276,15 +276,14 @@ class LinearRegionItem(UIGraphicsItem):
|
||||||
#print "rgn hover leave"
|
#print "rgn hover leave"
|
||||||
#ev.ignore()
|
#ev.ignore()
|
||||||
#self.updateHoverBrush(False)
|
#self.updateHoverBrush(False)
|
||||||
|
|
||||||
#def updateHoverBrush(self, hover=None):
|
#def updateHoverBrush(self, hover=None):
|
||||||
#if hover is None:
|
#if hover is None:
|
||||||
#scene = self.scene()
|
#scene = self.scene()
|
||||||
#hover = scene.claimEvent(self, QtCore.Qt.LeftButton, scene.Drag)
|
#hover = scene.claimEvent(self, QtCore.Qt.LeftButton, scene.Drag)
|
||||||
|
|
||||||
#if hover:
|
#if hover:
|
||||||
#self.currentBrush = fn.mkBrush(255, 0,0,100)
|
#self.currentBrush = fn.mkBrush(255, 0,0,100)
|
||||||
#else:
|
#else:
|
||||||
#self.currentBrush = self.brush
|
#self.currentBrush = self.brush
|
||||||
#self.update()
|
#self.update()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user