Search code examples
pythonqtpyqt5qgraphicsviewqgraphicsscene

pyqt5 drawing lines with a button on a QGraphicsScene


I've a problem with pyqt5. I've create a windows with a scene that has a background image, re-implementing drawBackground. I've also a button that allow me to add a line in a position on the scene. The problem is that if i click the button to draw the line, then this line is drawn in a separate scene with it's own background, instead of into the scene i have. Seems like it create a new scene to draw the line on. Here is my code:

import sys

from PyQt5 import QtGui
from PyQt5.QtGui import QImage
from PyQt5.QtWidgets import (QMainWindow, QGraphicsView, QPushButton, 
    QHBoxLayout, QVBoxLayout, QWidget, QApplication, QGraphicsScene)

class GraphicsScene(QGraphicsScene):

  def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self._image = QImage()

  @property
  def image(self):
    return self._image

  @image.setter
  def image(self, img):
    self._image = img
    self.update()

  def drawBackground(self, painter, rect):
    if self.image.isNull():
      super().drawBackground(painter, rect)
    else:
      painter.drawImage(rect, self._image)

class MainWindow(QWidget):
    def __init__(self):
        super().__init__()

        self.title = "parcelDeliveryIta";
        self.top = 100
        self.left = 100
        self.width = 1500
        self.height = 900
        self.initUI()

    def initUI(self):

        self.scene = GraphicsScene(self)
        self.scene._image = QImage('Italy.png')
        view = QGraphicsView(self.scene, self)
        self.scene.setSceneRect(0, 0, view.width(), view.height())

        addLine = QPushButton('AddLine')
        addLine.clicked.connect(self.addLine)

        hbox = QHBoxLayout(self)
        hbox.addWidget(view)

        vbox = QVBoxLayout(self)
        vbox.addWidget(addLine)

        hbox.addLayout(vbox)

        self.setWindowTitle(self.title)
        self.setGeometry(self.top, self.left, self.width, self.height)
        self.setFixedSize(self.width, self.height)
        self.setLayout(hbox)
        self.show()

    def addLine(self):
        self.scene.addLine(0, 0, 100, 100)


if __name__ == "__main__":

    app = QApplication(sys.argv)
    window = MainWindow()
    sys.exit(app.exec_())

and this is the result when clicking the button:

enter image description here

As it is possible to see, the line is drawn with its own backgroung, that is image I've setted as background to the scene (the image above is cropped to show better the line) Thanks for helping.


Solution

  • Explanation:

    It seems that my previous solution is not suitable for your case since as the docs points out:

    void QGraphicsScene::drawBackground(QPainter *painter, const QRectF &rect)

    Draws the background of the scene using painter, before any items and the foreground are drawn. Reimplement this function to provide a custom background for the scene.

    All painting is done in scene coordinates. The rect parameter is the exposed rectangle.

    If all you want is to define a color, texture, or gradient for the background, you can call setBackgroundBrush() instead.

    See also drawForeground() and drawItems().

    (emphasis mine)

    That paint will also be used to paint the base of the items and therefore caused that behavior.


    Solution:

    So you will have to resort to another solution, for example to use a QGraphicsPixmapItem as a base and readjust the size of the window with that information:

    import sys
    
    from PyQt5.QtGui import QPixmap
    from PyQt5.QtWidgets import (
        QGraphicsView,
        QPushButton,
        QHBoxLayout,
        QVBoxLayout,
        QWidget,
        QApplication,
        QGraphicsScene,
    )
    
    
    class GraphicsView(QGraphicsView):
        def __init__(self, parent=None):
            super().__init__(parent)
            scene = QGraphicsScene(self)
            self.setScene(scene)
            self._pixmap_item = self.scene().addPixmap(QPixmap())
            self._pixmap_item.setZValue(-1)
    
        @property
        def pixmap(self):
            return self._pixmap_item.pixmap()
    
        @pixmap.setter
        def pixmap(self, pixmap):
            self._pixmap_item.setPixmap(pixmap)
            self.scene().setSceneRect(self._pixmap_item.boundingRect())
    
        def resizeEvent(self, event):
            if not self._pixmap_item.pixmap().isNull():
                self.fitInView(self._pixmap_item)
            super().resizeEvent(event)
    
    
    class MainWindow(QWidget):
        def __init__(self):
            super().__init__()
    
            self.title = "parcelDeliveryIta"
            self.top = 100
            self.left = 100
            self.width = 1500
            self.height = 900
            self.initUI()
    
        def initUI(self):
    
            self.view = GraphicsView(self)
            self.view.pixmap = QPixmap("Italy.png")
    
            addLine = QPushButton("AddLine")
            addLine.clicked.connect(self.addLine)
    
            hbox = QHBoxLayout(self)
            hbox.addWidget(self.view)
    
            vbox = QVBoxLayout()
            vbox.addWidget(addLine)
    
            hbox.addLayout(vbox)
    
            self.setWindowTitle(self.title)
            self.setGeometry(self.top, self.left, self.width, self.height)
            self.setFixedSize(self.width, self.height)
            self.show()
    
        def addLine(self):
            self.view.scene().addLine(0, 0, 100, 100)
    
    
    if __name__ == "__main__":
    
        app = QApplication(sys.argv)
        window = MainWindow()
        sys.exit(app.exec_())