Framework for dynamic submenus

This commit is contained in:
Keith Edmunds 2025-02-21 15:18:45 +00:00
parent afd3be608c
commit b0f6e4e819
3 changed files with 69 additions and 62 deletions

View File

@ -484,8 +484,8 @@ class Window(QMainWindow):
with open("menu.yaml", "r") as file: with open("menu.yaml", "r") as file:
menu_data = yaml.safe_load(file) menu_data = yaml.safe_load(file)
# Store references for enabling/disabling actions later self.menu_actions = {} # Store reference for enabling/disabling actions
self.menu_actions = {} self.dynamic_submenus = {} # Store submenus for dynamic population
for menu_item in menu_data["menus"]: for menu_item in menu_data["menus"]:
menu = menu_bar.addMenu(menu_item["title"]) menu = menu_bar.addMenu(menu_item["title"])
@ -495,60 +495,68 @@ class Window(QMainWindow):
menu.addSeparator() menu.addSeparator()
continue continue
# Handle external function calls via lambda # Check whether this is a submenu first
if action_item.get("use_lambda"): if action_item.get("submenu"):
handler = lambda: self.active_tab().select_duplicate_rows() # ✅ Keeps lambda logic submenu = QMenu(action_item["text"], self)
else: menu.addMenu(submenu)
handler = getattr(self, action_item["handler"], None)
# Store submenu reference for dynamic population
self.dynamic_submenus[action_item["handler"]] = submenu
submenu.aboutToShow.connect(self.populate_dynamic_submenu)
continue # Skip the rest of the loop (no handler needed)
# Now check for a normal menu action
handler = getattr(self, action_item["handler"], None)
if handler is None: if handler is None:
print(f"Warning: No handler found for {action_item['text']}") print(f"Warning: No handler found for {action_item['text']}")
continue # Skip adding the action if the handler is missing continue
action = self.create_action( action = self.create_action(
action_item["text"], handler, action_item.get("shortcut") action_item["text"], handler, action_item.get("shortcut")
) )
# Store reference to "Clear Selection" so we can enable/disable it
if action_item.get("store_reference"): if action_item.get("store_reference"):
self.menu_actions[action_item["handler"]] = action # Store reference self.menu_actions[action_item["handler"]] = action
menu.addAction(action) menu.addAction(action)
# Handle dynamic submenu
if action_item.get("submenu"):
submenu = QMenu(action_item["text"], self)
action.setMenu(submenu)
menu.addMenu(submenu)
# Store submenu reference for dynamic population
self.dynamic_submenus = getattr(self, "dynamic_submenus", {})
self.dynamic_submenus[action_item["handler"]] = submenu
# Connect submenu aboutToShow signal to population function
submenu.aboutToShow.connect(self.populate_dynamic_submenu)
def populate_dynamic_submenu(self): def populate_dynamic_submenu(self):
"""Dynamically populates the submenu when selected.""" """Dynamically populates submenus when they are selected."""
submenu = self.dynamic_submenus["populate_dynamic_submenu"] submenu = self.sender() # Get the submenu that triggered the event
submenu.clear() # Remove previous items
# Example: Populate submenu with dynamically generated items
items = self.get_dynamic_menu_items() # Assume this method provides a list of items
# Find which submenu it is
for key, stored_submenu in self.dynamic_submenus.items():
if submenu == stored_submenu:
submenu.clear()
# Dynamically call the correct function
items = getattr(self, f"get_{key}_items")()
for item in items: for item in items:
action = QAction(item["text"], self) action = QAction(item["text"], self)
action.triggered.connect(lambda _, i=item["handler"]: getattr(self, i)()) action.triggered.connect(
lambda _, i=item["handler"]: getattr(self, i)()
)
submenu.addAction(action) submenu.addAction(action)
break
def get_dynamic_menu_items(self): def get_new_playlist_dynamic_submenu_items(self):
"""Returns dynamically generated menu items (replace with real logic).""" """Returns dynamically generated menu items for Submenu 1."""
return [ return [
{"text": "Dynamic Option 1", "handler": "dynamic_option_1"}, {"text": "Option A", "handler": "option_a_handler"},
{"text": "Dynamic Option 2", "handler": "dynamic_option_2"}, {"text": "Option B", "handler": "option_b_handler"},
{"text": "Dynamic Option 3", "handler": "dynamic_option_3"},
] ]
def get_query_dynamic_submenu_items(self):
"""Returns dynamically generated menu items for Submenu 2."""
return [
{"text": "Action X", "handler": "action_x_handler"},
{"text": "Action Y", "handler": "action_y_handler"},
]
def select_duplicate_rows(self) -> None:
"""Call playlist to select duplicate rows"""
self.active_tab().select_duplicate_rows()
def about(self) -> None: def about(self) -> None:
"""Get git tag and database name""" """Get git tag and database name"""
@ -838,7 +846,8 @@ class Window(QMainWindow):
log.debug(f"enable_escape({enabled=})") log.debug(f"enable_escape({enabled=})")
self.action_Clear_selection.setEnabled(enabled) if "clear_selection" in self.menu_actions:
self.menu_actions["clear_selection"].setEnabled(enabled)
def end_of_track_actions(self) -> None: def end_of_track_actions(self) -> None:
""" """

