How to get customGraphicsItems to work correctly with AutoRange?

428 views
Skip to first unread message

anthony...@gmail.com

unread,
Apr 26, 2019, 1:20:47 PM4/26/19
to pyqtgraph
It seems like adding in any custom GraphicItems to a plot breaks autoranging in PyQTgraph, at least it seems to me.

I added an example below, even when the candlestick item is off to the side of the plot and is not visible autorange still acts as though it is onscreen.

Any ideas of the path to take to get it working ?

Thanks.











import pyqtgraph as pg
from pyqtgraph import QtCore, QtGui

## Create a subclass of GraphicsObject.
## The only required methods are paint() and boundingRect()
## (see QGraphicsItem documentation)
class CandlestickItem(pg.GraphicsObject):
   
def __init__(self, data):
        pg
.GraphicsObject.__init__(self)
       
self.data = data  ## data must have fields: time, open, close, min, max
       
self.generatePicture()
   
   
def generatePicture(self):
       
## pre-computing a QPicture object allows paint() to run much more quickly,
       
## rather than re-drawing the shapes every time.
       
self.picture = QtGui.QPicture()
        p
= QtGui.QPainter(self.picture)
        p
.setPen(pg.mkPen('w'))
        w
= (self.data[1][0] - self.data[0][0]) / 3.
       
for (t, open, close, min, max) in self.data:
            p
.drawLine(QtCore.QPointF(t, min), QtCore.QPointF(t, max))
           
if open > close:
                p
.setBrush(pg.mkBrush('r'))
           
else:
                p
.setBrush(pg.mkBrush('g'))
            p
.drawRect(QtCore.QRectF(t-w, open, w*2, close-open))
        p
.end()
   
   
def paint(self, p, *args):
        p
.drawPicture(0, 0, self.picture)
   
   
def boundingRect(self):
       
## boundingRect _must_ indicate the entire area that will be drawn on
       
## or else we will get artifacts and possibly crashing.
       
## (in this case, QPicture does all the work of computing the bouning rect for us)
       
return QtCore.QRectF(self.picture.boundingRect())

# rescales Y automatically
def setYRange():
    vb
.enableAutoRange(x=False, y=True)
    vb
.setAutoVisible(x=False, y=True)
   
print('Why me no rescale ? =)')

data
= [  ## fields are (time, open, close, min, max).
   
(1., 10, 13, 5, 15),
   
(2., 13, 17, 9, 20),
   
(3., 17, 14, 11, 23),
   
(4., 14, 15, 5, 19),
   
(5., 15, 9, 8, 22),
   
(6., 9, 15, 8, 16),
   
(7., 10, 13, 5, 15),
   
(8., 13, 17, 9, 20),
   
(9., 17, 14, 11, 23),
   
(10., 14, 15, 5, 19),
   
(11., 15, 9, 8, 22),
   
(12., 9, 15, 8, 16),
   
(13., 10, 13, 5, 15),
   
(14., 14, 15, 5, 19),
   
(15., 15, 9, 8, 22),
   
(16., 9, 15, 8, 16),
   
(17., 16, 17, 15, 20),
   
(18., 17, 21, 20, 23),
   
(19., 21, 30, 29, 24),
   
(20., 30, 39, 28, 32),
   
(21., 39, 37, 35, 40)

]

plt
= pg.plot()
item
= CandlestickItem(data)
plt
.addItem(item)
plt
.setWindowTitle('Scaletest')

# normal lines
import numpy
x_data
= numpy.linspace(22, 122, num=100)
y_data
= (10*numpy.random.rand(100,1)+30).flatten()
#y_data[5000:] = numpy.sin(x_data[5000:])
lineitem
= pg.PlotDataItem(x_data, y_data)
plt
.addItem(lineitem)

# autoscale aspects
vb
= plt.getViewBox()
vb
.setAspectLocked(lock=False)
vb
.setAutoVisible(y=1.0)
vb
.enableAutoRange(axis='y', enable=True)

# force it to update at rescale
vb
.sigXRangeChanged.connect(setYRange)

## 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_()



Patrick

unread,
Apr 27, 2019, 2:25:40 AM4/27/19
to pyqtgraph
Hi,

I don't have experience writing custom GraphicsObjects, but I think the ViewBox autoscale is doing what it is told to do - it's the way your CandlestickItem is being drawn and/or the way it reports its size to the containing ViewBox when it does its auto ranging.

At the moment, you construct a QPicture on initialisation containing the drawn candlesticks, and then just draw the picture on paint(). The boundingRect is correct, it is the region that the picture will be painted to. But I think you also need to implement a dataBounds() method (http://www.pyqtgraph.org/documentation/graphicsItems/plotdataitem.html#pyqtgraph.PlotDataItem.dataBounds) to report to the ViewBox the range currently in view. This will need to do something like find the intersection of the current self.viewRect() and the data and return the proper range. See maybe the implementations in one of the core GraphicsObjects like PlotCurveItem (http://www.pyqtgraph.org/documentation/_modules/pyqtgraph/graphicsItems/PlotCurveItem.html).

Also as an aside, you might be able to stop manual plot scaling and enforce autorange (when you get it working!) with http://www.pyqtgraph.org/documentation/graphicsItems/viewbox.html#pyqtgraph.ViewBox.setMouseEnabled methods.

Hope that helps,
Patrick

Anthony S

unread,
Apr 28, 2019, 3:28:09 AM4/28/19
to pyqt...@googlegroups.com
I think that you are correct thanks for the pointers. I found this after a bit of digging: https://github.com/pyqtgraph/pyqtgraph/issues/60

The databounds method doesnt seem to be documented for more complex objects, I will need to research it a bit more.

--
You received this message because you are subscribed to the Google Groups "pyqtgraph" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pyqtgraph+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/pyqtgraph/963edefe-f996-4de7-ab65-b857c5913817%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Patrick

unread,
Apr 28, 2019, 9:30:00 PM4/28/19
to pyqtgraph
Hi,

Yeah, the dataBounds() functionality doesn't seem to be "official", in that the ViewBox code just checks for the existence of the method and uses it if available, else defaults to what is provided by item's BoundingRect().

Patrick
To unsubscribe from this group and stop receiving emails from it, send an email to pyqt...@googlegroups.com.

Al

unread,
Jul 13, 2021, 8:15:52 AM7/13/21
to pyqtgraph
Hi, does anyone have an example implementation? Thanks.
понедельник, 29 апреля 2019 г. в 08:30:00 UTC+7, Patrick:
Reply all
Reply to author
Forward
0 new messages