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
相关产品推荐
相关产品推荐

