Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    Solved [PySide6] GraphicsView not showing - I'm evidently doing it wrong!

    Qt for Python
    pyside6 qgraphicsview layout background
    2
    8
    303
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • J
      jim87 last edited by jim87

      Hello!

      I'm trying to set a QGraphicsView's background using a SVG image. I've thus extended it overwriting the drawBackground 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:

      6c511357-acef-4ae0-9566-19f151766ba9-immagine.png

      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())
      
      
      1 Reply Last reply Reply Quote 0
      • SGaist
        SGaist Lifetime Qt Champion last edited by

        You could make a brush out of your SVG image.

        See QGraphicsView::backgroundBrush property

        Interested in AI ? www.idiap.ch
        Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

        1 Reply Last reply Reply Quote 1
        • SGaist
          SGaist Lifetime Qt Champion last edited by

          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.

          Interested in AI ? www.idiap.ch
          Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

          1 Reply Last reply Reply Quote 0
          • J
            jim87 last edited by

            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:
            7a8ffeb2-8336-45bf-951d-ba0460dc38de-immagine.png

            So, supposing that the default renderer works as expected, I'm evidently missing something else :(

            Thank you very much for the support :)

            1 Reply Last reply Reply Quote 0
            • J
              jim87 last edited by

              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.

              1 Reply Last reply Reply Quote 0
              • J
                jim87 last edited by jim87

                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):

                ec079d3d-b869-4de4-bf9c-8ee7ccd185d0-immagine.png

                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)
                
                
                1 Reply Last reply Reply Quote 0
                • SGaist
                  SGaist Lifetime Qt Champion last edited by

                  You could make a brush out of your SVG image.

                  See QGraphicsView::backgroundBrush property

                  Interested in AI ? www.idiap.ch
                  Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                  1 Reply Last reply Reply Quote 1
                  • J
                    jim87 last edited by jim87

                    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))
                    
                    
                    1 Reply Last reply Reply Quote 1
                    • SGaist
                      SGaist Lifetime Qt Champion last edited by

                      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 :-)

                      Interested in AI ? www.idiap.ch
                      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                      1 Reply Last reply Reply Quote 0
                      • First post
                        Last post