如何在 PYQT5 菜单栏中同时显示图标和文本

问题描述

所以,我想将文本和图标都显示菜单栏项。

我已将以下语句用作:

self.helpMenu = menubar1.addMenu(QtGui.QIcon("home.png"),"&TEXT")

但这仅显示图标而不显示文本。 所以需要帮助修复它

解决方法

前提

看来,尽管 Qt 提供了一个 addMenu() 函数来创建一个同时具有图标和文本的菜单,但它得到了完全支持。

关于此事有一个相关且非常古老的 bug report,已被标记为已关闭和“超出范围”。我现在无法对其进行测试,但我认为这是由于原生菜单栏支持所致,该支持主要用于支持该功能的 macOS 和 Linux 发行版。

也就是说,一种解决方法是可能的,并且可以通过 QProxyStyle 完成。

这有点复杂,但鉴于以下原因,它可以无缝运行:

  1. 仅当未使用本机菜单栏功能时才启用它(无论它是否可用或只是禁用);
  2. 它使用“融合”样式或 Windows 上的默认样式;

诀窍是确保代理为 sizeFromContents() 返回正确的大小,包括文本图标(如果两者都存在),并尽可能使用默认实现在 drawControl()drawItemText()(从更多标准样式调用)。

menubar with icon and text

class MenuProxy(QtWidgets.QProxyStyle):
    menuHack = False
    alertShown = False

    def useMenuHack(self,element,opt,widget):
        if (element in (self.CT_MenuBarItem,self.CE_MenuBarItem) and
            isinstance(widget,QtWidgets.QMenuBar) and
            opt.icon and not opt.icon.isNull() and opt.text):
                if not self.alertShown:
                    if widget.isNativeMenuBar():
                        # this will probably not be shown...
                        print('WARNING: menubar items with icons and text not supported for native menu bars')
                    styleName = self.baseStyle().objectName()
                    if not 'windows' in styleName and styleName != 'fusion':
                        print('WARNING: menubar items with icons and text not supported for "{}" style'.format(
                            styleName))
                    self.alertShown = True
                return True
        return False

    def sizeFromContents(self,content,size,widget=None):
        if self.useMenuHack(content,widget):
            # return a valid size that includes both the icon and the text
            alignment = (QtCore.Qt.AlignCenter | QtCore.Qt.TextShowMnemonic |
                QtCore.Qt.TextDontClip | QtCore.Qt.TextSingleLine)
            if not self.proxy().styleHint(self.SH_UnderlineShortcut,widget):
                alignment |= QtCore.Qt.TextHideMnemonic

            width = (opt.fontMetrics.size(alignment,opt.text).width() +
                self.pixelMetric(self.PM_SmallIconSize) +
                self.pixelMetric(self.PM_LayoutLeftMargin) * 2)

            textOpt = QtWidgets.QStyleOptionMenuItem(opt)
            textOpt.icon = QtGui.QIcon()
            height = super().sizeFromContents(content,textOpt,widget).height()

            return QtCore.QSize(width,height)

        return super().sizeFromContents(content,widget)

    def drawControl(self,ctl,qp,widget=None):
        if self.useMenuHack(ctl,widget):
            # create a new option with no icon to draw a menubar item; setting
            # the menuHack allows us to ensure that the icon size is taken into
            # account from the drawItemText function
            textOpt = QtWidgets.QStyleOptionMenuItem(opt)
            textOpt.icon = QtGui.QIcon()
            self.menuHack = True
            self.drawControl(ctl,widget)
            self.menuHack = False

            # compute the rectangle for the icon and call the default 
            # implementation to draw it
            iconExtent = self.pixelMetric(self.PM_SmallIconSize)
            margin = self.pixelMetric(self.PM_LayoutLeftMargin) / 2
            top = opt.rect.y() + (opt.rect.height() - iconExtent) / 2
            iconRect = QtCore.QRect(opt.rect.x() + margin,top,iconExtent,iconExtent)
            pm = opt.icon.pixmap(widget.window().windowHandle(),QtCore.QSize(iconExtent,iconExtent),QtGui.QIcon.Normal if opt.state & self.State_Enabled else QtGui.QIcon.Disabled)
            self.drawItemPixmap(qp,iconRect,QtCore.Qt.AlignCenter,pm)
            return
        super().drawControl(ctl,widget)

    def drawItemText(self,rect,alignment,palette,enabled,text,role=QtGui.QPalette.NoRole):
        if self.menuHack:
            margin = (self.pixelMetric(self.PM_SmallIconSize) + 
                self.pixelMetric(self.PM_LayoutLeftMargin))
            rect = rect.adjusted(margin,0)
        super().drawItemText(qp,role)


class Test(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        menu = self.menuBar().addMenu(QtGui.QIcon.fromTheme('document-new'),'File')
        menu.addAction(QtGui.QIcon.fromTheme('application-exit'),'Quit')
        self.menuBar().addMenu(QtGui.QIcon.fromTheme('edit-cut'),'Edit')


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    app.setStyle(MenuProxy(QtWidgets.QStyleFactory.create('fusion')))
    # or,for windows systems:
    # app.setStyle(MenuProxy())

    test = Test()
    test.show()
    sys.exit(app.exec_())
,

我在 Windows 7 和 PyQt 5.12.2 上有同样的故事,并试图像这样解决它:

import sys
from PyQt5.Qt import *


class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.centralWidget = QLabel("Hello,World")
        self.centralWidget.setAlignment(Qt.AlignCenter)
        self.setCentralWidget(self.centralWidget)

        menuBar = QMenuBar(self)
        self.setMenuBar(menuBar)    
        
        self.helpContentAction = QAction(QIcon("img/readMe.png"),"&Help Content",self)
        self.aboutAction = QAction("&About",self)

#        helpMenu = menuBar.addMenu(QIcon("img/qtlogo.png"),"&Help")
        helpMenu = menuBar.addMenu("            &Help")                   # +++
#                                   ^^^^^^^^^^^^        
        
        helpMenu.addAction(self.helpContentAction)
        helpMenu.addAction(self.aboutAction)
 
qss = """        
QMenuBar {
    background-color: qlineargradient(
        x1:0,y1:0,x2:0,y2:1,stop:0 lightgray,stop:1 darkgray
    );
}
QMenuBar::item {
    background-color: darkgray;      
    padding: 1px 5px 1px -25px;                                           /* +++  */
    background: transparent;
    image: url(img/qtlogo.png);                                           /* +++ * /
}
QMenuBar::item:selected {    
    background-color: lightgray;
}
QMenuBar::item:pressed {
    background: lightgray;
}        
"""


if __name__ == "__main__":
    app = QApplication(sys.argv)
    
#    app.setStyle('Fusion')  
    app.setStyleSheet(qss)                                                # +++
    app.setFont(QFont("Times",10,QFont.Bold))
    
    win = Window()
    win.setWindowTitle("Python Menus")
    win.resize(600,350)
    win.show()
    sys.exit(app.exec_())

enter image description here