问题描述
我已将以下语句用作:
self.helpMenu = menubar1.addMenu(QtGui.QIcon("home.png"),"&TEXT")
解决方法
前提
看来,尽管 Qt 提供了一个 addMenu()
函数来创建一个同时具有图标和文本的菜单,但它不得到了完全支持。
关于此事有一个相关且非常古老的 bug report,已被标记为已关闭和“超出范围”。我现在无法对其进行测试,但我认为这是由于原生菜单栏支持所致,该支持主要用于支持该功能的 macOS 和 Linux 发行版。
也就是说,一种解决方法是可能的,并且可以通过 QProxyStyle 完成。
这有点复杂,但鉴于以下原因,它可以无缝运行:
- 仅当未使用本机菜单栏功能时才启用它(无论它是否可用或只是禁用);
- 它使用“融合”样式或 Windows 上的默认样式;
诀窍是确保代理为 sizeFromContents()
返回正确的大小,包括文本和图标(如果两者都存在),并尽可能使用默认实现在 drawControl()
和 drawItemText()
(从更多标准样式调用)。
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_())