I am using PyQt4 as it is the requirement for the development. One of my task is to restore the state of the menu. For example, after the user close and reopen the app, the widget state should remain the same. In this case, I wanted to restore the QMenu state that teared off in the previous instance. It's been a struggle to do so, there are very little document regarding this problem. In PyQt5, I saw there is showTearOffMenu() in QMenu document but I couldnt find such method in PyQt4. For your information, QMenu isTearOffEnabled is already set to True, now I would like to tear off menu programmatically.
Here is the code block that I wrote and it occurs error:
# 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
The error shows:
AttributeError: 'QMenu' object has no attribute 'showTearOffMenu'
I have tried different ways to open menu such as exec_() but it didnt seem like a tear off menu, while popup() and show() did not make any changes on the widget.
I am aware that dash line in QMenu is the item that make the menu teared off, however, I tried print out all actions from that menu, there's no dash line action in the actions list. My thought was to find a way to access the dash line item so that I can call it to tear off menu programmatically but no luck.
The only solution I could think of is to try to imitate what QMenu actually expects when a menu is being teared off, which is to react to a mouse button release (following a mouse movement) within the geometry of the "tear off" area.
You need to get the possible coordinates of that area using the menu style and the
pixelMetric()function, then send two fake mouse events at those coordinates.It may not be 100% safe for any case and situation, but should be enough.
Note that in order to work properly, this should probably be done once the parent window has been already shown, possibly by using a
QTimer.singleShot()the first timeshowEvent()is called on the main window.