PyQt4 QMenu中没有showTearOffMenu()

0 投票
1 回答
25 浏览
提问于 2025-04-14 15:19

我正在使用PyQt4,因为这是开发的要求。我的任务之一是恢复菜单的状态。比如说,当用户关闭并重新打开应用程序时,窗口的状态应该保持不变。在这种情况下,我想恢复之前实例中被分离出来的QMenu状态。为此我费了不少劲,因为关于这个问题的文档非常少。在PyQt5中,我看到QMenu文档里有一个showTearOffMenu()的方法,但在PyQt4中找不到类似的方法。顺便提一下,QMenu的isTearOffEnabled已经设置为True,现在我想通过编程的方式来分离菜单。

这是我写的代码块,但出现了错误:

# restore the menu widget state
self.menuWidgets = self.menubar.findChildren(QtWidgets.QMenu)
totalMenu = [(m.title(), m) for m in self.menuWidgets]
# get the value from setting to restore back the state
tearOffVisibleMenuNames = self.settings.value("tearOffMenuVisible")

for menu in totalMenu:
    name = menu[0]
    widget = menu[1]
    
    # if the menu is teared off in previously instance
    if tearOffVisibleMenuNames and name in tearOffVisibleMenuNames:
        widget.showTearOffMenu() # this is not working

错误信息是:

AttributeError: 'QMenu' object has no attribute 'showTearOffMenu'

我尝试了不同的方法来打开菜单,比如exec_(),但这看起来并不是一个分离菜单,而popup()和show()也没有对窗口产生任何变化。

我知道QMenu中的虚线是用来让菜单分离的项目,但是我尝试打印出那个菜单的所有操作,发现操作列表中没有虚线的操作。我想找到一种方法来访问这个虚线项目,这样我就可以通过编程调用它来分离菜单,但没有成功。

1 个回答

1

我想到的唯一解决办法是模仿QMenu在菜单被“撕下”时的实际反应,也就是在“撕下”区域内,鼠标移动后释放鼠标按钮时的反应。

你需要使用菜单的样式和pixelMetric()函数来获取这个区域的可能坐标,然后在这些坐标上发送两个假装的鼠标事件。

虽然这样做可能并不是在所有情况下都百分之百安全,但应该足够应付大部分情况。

def tearOff(menu):
    if not menu.isTearOffEnabled():
        return

    style = menu.style()
    opt = QStyleOption()
    opt.initFrom(menu)

    menuBorder = menu.contentsMargins().top()
    vmargin = style.pixelMetric(QStyle.PM_MenuVMargin, opt, menu)
    tearHeight = style.pixelMetric(QStyle.PM_MenuTearoffHeight, opt, menu)

    pos = QPoint(menu.width() // 2, menuBorder + vmargin + tearHeight // 2)

    moveEvent = QMouseEvent(QEvent.MouseButtonRelease, 
        pos, menu.mapToGlobal(pos), 
        Qt.LeftButton, Qt.MouseButtons(Qt.LeftButton), Qt.KeyboardModifier(0)
    )
    QApplication.sendEvent(menu, moveEvent)

    releaseEv = QMouseEvent(QEvent.MouseButtonRelease, 
        pos, menu.mapToGlobal(pos), 
        Qt.LeftButton, Qt.MouseButtons(Qt.LeftButton), Qt.KeyboardModifier(0)
    )
    QApplication.sendEvent(menu, releaseEv)

需要注意的是,为了让这个方法正常工作,可能需要等到父窗口已经显示出来后再进行,最好是在主窗口第一次调用showEvent()时,使用QTimer.singleShot()来实现。

撰写回答