I am trying to make a custom border for a graph in pyqtgraph. I am doing so by adding a QGraphicsLineItem to the scene / graphicsview (ViewBox in pyqtgraph) but am having trouble with either approach. Adding the QGraphicsLineItem to the scene gives me what I want to get (a border around the top and right axis), but it does not scale:
upperplot = graphics_layout_widget.addPlot(0, 0, 1, 1)
self.curve_upper = upperplot.plot(np.linspace(0,0,8192),
np.linspace(0,0,8192), # loaded_file.data.vm_array[0]
pen=plotpen)
tl = upperplot.getViewBox().boundingRect().topLeft()
tr = upperplot.getViewBox().boundingRect().topRight()
topline = QtGui.QGraphicsLineItem(tl.x(), tl.y(), tr.x(), tr.y())
topline.setParentItem(upperplot.getViewBox())
topline.setPen(pg.mkPen(color=(211, 211, 211), width=10))
upperplot.getViewBox().scene().addItem(topline)
I saw GraphicsView handles all resizing, and tried adding the item directly to GraphicView:
upperplot.getViewBox().addItem(topline)
works except for the line is now centered around Y = 0, not the top left. Interestingly the X axis is okay.
I feel it is a simple solution but cannot for the life of me find the answer - I am not sure if it is a problem with mapping Scene to View or with alignment of the scene in the viewbox but I've had no sucess playing around with either. Any help would be much appreciated.
Minimal reproducable example:
from PyQt5 import QtWidgets, QtGui
import pyqtgraph as pg
import numpy as np
class UiMainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(UiMainWindow, self).__init__()
# set mainwindow + widgets
self.mainwidget = QtWidgets.QWidget(self)
self.mainwidget_gridlayout = QtWidgets.QGridLayout(self.mainwidget)
self.setCentralWidget(QtGui.QWidget(self))
self.centralWidget().setLayout(self.mainwidget_gridlayout)
self.graphics_layout_widget = pg.GraphicsLayoutWidget() # contains a graphicsview
self.graphics_layout_widget.setBackground('w')
pg.setConfigOption('foreground', 'k')
self.mainwidget_gridlayout.addWidget(self.graphics_layout_widget)
# make plot
plotpen = pg.mkPen(color='k', width=1)
self.upperplot = self.graphics_layout_widget.addPlot(0, 0, 1, 1)
self.curve_upper = self.upperplot.plot(np.linspace(0, 100, 8192),
np.linspace(0, 0, 8192),
pen=plotpen)
# draw top border line
QtWidgets.QApplication.processEvents() # I could not get the boundingRect of the ViewBox without drawing first
tl = self.upperplot.getViewBox().boundingRect().topLeft()
tr = self.upperplot.getViewBox().boundingRect().topRight()
br = self.upperplot.getViewBox().boundingRect().bottomRight()
topline = QtGui.QGraphicsLineItem(tl.x(), tl.y(), tr.x(), tr.y())
topline.setParentItem(self.upperplot.getViewBox())
topline.setPen(pg.mkPen(color=(211, 211, 211), width=10))
rightline = QtGui.QGraphicsLineItem(tr.x(), tr.y(), br.x(), br.y())
rightline.setParentItem(self.upperplot.getViewBox())
rightline.setPen(pg.mkPen(color=(211, 211, 211), width=10))
self.upperplot.getViewBox().addItem(topline) # correct scaling, but Y axis is centered as zero
self.upperplot.getViewBox().addItem(rightline)
# vs
# self.upperplot.getViewBox().scene().addItem(topline) # correct position, but cannot scale
# self.upperplot.getViewBox().scene().addItem(rightline)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
mw = UiMainWindow()
mw.show()
sys.exit(app.exec_())
Update around border:
self.upperplot.getViewBox().setBorder(color=(211, 211, 211), width=10)
gives:
rather than:
One possible solution is to implement a custom ViewBox by doing the custom painting:
class CustomViewBox(pg.ViewBox):
def paint(self, p, opt, widget):
super().paint(p, opt, widget)
r = QtCore.QRectF(self.boundingRect())
p.save()
tl = r.topLeft()
tr = r.topRight()
br = r.bottomRight()
pen = pg.mkPen(color=(211, 211, 211), width=10)
p.setPen(pen)
p.drawLine(tl, tr)
p.drawLine(tr, br)
p.restore()
self.upperplot = self.graphics_layout_widget.addPlot(
0, 0, 1, 1, viewBox=CustomViewBox()
)