gpt4 book ai didi

python - 如何在 QGraphicsView 中制作 2 层?

转载 作者:行者123 更新时间:2023-12-01 07:33:29 25 4
gpt4 key购买 nike

在下面的程序中,加载背景图像并在其上绘制。

但是,我遇到了一个问题。

在这个程序中,当我使用“橡皮擦”工具时,背景图像也会被删除!

其实我只是想把我画的东西擦掉,除了背景图。

然后,我只想将绘制的图层(图层)保存为图像。

这种情况我该怎么办?

import sys

from PyQt5.QtCore import *
from PyQt5.QtCore import Qt
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtWidgets import (QApplication, QCheckBox, QGridLayout, QGroupBox,
QPushButton, QVBoxLayout, QWidget, QSlider)

QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)

class CWidget(QWidget):

def __init__(self):

super().__init__()

# 전체 폼 박스
formbox = QHBoxLayout()
self.setLayout(formbox)

# 좌, 우 레이아웃박스
left = QVBoxLayout()
right = QVBoxLayout()

# 그룹박스2
gb = QGroupBox('펜 설정')
left.addWidget(gb)

grid = QGridLayout()
gb.setLayout(grid)

label = QLabel('펜 색상')
grid.addWidget(label, 1, 0)

self.pencolor = QColor(0, 0, 0)
self.penbtn = QPushButton()
self.penbtn.setStyleSheet('background-color: rgb(0,0,0)')
self.penbtn.clicked.connect(self.showColorDlg)
grid.addWidget(self.penbtn, 1, 1)


label = QLabel('펜 굵기')
grid.addWidget(label, 2, 0)

self.slider = QSlider(Qt.Horizontal)
self.slider.setMinimum(3)
self.slider.setMaximum(21)
self.slider.setValue(5)
self.slider.setFocusPolicy(Qt.StrongFocus)
self.slider.setTickPosition(QSlider.TicksBothSides)
self.slider.setTickInterval(1)
self.slider.setSingleStep(1)
grid.addWidget(self.slider)

# 그룹박스4
gb = QGroupBox('Eraser')
left.addWidget(gb)

hbox = QHBoxLayout()
gb.setLayout(hbox)

self.checkbox = QCheckBox('Eraser')
self.checkbox.stateChanged.connect(self.checkClicked)
hbox.addWidget(self.checkbox)

left.addStretch(1)

self.view = CView(self)
right.addWidget(self.view)

formbox.addLayout(left)
formbox.addLayout(right)

formbox.setStretchFactor(left, 0)
formbox.setStretchFactor(right, 1)

self.setGeometry(100, 100, 800, 500)

def checkClicked(self, state):
pass

def createExampleGroup(self):
groupBox = QGroupBox("Slider Example")

slider = QSlider(Qt.Horizontal)
slider.setFocusPolicy(Qt.StrongFocus)
slider.setTickPosition(QSlider.TicksBothSides)
slider.setTickInterval(10)
slider.setSingleStep(1)

vbox = QVBoxLayout()
vbox.addWidget(slider)
vbox.addStretch(1)
groupBox.setLayout(vbox)

return groupBox

def showColorDlg(self):

color = QColorDialog.getColor()

sender = self.sender()

self.pencolor = color
self.penbtn.setStyleSheet('background-color: {}'.format(color.name()))


# QGraphicsView display QGraphicsScene
class CView(QGraphicsView):

def __init__(self, parent):

super().__init__(parent)
self.scene = QGraphicsScene()

self.setScene(self.scene)

self.items = []

self.start = QPointF()
self.end = QPointF()

self.backgroundImage = None
self.graphicsPixmapItem = None

self.setRenderHint(QPainter.HighQualityAntialiasing)

self.open()

def moveEvent(self, e):
rect = QRectF(self.rect())
rect.adjust(0, 0, -2, -2)

self.scene.setSceneRect(rect)

def mousePressEvent(self, e):

if e.button() == Qt.LeftButton:
# 시작점 저장
self.start = e.pos()
self.end = e.pos()

def mouseMoveEvent(self, e):

# e.buttons()는 정수형 값을 리턴, e.button()은 move시 Qt.Nobutton 리턴
if e.buttons() & Qt.LeftButton:

