0

The given code below is derived from another question on SO. It displays a QMainWindow with 4 QGraphicsView to draw with the mouse in it, a QPushButton to clear the 4 QGraphicsView, and a QPushButton to get a new random color for drawing.

Goal: I would like to change the color of future drawings without changing the color of existing drawings.

Attempt: Changing the color of a QPen object immediately changed the color of the drawings already drawn. Therefore I created new GraphicsPathItem objects with new QPen objects with the new color to avoid this.

Problem: After a color change the color of existing drawings in a QGraphicsView changes as soon as you draw something in it again.

How can this problem be solved?


main.py

import sys
from random import choice

from PyQt5.QtWidgets import QApplication, QMainWindow, QGraphicsView, QGraphicsScene, QGraphicsPathItem
from PyQt5.QtGui import QPainterPath, QPen, QColor
from PyQt5.QtCore import Qt
from PyQt5.uic import loadUi

# Based on code from https://stackoverflow.com/a/44248794/7481773


class MainWindow(QMainWindow):
    COLORS = ("black", "red", "green", "blue", "yellow")

    def __init__(self):
        super().__init__()
        loadUi("mainwindow.ui", self)
        self.color = "black"

        self.layouts = (self.verticalLayout_top_left, self.verticalLayout_top_right,
                        self.verticalLayout_bottom_left, self.verticalLayout_bottom_right)

        self._views = []

        for layout in self.layouts:
            graphics_view = GraphicsView(self.color)
            self._views.append(graphics_view)
            layout.addWidget(graphics_view)

        self.clear_button.clicked.connect(self.clear_views)
        self.color_button.clicked.connect(self.update_color)

    def _get_new_random_color(self):
        new_colors = list(self.COLORS)
        new_colors.remove(self.color)
        return choice(new_colors)

    def clear_views(self):
        for view in self._views:
            view.clear_view()

    def update_color(self):
        new_color = self._get_new_random_color()
        self.color = new_color
        for view in self._views:
            view.new_item(new_color)


class GraphicsView(QGraphicsView):
    def __init__(self, color):
        super().__init__()
        self.start = None
        self.end = None
        self.item = None

        self.setScene(QGraphicsScene())
        self.path = QPainterPath()
        self.new_item(color)

        self.contents_rect = self.contentsRect()
        self.setSceneRect(0, 0, self.contents_rect.width(), self.contents_rect.height())
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

    def clear_view(self):
        try:
            self.path.clear()
        except AttributeError:
            self.path = QPainterPath()
        self.item.setPath(self.path)

    def new_item(self, color):
        self.item = GraphicsPathItem(color)
        self.scene().addItem(self.item)

    def mousePressEvent(self, event):
        self.start = self.mapToScene(event.pos())
        self.path.moveTo(self.start)

    def mouseMoveEvent(self, event):
        self.end = self.mapToScene(event.pos())
        self.path.lineTo(self.end)
        self.start = self.end
        self.item.setPath(self.path)


class GraphicsPathItem(QGraphicsPathItem):

    def __init__(self, color):
        super().__init__()
        self.pen = QPen()
        self.pen.setColor(QColor(color))
        self.pen.setWidth(5)
        self.setPen(self.pen)


def main():
    app = QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    app.exec_()
    del main_window, app


if __name__ == "__main__":
    main()

mainwindow.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Paint and Clear</string>
  </property>
  <property name="locale">
   <locale language="English" country="UnitedKingdom"/>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QGridLayout" name="gridLayout">
    <item row="2" column="0">
     <layout class="QVBoxLayout" name="verticalLayout_bottom_left"/>
    </item>
    <item row="2" column="1">
     <layout class="QVBoxLayout" name="verticalLayout_bottom_right"/>
    </item>
    <item row="0" column="0">
     <layout class="QVBoxLayout" name="verticalLayout_top_left"/>
    </item>
    <item row="0" column="1">
     <layout class="QVBoxLayout" name="verticalLayout_top_right"/>
    </item>
    <item row="1" column="0">
     <widget class="QPushButton" name="clear_button">
      <property name="text">
       <string>Clear</string>
      </property>
     </widget>
    </item>
    <item row="1" column="1">
     <widget class="QPushButton" name="color_button">
      <property name="text">
       <string>New random color</string>
      </property>
     </widget>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>24</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Atalanttore
  • 349
  • 5
  • 22
  • One question per post, if you have others you must create another question since those are the rules of SO. – eyllanesc Jan 06 '20 at 15:05

1 Answers1

1

Each time you create a new item, you are still using the same QPainterPath, so what you are seeing is not the new color applied on existing "drawings", but a new QGraphicsPathItem item that still uses the previous path in it.

You can easily solve your issue by creating a new path each time a new item is created:

    def new_item(self, color):
        self.item = GraphicsPathItem(color)
        self.scene().addItem(self.item)
        self.path = QPainterPath()
musicamante
  • 41,230
  • 6
  • 33
  • 58