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

PySide6 QMenuBar的QMenu首次点击下拉菜单显示过短问题

问题描述

在QMainWindow挂载的QMenuBar中添加多个QMenu及对应QAction后,程序首次启动点击任意菜单时,弹出的下拉菜单高度过短,无法完整展示所有菜单项;第二次点击打开同一菜单时显示恢复正常,后续使用过程中菜单显示始终符合预期。重启程序后,首次点击菜单时会再次复现下拉菜单过短的问题。

  • 首次点击效果:
    首次点击菜单效果
  • 后续正常点击效果:
    后续点击菜单效果

该问题在File、Edit、Record、Help所有菜单上均会复现,测试环境为Windows 11,暂未在其他平台验证。

核心代码

QMainWindow实现类

class Win(QMainWindow):
    from PySide6.QtGui import QCloseEvent
    gui = None
    def __init__(self, gui):
        QMainWindow.__init__(self)
        self.gui = gui
        self.setWindowTitle('Sermon Prep Database')
        self.setStyleSheet('background-color: white')
        self.resize(1000, 800)

    def closeEvent(self, event:QCloseEvent) -> None:
        event.ignore()
        self.gui.doExit()

    def keyPressEvent(self, event:QKeyEvent):
        if event.modifiers() & Qt.ControlModifier and event.key() == Qt.Key_B:
            self.gui.setBold()
        # 其余快捷键处理逻辑省略
        event.accept()

菜单栏构建逻辑

def buildMenuBar(self):
    menubar = self.win.menuBar() # win为Win类实例
    menubar.setStyleSheet('QMenuBar::item:selected { background: WhiteSmoke; }')

    filemenu = menubar.addMenu('File')
    filemenu.setStyleSheet('QMenu::item:selected { background: WhiteSmoke; color: black}')

    saveaction = filemenu.addAction('Save (Ctrl-S)')
    saveaction.triggered.connect(self.spd.saveRec)

    printaction = filemenu.addAction('Print (Ctrl-P)')
    printaction.setStatusTip('Print the current record')
    printaction.triggered.connect(self.printRec)

    backupaction = filemenu.addAction('Create Backup')
    backupaction.setStatusTip('Manually save a backup of your database')
    backupaction.triggered.connect(self.dobackup)

    filemenu.addSeparator()

    exitaction = filemenu.addAction('Exit (Ctrl-Q)')
    exitaction.setStatusTip('Quit the program')
    exitaction.triggered.connect(self.doExit)
    # 其余菜单项逻辑省略
最小复现代码
import sys
from PySide6.QtWidgets import QApplication, QBoxLayout, QWidget, QMainWindow

class SermonPrepDatabase:
    gui = None

    def __init__(self):
        self.startup()

    def startup(self):
        self.app = QApplication(sys.argv)
        self.gui = GUI(self)
        self.gui.initComponents()
        self.gui.win.show()

        sys.exit(self.app.exec())