self.end = e.pos()

if self.parent().checkbox.isChecked():
pen = QPen(QColor(255, 255, 255), 10)
path = QPainterPath()
path.moveTo(self.start)
path.lineTo(self.end)
self.scene.addPath(path, pen)
self.start = e.pos()
return None

pen = QPen(self.parent().pencolor, self.parent().slider.value())

# Path 이용
path = QPainterPath()
path.moveTo(self.start)
path.lineTo(self.end)
self.scene.addPath(path, pen)

# 시작점을 다시 기존 끝점으로
self.start = e.pos()

def stretch(self, state):
self._set_image(state == 2)

def open(self):
fileName, _ = QFileDialog.getOpenFileName(self, "Open File", QDir.currentPath(), filter='Images (*.png *.xpm *.jpg *jpeg)')
if fileName:
image = QImage(fileName)
if image.isNull():
QMessageBox.information(self, "Image Viewer",
"Cannot load %s." % fileName)
return

self.backgroundImage = fileName

self._set_image(False)

def _set_image(self, stretch: bool):
tempImg = QPixmap(self.backgroundImage)

if stretch:
tempImg = tempImg.scaled(self.scene.width(), self.scene.height())

if self.graphicsPixmapItem is not None:
self.scene.removeItem(self.graphicsPixmapItem)

self.graphicsPixmapItem = QGraphicsPixmapItem(tempImg)
self.scene.addItem(self.graphicsPixmapItem)


if __name__ == '__main__':
app = QApplication(sys.argv)
w = CWidget()
w.show()
sys.exit(app.exec_())

最佳答案

您可以在您绘制的位置创建另一个透明项目,该项目位于 QGraphicsPixmapItem 上。对于绘画,只需在透明项中的透明 QPixmap 上进行绘制,对于删除,我们使用合成模式 QPainter::CompositionMode_Clear正如我在this answer中指出的那样.

考虑到上述情况,解决方案是:

import sys
from PyQt5 import QtCore, QtGui, QtWidgets


class LayerItem(QtWidgets.QGraphicsRectItem):
DrawState, EraseState = range(2)

def __init__(self, parent=None):
super().__init__(parent)
self.current_state = LayerItem.DrawState
self.setPen(QtGui.QPen(QtCore.Qt.NoPen))

self.m_line_eraser = QtCore.QLineF()
self.m_line_draw = QtCore.QLineF()
self.m_pixmap = QtGui.QPixmap()

def reset(self):
r = self.parentItem().pixmap().rect()
self.setRect(QtCore.QRectF(r))
self.m_pixmap = QtGui.QPixmap(r.size())
self.m_pixmap.fill(QtCore.Qt.transparent)

def paint(self, painter, option, widget=None):
super().paint(painter, option, widget)
painter.save()
painter.drawPixmap(QtCore.QPoint(), self.m_pixmap)
painter.restore()

def mousePressEvent(self, event):
if self.current_state == LayerItem.EraseState:
self._clear(event.pos().toPoint())
elif self.current_state == LayerItem.DrawState:
self.m_line_draw.setP1(event.pos())
self.m_line_draw.setP2(event.pos())
super().mousePressEvent(event)
event.accept()

def mouseMoveEvent(self, event):
if self.current_state == LayerItem.EraseState:
self._clear(event.pos().toPoint())
elif self.current_state == LayerItem.DrawState:
self.m_line_draw.setP2(event.pos())
self._draw_line(
self.m_line_draw, QtGui.QPen(self.pen_color, self.pen_thickness)
)
self.m_line_draw.setP1(event.pos())
super().mouseMoveEvent(event)

def _draw_line(self, line, pen):
painter = QtGui.QPainter(self.m_pixmap)
painter.setPen(pen)
painter.drawLine(line)
painter.end()
self.update()

def _clear(self, pos):
painter = QtGui.QPainter(self.m_pixmap)
r = QtCore.QRect(QtCore.QPoint(), 10 * QtCore.QSize())
r.moveCenter(pos)
painter.setCompositionMode(QtGui.QPainter.CompositionMode_Clear)
painter.eraseRect(r)
painter.end()
self.update()

@property
def pen_thickness(self):
return self._pen_thickness

