class MenuService:
    def __init__(self, app_window):
        self._window = app_window
        # No idea why, but if we add the child widget when the window is shown, it either doesn't get added or it doesn't get keyboard focus, no idea which of these two actually happens.
        app_window.hide()
        self._key_capturer = QWidget(self._window)
        self._key_capturer.setFocus()
        app_window.show()
        self._menubar = app_window.menuBar()
        self._menu_items_by_name = {}
        self._menus = {}

    def _menu_item_callables(self, source):
        for member_name in source.__class__.__dict__.keys():
            member_obj = getattr(source, member_name)
            if hasattr(member_obj, "item_label"):
                yield member_obj

    def register_menu_commands(self, source):
        for cmd in self._menu_item_callables(source):
            self.register_menu_command(cmd)

    def register_menu_command(self, cmd):
        menu_path = cmd.menu.split("/")
        menu = self._ensure_menu(menu_path)
        label = cmd.item_label
        if cmd.item_shortcut:
            label += "\t%s" % cmd.item_shortcut
        item = QAction(label, self._key_capturer)
        item.setShortcutContext(Qt.WidgetShortcut)
        # We must add a separate action to the menu, otherwise the shortcut is being triggered even when the menubar is focused.
        menu_action = menu.addAction(label)
        self._key_capturer.addAction(item)
        item.triggered.connect(cmd)
        menu_action.triggered.connect(cmd)
        item.setCheckable(cmd.checkable)
        menu_action.setCheckable(cmd.checkable)
        if cmd.item_shortcut:
            seq = QKeySequence(cmd.item_shortcut)
            item.setShortcut(seq)
        if cmd.item_name:
            self._menu_items_by_name[cmd.item_name] = menu_action
        if cmd.checkable:
            item.toggled.connect(
                lambda checked, mi=menu_action: safe_set_checked(mi, checked))
            menu_action.toggled.connect(
                lambda checked, si=item: safe_set_checked(si, checked))

    def _ensure_menu(self, path, parent=None):
        if tuple(path) in self._menus:
            return self._menus[tuple(path)]
        # Find the highest level which still exists.
        parent = None
        must_create = []
        while True:
            try:
                component = path.pop()
                must_create.append(component)
                if tuple(path) in self._menus:
                    parent = self._menus[tuple(path)]
                    break  # Found our child.
            except IndexError:
                break
        if not parent:
            parent = self._menubar
        while True:
            try:
                menu_name = must_create.pop()
                parent = parent.addMenu(menu_name)
                path.append(menu_name)
                self._menus[tuple(path)] = parent
            except IndexError:
                return parent

    def menu_item_with_name(self, name):
        return self._menu_items_by_name[name]

    def ensure_key_capturer_focus(self):
        self._window.activateWindow()
        self._key_capturer.setFocus()