【问题标题】:How to show a preview of the line I'm drawing with QPainter in PyQt5如何在 PyQt5 中显示我用 QPainter 绘制的线条的预览
【发布时间】:2021-01-25 22:45:16
【问题描述】:

我的代码是使用 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()

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

【问题讨论】:

    标签: python pyqt pyqt5 qpainter


    【解决方案1】:

    我认为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 类(或者你想给它的任何名字)。例如在主窗口中:

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

    希望这会有所帮助!快乐编码! :)

    【讨论】:

    • 感谢 Andris 的回答,我已经看到了这个网站。如果我没有让自己足够清楚,我很抱歉,但我不是在寻找一种在画布上“涂鸦”的方法,而是显示我正在使用 QPainter 的 drawLine 函数绘制的直线的预览。它看起来像 MS 画线工具:link
    • @MrKartofel 好的,抱歉,我可能误解了您的问题。感谢您的澄清!
    【解决方案2】:

    您可以在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(),因为它会自动调用。

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

    【讨论】:

    • 感谢您的帮助,它完美运行,并且对于代码中不必要的位的精确度,它真的很有帮助。
    猜你喜欢
    • 2019-09-09
    • 2018-10-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多