如何在PyQt5中显示我使用QPainter绘制的线的预览

问题描述

我的代码是使用mousepressEvent和mouseReleaseEvent在QImage上画线。它工作正常,但是当我绘制一条动态预览线(即在MouseMoveEvent上)时,我希望它会出现。现在,当我松开鼠标左键并且看不到绘图时,该行才出现。

我希望在移动鼠标时显示并更新该行的预览,并且在释放鼠标左键时仅“固定”。就像MS Paint Line工具一样:https://youtu.be/YIw9ybdoM6o?t=207

这是我的代码(它是从Scribble示例派生的):

from PyQt5.QtCore import QPoint,QRect,QSize,Qt
from PyQt5.QtGui import QImage,QPainter,QPen,QColor,qRgb
from PyQt5.QtWidgets import QApplication,QWidget,QMainWindow
import sys

class DrawingArea(QWidget):
    def __init__(self,parent=None):
        super(DrawingArea,self).__init__(parent)

        self.setAttribute(Qt.WA_StaticContents)
        self.scribbling = False
        self.myPenWidth = 1
        self.myPenColor = QColor('#000000') 
        self.image = QImage()
        self.startPoint = QPoint()

    def mousepressEvent(self,event):
        if event.button() == Qt.LeftButton:
            self.startPoint = event.pos()
            self.scribbling = True

    def mouseReleaseEvent(self,event):
        if event.button() == Qt.LeftButton and self.scribbling:
            self.drawLineto(event.pos())
            self.scribbling = False

    def paintEvent(self,event):
        painter = QPainter(self)
        dirtyRect = event.rect()
        painter.drawImage(dirtyRect,self.image,dirtyRect)

    def resizeEvent(self,event):
        if self.width() > self.image.width() or self.height() > self.image.height():
            newWidth = max(self.width() + 128,self.image.width())
            newHeight = max(self.height() + 128,self.image.height())
            self.resizeImage(self.image,QSize(newWidth,newHeight))
            self.update()

        super(DrawingArea,self).resizeEvent(event)

    def drawLineto(self,endPoint):
        painter = QPainter(self.image)
        painter.setPen(QPen(self.myPenColor,self.myPenWidth,Qt.solidLine,Qt.RoundCap,Qt.RoundJoin))
        painter.drawLine(self.startPoint,endPoint)

        rad = self.myPenWidth / 2 + 2
        self.update(QRect(self.startPoint,endPoint).normalized().adjusted(-rad,-rad,+rad,+rad))

    def resizeImage(self,image,newSize):
        if image.size() == newSize:
            return

        newImage = QImage(newSize,QImage.Format_RGB32)
        newImage.fill(qRgb(255,255,255))
        painter = QPainter(newImage)
        painter.drawImage(QPoint(0,0),image)
        self.image = newImage


class MainWindow(QMainWindow):
    def __init__(self,parent=None):
        QMainWindow.__init__(self,parent)
        self.setCentralWidget(DrawingArea())
        self.show()

def main():
    app = QApplication(sys.argv)
    ex = MainWindow()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

我不知道如何显示正在绘制的线条的预览,并且还没有找到合适的答案。我该怎么做呢?

解决方法

您可以在paintEvent()方法内绘制线条,而不是直接在图像上绘制线条,然后在实际释放鼠标时在图像上绘制线条。

class DrawingArea(QWidget):
    def __init__(self,parent=None):
        super(DrawingArea,self).__init__(parent)

        self.setAttribute(Qt.WA_StaticContents)
        self.scribbling = False
        self.myPenWidth = 1
        self.myPenColor = QColor('#000000') 
        self.image = QImage()
        self.startPoint = self.endPoint = None

    def mousePressEvent(self,event):
        if event.button() == Qt.LeftButton:
            self.startPoint = event.pos()

    def mouseMoveEvent(self,event):
        if self.startPoint:
            self.endPoint = event.pos()
            self.update()

    def mouseReleaseEvent(self,event):
        if self.startPoint and self.endPoint:
            self.updateImage()

    def paintEvent(self,event):
        painter = QPainter(self)
        dirtyRect = event.rect()
        painter.drawImage(dirtyRect,self.image,dirtyRect)
        if self.startPoint and self.endPoint:
            painter.drawLine(self.startPoint,self.endPoint)

    def updateImage(self):
        if self.startPoint and self.endPoint:
            painter = QPainter(self.image)
            painter.setPen(QPen(self.myPenColor,self.myPenWidth,Qt.SolidLine,Qt.RoundCap,Qt.RoundJoin))
            painter.drawLine(self.startPoint,self.endPoint)
            painter.end()
            self.startPoint = self.endPoint = None
            self.update()

请注意,您无需在resize事件内调用update(),因为它会自动调用。

我还删除了不必要的update rect调用,因为在这种情况下它几乎没有用:通常在绘制非常复杂的小部件时(尤其是当许多执行计算以正确绘制所有内容,实际上只有一小部分小部件需要更新)。就您而言,计算实际的更新矩形要比绘制小部件的所有内容花费更多的时间。

,

我认为this page为您的问题提供了一些非常好的解决方案。例如,它显示了如何实现实际上为您提供“绘图板”的自定义类:

class Canvas(QLabel):

    def __init__(self):
        super().__init__()
        pixmap = QtGui.QPixmap(600,300)
        self.setPixmap(pixmap)

        self.last_x,self.last_y = None,None
        self.pen_color = QtGui.QColor('#000000')

    def set_pen_color(self,c):
        self.pen_color = QtGui.QColor(c)

    def mouseMoveEvent(self,e):
        if self.last_x is None:  # First event.
            self.last_x = e.x()
            self.last_y = e.y()
            return  # Ignore the first time.

        painter = QtGui.QPainter(self.pixmap())
        p = painter.pen()
        p.setWidth(1)
        p.setColor(self.pen_color)
        painter.setPen(p)
        painter.drawLine(self.last_x,self.last_y,e.x(),e.y())
        painter.end()
        self.update()

        # Update the origin for next time.
        self.last_x = e.x()
        self.last_y = e.y()

    def mouseReleaseEvent(self,e):
        self.last_x = None
        self.last_y = None

您可以在需要的任何地方使用此Canvas类(或提供的任何名称)。例如在MainWindow中:

class MainWindow(QMainWindow):
    def __init__(self,parent=None):
        QMainWindow.__init__(self,parent)
        self.canvas = Canvas()
        self.canvas.set_pen_color('#fffee5')  # set the colour you want

        self.setCentralWidget(self.canvas)
        self.show()

希望这会有所帮助!祝您编码愉快! :)