You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyQt5:通过鼠标绘制QRect时窗口显示异常问题

问题:在图像内通过鼠标追踪绘制QRect失效

开发GUI应用时,尝试在图像内通过mouseMoveEvent追踪鼠标绘制单个矩形,但无论直接在窗口绘制还是使用真实坐标,都无法正常显示绘制的矩形。

问题截图:
当前问题

最小可复现代码:

import sys
from PyQt5.QtWidgets import QWidget, QApplication, QGridLayout, QVBoxLayout, QLabel
from PyQt5.QtGui import QPixmap, QPainter, QColor, QPoint, QRect
from PyQt5.QtCore import Qt

class Window(QWidget):
    def __init__(self):
        super().__init__()

        self.setWindowTitle('Python')

        # setting geometry of window
        self.setGeometry(100, 100, 600, 400)

        #Display layouts
        self.zone_plot = QGridLayout()

        self.center_layout = QVBoxLayout()
        
        #Image creation
        path = '/home/lidaoa/PycharmProjects/stars.jpeg' #path to change
        self.image = QPixmap(path)

        self.image = self.image.scaled(700,500, Qt.KeepAspectRatio, Qt.FastTransformation)

        self.lab_im = QLabel()
        self.lab_im.setPixmap(self.image)
        self.lab_im.adjustSize()
        
        #Events to draw on the image
        self.lab_im.mousePressEvent, self.lab_im.mouseMoveEvent, self.lab_im.mouseReleaseEvent = self.press_zone, self.track_zone, self.release_zone

        self.flag = False
        
        #Initialization of points
        self.begin,self.dest = QPoint(),QPoint()

        self.center_layout.addWidget(self.lab_im,alignment=Qt.AlignCenter)

        self.zone_plot.addLayout(self.center_layout,1,1)

        self.setLayout(self.zone_plot)

        self.show()

    def paintEvent(self,event):
        if not self.flag:
            return None

        qpaint = QPainter(self.lab_im.pixmap())

        qpaint.setPen(QColor(200,0,0))

        qpaint.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform,True)
        if not self.begin.isNull() and not self.dest.isNull():
            rect = QRect(self.begin,self.dest).normalized()
            qpaint.drawRect(rect)

        qpaint.end()
        del qpaint

    def press_zone(self,event):
        self.flag = True
        if event.buttons() & Qt.LeftButton:
            self.begin = event.pos()
            self.dest = self.begin

        self.update()

    def track_zone(self, event):
        if event.buttons() & Qt.LeftButton:
            self.dest =  event.pos()
        self.update()

    def release_zone(self,event):
        if event.buttons() & Qt.LeftButton:
            rect = QRect(self.begin,self.dest).normalized()
            qpaint = QPainter(self.lab_im.pixmap())
            qpaint.drawRect(rect)
            self.begin, self.dest = QPoint(),QPoint()

        self.update()
        self.flag = False

# App creation
App = QApplication(sys.argv)

window = Window()

sys.exit(App.exec())
解决方案

你的代码存在几个核心问题,导致绘制的矩形无法正常显示:

  • 直接修改QLabel的Pixmap并同步绘制错误:在paintEvent中直接对self.lab_im.pixmap()进行绘制,这个操作不会触发QLabel的重绘,而且Pixmap是不可变对象,直接绘制不会更新显示。
  • paintEvent的作用对象错误:Window的paintEvent是绘制窗口本身,而非QLabel内的图像,应该为QLabel自定义子类,在子类的paintEvent中完成临时矩形的绘制,避免污染原始图像。
  • 鼠标事件与绘制逻辑耦合混乱:将事件处理直接绑定到QLabel,导致绘制范围和事件坐标的对应关系出错。

修正后的完整代码:

import sys
from PyQt5.QtWidgets import QWidget, QApplication, QGridLayout, QVBoxLayout, QLabel
from PyQt5.QtGui import QPixmap, QPainter, QColor, QPoint, QRect
from PyQt5.QtCore import Qt

class ImageLabel(QLabel):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.begin = QPoint()
        self.dest = QPoint()
        self.drawing = False
        self.original_pixmap = None

    def setPixmap(self, pixmap):
        self.original_pixmap = pixmap.copy()
        super().setPixmap(pixmap)

    def paintEvent(self, event):
        super().paintEvent(event)  # 先绘制原始图像
        if self.drawing and not self.begin.isNull() and not self.dest.isNull():
            painter = QPainter(self)
            painter.setPen(QColor(200, 0, 0))
            painter.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform, True)
            rect = QRect(self.begin, self.dest).normalized()
            painter.drawRect(rect)
            painter.end()

    def mousePressEvent(self, event):
        if event.buttons() & Qt.LeftButton:
            self.drawing = True
            self.begin = event.pos()
            self.dest = self.begin
            self.update()

    def mouseMoveEvent(self, event):
        if self.drawing and event.buttons() & Qt.LeftButton:
            self.dest = event.pos()
            self.update()

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton and self.drawing:
            # 释放鼠标后将矩形永久绘制到原始图像
            painter = QPainter(self.original_pixmap)
            painter.setPen(QColor(200, 0, 0))
            rect = QRect(self.begin, self.dest).normalized()
            painter.drawRect(rect)
            painter.end()
            self.setPixmap(self.original_pixmap)  # 更新显示
            self.drawing = False
            self.begin = QPoint()
            self.dest = QPoint()

class Window(QWidget):
    def __init__(self):
        super().__init__()

        self.setWindowTitle('Python')
        self.setGeometry(100, 100, 600, 400)

        # 布局设置
        self.zone_plot = QGridLayout()
        self.center_layout = QVBoxLayout()
        
        # 图像加载与设置
        path = '/home/lidaoa/PycharmProjects/stars.jpeg'  # 修改为你的图像路径
        self.image = QPixmap(path)
        self.image = self.image.scaled(700, 500, Qt.KeepAspectRatio, Qt.FastTransformation)

        self.lab_im = ImageLabel()
        self.lab_im.setPixmap(self.image)
        self.lab_im.adjustSize()

        self.center_layout.addWidget(self.lab_im, alignment=Qt.AlignCenter)
        self.zone_plot.addLayout(self.center_layout, 1, 1)
        self.setLayout(self.zone_plot)

        self.show()

if __name__ == "__main__":
    App = QApplication(sys.argv)
    window = Window()
    sys.exit(App.exec())

关键修改说明:

  • 自定义ImageLabel子类:将图像显示、鼠标事件处理、绘制逻辑封装到QLabel子类中,确保绘制范围精准对应图像区域。
  • 分离临时绘制与永久绘制:鼠标拖拽时在paintEvent中临时绘制矩形(不修改原始图像),释放鼠标后再将矩形绘制到原始Pixmap中,实现永久保留。
  • 正确触发重绘:每次更新坐标后调用self.update()触发paintEvent,确保临时矩形实时刷新。
  • 保护原始图像:通过保存original_pixmap副本,避免临时绘制操作污染原始图像,防止多次绘制导致图像质量下降。

内容的提问来源于stack exchange,提问作者Lid Al

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 18:57:57