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

如何让QSplitter内带WA_TransparentForMouseEvents的组件穿透鼠标事件?

问题描述

我创建了以QScrollArea为核心的QMainWindow,可访问后台布局self.scroll_area_layout和前台布局self.main_layout。当前遇到的问题:

  • 在QSplitter内的widget2已设置WA_TransparentForMouseEvents属性,但鼠标事件被QSplitter拦截,无法与下方scroll_area里的可选中标签交互;
  • 不在QSplitter中的widget3可正常穿透鼠标事件,与下方scroll_area交互正常;
  • 尝试用SignalTransmitterWidget重定向事件,但仅滚轮事件生效,无法实现像widget3那样的完整交互(如选中文本)。
解决方案

核心问题是QSplitter自身会拦截鼠标事件,即便子组件设置了透明属性,QSplitter的区域仍会阻止事件穿透。以下是贴合原有尝试、改动最小的解决方案:

通过坐标转换修正事件转发逻辑,解决之前SignalTransmitterWidget仅滚轮事件生效的问题——原代码未处理坐标转换,导致点击、选中文本这类依赖精确坐标的事件无法正确传递到目标组件。

修改后完整代码

SignalTransmitterWidget类

from PySide6.QtCore import QEvent
from PySide6.QtGui import QMouseEvent, QWheelEvent
from PySide6.QtWidgets import QWidget

class SignalTransmitterWidget(QWidget):
    def __init__(self, target_widget: QWidget, parent=None):
        super().__init__(parent)
        self.target_widget = target_widget  # 目标为scroll_area内部的消息容器
        self.setStyleSheet('background: rgba(0,200,0,200)')
        
    def event(self, event):
        # 仅处理需要坐标的鼠标事件
        event_types = [QEvent.MouseButtonPress, QEvent.MouseButtonRelease, 
                       QEvent.MouseMove, QEvent.MouseButtonDblClick, QEvent.Wheel]
        if event.type() in event_types:
            # 转换事件坐标到目标组件的局部坐标系
            global_pos = self.mapToGlobal(event.pos())
            target_pos = self.target_widget.mapFromGlobal(global_pos)
            
            # 根据事件类型创建带正确坐标的新事件
            if isinstance(event, QMouseEvent):
                new_event = QMouseEvent(
                    event.type(), target_pos, event.globalPos(),
                    event.button(), event.buttons(), event.modifiers()
                )
            elif isinstance(event, QWheelEvent):
                new_event = QWheelEvent(
                    target_pos, global_pos, event.pixelDelta(),
                    event.angleDelta(), event.buttons(), event.modifiers(),
                    event.phase(), event.inverted(), event.source()
                )
            else:
                new_event = event
                
            QApplication.sendEvent(self.target_widget, new_event)
            return True
        return super().event(event)

主窗口代码

from PySide6.QtWidgets import QMainWindow, QApplication, QVBoxLayout, QWidget, QScrollArea, QLabel, QSplitter
from PySide6.QtCore import Qt

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.resize(500, 400)

        self.scroll_area_layout = QVBoxLayout()
        messages_widget = QWidget()
        messages_widget.setLayout(self.scroll_area_layout)

        self.messages_scroll = QScrollArea()
        self.messages_scroll.setWidgetResizable(True)
        self.messages_scroll.setWidget(messages_widget)
        self.setCentralWidget(self.messages_scroll)

        self.main_layout = QVBoxLayout(self.messages_scroll)
        self.main_layout.setSpacing(0)
        
        # 添加可选中标签
        for i in range(20):
            label = QLabel(f"Selectable Text {i+1}")
            label.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse)
            self.scroll_area_layout.addWidget(label)
            
        self.splitter = QSplitter(Qt.Orientation.Vertical)

        widget1 = QLabel("widget1 no interaction")
        widget1.setStyleSheet("background: rgba(0,0,0,100)")
        widget1.setAlignment(Qt.AlignmentFlag.AlignCenter)

        # 用SignalTransmitterWidget包裹widget2,实现事件穿透
        widget2_container = SignalTransmitterWidget(messages_widget)
        widget2_layout = QVBoxLayout(widget2_container)
        widget2 = QLabel("This widget2 must be transparent for mouse events as well as widget3") 
        widget2.setStyleSheet("background: rgba(0,0,0,100)")
        widget2.setAlignment(Qt.AlignmentFlag.AlignCenter)
        widget2_layout.addWidget(widget2)

        widget3 = QLabel("This widget3 is transparent for mouse events, but it must be in splitter")
        widget3.setAlignment(Qt.AlignmentFlag.AlignCenter)
        widget3.setFixedHeight(200)
        widget3.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)

        self.splitter.addWidget(widget1)
        self.splitter.addWidget(widget2_container)  # 将容器添加到splitter

        self.main_layout.addWidget(self.splitter)
        self.main_layout.addWidget(widget3)

if __name__ == "__main__":
    app = QApplication([])
    window = MainWindow()
    window.show()

关键改动说明

  1. 坐标转换:在事件转发前,将当前组件的局部坐标转换为目标组件(scroll_area内部的消息容器)的局部坐标,确保点击、选中文本等事件能定位到正确的标签;
  2. 容器包裹:用SignalTransmitterWidget作为widget2的父容器,替代直接设置WA_TransparentForMouseEvents,绕过QSplitter的事件拦截;
  3. 目标精准定位:将事件转发目标设为scroll_area内部的messages_widget,而非scroll_area的viewport,让事件直接传递到可交互的标签上。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 14:41:03