[PySide6] GraphicsView not showing - I'm evidently doing it wrong!
-
Hello!
I'm trying to set a
QGraphicsView
's background using a SVG image. I've thus extended it overwriting thedrawBackground
method (see PoC at the bottom).I currently create a scene, add a "stupid" rectangle, create a GraphicsView using the scene in the constructor and add the view to the layout of the application. The result is the following:
Which is... not what I wanted :D (I'd expect a window with a GraphicsScene with at least a rectangle with a custom view's background). What am I doing wrong?
I've prepared a one-file PoC you can copy and try out.
In the PoC I've created a click event listener to print out what's being clicked, and when you click on the darker area, it says "QObject" (because there is no object name, see code). I'd expect the MyGraphicsView to be logged.
Thanks :)
import sys from typing import Union from PySide6 import QtCore, QtGui from PySide6.QtSvg import QSvgRenderer from PySide6.QtWidgets import QApplication, QGraphicsScene, QGraphicsView, QMainWindow, QVBoxLayout, QWidget class MyGraphicsView(QGraphicsView): def drawBackground(self, painter: QtGui.QPainter, rect: Union[QtCore.QRectF, QtCore.QRect]) -> None: svg_data = ''' <svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"> <rect width="100%" height="100%" fill="#434343" fill-opacity="1" /> <g fill-rule="evenodd"> <g fill="#979797" fill-opacity="1"> <path opacity=".5" d="M96 95h4v1h-4v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9zm-1 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9z" /> <path d="M6 5V0H5v5H0v1h5v94h1V6h94V5H6z" /> </g> </g> </svg> '''.encode('utf8') renderer = QSvgRenderer(contents=svg_data, parent=self) renderer.render(painter, rect) class WhatsUnderMouse(QtCore.QObject): def eventFilter(self, watched: QtCore.QObject, event: QtCore.QEvent) -> bool: if event.type() == QtCore.QEvent.MouseButtonPress: click_name = watched.objectName().strip() or type(watched) print(f'Clicked: {click_name}') return super().eventFilter(watched, event) if __name__ == '__main__': app = QApplication() app.setObjectName('The application') mouseEventFilter = WhatsUnderMouse() app.installEventFilter(mouseEventFilter) main = QMainWindow() main.setObjectName('Main window') central_widget = QWidget() layout = QVBoxLayout() central_widget.setLayout(layout) main.setCentralWidget(central_widget) rect = QtCore.QRect() rect.setWidth(400) rect.setHeight(400) main.setGeometry(rect) scene = QGraphicsScene() scene.setObjectName('The scene') rect = QtCore.QRectF(10, 10, 100, 100) scene.addRect(rect) view = MyGraphicsView(main) view.setObjectName('The graphics view') layout.addWidget(view) main.show() sys.exit(app.exec())
-
You could make a brush out of your SVG image.
-
Hi,
I don't have a machine a hand so here are some suggestions:
- Nuke all QMainWindow related code, you don't need it. Just show the graphics view.
- You should check that your renderer is valid. You might be drawing a black rectangle on a black background.
Once you have ensured that the renderer is valid and if you are still getting a black background, you can just call the base class implementation of drawBackground to check that it is working properly.
-
Hello @SGaist ,
thanks for replying :)
I've simplified as much as possible down to this:
import sys from PySide6 import QtCore from PySide6.QtWidgets import QApplication, QGraphicsScene, QGraphicsView if __name__ == '__main__': app = QApplication() scene = QGraphicsScene() rect = QtCore.QRectF(10, 10, 100, 100) scene.addRect(rect) view = QGraphicsView() view.show() sys.exit(app.exec())
The resulting view is still empty:
So, supposing that the default renderer works as expected, I'm evidently missing something else :(
Thank you very much for the support :)
-
Ok, got some hints reading the code again:
import sys from typing import Union from PySide6 import QtCore, QtGui from PySide6.QtSvg import QSvgRenderer from PySide6.QtWidgets import QApplication, QGraphicsScene, QGraphicsView class MyGraphicsView(QGraphicsView): svg_renderer: QSvgRenderer def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) svg_data = ''' <svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"> <rect width="100%" height="100%" fill="#434343" fill-opacity="1" /> <g fill-rule="evenodd"> <g fill="#979797" fill-opacity="1"> <path opacity=".5" d="M96 95h4v1h-4v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9zm-1 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9z" /> <path d="M6 5V0H5v5H0v1h5v94h1V6h94V5H6z" /> </g> </g> </svg> '''.encode('utf8') background_content = QtCore.QByteArray(svg_data) self.svg_renderer = QSvgRenderer(background_content) self.svg_renderer.setAspectRatioMode( QtCore.Qt.AspectRatioMode.KeepAspectRatio) def drawBackground(self, painter: QtGui.QPainter, rect: Union[QtCore.QRectF, QtCore.QRect]) -> None: self.svg_renderer.render(painter, rect) class WhatsUnderMouse(QtCore.QObject): def eventFilter(self, watched: QtCore.QObject, event: QtCore.QEvent) -> bool: if event.type() == QtCore.QEvent.MouseButtonPress: click_name = watched.objectName().strip() or type(watched) print(f'Clicked: {click_name}') return super().eventFilter(watched, event) if __name__ == '__main__': app = QApplication() app.setObjectName('The application') mouseEventFilter = WhatsUnderMouse() app.installEventFilter(mouseEventFilter) scene = QGraphicsScene() scene.setObjectName('The scene') pen = QtGui.QPen(QtGui.Qt.GlobalColor.yellow) brush = QtGui.QBrush(QtGui.Qt.GlobalColor.red) scene.addRect(10, 10, 100, 100, pen, brush) view = MyGraphicsView(scene) view.setObjectName('The graphics view') view.show() sys.exit(app.exec())
Now it "works" just fine. The only thing missing now is that the drawBackground won't repeat the SVG pattern, but will render it once only, enlarging the graphic to match the size of the graphic view.
-
Ok, kind of got it working.
This is the SVG background graphic view:
class MyGraphicsView(QGraphicsView): svg_renderer: QSvgRenderer def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) svg_data = ''' <svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"> <rect width="100%" height="100%" fill="#434343" fill-opacity="1" /> <g fill-rule="evenodd"> <g fill="#979797" fill-opacity="1"> <path opacity=".5" d="M96 95h4v1h-4v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4h-9v4h-1v-4H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15v-9H0v-1h15V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h9V0h1v15h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9h4v1h-4v9zm-1 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm9-10v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-10 0v-9h-9v9h9zm-9-10h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9zm10 0h9v-9h-9v9z" /> <path d="M6 5V0H5v5H0v1h5v94h1V6h94V5H6z" /> </g> </g> </svg> '''.encode('utf8') background_content = QtCore.QByteArray(svg_data) self.svg_renderer = QSvgRenderer(background_content) self.svg_renderer.setAspectRatioMode( QtCore.Qt.AspectRatioMode.KeepAspectRatio) def drawBackground(self, painter: QtGui.QPainter, rect: Union[QtCore.QRectF, QtCore.QRect]) -> None: pattern_size = self.svg_renderer.defaultSize() p_width = pattern_size.width() p_height = pattern_size.height() left_steps = math.ceil(rect.width() / pattern_size.width()) top_steps = math.ceil(rect.height() / pattern_size.height()) for w in range(0, left_steps): for h in range(0, top_steps): # print(w, h) left = w * p_width top = h * p_height # print(top, left) rect = QtCore.QRect() rect.setTop(top) rect.setLeft(left) rect.setWidth(p_width) rect.setHeight(p_height) self.svg_renderer.render(painter, rect) self.svg_renderer.render(painter, rect)
Last step: it seems that the scene is centered, though all the surface seems to be the QGraphicsView (clicking on the surface with the click event handler):
Any hint on how to let the renderer paint on all the surface, and not only from the center downwards?
EDIT: I kinda sorted it out, but I'm unsure whether it's the correct solution (due to the cyclic complexity). This is the
drawBackground
overwritten method:def drawBackground(self, painter: QtGui.QPainter, rect: Union[QtCore.QRectF, QtCore.QRect]) -> None: pattern_size = self.svg_renderer.defaultSize() p_width = pattern_size.width() p_height = pattern_size.height() left_steps = math.ceil(rect.width() / pattern_size.width()) top_steps = math.ceil(rect.height() / pattern_size.height()) for w in range(0, left_steps): for h in range(0, top_steps): left = w * p_width top = h * p_height for top_multiplier in [1, -1]: for left_multiplier in [1, -1]: rect = QtCore.QRect() rect.setTop(top_multiplier * top) rect.setLeft(left_multiplier * left) rect.setWidth(p_width) rect.setHeight(p_height) self.svg_renderer.render(painter, rect)
-
You could make a brush out of your SVG image.
-
Thanks @SGaist for the hint :) I made a way better solution for what I want to achieve. The code follows for future reference :)
class BlueprintGraphicsView(QGraphicsView): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) svg_data = Path(__file__).parent.joinpath( 'images', 'background', 'graph-paper.svg').read_bytes() background_content = QtCore.QByteArray(svg_data) svg_renderer = QSvgRenderer(background_content) svg_renderer.setAspectRatioMode( QtCore.Qt.AspectRatioMode.KeepAspectRatio) brush_pixmap = QtGui.QPixmap(svg_renderer.defaultSize()) brush_painter = QtGui.QPainter(brush_pixmap) svg_renderer.render(brush_painter, brush_pixmap.rect()) brush_painter.end() self.setBackgroundBrush(QtGui.QBrush(brush_pixmap))
-
Great !
Since you have it working now, please mark the thread as solved using the "Topic Tools" button or the three dotted menu beside the answer you deem correct so that other forum users may know a solution has been found :-)