Basically I am using Qt Menu by adding actions with Shortcuts. They look fine when the menu shows up.
But I want to add more spacing between the action item text and its shortcut. Is this possible? I couldn't find any function to do this.
setMinimumWidth works but I need add on top of the dynamic width computed by Qt.
Due to the complex way QMenu computes its size, using setMinimumWidth()
is not a reliable option: once that size is set, there's no way to unset it, and the sizeHint()
will always use that minimum.
There are two possible workarounds for that, though.
This is a bit hacky and dirty, and might not work well in all situations. The trick is to force layouting again by adding a "fake" action that will be instantly removed.
You can do this by connecting specific functions to the aboutToShow
and aboutToHide
signals:
def resizeMenu(menu):
for action in menu.actions():
if action.shortcut():
menu.setMinimumWidth(menu.sizeHint().width() + 50)
menu.removeAction(menu.addAction(''))
break
def restoreMenu(menu):
menu.setMinimumWidth(0)
menu.removeAction(menu.addAction(''))
menu.aboutToShow.connect(lambda: resizeMenu(menu))
menu.aboutToHide.connect(lambda: restoreMenu(menu))
You could also install an event filter on the application so that it will work for all menus:
class MenuWatcher(QtCore.QObject):
def eventFilter(self, obj, event):
if isinstance(obj, QtWidgets.QMenu):
if event.type() == event.Show:
for action in obj.actions():
if action.shortcut():
menu.setMinimumWidth(menu.sizeHint().width() + 50)
menu.removeAction(menu.addAction(''))
break
elif event.type() == event.Hide:
if menu.minimumWidth():
menu.setMinimumWidth(0)
menu.removeAction(menu.addAction(''))
return super().eventFilter(obj, event)
# ...
watcher = MenuWatcher()
app.installEventFilter(watcher)
By overriding the sizeFromContents()
of a QProxyStyle we can check whether the option is a CT_MenuItem
, and if it has a tabWidth
(indicating that at least a previous action has a shortcut) or a \t
in its text
(meaning that it probably has a shortcut), we can add a specific width before returning the size provided by the default implementation:
class ProxyStyle(QtWidgets.QProxyStyle):
def sizeFromContents(self, ct, opt, size, widget=None):
size = super().sizeFromContents(ct, opt, size, widget)
if ct == self.CT_MenuItem and (opt.tabWidth or '\t' in opt.text):
size.setWidth(size.width() + 50)
return size
# ...
app.setStyle(ProxyStyle())