Не получается сделать нормальную кисть и ластик в программе

В моем простеньком коде кисть рисуется с помощью кружком, но это, мягко говоря, не очень. Хотел сделать хорошую кисть, которая рисует беспрерывной линией, но как не пытался, не получилось ее сделать. Такие же дела и с ластиком, он удаляет только если правильно попасть.

import sys

from PyQt5.QtWidgets import *
from PyQt5 import QtCore, QtGui
from PyQt5.QtGui import *
from PyQt5 import uic
from PyQt5.QtWidgets import QWidget, QApplication
from PyQt5 import QtCore, QtWidgets, QtMultimedia
import pygame as pg
import pygame
pygame.init()
pg.init()


class Text_class:
    def __init__(self, x, y, text, color1, color2):
        self.t = 'text'
        self.x = x
        self.y = y
        self.text = text
        self.color1 = color1
        self.color2 = color2

    def draw(self, painter):
        painter.setBrush(QBrush(self.color1))
        painter.setPen(self.color1)
        painter.setFont(QFont('Helvetica', 48))
        painter.drawText(self.x, self.y, self.text)


class BrushPoint:
    def __init__(self, x, y, color1, color2):
        self.t = 'brush'
        self.x = x
        self.y = y
        self.color1 = color1
        self.color2 = color2

    def draw(self, painter):
        painter.setBrush(QBrush(self.color1))
        painter.setPen(self.color1)
        painter.drawEllipse(self.x - 5, self.y - 5, 9, 9)


class Line:
    def __init__(self, sx, sy, ex, ey, color1, color2):
        self.t = 'line'
        self.sx = sx
        self.sy = sy
        self.ex = ex
        self.ey = ey
        self.color1 = color1
        self.color2 = color2

    def draw(self, painter):
        painter.setBrush(QBrush(self.color1))
        painter.setPen(self.color2)
        painter.drawLine(self.sx, self.sy, self.ex, self.ey)


class Circle:
    def __init__(self, cx, cy, x, y, color1, color2):
        self.t = 'circle'
        self.cx = cx
        self.cy = cy
        self.x = x
        self.y = y
        self.color1 = color1
        self.color2 = color2

    def draw(self, painter):
        painter.setBrush(QBrush(self.color1))
        painter.setPen(self.color2)
        radius = int(((self.cx - self.x) ** 2 + (self.cy - self.y) ** 2) ** 0.5)
        painter.drawEllipse(self.cx - radius, self.cy - radius, radius * 2, radius * 2)


class Rectangle:
    def __init__(self, sx, sy, ex, ey, color1, color2):
        self.t = 'rect'
        self.sx = sx
        self.sy = sy
        self.ex = ex
        self.ey = ey
        self.color1 = color1
        self.color2 = color2

    def draw(self, painter):
        painter.setBrush(QBrush(self.color1))
        painter.setPen(self.color2)
        painter.drawRect(self.sx, self.sy, self.ex - self.sx, self.ey - self.sy)


class RoundedRect:
    def __init__(self, sx, sy, ex, ey, color1, color2):
        self.t = 'rounders'
        self.sx = sx
        self.sy = sy
        self.ex = ex
        self.ey = ey
        self.color1 = color1
        self.color2 = color2

    def draw(self, painter):
        painter.setBrush(QBrush(self.color1))
        painter.setPen(self.color2)
        painter.drawRoundedRect(self.sx, self.sy, self.ex - self.sx, self.ey - self.sy, 30.0, 15.0)


class Oval:
    def __init__(self, sx, sy, ex, ey, color1, color2):
        self.t = 'oval'
        self.sx = sx
        self.sy = sy
        self.ex = ex
        self.ey = ey
        self.color1 = color1
        self.color2 = color2

    def draw(self, painter):
        painter.setBrush(QBrush(self.color1))
        painter.setPen(self.color2)
        painter.drawRoundedRect(self.sx, self.sy, self.ex - self.sx, self.ey - self.sy, 360.0, 360.0)


class Arc:
    def __init__(self, sx, sy, ex, ey, color1, color2):
        self.t = 'arc'
        self.sx = sx
        self.sy = sy
        self.ex = ex
        self.ey = ey
        self.color1 = color1
        self.color2 = color2

    def draw(self, painter):
        painter.setBrush(QBrush(self.color1))
        painter.setPen(self.color2)
        painter.drawArc(self.sx, self.sy, self.ex - self.sx, (self.ey - self.sy) * 4, 30 * 16, 120 * 16)