@pen_thickness.setter
def pen_thickness(self, thickness):
self._pen_thickness = thickness

@property
def pen_color(self):
return self._pen_color

@pen_color.setter
def pen_color(self, color):
self._pen_color = color

@property
def current_state(self):
return self._current_state

@current_state.setter
def current_state(self, state):
self._current_state = state


class GraphicsView(QtWidgets.QGraphicsView):
def __init__(self, parent=None):
super().__init__(parent)
self.setScene(QtWidgets.QGraphicsScene(self))
self.setRenderHint(QtGui.QPainter.HighQualityAntialiasing)
self.setAlignment(QtCore.Qt.AlignCenter)

self.background_item = QtWidgets.QGraphicsPixmapItem()
self.foreground_item = LayerItem(self.background_item)

self.scene().addItem(self.background_item)

def set_image(self, image):
self.scene().setSceneRect(
QtCore.QRectF(QtCore.QPointF(), QtCore.QSizeF(image.size()))
)
self.background_item.setPixmap(image)
self.foreground_item.reset()
self.fitInView(self.background_item, QtCore.Qt.KeepAspectRatio)
self.centerOn(self.background_item)


class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)

menu = self.menuBar().addMenu(self.tr("File"))
open_action = menu.addAction(self.tr("Open image..."))
open_action.triggered.connect(self.open_image)

pen_group = QtWidgets.QGroupBox(self.tr("Pen settings"))
eraser_group = QtWidgets.QGroupBox(self.tr("Eraser"))

self.pen_button = QtWidgets.QPushButton(clicked=self.showColorDlg)
color = QtGui.QColor(0, 0, 0)
self.pen_button.setStyleSheet(
"background-color: {}".format(color.name())
)
self.pen_slider = QtWidgets.QSlider(
QtCore.Qt.Horizontal,
minimum=3,
maximum=21,
value=5,
focusPolicy=QtCore.Qt.StrongFocus,
tickPosition=QtWidgets.QSlider.TicksBothSides,
tickInterval=1,
singleStep=1,
valueChanged=self.onThicknessChanged,
)

self.eraser_checkbox = QtWidgets.QCheckBox(
self.tr("Eraser"), stateChanged=self.onStateChanged
)

self.view = GraphicsView()
self.view.foreground_item.pen_thickness = self.pen_slider.value()
self.view.foreground_item.pen_color = color

# layouts
pen_lay = QtWidgets.QFormLayout(pen_group)
pen_lay.addRow(self.tr("Pen color"), self.pen_button)
pen_lay.addRow(self.tr("Pen thickness"), self.pen_slider)

eraser_lay = QtWidgets.QVBoxLayout(eraser_group)
eraser_lay.addWidget(self.eraser_checkbox)

vlay = QtWidgets.QVBoxLayout()
vlay.addWidget(pen_group)
vlay.addWidget(eraser_group)
vlay.addStretch()

central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)

lay = QtWidgets.QHBoxLayout(central_widget)
lay.addLayout(vlay, stretch=0)
lay.addWidget(self.view, stretch=1)

self.resize(640, 480)

@QtCore.pyqtSlot(int)
def onStateChanged(self, state):
self.view.foreground_item.current_state = (
LayerItem.EraseState
if state == QtCore.Qt.Checked
else LayerItem.DrawState
)

@QtCore.pyqtSlot(int)
def onThicknessChanged(self, value):
self.view.foreground_item.pen_thickness = value

@QtCore.pyqtSlot()
def showColorDlg(self):
color = QtWidgets.QColorDialog.getColor(
self.view.foreground_item.pen_color, self
)
self.view.foreground_item.pen_color = color
self.pen_button.setStyleSheet(
"background-color: {}".format(color.name())
)

def open_image(self):
filename, _ = QtWidgets.QFileDialog.getOpenFileName(
self,
"Open File",
QtCore.QDir.currentPath(),
filter="Images (*.png *.xpm *.jpg *jpeg)",
)
if filename:
pixmap = QtGui.QPixmap(filename)
if pixmap.isNull():
QtWidgets.QMessageBox.information(
self, "Image Viewer", "Cannot load %s." % filename
)
return
self.view.set_image(pixmap)


if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())

关于python - 如何在 QGraphicsView 中制作 2 层?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57110952/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com