New features for LegendItem:

- Can be anchored to parent item at any location
  - Support for filled plot styles
  - Automatically resizes to fit contents
  - PlotItem can auto-generate legend
This commit is contained in:
Luke Campagnola 2012-11-23 16:05:14 -05:00
parent e5f383fbb5
commit 9b41c90034
6 changed files with 151 additions and 14 deletions

View File

@ -6,13 +6,14 @@ from pyqtgraph.Qt import QtCore, QtGui
plt = pg.plot()
l = pg.LegendItem((100,60), (60,10)) # args are (size, position)
l.setParentItem(plt.graphicsItem()) # Note we do NOT call plt.addItem in this case
plt.addLegend()
#l = pg.LegendItem((100,60), offset=(70,30)) # args are (size, offset)
#l.setParentItem(plt.graphicsItem()) # Note we do NOT call plt.addItem in this case
c1 = plt.plot([1,3,2,4], pen='r')
c2 = plt.plot([2,1,4,3], pen='g')
l.addItem(c1, 'red plot')
l.addItem(c2, 'green plot')
c1 = plt.plot([1,3,2,4], pen='r', name='red plot')
c2 = plt.plot([2,1,4,3], pen='g', fillLevel=0, fillBrush=(255,255,255,30), name='green plot')
#l.addItem(c1, 'red plot')
#l.addItem(c2, 'green plot')
## Start Qt event loop unless running in interactive mode or using pyside.

View File

@ -0,0 +1,63 @@
from ..Qt import QtGui, QtCore
from ..Point import Point
class GraphicsWidgetAnchor:
"""
Class used to allow GraphicsWidgets to anchor to a specific position on their
parent.
"""
def __init__(self):
self.__parent = None
self.__parentAnchor = None
self.__itemAnchor = None
self.__offset = (0,0)
if hasattr(self, 'geometryChanged'):
self.geometryChanged.connect(self.__geometryChanged)
def anchor(self, itemPos, parentPos, offset=(0,0)):
"""
Anchors the item at its local itemPos to the item's parent at parentPos.
Both positions are expressed in values relative to the size of the item or parent;
a value of 0 indicates left or top edge, while 1 indicates right or bottom edge.
Optionally, offset may be specified to introduce an absolute offset.
Example: anchor a box such that its upper-right corner is fixed 10px left
and 10px down from its parent's upper-right corner::
box.anchor(itemPos=(1,0), parentPos=(1,0), offset=(-10,10))
"""
parent = self.parentItem()
if parent is None:
raise Exception("Cannot anchor; parent is not set.")
if self.__parent is not parent:
if self.__parent is not None:
self.__parent.geometryChanged.disconnect(self.__geometryChanged)
self.__parent = parent
parent.geometryChanged.connect(self.__geometryChanged)
self.__itemAnchor = itemPos
self.__parentAnchor = parentPos
self.__offset = offset
self.__geometryChanged()
def __geometryChanged(self):
if self.__parent is None:
return
if self.__itemAnchor is None:
return
o = self.mapToParent(Point(0,0))
a = self.boundingRect().bottomRight() * Point(self.__itemAnchor)
a = self.mapToParent(a)
p = self.__parent.boundingRect().bottomRight() * Point(self.__parentAnchor)
off = Point(self.__offset)
pos = p + (o-a) + off
self.setPos(pos)

View File