class Chord:
    def __init__(self, sx, sy, ex, ey, color1, color2):
        self.t = 'chord'
        self.sx = sx
        self.sy = sy
        self.ex = ex
        self.ey = ey
        self.color1 = color1
        self.color2 = color2

    def draw(self, painter):
        painter.setBrush(QBrush(self.color1))
        painter.setPen(self.color2)
        painter.drawChord(self.sx, self.sy, (self.ex - self.sx), int((self.ey - self.sy) * 4), 30 * 16, 120 * 16)


class Canvas(QWidget):
    def __init__(self):
        super(Canvas, self).__init__()

        self.objects = []  # это массив где находятся все фигуры чтоб не пропали после рисования
        self.instrument = 'brush'  # по умолчанию кисточка
        self.color1 = QColor(255, 0, 255)
        self.color2 = QColor(255, 0, 255)
        # self.mus = pg.mixer.Sound('mus.mp3')

    def paintEvent(self, event):  # основной процесс отрисовки и отображения
        painter = QPainter()
        painter.begin(self)
        for obj in self.objects:
            obj.draw(painter)
        painter.end()

    def mousePressEvent(self, event):  # тут происходит чтение данных с помощью курсора мыши
        if self.instrument == 'brush':
            self.objects.append(BrushPoint(event.x(), event.y(), self.color1, self.color2))
            self.update()
        elif self.instrument == 'line':
            self.objects.append(Line(event.x(), event.y(), event.x(), event.y(), self.color1, self.color2))
            self.update()
        elif self.instrument == 'circle':
            self.objects.append(Circle(event.x(), event.y(), event.x(), event.y(), self.color1, self.color2))
            self.update()
        elif self.instrument == 'rect':
            self.objects.append(Rectangle(event.x(), event.y(), event.x(), event.y(), self.color1, self.color2))
            self.update()
        elif self.instrument == 'rounders':
            self.objects.append(RoundedRect(event.x(), event.y(), event.x(), event.y(), self.color1, self.color2))
            self.update()
        elif self.instrument == 'oval':
            self.objects.append(Oval(event.x(), event.y(), event.x(), event.y(), self.color1, self.color2))
            self.update()
        elif self.instrument == 'arc':
            self.objects.append(Arc(event.x(), event.y(), event.x(), event.y(), self.color1, self.color2))
            self.update()
        elif self.instrument == 'chord':
            self.objects.append(Chord(event.x(), event.y(), event.x(), event.y(), self.color1, self.color2))
            self.update()
        elif self.instrument == 'text':
            self.objects.append(Text_class(event.x(), event.y(), '/', self.color1, self.color2))
            self.update()
        elif self.instrument == 'eraser':
            x = event.x()
            y = event.y()

            for i in self.objects:
                if i.t == 'circle':
                    radius = int(((i.cx - i.x) ** 2 + (i.cy - i.y) ** 2) ** 0.5)
                    if i.cx - radius * 2 < x and x < i.cx and i.cy - radius * 2 < y and y < i.cy:
                        self.objects.remove(i)
                        self.update()
                        break

                elif i.t == 'brush' or i.t == 'text':
                    if i.x - 5 < x and x < i.x + 5 and i.y - 5 < y and y < i.y + 5:
                        self.objects.remove(i)
                        self.update()
                        break

                elif i.sx < x and x < i.ex and i.sy < y and y < i.ey:
                    self.objects.remove(i)
                    self.update()
                    break

    def mouseMoveEvent(self, event):  # тут так же но эта выполняет функции при движении
        if self.instrument == 'brush':
            self.objects.append(BrushPoint(event.x(), event.y(), self.color1, self.color2))
            self.update()
        elif self.instrument == 'line':
            self.objects[-1].ex = event.x()  # чтоб продолжить обращаемся к последнему месту нахождения мыши
            self.objects[-1].ey = event.y()
            self.update()
        elif self.instrument == 'circle':
            self.objects[-1].x = event.x()
            self.objects[-1].y = event.y()
            self.update()
        elif self.instrument == 'rect':
            self.objects[-1].ex = event.x()
            self.objects[-1].ey = event.y()
            self.update()
        elif self.instrument == 'rounders':
            self.objects[-1].ex = event.x()
            self.objects[-1].ey = event.y()
            self.update()
        elif self.instrument == 'oval':
            self.objects[-1].ex = event.x()
            self.objects[-1].ey = event.y()
            self.update()
        elif self.instrument == 'arc':
            self.objects[-1].ex = event.x()
            self.objects[-1].ey = event.y()
            self.update()
        elif self.instrument == 'chord':
            self.objects[-1].ex = event.x()
            self.objects[-1].ey = event.y()
            self.update()
    # тут просто меняем инструменты по нажатию кнопок

    def setEraser(self):
        self.instrument = 'eraser'

    def setText(self):
        self.instrument = 'text'

    def setBrush(self):
        self.instrument = 'brush'

    def setLine(self):
        self.instrument = 'line'

    def setCircle(self):
        self.instrument = 'circle'

    def setRect(self):
        self.instrument = 'rect'

    def setRoundedRect(self):
        self.instrument = 'rounders'

    def setOval(self):
        self.instrument = 'oval'

    def setArc(self):
        self.instrument = 'arc'

    def setChord(self):
        self.instrument = 'chord'

    def setColIn(self):
        self.color1 = QColorDialog.getColor()

    def setCol2(self):
        self.color2 = QColorDialog.getColor()

    # def Music_on(self):
        # self.mus.play()

    # def Music_off(self):
        # self.mus.stop()

    def setClear(self):
        self.objects.clear()
        self.update()


