From 66dd6f974ead98463ce0c57888f5579782a95e70 Mon Sep 17 00:00:00 2001
From: Luke Campagnola <>
Date: Sat, 17 Mar 2012 23:10:00 -0400
Subject: [PATCH] Added TextItem and example
---
examples/__main__.py | 1 +
examples/text.py | 37 +++++++++--
graphicsItems/CurvePoint.py | 10 ++-
graphicsItems/TextItem.py | 120 ++++++++++++++++++++++++++++++++++++
4 files changed, 161 insertions(+), 7 deletions(-)
create mode 100644 graphicsItems/TextItem.py
diff --git a/examples/__main__.py b/examples/__main__.py
index 02c27b76..628b93fd 100644
--- a/examples/__main__.py
+++ b/examples/__main__.py
@@ -17,6 +17,7 @@ examples = OrderedDict([
('Region-of-Interest', 'ROItypes.py'),
('GraphicsLayout', 'GraphicsLayout.py'),
('Scatter Plot', 'ScatterPlot.py'),
+ ('Text Item', 'text.py'),
('ViewBox', 'ViewBox.py'),
('Arrow', 'Arrow.py'),
])),
diff --git a/examples/text.py b/examples/text.py
index da2025fd..202fb572 100644
--- a/examples/text.py
+++ b/examples/text.py
@@ -9,15 +9,44 @@ from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
-x = np.linspace(-100, 100, 1000)
+x = np.linspace(-20, 20, 1000)
y = np.sin(x) / x
-plot = pg.plot(x, y)
+plot = pg.plot() ## create an empty plot widget
+plot.setYRange(-1, 2)
+curve = plot.plot(x,y) ## add a single curve
-## Create text object, use HTML tags to specify color (default is black; won't be visible)
-text = pg.TextItem(html='
This is the
PEAK
')
+## Create text object, use HTML tags to specify color/size
+text = pg.TextItem(html='This is the
PEAK
', anchor=(-0.3,1.3), border='w', fill=(0, 0, 255, 100))
plot.addItem(text)
text.setPos(0, y.max())
+## Draw an arrowhead next to the text box
+arrow = pg.ArrowItem(pos=(0, y.max()), angle=-45)
+plot.addItem(arrow)
+
+
+## Set up an animated arrow and text that track the curve
+curvePoint = pg.CurvePoint(curve)
+plot.addItem(curvePoint)
+text2 = pg.TextItem("test", anchor=(0.5, -1.0))
+text2.setParentItem(curvePoint)
+arrow2 = pg.ArrowItem(angle=90)
+arrow2.setParentItem(curvePoint)
+
+## update position every 10ms
+index = 0
+def update():
+ global curvePoint, index
+ index = (index + 1) % len(x)
+ curvePoint.setPos(float(index)/(len(x)-1))
+ #text2.viewRangeChanged()
+ text2.setText('[%0.1f, %0.1f]' % (x[index], y[index]))
+
+timer = QtCore.QTimer()
+timer.timeout.connect(update)
+timer.start(10)
+
+
## Start Qt event loop unless running in interactive mode or using pyside.
import sys
diff --git a/graphicsItems/CurvePoint.py b/graphicsItems/CurvePoint.py
index a1ec5ae4..8fae5e7f 100644
--- a/graphicsItems/CurvePoint.py
+++ b/graphicsItems/CurvePoint.py
@@ -14,12 +14,15 @@ class CurvePoint(GraphicsObject):
Note: This class does not display anything; see CurveArrow for an applied example
"""
- def __init__(self, curve, index=0, pos=None):
+ def __init__(self, curve, index=0, pos=None, rotate=True):
"""Position can be set either as an index referring to the sample number or
- the position 0.0 - 1.0"""
+ the position 0.0 - 1.0
+ If *rotate* is True, then the item rotates to match the tangent of the curve.
+ """
GraphicsObject.__init__(self)
#QObjectWorkaround.__init__(self)
+ self._rotate = rotate
self.curve = weakref.ref(curve)
self.setParentItem(curve)
self.setProperty('position', 0.0)
@@ -76,7 +79,8 @@ class CurvePoint(GraphicsObject):
p2 = self.parentItem().mapToScene(QtCore.QPointF(x[i2], y[i2]))
ang = np.arctan2(p2.y()-p1.y(), p2.x()-p1.x()) ## returns radians
self.resetTransform()
- self.rotate(180+ ang * 180 / np.pi) ## takes degrees
+ if self._rotate:
+ self.rotate(180+ ang * 180 / np.pi) ## takes degrees
QtGui.QGraphicsItem.setPos(self, *newPos)
return True
diff --git a/graphicsItems/TextItem.py b/graphicsItems/TextItem.py
new file mode 100644
index 00000000..734de9c3
--- /dev/null
+++ b/graphicsItems/TextItem.py
@@ -0,0 +1,120 @@
+from pyqtgraph.Qt import QtCore, QtGui
+import pyqtgraph as pg
+from UIGraphicsItem import *
+
+class TextItem(UIGraphicsItem):
+ """
+ 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):
+ """
+ Arguments:
+ *text* The text to display
+ *color* The color of the text (any format accepted by pg.mkColor)
+ *html* If specified, this overrides both *text* and *color*
+ *anchor* A QPointF or (x,y) sequence indicating what region of the text box will
+ be anchored to the item's position. A value of (0,0) sets the upper-left corner
+ of the text box to be at the position specified by setPos(), while a value of (1,1)
+ sets the lower-right corner.
+ *border* A pen to use when drawing the border
+ *fill* A brush to use when filling within the border
+ """
+ UIGraphicsItem.__init__(self)
+ self.textItem = QtGui.QGraphicsTextItem()
+ self.lastTransform = None
+ self._bounds = QtCore.QRectF()
+ if html is None:
+ self.setText(text, color)
+ else:
+ self.setHtml(html)
+ self.anchor = pg.Point(anchor)
+ self.fill = pg.mkBrush(fill)
+ self.border = pg.mkPen(border)
+ #self.setFlag(self.ItemIgnoresTransformations) ## This is required to keep the text unscaled inside the viewport
+
+ def setText(self, text, color=(200,200,200)):
+ color = pg.mkColor(color)
+ self.textItem.setDefaultTextColor(color)
+ self.textItem.setPlainText(text)
+ #html = '%s' % (color, text)
+ #self.setHtml(html)
+
+ def updateAnchor(self):
+ pass
+ #self.resetTransform()
+ #self.translate(0, 20)
+
+ def setPlainText(self, *args):
+ self.textItem.setPlainText(*args)
+ self.updateText()
+
+ def setHtml(self, *args):
+ self.textItem.setHtml(*args)
+ self.updateText()
+
+ def setTextWidth(self, *args):
+ self.textItem.setTextWidth(*args)
+ self.updateText()
+
+ def setFont(self, *args):
+ self.textItem.setFont(*args)
+ self.updateText()
+
+ def updateText(self):
+ self.viewRangeChanged()
+
+ #def getImage(self):
+ #if self.img is None:
+ #br = self.textItem.boundingRect()
+ #img = QtGui.QImage(int(br.width()), int(br.height()), QtGui.QImage.Format_ARGB32)
+ #p = QtGui.QPainter(img)
+ #self.textItem.paint(p, QtGui.QStyleOptionGraphicsItem(), None)
+ #p.end()
+ #self.img = img
+ #return self.img
+
+ 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):
+ br = self.textBoundingRect()
+ if br is None:
+ return
+ self.prepareGeometryChange()
+ self._bounds = self.deviceTransform().inverted()[0].mapRect(br)
+ #print self._bounds
+
+ def boundingRect(self):
+ return self._bounds
+
+ def paint(self, p, *args):
+ tr = p.transform()
+ if self.lastTransform is not None:
+ if tr != self.lastTransform:
+ self.viewRangeChanged()
+ self.lastTransform = tr
+
+
+ tbr = self.textBoundingRect()
+
+ #p.setPen(pg.mkPen('r'))
+ #p.drawRect(self.boundingRect())
+
+ p.setPen(self.border)
+ p.setBrush(self.fill)
+
+
+ #p.fillRect(tbr)
+ p.resetTransform()
+ p.drawRect(tbr)
+
+
+ p.translate(tbr.left(), tbr.top())
+ self.textItem.paint(p, QtGui.QStyleOptionGraphicsItem(), None)
+
\ No newline at end of file