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

PySide6中在QLabel的QImage上绘制ROI矩形的异常问题

QLabel显示图片时,绘制的矩形在窗口背景而非图片上的问题解决

问题描述

基于PySide6创建了AnotherWidget窗口,内部包含QLabel(用于显示QImage)和多个QLineEdit(展示ROI的坐标与尺寸)。目前QLabel能正常显示图片,但绘制的矩形选区出现在窗口背景而非图片上,鼠标事件与绘制事件逻辑看似正常。

原代码片段

AnotherWidget代码

class AnotherWidget(QWidget, Ui_ROIWidget):
    roi_selected = Signal(int,int,int,int)

    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.setWindowTitle("Custom ROI selection")
        self.begin = QPoint()
        self.end = QPoint()

    def paintEvent(self, event):
        super().paintEvent(event)

        qp = QPainter(self)
        br = QBrush(QColor(100, 10, 10, 40))  
        qp.setBrush(br)   
        qp.drawRect(QRect(self.begin, self.end))       

    def mousePressEvent(self, event):
        self.begin = event.pos()
        self.end = event.pos()
        self.update()

    def mouseMoveEvent(self, event):
        self.end = event.pos()
        self.update()

    def mouseReleaseEvent(self, event):
        self.begin = event.pos()
        self.end = event.pos()
        self.rect = QRect(self.start_point, self.end_point).normalized()
        self.update()
        x, y, width, height = self.rect.x(), self.rect.y(), self.rect.width(), self.rect.height()
        self.roi_selected.emit(x, y, width, height)
        self.update()

    @Slot()
    def show_image(self,image):
        self.label.setPixmap(QPixmap.fromImage(image))

线程与主窗口代码

class CameraProccess(QThread):
    #Signal type definition 
    Image=Signal(QImage)
    ImageProcess = Signal(QImage)
    ImageCaptured = Signal(QImage)
    ImageROI = Signal(QImage)
    
    [...]

class mainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
       super().__init__()
        self.setupUi(self)
        self.setWindowTitle("Recorder Inspection Monitor V1.0")
        self.resize(1280,800)

        self.ROIwindow = AnotherWidget()
        self.setROIButton.clicked.connect(self.toggle_roiWindow)
 
    [...]
 
    def toggle_roiWindow(self):
        global setROI

        setROI = True

        if self.ROIwindow.isVisible()==False:
            self.Worker1_Opencv.ImageROI.connect(self.ROIwindow.show_image)
            self.ROIwindow.show()
        
        else:
            self.Worker1_Opencv.ImageROI.connect(self.ROIwindow.show_image)

问题原因

  1. 绘制层级错误:当前在AnotherWidget(父窗口)的paintEvent中绘制矩形,而QLabel是父窗口的子控件,Qt的绘制顺序是父窗口先绘制,子控件覆盖在父窗口之上,导致矩形被QLabel的图片遮挡,看起来像是在窗口背景上。
  2. 坐标不匹配:鼠标事件获取的坐标是相对于父窗口的,而非QLabel内部图片的坐标,即使解决层级问题,选中的矩形也无法对应图片的实际像素位置(尤其是图片在QLabel中缩放显示时)。
  3. 代码bug:mouseReleaseEvent中使用了未定义的self.start_point和self.end_point,应该使用self.begin和self.end。
  4. 重复连接信号:每次切换窗口显示状态都重复连接ImageROI信号,会导致信号触发多次。

解决方案

核心思路是将绘制与鼠标事件逻辑转移到QLabel内部,确保矩形绘制在图片之上,同时处理坐标转换匹配图片实际像素。

步骤1:自定义ROIImageLabel控件

创建继承自QLabel的自定义控件,内置ROI绘制与鼠标事件处理:

from PySide6.QtWidgets import QLabel
from PySide6.QtGui import QPainter, QColor, QBrush, QPixmap, QImage
from PySide6.QtCore import QPoint, QRect, Signal, Slot

class ROIImageLabel(QLabel):
    roi_selected = Signal(int, int, int, int)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.begin = QPoint()
        self.end = QPoint()
        self.setMouseTracking(True)

    def paintEvent(self, event):
        # 先绘制QLabel原有的图片内容
        super().paintEvent(event)
        # 绘制ROI矩形
        if not self.begin.isNull() and not self.end.isNull():
            painter = QPainter(self)
            brush = QBrush(QColor(100, 10, 10, 40))
            painter.setBrush(brush)
            # 归一化矩形,确保左上角坐标小于右下角
            painter.drawRect(QRect(self.begin, self.end).normalized())

    def mousePressEvent(self, event):
        self.begin = event.pos()
        self.end = event.pos()
        self.update()

    def mouseMoveEvent(self, event):
        self.end = event.pos()
        self.update()

    def mouseReleaseEvent(self, event):
        self.end = event.pos()
        rect = QRect(self.begin, self.end).normalized()
        pixmap = self.pixmap()
        if pixmap:
            # 获取图片在QLabel中的实际显示区域(处理对齐、缩放)
            pix_rect = self.pixmapRect()
            # 计算图片与显示区域的缩放比例
            scale_x = pixmap.width() / pix_rect.width()
            scale_y = pixmap.height() / pix_rect.height()
            # 将QLabel坐标转换为图片实际像素坐标
            img_x = int((rect.x() - pix_rect.x()) * scale_x)
            img_y = int((rect.y() - pix_rect.y()) * scale_y)
            img_width = int(rect.width() * scale_x)
            img_height = int(rect.height() * scale_y)
            # 确保坐标在图片范围内,避免越界
            img_x = max(0, img_x)
            img_y = max(0, img_y)
            img_width = min(img_width, pixmap.width() - img_x)
            img_height = min(img_height, pixmap.height() - img_y)
            self.roi_selected.emit(img_x, img_y, img_width, img_height)
        self.update()

    @Slot(QImage)
    def set_image(self, image):
        self.setPixmap(QPixmap.fromImage(image))

步骤2:修改AnotherWidget

将QtDesigner中创建的QLabel提升为ROIImageLabel(QtDesigner操作:选中QLabel → 右键 → 提升为 → 输入类名ROIImageLabel,指定对应的模块路径),然后修改AnotherWidget代码:

class AnotherWidget(QWidget, Ui_ROIWidget):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.setWindowTitle("Custom ROI selection")
        # 复用自定义Label的信号
        self.roi_selected = self.label.roi_selected

    @Slot(QImage)
    def show_image(self, image):
        self.label.set_image(image)

步骤3:修复主窗口的信号连接

避免重复连接信号,提前在初始化时完成连接:

class mainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.setWindowTitle("Recorder Inspection Monitor V1.0")
        self.resize(1280,800)

        self.ROIwindow = AnotherWidget()
        self.setROIButton.clicked.connect(self.toggle_roiWindow)
        # 提前连接信号,避免重复连接
        self.Worker1_Opencv.ImageROI.connect(self.ROIwindow.show_image)
 
    def toggle_roiWindow(self):
        global setROI
        setROI = True
        if not self.ROIwindow.isVisible():
            self.ROIwindow.show()
        else:
            self.ROIwindow.hide()

关键说明

  • 自定义QLabel确保绘制顺序是先图片后矩形,矩形会叠加在图片上方,不会被遮挡。
  • 坐标转换处理了图片在QLabel中的缩放、对齐问题,保证ROI坐标对应图片的实际像素。
  • 修复了原代码中的变量未定义bug,以及重复连接信号的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 20:24:54