@ -2,12 +2,14 @@ from .GraphicsWidget import GraphicsWidget
from .LabelItem import LabelItem
from ..Qt import QtGui, QtCore
from .. import functions as fn
from ..Point import Point
from .GraphicsWidgetAnchor import GraphicsWidgetAnchor
__all__ = ['LegendItem']
class LegendItem(GraphicsWidget):
class LegendItem(GraphicsWidget, GraphicsWidgetAnchor):
"""
Displays a legend used for describing the contents of a plot.
LegendItems are most commonly created by calling PlotItem.addLegend().
Note that this item should not be added directly to a PlotItem. Instead,
Make it a direct descendant of the PlotItem::
@ -15,17 +17,45 @@ class LegendItem(GraphicsWidget):
legend.setParentItem(plotItem)
"""
def __init__(self, size, offset):
def __init__(self, size=None, offset=None):
"""
========== ===============================================================
Arguments
size Specifies the fixed size (width, height) of the legend. If
this argument is omitted, the legend will autimatically resize
to fit its contents.
offset Specifies the offset position relative to the legend's parent.
Positive values offset from the left or top; negative values
offset from the right or bottom. If offset is None, the
legend must be anchored manually by calling anchor() or
positioned by calling setPos().
========== ===============================================================
"""
GraphicsWidget.__init__(self)
GraphicsWidgetAnchor.__init__(self)
self.setFlag(self.ItemIgnoresTransformations)
self.layout = QtGui.QGraphicsGridLayout()
self.setLayout(self.layout)
self.items = []
self.size = size
self.offset = offset
self.setGeometry(QtCore.QRectF(self.offset[0], self.offset[1], self.size[0], self.size[1]))
if size is not None:
self.setGeometry(QtCore.QRectF(0, 0, self.size[0], self.size[1]))
def addItem(self, item, title):
def setParentItem(self, p):
ret = GraphicsWidget.setParentItem(self, p)
if self.offset is not None:
offset = Point(self.offset)
anchorx = 1 if offset[0] <= 0 else 0
anchory = 1 if offset[1] <= 0 else 0
anchor = (anchorx, anchory)
self.anchor(itemPos=anchor, parentPos=anchor, offset=offset)
return ret
def addItem(self, item, name):
"""
Add a new entry to the legend.
@ -36,15 +66,30 @@ class LegendItem(GraphicsWidget):
title The title to display for this item. Simple HTML allowed.
=========== ========================================================
"""
label = LabelItem(title)
label = LabelItem(name)
sample = ItemSample(item)
row = len(self.items)
self.items.append((sample, label))
self.layout.addItem(sample, row, 0)
self.layout.addItem(label, row, 1)
self.updateSize()
def updateSize(self):
if self.size is not None:
return
height = 0
width = 0
print "-------"
for sample, label in self.items:
height += max(sample.height(), label.height()) + 3
width = max(width, sample.width()+label.width())
print width, height
print width, height
self.setGeometry(0, 0, width+25, height)
def boundingRect(self):
return QtCore.QRectF(0, 0, self.size[0], self.size[1])
return QtCore.QRectF(0, 0, self.width(), self.height())
def paint(self, p, *args):
p.setPen(fn.mkPen(255,255,255,100))
@ -61,8 +106,16 @@ class ItemSample(GraphicsWidget):
return QtCore.QRectF(0, 0, 20, 20)
def paint(self, p, *args):
p.setPen(fn.mkPen(self.item.opts['pen']))
opts = self.item.opts
if opts.get('fillLevel',None) is not None and opts.get('fillBrush',None) is not None:
p.setBrush(fn.mkBrush(opts['fillBrush']))
p.setPen(fn.mkPen(None))
p.drawPolygon(QtGui.QPolygonF([QtCore.QPointF(2,18), QtCore.QPointF(18,2), QtCore.QPointF(18,18)]))
p.setPen(fn.mkPen(opts['pen']))
p.drawLine(2, 18, 18, 2)

View File

@ -65,6 +65,7 @@ class PlotCurveItem(GraphicsObject):
'fillLevel': None,
'brush': None,
'stepMode': False,
'name': None
}
self.setClickable(kargs.get('clickable', False))
self.setData(*args, **kargs)
@ -238,6 +239,9 @@ class PlotCurveItem(GraphicsObject):
self.fillPath = None
#self.xDisp = self.yDisp = None
if 'name' in kargs:
self.opts['name'] = kargs['name']
if 'pen' in kargs:
self.setPen(kargs['pen'])
if 'shadowPen' in kargs:

View File

@ -317,6 +317,8 @@ class PlotDataItem(GraphicsObject):
## pull in all style arguments.
## Use self.opts to fill in anything not present in kargs.
if 'name' in kargs:
self.opts['name'] = kargs['name']
## if symbol pen/brush are given with no symbol, then assume symbol is 'o'

View File

@ -33,6 +33,7 @@ from .. PlotDataItem import PlotDataItem
from .. ViewBox import ViewBox
from .. AxisItem import AxisItem
from .. LabelItem import LabelItem
from .. LegendItem import LegendItem
from .. GraphicsWidget import GraphicsWidget
from .. ButtonItem import ButtonItem
from pyqtgraph.WidgetGroup import WidgetGroup
@ -528,6 +529,9 @@ class PlotItem(GraphicsWidget):
#c.connect(c, QtCore.SIGNAL('plotChanged'), self.plotChanged)
#item.sigPlotChanged.connect(self.plotChanged)
#self.plotChanged()
name = kargs.get('name', getattr(item, 'opts', {}).get('name', None))
if name is not None and self.legend is not None:
self.legend.addItem(item, name=name)
def addDataItem(self, item, *args):
@ -596,6 +600,16 @@ class PlotItem(GraphicsWidget):
return item
def addLegend(self, size=None, offset=(30, 30)):
"""
Create a new LegendItem and anchor it over the internal ViewBox.
Plots will be automatically displayed in the legend if they
are created with the 'name' argument.
"""
self.legend = LegendItem(size, offset)
self.legend.setParentItem(self.vb)
return self.legend
def scatterPlot(self, *args, **kargs):
if 'pen' in kargs:
kargs['symbolPen'] = kargs['pen']