Working with MenuApp
Working with Class MenuApp.
from ooodev.calc import CalcDoc
from ooodev.utils.kind.menu_lookup_kind import MenuLookupKind
doc = CalcDoc.create_doc(loader=loader, visible=True)
menu = doc.menu[MenuLookupKind.TOOLS] # or .menu[".uno:ToolsMenu"]
itm = menu.items[".uno:AutoComplete"] # or .items[6]
The menu can be accessed via the doc.menu
property.
The doc.menu
is an instance of the Class MenuApp and has access to all the menu items of the current menu bar.
The :py:class`~ooodev.utils.kind.menu_lookup_kind.MenuLookupKind` Enum is for convenience and can be replaced with the command name of the menu.
doc.menu[MenuLookupKind.TOOLS]
is the same as doc.menu[".uno:ToolsMenu"]
.
The menu can be accessed even if the menu is not visible in the LibreOffice Window.
Menu Items
Menu items in this context are either a MenuItem
, MenuItemSub
or MenuItemSep
class instances.
The MenuItemKind`
Enum is used to check the type of menu item.
from ooodev.gui.menu.item import MenuItem
from ooodev.gui.menu.item import MenuItemSep
from ooodev.gui.menu.item import MenuItemSub
from ooodev.gui.menu.item import MenuItemKind
MenuItemSub
MenuItemSub
is a child class of MenuItem
so:
Sub Menu Item
menu = doc.menu[MenuLookupKind.TOOLS]
itm = menu.items['.uno:LanguageMenu']
assert itm.item_kind == MenuItemKind.ITEM_SUBMENU # equals 3
assert itm.item_kind >= MenuItemKind.ITEM # equals 2
assert itm.item_kind != MenuItemKind.SEP # equals 1
assert itm.item_kind > MenuItemKind.SEP
assert isinstance(itm, MenuItem)
assert isinstance(itm, MenuItemSub)
assert not isinstance(itm, MenuItemSep)
MenuItemSub
menu items also contain a sub_menu
property that get access to is sub menu as another instance of the Menu
class.
MenuItem
Menu
Item.
itm = menu.items[".uno:AutoComplete"]
assert itm.item_kind < MenuItemKind.ITEM_SUBMENU
assert itm.item_kind == MenuItemKind.ITEM
assert itm.item_kind != MenuItemKind.SEP
assert itm.item_kind > MenuItemKind.SEP
assert isinstance(itm, MenuItem)
assert not isinstance(itm, MenuItemSub)
assert not isinstance(itm, MenuItemSep)
MenuItemSep
MenuItemSep
represent a separator in a menu.
itm = menu.items[4]
assert itm.item_kind == MenuItemKind.SEP
assert itm.item_kind < MenuItemKind.ITEM
assert itm.item_kind < MenuItemKind.ITEM_SUBMENU
assert isinstance(itm, MenuItemSep)
assert not isinstance(itm, MenuItem)
assert not isinstance(itm, MenuItemSub)
Getting Access to Menus
Accessing a menu is simple when working with a doc.
from ooodev.utils.kind.menu_lookup_kind import MenuLookupKind
from ooodev.calc import CalcDoc
from ooodev.loader import Lo
# ...
loader = Lo.load_office(connector=Lo.ConnectPipe())
doc = CalcDoc.create_doc(loader=loader, visible=True)
# doc.menu contains all the top level menus
tool_menu = doc.menu[MenuLookupKind.TOOLS]
# ...
The MenuLookupKind
is for convenience and in this case returns .uno:ToolsMenu
.
Index Access
The doc.menu[]
index access can take a string or a zero-based index number.
doc.menu[0]
would give access to the first menu, most likely the File
menu.
There is no recursive search in the MenuApp
or MenuItem*
classes. There is index access via menu position and menu command name.
Usually using the name is more practical as it will find the menu even if the user has reorder it in a different place.
The MenuLookupKind
Enum is for convenience and can be replaced with the command name of the menu.
doc.menu[MenuLookupKind.TOOLS]
is the same as doc.menu[".uno:ToolsMenu"]
.
Getting menu items in a menu is basically the same as finding a menu.
Menus has some limits as not all popup menus are actually sub menus.
For instance the menu Insert -> Shapes -> Basic Shapes
corresponds to the following:
>>> itm = (
>>> doc.menu[".uno:InsertMenu"]
>>> .items[".uno:ShapesMenu"]
>>> .sub_menu.items[".uno:BasicShapes"]
>>> )
>>> repl(itm)
'<MenuItem(command=".uno:BasicShapes", kind=MenuItemKind.ITEM)>'
Although there is a popup menu for Insert -> Shapes -> Basic Shapes
it is reported as a MenuItem
and not a MenuItemSub
.
This by design because .uno:BasicShapes
has a popup menu but the popup is not really a submenu.
This is also reflected by the Menu Id of the .uno:BasicShapes
popup items.
The first item in the popup has a menu id of 1
and the second item has an id of 2
and so on.
Alternatively the Basic Shapes
menu can be access in the following way:
basic_shapes = doc.menu[".uno:InsertMenu"][".uno:ShapesMenu"][".uno:BasicShapes"]
However, like the previous method the sub menu is still not available.
>>> basic_shapes.items[".uno:BasicShapes.circle"]
KeyError: "Menu item '.uno:BasicShapes.circle' not found"
In the menubar.xml
file you can also see that .uno:BasicShapes
has no popup menu in the configuration.
<menu menu:id=".uno:ShapesMenu">
<menupopup>
<menu menu:id=".uno:ShapesLineMenu">
<menupopup>
<menuitem menu:id=".uno:Line" />
<menuitem menu:id=".uno:Freeline_Unfilled" />
<menuitem menu:id=".uno:Freeline" />
<menuitem menu:id=".uno:Bezier_Unfilled" />
<menuitem menu:id=".uno:BezierFill" />
<menuitem menu:id=".uno:Polygon_Unfilled" />
<menuitem menu:id=".uno:Polygon_Diagonal_Unfilled" />
<menuitem menu:id=".uno:Polygon_Diagonal" />
</menupopup>
</menu>
<menuitem menu:id=".uno:BasicShapes" />
<menuitem menu:id=".uno:ArrowShapes" />
<menuitem menu:id=".uno:SymbolShapes" />
<menuitem menu:id=".uno:StarShapes" />
<menuitem menu:id=".uno:CalloutShapes" />
<menuitem menu:id=".uno:FlowChartShapes" />
</menupopup>
</menu>
The MenuItem
, MenuItemSub
and MenuItemSep
have a item_kind
property that also can be used to check for the appropriate type before taking action.
from ooodev.gui.menu.item import MenuItemKind
# ...
if itm.item_kind >= MenuItemKind.ITEM:
# `MenuItem, do work
MenuItem.execute() # run the menu command
Add Menu
from ooodev.calc import CalcDoc
from ooodev.utils.kind.menu_lookup_kind import MenuLookupKind
doc = CalcDoc.create_doc(loader=loader, visible=True)
menu = doc.menu[MenuLookupKind.TOOLS] # or .menu[".uno:ToolsMenu"]
itm = menu.items[".uno:AutoComplete"] # or .items[6]
menu_name = ".custom:my.custom_menu"
new_menu = {
"Label": "My Menu",
"CommandURL": menu_name,
"Submenu": [
{
"Label": "Execute macro...",
"CommandURL": "RunMacro",
"ShortCut": "Shift+Ctrl+Alt+E",
},
{
"Label": "Python Hello World",
"CommandURL": {
"library": "HelloWorld",
"name": "HelloWorldPython",
"language": "Python",
"location": "share",
},
},
],
}
Adding a menu is done with the insert()
method.
Only add the menu if it does not exist.
If the menu did exist then this could cause some issues at getting a menu my name or index may return the incorrect instance if the menu was added twice with the same name.
The save=True
option means the changes will be persisted.
If you only wanted the menu to be available for the current instance then save=False
could be used and the menu would not be persisted.
if not menu_name in menu:
# only add the menu if it does not already exist
menu.insert(new_menu, after=itm.command, save=True)
Remove Menu
The remove()
method is used to remove a submenu from a menu.
The save=True
option means the changes will be persisted.
menu_name = ".custom:my.custom_menu" # or can just be "my.custom_menu"
if menu_name in menu:
menu.remove(menu_name, save=True)
Execute Menu Item
Menu commands are mostly dispatch calls or a URL to run a macro. MenuItem
and MenuItemSub
have an execute method that will call call the dispatch or run the macro.
from ooodev.gui.menu.item import MenuItemKind
# ...
menu = doc.menu[MenuLookupKind.TOOLS]
itm = menu.items[".uno:AutoComplete"]
if itm.item_kind >= MenuItemKind.ITEM:
MenuItem.execute() # run the menu command