Bug 423341 - Add menu manipulation to Python API
Summary: Add menu manipulation to Python API
Status: RESOLVED NOT A BUG
Alias: None
Product: krita
Classification: Applications
Component: Scripting (show other bugs)
Version: 4.2.9
Platform: Microsoft Windows Microsoft Windows
: NOR wishlist
Target Milestone: ---
Assignee: Krita Bugs
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-06-22 04:53 UTC by Adric Worley
Modified: 2020-07-17 18:10 UTC (History)
2 users (show)

See Also:
Latest Commit:
Version Fixed In:
Sentry Crash Report:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Adric Worley 2020-06-22 04:53:27 UTC
It would be useful to scripters for the Python API to expose methods for manipulating the menuing system. There does not currently appear to be any way to create menus/submenus, limiting scripters' ability to organize registered actions.

The method of adding actions listed in the extension documentation uses a string tag to place the item in a menu. This works only as long as the tags correspond to preexisting menus, and provides little direct control over the presentation and order of the resulting menu.

Additionally, there appears to be some "special sauce" to how the menus are generated and populated that prevents vanilla PyQt methods from being a sufficient workaround. A new menu can be added to the menubar trivially with the following code:

    krita.Krita.instance().windows()[0].qwindow().menuBar().addMenu("My Menu")

but the log viewer shows a warning "setMouseGrabEnabled: Not setting mouse grab for invisible window QWidgetWindow/'QMenuClassWindow'" when the new menu is clicked, and no menu items appear even when the menu exists before action creation and the action is registered with the appropriate placement tags for the hack-added menu. Also, if a new windows is created after hack-adding the menu, it is only visible on the instance where it was originally added; the new window will not have it.
Comment 1 Halla Rempt 2020-06-22 13:38:23 UTC
The way the menus are defined in Krita is pretty old (dating back to the late nineties old): it uses .kxmlgui files to define the structure of menus and toolbars. Theoretically, a plugin could install its own kxmlgui file and that should get merged, but I'm not sure that still works...
Comment 2 Adric Worley 2020-06-22 15:17:29 UTC
Is there an example of how to do that? I found some xmlgui files in the source, but it's not clear to me where/how they get loaded and used so I haven't been able to test whether it works the way you described.

Overall, though, kxmlgui route doesn't sound promising, especially depending on how much control Extensions have over load/init order. Our pipeline framework dynamically populates its available actions based on contextual data baked into the application environment on run, so our extension would need to (re)write the xmlgui at load time before installing it, if that's even feasible.
Comment 3 Scott Petrovic 2020-06-23 02:49:55 UTC
I am not sure if this helps, but adding actions to menus to do something. This seems to work for something very simple.

from PyQt5.QtWidgets import (QWidget, QAction)
import krita

# connection methods
def close_application():
    print("whooaaaa so custom!!!")

#create an action that does stuff
extractAction = QAction("&GET TO THE CHOPPAH!!!")
extractAction.setShortcut("Ctrl+Q")
extractAction.setStatusTip('Leave The App')
extractAction.triggered.connect(close_application)
      

# Create menu off main menu and add a new action to it
main_menu = Krita.instance().activeWindow().qwindow().menuBar()
custom_menu = main_menu.addMenu("Special Actions Menu")
custom_menu.addAction(extractAction) # how to get items/actions in a menu


You are right thought that each new window in Krita is isolated...so the menu isn't on new windows. If you look in the pykrita folder at some of the pre-installed Python scripts, they have a "canvasChanged" method that signals when the document changes. You might be able to do something with that. 

I made a little docker plugin that handles these document changed events if that helps at all for reference... https://invent.kde.org/scottpetrovic/krita-scratchpad-docker/-/blob/master/scratchpad/scratchpad.py#L201
Comment 4 Halla Rempt 2020-06-23 09:33:34 UTC
Weird... I tried something like that yesterday, and it didn't work for me. Maybe because I first created the menu, then the action.
Comment 5 Scott Petrovic 2020-07-15 15:22:40 UTC
Is this still an issue, or did that last code snippet help to do whatever you were trying to do?
Comment 6 Adric Worley 2020-07-17 17:34:02 UTC
I think it should get me far enough along to hack together what I need. I did notice that there are no windows available when `setup` or `addActions` are called in the Extension lifecycle, so I'll likely need to set up a timer to re-check until a window is available before adding the menu(s).
Comment 7 Scott Petrovic 2020-07-17 18:10:23 UTC
Alright. I am going to mark this as closed since this ticket was about manipulating the menu. Outside of this ticket I can look into the Extensions API and what is going on with that. I don't know exactly how that setup() call is working, so there might not be something wrong with it -- or it just isn't clear what it is actually doing.

Adding a signal when the window is created might be a solution that is more general. I can look into adding that.