2013-12-22 07:08:39 +00:00
|
|
|
from ..Qt import QtGui, QtCore
|
|
|
|
from .. import functions as fn
|
2012-05-30 03:19:20 +00:00
|
|
|
import numpy as np
|
2012-03-02 02:55:32 +00:00
|
|
|
__all__ = ['ArrowItem']
|
2012-05-30 03:19:20 +00:00
|
|
|
|
|
|
|
class ArrowItem(QtGui.QGraphicsPathItem):
|
2012-03-02 02:55:32 +00:00
|
|
|
"""
|
|
|
|
For displaying scale-invariant arrows.
|
|
|
|
For arrows pointing to a location on a curve, see CurveArrow
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, **opts):
|
2013-02-10 19:12:56 +00:00
|
|
|
"""
|
|
|
|
Arrows can be initialized with any keyword arguments accepted by
|
|
|
|
the setStyle() method.
|
|
|
|
"""
|
2012-05-30 03:19:20 +00:00
|
|
|
QtGui.QGraphicsPathItem.__init__(self, opts.get('parent', None))
|
2012-05-31 20:07:23 +00:00
|
|
|
if 'size' in opts:
|
|
|
|
opts['headLen'] = opts['size']
|
|
|
|
if 'width' in opts:
|
|
|
|
opts['headWidth'] = opts['width']
|
2012-03-02 02:55:32 +00:00
|
|
|
defOpts = {
|
|
|
|
'pxMode': True,
|
2012-03-02 03:17:55 +00:00
|
|
|
'angle': -150, ## If the angle is 0, the arrow points left
|
2012-03-02 02:55:32 +00:00
|
|
|
'pos': (0,0),
|
2012-05-30 03:19:20 +00:00
|
|
|
'headLen': 20,
|
2012-03-02 02:55:32 +00:00
|
|
|
'tipAngle': 25,
|
2012-05-30 03:19:20 +00:00
|
|
|
'baseAngle': 0,
|
|
|
|
'tailLen': None,
|
|
|
|
'tailWidth': 3,
|
2012-03-02 02:55:32 +00:00
|
|
|
'pen': (200,200,200),
|
|
|
|
'brush': (50,50,200),
|
|
|
|
}
|
|
|
|
defOpts.update(opts)
|
|
|
|
|
|
|
|
self.setStyle(**defOpts)
|
|
|
|
|
|
|
|
self.setPen(fn.mkPen(defOpts['pen']))
|
|
|
|
self.setBrush(fn.mkBrush(defOpts['brush']))
|
|
|
|
|
|
|
|
self.rotate(self.opts['angle'])
|
|
|
|
self.moveBy(*self.opts['pos'])
|
|
|
|
|
|
|
|
def setStyle(self, **opts):
|
2013-02-10 19:12:56 +00:00
|
|
|
"""
|
|
|
|
Changes the appearance of the arrow.
|
|
|
|
All arguments are optional:
|
|
|
|
|
|
|
|
================= =================================================
|
|
|
|
Keyword Arguments
|
|
|
|
angle Orientation of the arrow in degrees. Default is
|
|
|
|
0; arrow pointing to the left.
|
|
|
|
headLen Length of the arrow head, from tip to base.
|
|
|
|
default=20
|
|
|
|
headWidth Width of the arrow head at its base.
|
|
|
|
tipAngle Angle of the tip of the arrow in degrees. Smaller
|
|
|
|
values make a 'sharper' arrow. If tipAngle is
|
|
|
|
specified, ot overrides headWidth. default=25
|
|
|
|
baseAngle Angle of the base of the arrow head. Default is
|
|
|
|
0, which means that the base of the arrow head
|
|
|
|
is perpendicular to the arrow shaft.
|
|
|
|
tailLen Length of the arrow tail, measured from the base
|
|
|
|
of the arrow head to the tip of the tail. If
|
|
|
|
this value is None, no tail will be drawn.
|
|
|
|
default=None
|
|
|
|
tailWidth Width of the tail. default=3
|
|
|
|
pen The pen used to draw the outline of the arrow.
|
|
|
|
brush The brush used to fill the arrow.
|
|
|
|
================= =================================================
|
|
|
|
"""
|
2012-03-02 02:55:32 +00:00
|
|
|
self.opts = opts
|
|
|
|
|
2012-09-13 14:12:59 +00:00
|
|
|
opt = dict([(k,self.opts[k]) for k in ['headLen', 'tipAngle', 'baseAngle', 'tailLen', 'tailWidth']])
|
2012-05-30 03:19:20 +00:00
|
|
|
self.path = fn.makeArrowPath(**opt)
|
|
|
|
self.setPath(self.path)
|
2012-03-02 02:55:32 +00:00
|
|
|
|
|
|
|
if opts['pxMode']:
|
|
|
|
self.setFlags(self.flags() | self.ItemIgnoresTransformations)
|
|
|
|
else:
|
|
|
|
self.setFlags(self.flags() & ~self.ItemIgnoresTransformations)
|
|
|
|
|
|
|
|
def paint(self, p, *args):
|
|
|
|
p.setRenderHint(QtGui.QPainter.Antialiasing)
|
2012-05-30 03:19:20 +00:00
|
|
|
QtGui.QGraphicsPathItem.paint(self, p, *args)
|
2013-02-14 19:22:30 +00:00
|
|
|
|
|
|
|
#p.setPen(fn.mkPen('r'))
|
|
|
|
#p.setBrush(fn.mkBrush(None))
|
|
|
|
#p.drawRect(self.boundingRect())
|
2012-12-26 21:29:29 +00:00
|
|
|
|
|
|
|
def shape(self):
|
|
|
|
#if not self.opts['pxMode']:
|
|
|
|
#return QtGui.QGraphicsPathItem.shape(self)
|
2013-02-14 19:22:30 +00:00
|
|
|
return self.path
|
|
|
|
|
|
|
|
## dataBounds and pixelPadding methods are provided to ensure ViewBox can
|
|
|
|
## properly auto-range
|
|
|
|
def dataBounds(self, ax, frac, orthoRange=None):
|
|
|
|
pw = 0
|
|
|
|
pen = self.pen()
|
|
|
|
if not pen.isCosmetic():
|
|
|
|
pw = pen.width() * 0.7072
|
|
|
|
if self.opts['pxMode']:
|
|
|
|
return [0,0]
|
|
|
|
else:
|
|
|
|
br = self.boundingRect()
|
|
|
|
if ax == 0:
|
|
|
|
return [br.left()-pw, br.right()+pw]
|
|
|
|
else:
|
|
|
|
return [br.top()-pw, br.bottom()+pw]
|
|
|
|
|
|
|
|
def pixelPadding(self):
|
|
|
|
pad = 0
|
|
|
|
if self.opts['pxMode']:
|
|
|
|
br = self.boundingRect()
|
|
|
|
pad += (br.width()**2 + br.height()**2) ** 0.5
|
|
|
|
pen = self.pen()
|
|
|
|
if pen.isCosmetic():
|
|
|
|
pad += max(1, pen.width()) * 0.7072
|
|
|
|
return pad
|
|
|
|
|
|
|
|
|
|
|
|
|