View File

@ -364,7 +364,7 @@ class PlaylistTab(QTableView):
Override closeEditor to enable play controls and update display. Override closeEditor to enable play controls and update display.
""" """
self.musicmuster.action_Clear_selection.setEnabled(True) self.musicmuster.enable_escape(True)
super(PlaylistTab, self).closeEditor(editor, hint) super(PlaylistTab, self).closeEditor(editor, hint)

View File

@ -1,11 +1,23 @@
menus: menus:
- title: "&File" - title: "&File"
actions:
- text: "Save as Template"
handler: "save_as_template"
- text: "Manage Templates"
handler: "manage_templates"
- separator: true
- separator: true
- text: "Exit"
handler: "close"
- title: "&Playlist"
actions: actions:
- text: "Open Playlist" - text: "Open Playlist"
handler: "open_playlist" handler: "open_playlist"
shortcut: "Ctrl+O" shortcut: "Ctrl+O"
- text: "New Playlist" - text: "New Playlist"
handler: "new_playlist" handler: "new_playlist_dynamic_submenu"
submenu: true
- text: "Close Playlist" - text: "Close Playlist"
handler: "close_playlist_tab" handler: "close_playlist_tab"
- text: "Rename Playlist" - text: "Rename Playlist"
@ -13,31 +25,18 @@ menus:
- text: "Delete Playlist" - text: "Delete Playlist"
handler: "delete_playlist" handler: "delete_playlist"
- separator: true - separator: true
- text: "Save as Template"
handler: "save_as_template"
- text: "Manage Templates"
handler: "manage_templates"
- separator: true
- text: "Import Files"
handler: "import_files_wrapper"
shortcut: "Ctrl+Shift+I"
- separator: true
- text: "Dynamic Submenu"
handler: "populate_dynamic_submenu"
submenu: true
- separator: true
- text: "Exit"
handler: "close"
- title: "&Playlist"
actions:
- separator: true
- text: "Insert Track" - text: "Insert Track"
handler: "insert_track" handler: "insert_track"
shortcut: "Ctrl+T" shortcut: "Ctrl+T"
- text: "Select Track from Query"
handler: "query_dynamic_submenu"
submenu: true
- text: "Insert Section Header" - text: "Insert Section Header"
handler: "insert_header" handler: "insert_header"
shortcut: "Ctrl+H" shortcut: "Ctrl+H"
- text: "Import Files"
handler: "import_files_wrapper"
shortcut: "Ctrl+Shift+I"
- separator: true - separator: true
- text: "Mark for Moving" - text: "Mark for Moving"
handler: "mark_rows_for_moving" handler: "mark_rows_for_moving"
@ -53,7 +52,6 @@ menus:
- separator: true - separator: true
- text: "Select Duplicate Rows" - text: "Select Duplicate Rows"
handler: "select_duplicate_rows" handler: "select_duplicate_rows"
use_lambda: true
- text: "Move Selected" - text: "Move Selected"
handler: "move_selected" handler: "move_selected"
- text: "Move Unplayed" - text: "Move Unplayed"