首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >PyQt5 QGraphicsView绘图分辨率

PyQt5 QGraphicsView绘图分辨率
EN

Stack Overflow用户
提问于 2022-09-11 00:16:30
回答 1查看 149关注 0票数 3

我是Qt的新手,我正在尝试使用QGraphicsScene和QGraphicsView开发一个油漆应用程序。我发现的唯一绘制方法是在QGraphicsScene上mouseMoveEvent上添加圆圈和线条。它工作得很好,但是有没有一种方法可以像在FabricJS中那样绘制(当添加的项具有与图像相同的分辨率时)?

PyQt绘图:

fabricJS绘图:

我的代码:

代码语言:javascript
运行
复制
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_())

编辑:我最近发现了另一种绘画方法:

代码语言:javascript
运行
复制
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_())
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-09-11 16:51:23

这是因为形状项目总是矢量的,所以没有“分辨率”的概念:无论大小,圆总是一个圆,而不是使用像素概念的光栅图像。

由于用于缩放的平滑转换类似于模糊效果,因此有可能对项目使用QGraphicsBlurEffectblurRadius值为1 (如"1像素“中的值)。

虽然您可以对每个项目设置效果,但出于性能原因,这并不是一个好的选择:您应该将所有这些项分组到一个单独的父项中。Qt提供了可以通过使用QGraphicsGroupItem轻松创建的scene.createItemGroup()类。

代码语言:javascript
运行
复制
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)

考虑一下,当您要导出图像时,您可能希望暂时禁用图形效果。

进一步说明:

  • 同样,出于优化的原因,您应该在绘制连续线时使用QPainterPathItem,并在鼠标释放之前向其路径添加线条,然后在鼠标再次按下时创建一个新项;
  • 在创建椭圆或启动新的行/路径时,您应该进行区分,否则您将始终两者兼备;
  • 由于您可能希望始终使用非十进制值(这是使用原语构造函数addEllipse()时所发生的情况,因为它们总是使用整数值),因此您应该始终围绕场景位置,就像上面所做的那样;或者,只需再次将场景点转换为QPoint和QPointF:
代码语言:javascript
运行
复制
    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()中);然后,如果鼠标移动得非常快,则该项目仍将在场景的某个地方可见:考虑在视图的enterEventleaveEvent中切换其可见性;
  • 如果您不够小心的话,为鼠标按钮和键使用标志可能会很危险;请使用正确的事件属性,这更简单、更清晰:
代码语言:javascript
运行
复制
    if event.buttons() == Qt.LeftButton:
        # left mouse button pressed *during* a mouse move event
    if event.modifiers() == Qt.ControlModifier:
        # Ctrl key pressed
  • 简化缩放功能:
代码语言:javascript
运行
复制
    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)
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73676005

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档