class Window(QMainWindow):
    def __init__(self):
        super(Window, self).__init__()

        uic.loadUi('window.ui', self)  # подключения дизайна

        self.setWindowTitle("Graphic designer v3.0")
        self.canvas = Canvas()
        self.setCentralWidget(self.canvas)  # главный виджет делаем Canvas
        self.capsLck = False

        # self.setWindowIcon(QIcon('image.png'))

        self.action_eraser.triggered.connect(self.centralWidget().setEraser)
        self.action_text.triggered.connect(self.centralWidget().setText)
        self.action_brush.triggered.connect(self.centralWidget().setBrush)
        self.action_line.triggered.connect(self.centralWidget().setLine)
        self.action_circle.triggered.connect(self.centralWidget().setCircle)
        self.action_rect.triggered.connect(self.centralWidget().setRect)
        self.action_rectangle.triggered.connect(self.centralWidget().setRoundedRect)
        self.action_oval.triggered.connect(self.centralWidget().setOval)
        self.action_arc.triggered.connect(self.centralWidget().setArc)
        self.action_chord.triggered.connect(self.centralWidget().setChord)
        self.action_color_inside.triggered.connect(self.centralWidget().setColIn)
        self.action__color_outside.triggered.connect(self.centralWidget().setCol2)
        # self.action_music_on.triggered.connect(self.centralWidget().Music_on)
        # self.action_music_off.triggered.connect(self.centralWidget().Music_off)
        self.action_clear.triggered.connect(self.centralWidget().setClear)

    def keyPressEvent(self, e):
        if self.canvas.objects[-1].tür == "text":
            i = e.key()
            letter = ''
            if i == 32:
                letter = ' '
            if 1040 <= i and i <= 1105:
                if self.capsLck:
                    letter = chr(i)
                else:
                    letter = chr(i + 32)
            if letter != '':
                self.canvas.objects[-1].text += letter
                self.canvas.update()
            if e.key() == QtCore.Qt.Key_Backspace:
                if len(self.canvas.objects[-1].text) == 1:
                    self.canvas.objects.remove(self.canvas.objects[-1])
                    self.canvas.update()
                else:
                    self.canvas.objects[-1].text = self.canvas.objects[-1].text[:-1]
                    self.canvas.update()
            if e.key() == QtCore.Qt.Key_CapsLock:
                if self.capsLck:
                    self.capsLck = False
                else:
                    self.capsLck = True


if __name__ == '__main__':
    app = QApplication(sys.argv)
    wnd = Window()
    wnd.show()
    sys.exit(app.exec())

Это стиль:

