我是Qt的新手,我正在尝试使用QGraphicsScene和QGraphicsView开发一个油漆应用程序。我发现的唯一绘制方法是在QGraphicsScene上mouseMoveEvent上添加圆圈和线条。它工作得很好,但是有没有一种方法可以像在FabricJS中那样绘制(当添加的项具有与图像相同的分辨率时)?
PyQt绘图:
fabricJS绘图:
我的代码:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class QDMWorkingAreaScene(QGraphicsScene):
def __init__(self, parent=None):
super().__init__(parent)
self._color_background = QColor("#393939")
self.textItems = []
self.drawingItems = []
self.empty = True
self.mainImage = QGraphicsPixmapItem()
self.mainImage.setTransformationMode(Qt.SmoothTransformation)
self.dirtySpeechBubbles = []
self.setBackgroundBrush(self._color_background)
def setImage(self, pixmap=None):
if pixmap and not pixmap.isNull():
self.empty = False
self.mainImage.setPixmap(pixmap)
else:
self.empty = True
self.mainImage.setPixmap(QPixmap())
self.addItem(self.mainImage)
#self.fitInView()
def hasPhoto(self):
return not self.empty
def drawCircle(self, x, y, brushSize, pen, brush):
self.drawingItems.append(self.addEllipse(x, y, brushSize, brushSize, pen, brush))
print(len(self.drawingItems))
def drawLine(self, start_x, start_y, x, y, pen):
self.drawingItems.append(self.addLine(start_x, start_y, x, y, pen))
print(len(self.drawingItems))
class QDMGraphicsView(QGraphicsView):
def __init__(self, grScene, parent = None):
super().__init__(parent)
self.empty = True
#brush drawing settings
self.drawingMode = True
self.brushSize = 10
self.brushColor = Qt.black
self.lastPoint = QPoint()
self.brush_line_pen = QPen(self.brushColor, self.brushSize, Qt.SolidLine, Qt.RoundCap)
#scene settings
self.grScene = grScene
self.initUI()
self.setScene(self.grScene)
#pan settings
self.setDragMode(QGraphicsView.RubberBandDrag)
self._isPanning = False
self._mousePressed = False
#zoom settings
self.zoomInFactor = 1.25
self.zoomOutFactor = 0.8
self.zoomClamp = False
self.zoom = 10
self.zoomStep = 1
self.zoomRange = [0, 20]
if self.drawingMode:
self.brush = self.grScene.addEllipse(0, 0, self.brushSize, self.brushSize, QPen(Qt.NoPen), self.brushColor)
self.brush.setFlag(QGraphicsItem.ItemIsMovable)
self.brush.setZValue(100)
def initUI(self):
self.setRenderHints(QPainter.Antialiasing | QPainter.HighQualityAntialiasing | QPainter.TextAntialiasing | QPainter.SmoothPixmapTransform)
self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate)
def setMainImage(self, pixmapItem):
self.grScene.setImage(pixmapItem)
def mousePressEvent(self, event):
if self.drawingMode and (event.button() == Qt.LeftButton):
x = self.mapToScene(event.pos()).x()
y = self.mapToScene(event.pos()).y()
self.grScene.drawCircle(x - self.brushSize / 2, y - self.brushSize / 2, self.brushSize, QPen(Qt.NoPen), self.brushColor)
self.lastPoint = self.mapToScene(event.pos())
elif event.button() == Qt.LeftButton:
self._mousePressed = True
if self._isPanning:
self.setCursor(Qt.ClosedHandCursor)
self._dragPos = event.pos()
event.accept()
else:
super(QDMGraphicsView, self).mousePressEvent(event)
def mouseMoveEvent(self, event):
if self.drawingMode:
x = self.mapToScene(event.pos()).x()
y = self.mapToScene(event.pos()).y()
self.brush.setPos(x - self.brushSize / 2, y - self.brushSize / 2)
if(event.buttons() & Qt.LeftButton) & self.drawingMode:
x = self.mapToScene(event.pos()).x()
y = self.mapToScene(event.pos()).y()
self.grScene.drawLine(self.lastPoint.x(), self.lastPoint.y(), x, y, self.brush_line_pen)
self.lastPoint = self.mapToScene(event.pos())
elif self._mousePressed and self._isPanning:
newPos = event.pos()
diff = newPos - self._dragPos
self._dragPos = newPos
self.horizontalScrollBar().setValue(self.horizontalScrollBar().value() - diff.x())
self.verticalScrollBar().setValue(self.verticalScrollBar().value() - diff.y())
event.accept()
else:
super(QDMGraphicsView, self).mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
if event.modifiers() & Qt.ControlModifier:
self.setCursor(Qt.OpenHandCursor)
else:
self._isPanning = False
self.setCursor(Qt.ArrowCursor)
self._mousePressed = False
super(QDMGraphicsView, self).mouseReleaseEvent(event)
def keyPressEvent(self, event):
if event.key() == Qt.Key_Control and not self._mousePressed:
self._isPanning = True
self.setCursor(Qt.OpenHandCursor)
else:
super(QDMGraphicsView, self).keyPressEvent(event)
def keyReleaseEvent(self, event):
if event.key() == Qt.Key_Control:
if not self._mousePressed:
self._isPanning = False
self.setCursor(Qt.ArrowCursor)
elif event.key() == Qt.Key_Delete:
self.deleteSelected()
else:
super(QDMGraphicsView, self).keyPressEvent(event)
def deleteSelected(self):
for item in self.grScene.selectedItems():
self.grScene.removeItem(item)
def getZoomStep(self, mode):
if mode == "+":
if self.zoom + self.zoomStep not in range(self.zoomRange[0], self.zoomRange[1]):
return self.zoom, 1
else:
return self.zoom + self.zoomStep, self.zoomInFactor
elif mode == "-":
if self.zoom - self.zoomStep not in range(self.zoomRange[0], self.zoomRange[1]):
return self.zoom, 1
else:
return self.zoom - self.zoomStep, self.zoomOutFactor
return 10, 1
def wheelEvent(self, event):
if event.angleDelta().y() > 0:
self.zoom, zoomFactor = self.getZoomStep("+")
else:
self.zoom, zoomFactor = self.getZoomStep("-")
self.scale(zoomFactor, zoomFactor)
def fitInView(self, scale=True):
rect = QRectF(self.grScene.mainImage.pixmap().rect())
if not rect.isNull():
self.setSceneRect(rect)
if self.grScene.hasPhoto():
unity = self.transform().mapRect(QRectF(0, 0, 1, 1))
self.scale(1 / unity.width(), 1 / unity.height())
self.zoom = 5
class WorkingArea(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.initUI()
def loadImage(self):
self.view.setMainImage(QPixmap('roi.jpg'))
def initUI(self):
self.layout = QVBoxLayout()
self.layout.setContentsMargins(0, 0, 0, 0)
self.setLayout(self.layout)
self.grScene = QDMWorkingAreaScene()
self.view = QDMGraphicsView(self.grScene, self)
self.layout.addWidget(self.view)
gl = QOpenGLWidget()
gl.setMouseTracking(True)
format = QSurfaceFormat()
format.setSamples(4)
gl.setFormat(format)
self.view.setViewport(gl)
self.setWindowTitle("AutoMangaCleaner")
self.loadImage()
self.show()
#self.showMaximized()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = WorkingArea()
sys.exit(app.exec_())
编辑:我最近发现了另一种绘画方法:
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class Canvas(QGraphicsPixmapItem):
def __init__(self, image=None):
super().__init__()
self.last_pos = QPoint()
def setImage(self, image):
self.pixmap = image
self.pixmap_clone = self.pixmap.copy()
self.last_pos = QPoint()
self.setPixmap(self.pixmap)
def mousePressEvent(self, event):
pos = self.mapToParent(event.pos())
p = QPainter(self.pixmap_clone)
p.setBrush(Qt.black)
p.drawEllipse(pos, 5, 5)
self.last_pos = pos
self.setPixmap(self.pixmap_clone)
def mouseMoveEvent(self, event):
pos = self.mapToScene(event.pos())
if(event.buttons() & Qt.LeftButton):
p = QPainter(self.pixmap_clone)
p.setPen(QPen(Qt.black, 10, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
p.drawLine(self.last_pos, event.pos())
self.last_pos = pos
self.setPixmap(self.pixmap_clone)
class QDMWorkingAreaScene(QGraphicsScene):
def __init__(self, parent=None):
super().__init__(parent)
self.empty = True
self._color_background = QColor("#393939")
self.mainImage = Canvas()
self.mainImage.setTransformationMode(Qt.SmoothTransformation)
self.dirtySpeechBubbles = []
self.setBackgroundBrush(self._color_background)
def mouseMoveEvent(self, event):
super().mouseMoveEvent(event)
def setImage(self, pixmap=None):
if pixmap and not pixmap.isNull():
self.empty = False
self.mainImage.setImage(pixmap)
else:
self.empty = True
self.mainImage.setPixmap(QPixmap())
self.addItem(self.mainImage)
#self.fitInView()
def hasPhoto(self):
return not self.empty
class QDMGraphicsView(QGraphicsView):
def __init__(self, grScene, parent = None):
super().__init__(parent)
self.empty = True
self.photo = QGraphicsPixmapItem()
#text settings
#fonts, color, outline etc.
#brush drawing settings
#self.brush = QGraphicsEllipseItem
self.drawingMode = True
self.is_drawing = True
self.brushSize = 10
self.brushColor = Qt.black
self.lastPoint = QPoint()
self.brush_line_pen = QPen(self.brushColor, self.brushSize, Qt.SolidLine, Qt.RoundCap)
# self.brush = self.grScene.addEllipse(0, 0, self.brushSize, self.brushSize, QPen(Qt.NoPen), self.brushColor)
#scene settings
self.grScene = grScene
self.initUI()
self.setScene(self.grScene)
#pan settings
self.setDragMode(QGraphicsView.RubberBandDrag)
self.setDragMode(QGraphicsView.NoDrag)
self._isPanning = False
self._mousePressed = False
#zoom settings
self.zoomInFactor = 1.25
self.zoomOutFactor = 0.8
self.zoomClamp = False
self.zoom = 10
self.zoomStep = 1
self.zoomRange = [0, 20]
if self.drawingMode:
self.setDragMode(QGraphicsView.NoDrag)
self.brush = self.grScene.addEllipse(0, 0, self.brushSize, self.brushSize, QPen(Qt.NoPen), self.brushColor)
self.brush.setAcceptedMouseButtons(Qt.NoButton)
self.brush.setFlag(QGraphicsItem.ItemIsMovable)
self.brush.setZValue(100)
def initUI(self):
self.setRenderHints(QPainter.Antialiasing | QPainter.HighQualityAntialiasing | QPainter.TextAntialiasing | QPainter.SmoothPixmapTransform)
self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate)
#self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
#self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
def setMainImage(self, pixmapItem):
self.grScene.setImage(pixmapItem)
self.fitInView()
def mousePressEvent(self, event):
#print("view pos:", self.mapToScene(event.pos()))
if self.drawingMode and (event.button() == Qt.LeftButton):
super(QDMGraphicsView, self).mousePressEvent(event)
#self.grScene.mainImage.mousePressEvent(event)
if event.button() == Qt.LeftButton:
self._mousePressed = True
if self._isPanning:
self.setCursor(Qt.ClosedHandCursor)
self._dragPos = event.pos()
event.accept()
else:
super(QDMGraphicsView, self).mousePressEvent(event)
def mouseMoveEvent(self, event):
if self.drawingMode:
x = self.mapToScene(event.pos()).x()
y = self.mapToScene(event.pos()).y()
self.brush.setPos(x - self.brushSize / 2, y - self.brushSize / 2)
if(event.buttons() == Qt.LeftButton) & self.drawingMode:
super(QDMGraphicsView, self).mouseMoveEvent(event)
elif self._mousePressed and self._isPanning:
newPos = event.pos()
diff = newPos - self._dragPos
self._dragPos = newPos
self.horizontalScrollBar().setValue(self.horizontalScrollBar().value() - diff.x())
self.verticalScrollBar().setValue(self.verticalScrollBar().value() - diff.y())
event.accept()
else:
super(QDMGraphicsView, self).mouseMoveEvent(event)
super().mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
if event.modifiers() == Qt.ControlModifier:
self.setCursor(Qt.OpenHandCursor)
else:
self._isPanning = False
self.setCursor(Qt.ArrowCursor)
self._mousePressed = False
super(QDMGraphicsView, self).mouseReleaseEvent(event)
def keyPressEvent(self, event):
if event.key() == Qt.Key_Control and not self._mousePressed:
self.drawingMode = False
self._isPanning = True
self.setCursor(Qt.OpenHandCursor)
else:
super(QDMGraphicsView, self).keyPressEvent(event)
def keyReleaseEvent(self, event):
if event.key() == Qt.Key_Control:
if self.is_drawing:
self.drawingMode = True
if not self._mousePressed:
self._isPanning = False
self.setCursor(Qt.ArrowCursor)
elif event.key() == Qt.Key_Delete:
self.deleteSelected()
else:
super(QDMGraphicsView, self).keyPressEvent(event)
def deleteSelected(self):
for item in self.grScene.selectedItems():
self.grScene.removeItem(item)
def wheelEvent(self, event):
if event.angleDelta().y() > 0:
step = self.zoomStep
fact = self.zoomInFactor
else:
step = -self.zoomStep
fact = self.zoomOutFactor
zoom = max(self.zoomRange[0], min(self.zoom + step, self.zoomRange[1]))
if zoom != self.zoom:
self.zoom = zoom
self.scale(fact, fact)
def fitInView(self, scale=True):
rect = QRectF(self.grScene.mainImage.pixmap.rect())
if not rect.isNull():
self.setSceneRect(rect)
if self.grScene.hasPhoto():
unity = self.transform().mapRect(QRectF(0, 0, 1, 1))
self.scale(1 / unity.width(), 1 / unity.height())
self.zoom = 5
class WorkingArea(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.initUI()
def loadImage(self):
self.view.setMainImage(QPixmap('roi.jpg'))
def initUI(self):
self.setGeometry(0, 0, 800, 800)
self.layout = QVBoxLayout()
self.layout.setContentsMargins(0, 0, 0, 0)
self.setLayout(self.layout)
self.grScene = QDMWorkingAreaScene()
self.view = QDMGraphicsView(self.grScene, self)
self.layout.addWidget(self.view)
gl = QOpenGLWidget()
gl.setMouseTracking(True)
format = QSurfaceFormat()
format.setSamples(4)
gl.setFormat(format)
self.view.setViewport(gl)
self.setWindowTitle("AutoMangaCleaner")
self.loadImage()
self.show()
#self.view.setFocus()
def mouseMoveEvent(self, event):
self.view.mouseMoveEvent()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = WorkingArea()
sys.exit(app.exec_())
发布于 2022-09-11 16:51:23
这是因为形状项目总是矢量的,所以没有“分辨率”的概念:无论大小,圆总是一个圆,而不是使用像素概念的光栅图像。
由于用于缩放的平滑转换类似于模糊效果,因此有可能对项目使用QGraphicsBlurEffect,blurRadius
值为1
(如"1像素“中的值)。
虽然您可以对每个项目设置效果,但出于性能原因,这并不是一个好的选择:您应该将所有这些项分组到一个单独的父项中。Qt提供了可以通过使用QGraphicsGroupItem轻松创建的scene.createItemGroup()
类。
class QDMWorkingAreaScene(QGraphicsScene):
def __init__(self, parent=None):
# ...
self.drawingGroup = self.createItemGroup([])
blur = QGraphicsBlurEffect(blurRadius=1)
self.drawingGroup.setGraphicsEffect(blur)
self.drawingGroup.setZValue(100)
# ...
def drawCircle(self, x, y, brushSize, pen, brush):
item = QGraphicsEllipseItem(
round(x), round(y),
brushSize, brushSize,
self.drawingGroup
)
item.setPen(pen)
item.setBrush(brush)
def drawLine(self, start_x, start_y, x, y, pen):
item = QGraphicsLineItem(
round(start_x), round(start_y),
round(x), round(y),
self.drawingGroup
)
item.setPen(pen)
考虑一下,当您要导出图像时,您可能希望暂时禁用图形效果。
进一步说明:
addEllipse()
时所发生的情况,因为它们总是使用整数值),因此您应该始终围绕场景位置,就像上面所做的那样;或者,只需再次将场景点转换为QPoint和QPointF: scenePos = QPointF(self.mapToScene(event.pos()).toPoint())
self.grScene.drawLine(self.lastPoint, scenePos, self.brush_line_pen)
self.lastPoint = scenePos
self.setSceneRect(self.mainImage.sceneBoundingRect())
中使用setImage()
(而不是在fitInView()
中);然后,如果鼠标移动得非常快,则该项目仍将在场景的某个地方可见:考虑在视图的enterEvent
和leaveEvent
中切换其可见性; if event.buttons() == Qt.LeftButton:
# left mouse button pressed *during* a mouse move event
if event.modifiers() == Qt.ControlModifier:
# Ctrl key pressed
def wheelEvent(self, event):
if event.angleDelta().y() > 0:
step = self.zoomStep
fact = self.zoomInFactor
else:
step = -self.zoomStep
fact = self.zoomOutFactor
zoom = max(self.zoomRange[0], min(self.zoom + step, self.zoomRange[1]))
if zoom != self.zoom:
self.zoom = zoom
self.scale(fact, fact)
https://stackoverflow.com/questions/73676005
复制相似问题