class GUI():
    accentColor = '#202050'
    backgroundColor = '#f0f0ff'
    spd = None
    win = None

    def __init__(self, spd):
        self.spd = spd

    def initComponents(self):
        self.win = Win(self)

        self.layout = QBoxLayout(QBoxLayout.TopToBottom)
        self.mainwidget = QWidget()
        self.mainwidget.setStyleSheet('background-color: ' + self.backgroundColor + ';')
        self.win.setCentralWidget(self.mainwidget)
        self.mainwidget.setLayout(self.layout)

        self.buildMenuBar()

    def buildMenuBar(self):
        menubar = self.win.menuBar()
        menubar.setStyleSheet('QMenuBar::item:selected { background: WhiteSmoke; }')

        filemenu = menubar.addMenu('File')
        filemenu.setStyleSheet('QMenu::item:selected { background: WhiteSmoke; color: black; }')

        saveaction = filemenu.addAction('Save (Ctrl-S)')
        saveaction.triggered.connect(self.afunction)

        printaction = filemenu.addAction('Print (Ctrl-P)')
        printaction.setStatusTip('Print the current record')
        printaction.triggered.connect(self.afunction)

        backupaction = filemenu.addAction('Create Backup')
        backupaction.setStatusTip('Manually save a backup of your database')
        backupaction.triggered.connect(self.afunction)

        filemenu.addSeparator()

        exitaction = filemenu.addAction('Exit (Ctrl-Q)')
        exitaction.setStatusTip('Quit the program')
        exitaction.triggered.connect(self.afunction)

        editmenu = menubar.addMenu('Edit')
        editmenu.setStyleSheet('QMenu::item:selected { background: WhiteSmoke; color: black}')

        cutaction = editmenu.addAction('Cut (Ctrl-X)')
        cutaction.triggered.connect(self.afunction)

        copyaction = editmenu.addAction('Copy (Ctrl-C)')
        copyaction.triggered.connect(self.afunction)

        pasteaction = editmenu.addAction('Paste (Ctrl-V)')
        pasteaction.triggered.connect(self.afunction)

        configmenu = editmenu.addMenu('Configure')

        bgColorAction = configmenu.addAction('Change Accent Color')
        bgColorAction.setStatusTip('Choose a different color for accents and borders')
        bgColorAction.triggered.connect(self.afunction)

        fgColorAction = configmenu.addAction('Change Background Color')
        fgColorAction.setStatusTip('Choose a different color for the background')
        fgColorAction.triggered.connect(self.afunction)

        renameAction = configmenu.addAction('Rename Labels')
        renameAction.setStatusTip('Rename the labels in this program')
        renameAction.triggered.connect(self.afunction)

        fontaction = configmenu.addAction('Change Font')
        fontaction.setStatusTip(('Change the font and font size used in the program'))
        fontaction.triggered.connect(self.afunction)

        recordmenu = menubar.addMenu('Record')
        recordmenu.setStyleSheet('QMenu::item:selected { background: WhiteSmoke; color: black}')

        firstrecaction = recordmenu.addAction('Jump to First Record')
        firstrecaction.triggered.connect(self.afunction)

        prevrecaction = recordmenu.addAction('Go to Previous Record')
        prevrecaction.triggered.connect(self.afunction)

        nextrecaction = recordmenu.addAction('Go to Next Record')
        nextrecaction.triggered.connect(self.afunction)

        lastrecaction = recordmenu.addAction('Jump to Last Record')
        lastrecaction.triggered.connect(self.afunction)

        recordmenu.addSeparator()

        newrecaction = recordmenu.addAction('Create New Record')
        newrecaction.triggered.connect(self.afunction)

        delrecaction = recordmenu.addAction('Delete Current Record')
        delrecaction.triggered.connect(self.afunction)

    def afunction(self):
        pass

class Win(QMainWindow):
    from PySide6.QtGui import QCloseEvent
    gui = None
    def __init__(self, gui):
        QMainWindow.__init__(self)
        self.gui = gui
        self.setWindowTitle('Sermon Prep Database')
        self.setStyleSheet('background-color: white')
        self.move(50, 50)
        self.resize(1000, 800)

    def closeEvent(self, event:QCloseEvent) -> None:
        event.ignore()
        self.doExit()

    def doExit(self):
        sys.exit(0)

SermonPrepDatabase()
问题原因

这是PySide6对应Qt版本在Windows 11平台的原生样式渲染bug:菜单首次弹出前,Qt未完成嵌套子菜单的尺寸预计算,错误使用了不包含子菜单项的高度值渲染下拉框;第一次弹出后所有菜单项完成布局缓存,后续点击即可正常显示。

修复方案

可任选以下一种方案解决:

  • 全局使用Fusion样式,绕过Windows原生菜单渲染逻辑,在QApplication初始化后添加代码:
    self.app.setStyle('Fusion')
    
  • 保留原生样式的前提下,在所有菜单构建完成后强制触发尺寸预计算,在buildMenuBar函数末尾添加:
    for menu in menubar.findChildren(QMenu):
        menu.adjustSize()
    
  • 不要给每个QMenu单独设置样式表,将QMenu相关的样式规则统一放到QApplication或QMainWindow的全局样式中,避免单独设置样式引发的布局计算异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 01:09:30