<?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="cursor">
   <cursorShape>ArrowCursor</cursorShape>
  </property>
  <property name="mouseTracking">
   <bool>false</bool>
  </property>
  <property name="windowTitle">
   <string>LibertyPain</string>
  </property>
  <property name="windowIcon">
   <iconset>
    <normaloff>../../Downloads/128725.png</normaloff>../../Downloads/128725.png</iconset>
  </property>
  <property name="styleSheet">
   <string notr="true">background-color: rgb(255, 255, 255);</string>
  </property>
  <property name="tabShape">
   <enum>QTabWidget::Rounded</enum>
  </property>
  <widget class="QWidget" name="centralwidget"/>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>1920</width>
     <height>20</height>
    </rect>
   </property>
   <widget class="QMenu" name="menu">
    <property name="title">
     <string>Инструменты</string>
    </property>
    <addaction name="action_eraser"/>
    <addaction name="action_text"/>
    <addaction name="action_brush"/>
    <addaction name="action_line"/>
    <addaction name="action_circle"/>
    <addaction name="action_rect"/>
    <addaction name="action_rectangle"/>
    <addaction name="action_oval"/>
    <addaction name="action_arc"/>
    <addaction name="action_chord"/>
    <addaction name="action_color_inside"/>
    <addaction name="action__color_outside"/>
   </widget>
   <widget class="QMenu" name="menu_2">
    <property name="title">
     <string>Толщина рисования</string>
    </property>
   </widget>
   <addaction name="menu"/>
   <addaction name="menu_2"/>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
  <widget class="QToolBar" name="toolBar">
   <property name="cursor">
    <cursorShape>PointingHandCursor</cursorShape>
   </property>
   <property name="windowTitle">
    <string>toolBar</string>
   </property>
   <property name="styleSheet">
    <string notr="true">background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(0, 0, 0, 255), stop:0.05 rgba(14, 8, 73, 255), stop:0.36 rgba(28, 17, 145, 255), stop:0.6 rgba(126, 14, 81, 255), stop:0.75 rgba(234, 11, 11, 255), stop:0.79 rgba(244, 70, 5, 255), stop:0.86 rgba(255, 136, 0, 255), stop:0.935 rgba(239, 236, 55, 255));
background-color: rgb(85, 255, 255);</string>
   </property>
   <attribute name="toolBarArea">
    <enum>RightToolBarArea</enum>
   </attribute>
   <attribute name="toolBarBreak">
    <bool>false</bool>
   </attribute>
   <addaction name="action_text"/>
   <addaction name="action_eraser"/>
   <addaction name="action_brush"/>
   <addaction name="action_line"/>
   <addaction name="action_circle"/>
   <addaction name="action_rect"/>
   <addaction name="action_rectangle"/>
   <addaction name="action_oval"/>
   <addaction name="action_arc"/>
   <addaction name="action_chord"/>
   <addaction name="separator"/>
   <addaction name="action_color_inside"/>
   <addaction name="action__color_outside"/>
   <addaction name="separator"/>
  </widget>
  <widget class="QToolBar" name="toolBar_2">
   <property name="cursor">
    <cursorShape>PointingHandCursor</cursorShape>
   </property>
   <property name="windowTitle">
    <string>toolBar_2</string>
   </property>
   <property name="styleSheet">
    <string notr="true">background-color: rgb(85, 255, 255);</string>
   </property>
   <attribute name="toolBarArea">
    <enum>LeftToolBarArea</enum>
   </attribute>
   <attribute name="toolBarBreak">
    <bool>false</bool>
   </attribute>
   <addaction name="action_music_on"/>
   <addaction name="separator"/>
   <addaction name="action_music_off"/>
   <addaction name="separator"/>
   <addaction name="action_clear"/>
  </widget>
  <action name="action_eraser">
   <property name="icon">
    <iconset>
     <normaloff>../../Downloads/Без названия.png</normaloff>../../Downloads/Без названия.png</iconset>
   </property>
   <property name="text">
    <string>Ластик</string>
   </property>
   <property name="toolTip">
    <string>Ластик</string>
   </property>
   <property name="shortcut">
    <string>1</string>
   </property>
   <property name="menuRole">
    <enum>QAction::NoRole</enum>
   </property>
  </action>
  <action name="action_text">
   <property name="text">
    <string>Текст(русский)</string>
   </property>
   <property name="toolTip">
    <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt; font-weight:600; color:#ffffff;&quot;&gt;Текст(русский)&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
   </property>
   <property name="font">
    <font>
     <pointsize>10</pointsize>
    </font>
   </property>
  </action>
  <action name="action_brush">
   <property name="icon">
    <iconset>
     <normaloff>../../Downloads/w256h2561350817736brush.png</normaloff>../../Downloads/w256h2561350817736brush.png</iconset>
   </property>
   <property name="text">
    <string>Кисточка</string>
   </property>
  </action>
  <action name="action_line">
   <property name="text">
    <string>Линия</string>
   </property>
  </action>
  <action name="action_circle">
   <property name="text">
    <string>Круг</string>
   </property>
  </action>
  <action name="action_rect">
   <property name="text">
    <string>Прямоугольник</string>
   </property>
  </action>
  <action name="action_red">
   <property name="text">
    <string>Красный</string>
   </property>
  </action>
  <action name="action_green">
   <property name="text">
    <string>Зеленый</string>
   </property>
  </action>
  <action name="action">
   <property name="text">
    <string>Черный</string>
   </property>
  </action>
  <action name="action_rectangle">
   <property name="text">
    <string>Округленный прямоуг.</string>
   </property>
  </action>
  <action name="action_oval">
   <property name="text">
    <string>Овал</string>
   </property>
  </action>
  <action name="action_arc">
   <property name="text">
    <string>Дуга</string>
   </property>
  </action>
  <action name="action_chord">
   <property name="text">
    <string>Хорда</string>
   </property>
  </action>
  <action name="action_color_inside">
   <property name="text">
    <string>Цвет заполнения</string>
   </property>
   <property name="toolTip">
    <string>Цвет заполнения</string>
   </property>
  </action>
  <action name="action__color_outside">
   <property name="text">
    <string>Цвет обводки</string>
   </property>
   <property name="toolTip">
    <string>Цвет обводки</string>
   </property>
  </action>
  <action name="action_music_on">
   <property name="text">
    <string>Включить музыку</string>
   </property>
   <property name="toolTip">
    <string>Включить музыку</string>
   </property>
   <property name="font">
    <font>
     <pointsize>10</pointsize>
    </font>
   </property>
  </action>
  <action name="action_music_off">
   <property name="text">
    <string>Выключить музыку</string>
   </property>
   <property name="font">
    <font>
     <pointsize>10</pointsize>
    </font>
   </property>
  </action>
  <action name="action_clear">
   <property name="text">
    <string>Очистить все</string>
   </property>
   <property name="font">
    <font>
     <family>MS Shell Dlg 2</family>
     <pointsize>10</pointsize>
     <weight>50</weight>
     <italic>false</italic>
     <bold>false</bold>
     <underline>false</underline>
     <strikeout>false</strikeout>
     <kerning>true</kerning>
    </font>
   </property>
  </action>
 </widget>
 <resources/>
 <connections/>
