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

How to show a preview of the line I'm drawing with QPainter in PyQt5

我的代码使用 mousePressEvent 和 mouseReleaseEvent 在 QImage 上画线。它工作正常,但我希望在绘制所述线时(即在 MouseMoveEvent 上)出现动态预览线。现在,当我松开鼠标左键时,这条线才出现,我看不到我在画什么。

我希望在移动鼠标时显示并更新线条的预览,并且仅在松开鼠标左键时“固定”。与 MS 画线工具完全一样: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()

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

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

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 class(或您给它起的任何名称)。例如在主窗口中:

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()

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

您可以直接在 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()

请注意,您无需在调整大小事件中调用 update(),因为它会自动调用。

我还删除了不必要的更新矩形调用,因为它在这种情况下几乎没用:通常在绘制 非常 复杂的小部件时指定应该发生更新的矩形(特别是当执行大量计算以正确绘制所有内容并且实际上只有一小部分小部件需要更新时)。在您的情况下,计算实际的更新矩形比绘制小部件的所有内容几乎更耗时。