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

QMenu.setTearOffEnabled窗口无法正确适配QWidgetAction显示尺寸

问题现象

QMenu中包含带自定义尺寸QWidget的QWidgetAction时,菜单可正常显示;但将菜单撕下成为独立窗口后,窗口仅适配普通文本动作的尺寸,QWidgetAction对应的QWidget几乎完全被隐藏。

复现代码

#!/usr/bin/env python

from PySide2 import QtCore, QtWidgets


def main():
    app = QtWidgets.QApplication()
    window = QtWidgets.QWidget()
    layout = QtWidgets.QHBoxLayout()
    window.setLayout(layout)
    button = QtWidgets.QPushButton("Push Me")

    menu = QtWidgets.QMenu()
    menu.setTearOffEnabled(True)
    menu.addAction("Ok")

    qwa = QtWidgets.QWidgetAction(menu)
    big_button = QtWidgets.QPushButton("Big Button")
    big_button.setMinimumWidth(400)
    big_button.setMinimumHeight(400)
    qwa.setDefaultWidget(big_button)

    menu.addAction(qwa)
    button.setMenu(menu)
    layout.addWidget(button)
    window.show()
    app.exec_()

main()

截图说明

  • 正常显示菜单:菜单内同时展示"Ok"文本动作和400×400的大按钮,布局完全正常。
  • 撕下菜单后:独立窗口仅适配"Ok"动作的宽度,大按钮几乎被完全隐藏,仅能看到极小部分。
解决方案

问题根源是菜单被撕下为独立窗口时,Qt未正确计算QWidgetAction的尺寸,可通过以下两种方式解决:

方法一:自定义QWidgetAction并重写sizeHint

子类化QWidgetAction,让其返回对应Widget的尺寸提示,确保独立窗口能正确识别:

#!/usr/bin/env python

from PySide2 import QtCore, QtWidgets

class CustomWidgetAction(QtWidgets.QWidgetAction):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._widget = None

    def setDefaultWidget(self, widget):
        self._widget = widget
        super().setDefaultWidget(widget)

    def sizeHint(self):
        if self._widget:
            return self._widget.sizeHint()
        return super().sizeHint()

def main():
    app = QtWidgets.QApplication()
    window = QtWidgets.QWidget()
    layout = QtWidgets.QHBoxLayout()
    window.setLayout(layout)
    button = QtWidgets.QPushButton("Push Me")

    menu = QtWidgets.QMenu()
    menu.setTearOffEnabled(True)
    menu.addAction("Ok")

    qwa = CustomWidgetAction(menu)
    big_button = QtWidgets.QPushButton("Big Button")
    big_button.setMinimumWidth(400)
    big_button.setMinimumHeight(400)
    qwa.setDefaultWidget(big_button)

    menu.addAction(qwa)
    button.setMenu(menu)
    layout.addWidget(button)
    window.show()
    app.exec_()

main()

方法二:监听菜单撕下事件调整窗口尺寸

通过tearOffTriggered信号监听菜单撕下动作,手动计算所有动作的最大尺寸并调整独立窗口大小:

#!/usr/bin/env python

from PySide2 import QtCore, QtWidgets

def adjust_tearoff_window(menu):
    if menu.isTearOffMenuVisible():
        tearoff_window = menu.tearOffMenu()
        max_width = 0
        total_height = 0
        # 遍历所有动作计算所需尺寸
        for action in menu.actions():
            if isinstance(action, QtWidgets.QWidgetAction):
                widget = action.defaultWidget()
                if widget:
                    w = widget.minimumWidth()
                    h = widget.minimumHeight()
                    max_width = max(max_width, w)
                    total_height += h
            else:
                # 计算文本动作的尺寸(含边距)
                text_rect = menu.fontMetrics().boundingRect(action.text())
                item_width = text_rect.width() + 40  # 左右边距
                item_height = text_rect.height() + 10  # 上下边距
                max_width = max(max_width, item_width)
                total_height += item_height
        # 调整窗口大小,加上窗口框架的高度
        frame_height = tearoff_window.frameGeometry().height() - tearoff_window.geometry().height()
        tearoff_window.resize(max_width, total_height + frame_height)

def main():
    app = QtWidgets.QApplication()
    window = QtWidgets.QWidget()
    layout = QtWidgets.QHBoxLayout()
    window.setLayout(layout)
    button = QtWidgets.QPushButton("Push Me")

    menu = QtWidgets.QMenu()
    menu.setTearOffEnabled(True)
    menu.addAction("Ok")

    qwa = QtWidgets.QWidgetAction(menu)
    big_button = QtWidgets.QPushButton("Big Button")
    big_button.setMinimumWidth(400)
    big_button.setMinimumHeight(400)
    qwa.setDefaultWidget(big_button)

    menu.addAction(qwa)
    # 监听菜单撕下事件
    menu.tearOffTriggered.connect(lambda: adjust_tearoff_window(menu))
    button.setMenu(menu)
    layout.addWidget(button)
    window.show()
    app.exec_()

main()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 13:10:07