</ui>

Я поискал в интернете и нашел примерное решение, но я не знаю, как это можно подставить в мой код, так как недавно начал изучать данную библиотеку.

from PyQt5 import QtCore, QtGui, QtWidgets
 
class Window(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        top, left, width, height = 0, 0, 1920, 1080
        self.setWindowTitle("MyPainter")
        self.setGeometry(top, left, width, height)
 
        self.image = QtGui.QImage(self.size(), QtGui.QImage.Format_ARGB32)
        self.image.fill(QtCore.Qt.white)
        self.imageDraw = QtGui.QImage(self.size(), QtGui.QImage.Format_ARGB32)
        self.imageDraw.fill(QtCore.Qt.transparent)
 
        self.drawing = False
        self.brushSize = 2
        self._clear_size = 10
        self.brushColor = QtGui.QColor(QtCore.Qt.self.black)
        self.lastPoint = QtCore.QPoint()
 
        self.change = False
        mainMenu = self.menuBar()
        changeColour = mainMenu.addMenu("changeColour")
        changeColourAction = QtWidgets.QAction("change", self)
        changeColour.addAction(changeColourAction)
        changeColourAction.triggered.connect(self.changeColour)
 
    def mousePressEvent(self, event):
        if event.button() == QtCore.Qt.LeftButton:
            self.drawing = True
            self.lastPoint = event.pos()
 
    def mouseMoveEvent(self, event):
        if event.buttons() and QtCore.Qt.LeftButton and self.drawing:
            painter = QtGui.QPainter(self.imageDraw)
            painter.setPen(QtGui.QPen(self.brushColor, self.brushSize, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin))
            if self.change:
                r = QtCore.QRect(QtCore.QPoint(), self._clear_size * QtCore.QSize())
                r.moveCenter(event.pos())
                painter.save()
                painter.setCompositionMode(QtGui.QPainter.CompositionMode_Clear)
                painter.eraseRect(r)
                painter.restore()
            else:
                painter.drawLine(self.lastPoint, event.pos())
            painter.end()
            self.lastPoint = event.pos()
            self.update()
 
    def mouseReleaseEvent(self, event):
        if event.button == QtCore.Qt.LeftButton:
            self.drawing = False
 
    def paintEvent(self, event):
        canvasPainter = QtGui.QPainter(self)
        canvasPainter.drawImage(self.rect(), self.image, self.image.rect())
        canvasPainter.drawImage(self.rect(), self.imageDraw, self.imageDraw.rect())
 
    def changeColour(self):
        self.change = not self.change
        if self.change:
            pixmap = QtGui.QPixmap(QtCore.QSize(1, 1)*self._clear_size)
            pixmap.fill(QtCore.Qt.transparent)
            painter = QtGui.QPainter(pixmap)
            painter.setPen(QtGui.QPen(QtCore.Qt.black, 2))
            painter.drawRect(pixmap.rect())
            painter.end()
            cursor = QtGui.QCursor(pixmap)
            QtWidgets.QApplication.setOverrideCursor(cursor)
        else:
            QtWidgets.QApplication.restoreOverrideCursor()
 
 
if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec())

Ответы (1 шт):

Автор решения: S. Nick

Ваш пример слишком большой для понимания и ответа на то, что вы спрашиваете. Пример должен быть минимальным и демонстрировать только проблему.

Для удаления мы используем режим композиции QPainter::CompositionMode_Clear
и стереть с помощью eraseRect().

Больше https://doc.qt.io/qt-5/qpainter.html#CompositionMode-enum и https://doc.qt.io/qt-5/qpainter.html#eraseRect

from PyQt5 import QtCore, QtGui, QtWidgets

class Window(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setFixedSize(600, 600)

        self.image = QtGui.QImage(self.size(), QtGui.QImage.Format_ARGB32)
        self.image.fill(QtCore.Qt.white)
        self.imageDraw = QtGui.QImage(self.size(), QtGui.QImage.Format_ARGB32)
        self.imageDraw.fill(QtCore.Qt.transparent)

        self.drawing = False
        self.brushSize = 3
        self._clear_size = 20
        self.brushColor = QtGui.QColor(QtCore.Qt.blue)
        self.lastPoint = QtCore.QPoint()

        self.change = False
        mainMenu = self.menuBar()
        erase = mainMenu.addMenu("erase")
        changeColourAction = QtWidgets.QAction("change", self)
        erase.addAction(changeColourAction)
        changeColourAction.triggered.connect(self.erase)

    def mousePressEvent(self, event):
        if event.button() == QtCore.Qt.LeftButton:
            self.drawing = True
            self.lastPoint = event.pos()

    def mouseMoveEvent(self, event):
        if event.buttons() and QtCore.Qt.LeftButton and self.drawing:
            painter = QtGui.QPainter(self.imageDraw)
            painter.setPen(QtGui.QPen(
                self.brushColor, 
                self.brushSize, 
                QtCore.Qt.SolidLine, 
                QtCore.Qt.RoundCap, 
                QtCore.Qt.RoundJoin
            ))
            if self.change:
                r = QtCore.QRect(QtCore.QPoint(), self._clear_size*QtCore.QSize())
                r.moveCenter(event.pos())
                painter.save()
                painter.setCompositionMode(QtGui.QPainter.CompositionMode_Clear)           # 1!!
                painter.eraseRect(r)                                                       # 1!!
                painter.restore()
            else:
                painter.drawLine(self.lastPoint, event.pos())
            painter.end()
            self.lastPoint = event.pos()
            self.update()

    def mouseReleaseEvent(self, event):
        if event.button == QtCore.Qt.LeftButton:
            self.drawing = False

    def paintEvent(self, event):
        canvasPainter = QtGui.QPainter(self)
        canvasPainter.drawImage(self.rect(), self.image, self.image.rect())
        canvasPainter.drawImage(self.rect(), self.imageDraw, self.imageDraw.rect())

    def erase(self):
        self.change = not self.change
        if self.change:
            pixmap = QtGui.QPixmap(QtCore.QSize(1, 1)*self._clear_size)
            pixmap.fill(QtCore.Qt.transparent)
            painter = QtGui.QPainter(pixmap)
            painter.setPen(QtGui.QPen(QtCore.Qt.black, 2))
            painter.drawRect(pixmap.rect())
            painter.end()
            cursor = QtGui.QCursor(pixmap)
            QtWidgets.QApplication.setOverrideCursor(cursor)
        else:
            QtWidgets.QApplication.restoreOverrideCursor()


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = Window()
    w.setWindowTitle("Demo Painter")
    w.show()
    sys.exit(app.exec())

введите сюда описание изображения


Update

Я поискал в интернете и нашел примерное решение, но я не знаю, как это можно подставить в мой код, так как недавно начал изучать данную библиотеку.

Я убрал все лишнее, нажимаете на Ластик и стираете. Повторное нажатие на Ластик - отключает его.

import sys
from PyQt5.Qt import *
from PyQt5 import QtGui, QtCore, QtWidgets, uic


class Canvas(QWidget):
    def __init__(self):
        super(Canvas, self).__init__()
        self.setFixedSize(600, 600)

        self.image = QtGui.QImage(self.size(), QtGui.QImage.Format_ARGB32)
        self.image.fill(QtCore.Qt.white)
        self.imageDraw = QtGui.QImage(self.size(), QtGui.QImage.Format_ARGB32)
        self.imageDraw.fill(QtCore.Qt.transparent)

        self.drawing = False
        self.brushSize = 3
        self._clear_size = 20
        self.brushColor = QtGui.QColor(QtCore.Qt.blue)
        self.lastPoint = QtCore.QPoint()
        self.change = False

    def mousePressEvent(self, event):
        if event.button() == QtCore.Qt.LeftButton:
            self.drawing = True
            self.lastPoint = event.pos()

    def mouseMoveEvent(self, event):
        if event.buttons() and QtCore.Qt.LeftButton and self.drawing:
            painter = QtGui.QPainter(self.imageDraw)
            painter.setPen(QtGui.QPen(
                self.brushColor, 
                self.brushSize, 
                QtCore.Qt.SolidLine, 
                QtCore.Qt.RoundCap, 
                QtCore.Qt.RoundJoin
            ))
            if self.change:
                r = QtCore.QRect(QtCore.QPoint(), self._clear_size*QtCore.QSize())
                r.moveCenter(event.pos())
                painter.save()
                painter.setCompositionMode(QtGui.QPainter.CompositionMode_Clear)           # 1!!
                painter.eraseRect(r)                                                       # 1!!
                painter.restore()
            else:
                painter.drawLine(self.lastPoint, event.pos())
            painter.end()
            self.lastPoint = event.pos()
            self.update()

    def mouseReleaseEvent(self, event):
        if event.button == QtCore.Qt.LeftButton:
            self.drawing = False

    def paintEvent(self, event):
        canvasPainter = QtGui.QPainter(self)
        canvasPainter.drawImage(self.rect(), self.image, self.image.rect())
        canvasPainter.drawImage(self.rect(), self.imageDraw, self.imageDraw.rect())

    def erase(self):
        self.change = not self.change
        if self.change:
            pixmap = QtGui.QPixmap(QtCore.QSize(1, 1)*self._clear_size)
            pixmap.fill(QtCore.Qt.transparent)
            painter = QtGui.QPainter(pixmap)
            painter.setPen(QtGui.QPen(QtCore.Qt.black, 2))
            painter.drawRect(pixmap.rect())
            painter.end()
            cursor = QtGui.QCursor(pixmap)
            QtWidgets.QApplication.setOverrideCursor(cursor)
        else:
            QtWidgets.QApplication.restoreOverrideCursor()        
        

class Window(QMainWindow): 
    def __init__(self):
        super(Window, self).__init__()
        
        uic.loadUi('q1242924.ui', self)              # подключения дизайна
        
        self.setFixedSize(870, 639)
        self.setWindowTitle("Graphic designer v3.0")
        
        self.canvas = Canvas()
        self.setCentralWidget(self.canvas)  # главный виджет делаем Canvas

#        self.action_eraser.triggered.connect(self.centralWidget().setEraser)
#        self.action_brush.triggered.connect(self.centralWidget().setBrush)
        
        self.action_eraser.triggered.connect(self.canvas.erase)                     # !!!


if __name__ == '__main__':
    app = QApplication(sys.argv)
    wnd = Window()
    wnd.show()
    print(wnd.size())
    sys.exit(app.exec_())

введите сюда описание изображения

→ Ссылка