"""
Action Manager Menu and Toolbar Example
Demonstrates the menu and toolbar capabilities of ActionManager including:
- Creating submenus within submenus (multiple levels)
- Adding actions to nested submenus
- Creating and managing multiple toolbars
- Adding actions to both menus and toolbars
- Using icons for both actions and submenus
- Shared actions across menus and toolbars
"""
import sys
from qtpy import QtWidgets
from pymodaq_gui.managers.action_manager import ActionManager
[docs]
class ActionManagerExample(QtWidgets.QMainWindow, ActionManager):
"""Example application demonstrating nested menus and multiple toolbars with ActionManager"""
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
ActionManager.__init__(self, menu=self.menuBar())
self.setWindowTitle("ActionManager Menu and Toolbar Example")
self.resize(1000, 700)
# Create central widget
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
layout = QtWidgets.QVBoxLayout(central_widget)
# Add info label
info_label = QtWidgets.QLabel(
"<h2>ActionManager Menu and Toolbar Example</h2>"
"<p>This example demonstrates ActionManager capabilities:</p>"
"<ul>"
"<li><b>Multiple menu levels</b> - Submenus within submenus</li>"
"<li><b>Multiple toolbars</b> - File, Edit, View, and Tools toolbars</li>"
"<li><b>Icons</b> - Both menus and actions can have icons</li>"
"<li><b>Flexible organization</b> - Group related actions logically</li>"
"<li><b>Shared submenus</b> - 'Recent Files' appears in both File and Edit menus</li>"
"<li><b>Shared actions</b> - Actions appear in multiple menus AND toolbars</li>"
"<li><b>Toolbar management</b> - Create, retrieve, and populate toolbars dynamically</li>"
"</ul>"
"<p>Try clicking any menu item or toolbar button to see action details!</p>"
)
info_label.setWordWrap(True)
layout.addWidget(info_label)
# Status display
self.status_display = QtWidgets.QTextEdit()
self.status_display.setReadOnly(True)
self.status_display.setMaximumHeight(300)
layout.addWidget(QtWidgets.QLabel("<b>Action Log:</b>"))
layout.addWidget(self.status_display)
# Setup all menus and actions
self.setup_actions()
self.log_message("Application started. Click any menu item or toolbar button to see action details!")
[docs]
def setup_actions(self):
"""Create nested menu structure with actions and multiple toolbars"""
# ========== Create Multiple Toolbars ==========
# Create toolbars first so they can be referenced when adding actions
file_toolbar = self.add_toolbar('file_toolbar', 'File Toolbar', parent=self)
self.addToolBar(file_toolbar)
edit_toolbar = self.add_toolbar('edit_toolbar', 'Edit Toolbar', parent=self)
self.addToolBar(edit_toolbar)
view_toolbar = self.add_toolbar('view_toolbar', 'View Toolbar', parent=self)
self.addToolBar(view_toolbar)
tools_toolbar = self.add_toolbar('tools_toolbar', 'Tools Toolbar', parent=self)
self.addToolBar(tools_toolbar)
# ========== File Menu ==========
file_menu = self.add_menu('file', 'File', icon_name='Folder')
# Add simple actions to File menu AND File toolbar
self.add_action('new', 'New', icon_name='NewFile',
menu='file', toolbar=file_toolbar, shortcut='Ctrl+N', tip='Create new file')
self.add_action('open', 'Open...', icon_name='Open',
menu='file', toolbar=file_toolbar, shortcut='Ctrl+O', tip='Open existing file')
self.add_action('save', 'Save', icon_name='SaveAs',
menu='file', toolbar=file_toolbar, shortcut='Ctrl+S', tip='Save current file')
# Create "Recent Files" menu within File menu
recent_menu = self.add_menu('recent', 'Recent Files',
menu=file_menu, icon_name='Folder')
# Add recent file actions
for i in range(1, 4):
self.add_action(f'recent_{i}', f'Project_{i}.py',
menu=recent_menu)
# Add separator to file toolbar
file_toolbar.addSeparator()
# Create "Export" menu within File menu
export_menu = self.add_menu('export', 'Export',
menu=file_menu, icon_name='SaveAs')
# Add export format actions (menu only, not in toolbar)
self.add_action('export_pdf', 'Export as PDF', menu=export_menu, auto_toolbar=False)
self.add_action('export_png', 'Export as PNG', menu=export_menu, auto_toolbar=False)
self.add_action('export_svg', 'Export as SVG', menu=export_menu, auto_toolbar=False)
# ========== Edit Menu ==========
edit_menu = self.add_menu('edit', 'Edit')
# Add edit actions to both menu and edit toolbar
self.add_action('undo', 'Undo', icon_name='go-previous', menu='edit',
toolbar=edit_toolbar, shortcut='Ctrl+Z', tip='Undo last action')
self.add_action('redo', 'Redo', icon_name='go-next', menu='edit',
toolbar=edit_toolbar, shortcut='Ctrl+Y', tip='Redo last action')
edit_toolbar.addSeparator()
self.add_action('cut', 'Cut', menu='edit', toolbar=edit_toolbar,
shortcut='Ctrl+X', tip='Cut selection')
self.add_action('copy', 'Copy', menu='edit', toolbar=edit_toolbar,
shortcut='Ctrl+C', tip='Copy selection')
self.add_action('paste', 'Paste', menu='edit', toolbar=edit_toolbar,
shortcut='Ctrl+V', tip='Paste from clipboard')
# ========== View Menu with Deep Nesting ==========
view_menu = self.add_menu('view', 'View')
# Level 1: Panels menu
panels_menu = self.add_menu('panels', 'Panels', menu=view_menu)
# Level 2: Left Panel menu
left_panel_menu = self.add_menu('left_panel', 'Left Panel',
menu=panels_menu)
self.add_action('left_files', 'File Explorer', icon_name='Folder',
menu=left_panel_menu, toolbar=view_toolbar,
checkable=True, checked=True, tip='Toggle file explorer panel')
self.add_action('left_search', 'Search',
menu=left_panel_menu, checkable=True, tip='Toggle search panel')
self.add_action('left_git', 'Git',
menu=left_panel_menu, checkable=True, tip='Toggle git panel')
# Level 2: Right Panel menu
right_panel_menu = self.add_menu('right_panel', 'Right Panel',
menu=panels_menu)
self.add_action('right_outline', 'Outline',
menu=right_panel_menu, checkable=True)
self.add_action('right_terminal', 'Terminal',
menu=right_panel_menu, checkable=True, checked=True)
# Level 2: Bottom Panel menu
bottom_panel_menu = self.add_menu('bottom_panel', 'Bottom Panel',
menu=panels_menu)
self.add_action('bottom_console', 'Console',
menu=bottom_panel_menu, checkable=True, checked=True)
self.add_action('bottom_problems', 'Problems',
menu=bottom_panel_menu, checkable=True)
self.add_action('bottom_output', 'Output',
menu=bottom_panel_menu, checkable=True)
# Add separator to view toolbar
view_toolbar.addSeparator()
# Add appearance menu to View
appearance_menu = self.add_menu('appearance', 'Appearance',
menu=view_menu)
# Theme menu (3 levels deep!)
theme_menu = self.add_menu('theme', 'Theme', menu=appearance_menu)
self.add_action('theme_light', 'Light', menu=theme_menu, tip='Switch to light theme')
self.add_action('theme_dark', 'Dark', menu=theme_menu, tip='Switch to dark theme')
self.add_action('theme_auto', 'Auto', menu=theme_menu, toolbar=view_toolbar,
tip='Auto-detect theme based on system')
# Font size menu (3 levels deep!)
font_menu = self.add_menu('font', 'Font Size', menu=appearance_menu)
self.add_action('font_small', 'Small', menu=font_menu)
self.add_action('font_medium', 'Medium', menu=font_menu)
self.add_action('font_large', 'Large', menu=font_menu)
# ========== Tools Menu ==========
tools_menu = self.add_menu('tools', 'Tools')
# Add tools with nested settings
self.add_action('tool_format', 'Format Document', icon_name='Params',
menu='tools', toolbar=tools_toolbar, tip='Format current document')
settings_menu = self.add_menu('settings', 'Settings',
menu=tools_menu, icon_name='Params')
# General settings
general_settings = self.add_menu('general_settings',
'General', menu=settings_menu)
self.add_action('auto_save', 'Auto Save',
menu=general_settings, checkable=True, checked=True)
self.add_action('show_tooltips', 'Show Tooltips',
menu=general_settings, checkable=True, checked=True)
# Advanced settings
advanced_settings = self.add_menu('advanced_settings',
'Advanced', menu=settings_menu)
self.add_action('debug_mode', 'Debug Mode',
menu=advanced_settings, checkable=True)
self.add_action('verbose_logging', 'Verbose Logging',
menu=advanced_settings, checkable=True)
# ========== Help Menu ==========
help_menu = self.add_menu('help', 'Help')
self.add_action('docs', 'Documentation', menu='help')
self.add_action('about', 'About', menu='help')
# ========== Demonstration of Shared Menus ==========
# You can add the same menu to multiple parent menus!
# Let's add the "Recent Files" menu to the Edit menu as well
edit_menu_obj = self.get_menu('edit')
recent_menu_obj = self.get_menu('recent')
edit_menu_obj.addMenu(recent_menu_obj)
# Now "Recent Files" appears in both File and Edit menus,
# but it's the same menu instance - changes appear everywhere!
# ========== Demonstration of Shared Actions ==========
# The 'save' action can be added to multiple menus/toolbars
tools_menu_obj = self.get_menu('tools')
self.affect_to('save', tools_menu_obj)
# Now "Save" appears in File and Tools menus
# ========== Demonstration of Toolbar Retrieval and Management ==========
# You can retrieve toolbars by name and add actions to them later
retrieved_file_toolbar = self.get_toolbar('file_toolbar')
# Let's add the 'new' action to the tools toolbar as well
retrieved_tools_toolbar = self.get_toolbar('tools_toolbar')
tools_toolbar.addSeparator()
self.affect_to('new', retrieved_tools_toolbar)
# Now "New" appears in the File toolbar AND the Tools toolbar
# Add a widget to the tools toolbar to demonstrate widget support
self.add_widget('search_box', 'QLineEdit', tip='Search in document',
toolbar=tools_toolbar, visible=True)
self.get_action('search_box').setPlaceholderText('Search...')
self.get_action('search_box').setMaximumWidth(200)
# Log toolbar information
self.log_message(f"<b>Created {len(self.toolbars)} toolbars:</b> {', '.join(self.toolbars_names)}")
# Connect all actions to the same handler that shows the info
for action_name in self.actions_names:
if action_name != 'search_box': # Skip the widget
self.connect_action(action_name, lambda checked=False, name=action_name:
self.on_action_triggered(name))
[docs]
def on_action_triggered(self, action_name):
"""Handle action triggered - display action info"""
action = self.get_action(action_name)
# Build status message
message = f"<b>Action:</b> {action.text()}"
message += f"<br><b>Internal Name:</b> {action_name}"
# Find which toolbars contain this action
toolbars_with_action = []
for toolbar_name in self.toolbars_names:
toolbar = self.get_toolbar(toolbar_name)
if action in toolbar.actions():
toolbars_with_action.append(toolbar_name)
if toolbars_with_action:
message += f"<br><b>In Toolbars:</b> {', '.join(toolbars_with_action)}"
if action.isCheckable():
state = "Checked" if action.isChecked() else "Unchecked"
message += f"<br><b>State:</b> {state}"
if action.shortcut().toString():
message += f"<br><b>Shortcut:</b> {action.shortcut().toString()}"
if action.toolTip():
message += f"<br><b>Tooltip:</b> {action.toolTip()}"
self.log_message(message)
[docs]
def log_message(self, message):
"""Add message to status display"""
self.status_display.append(message)
self.status_display.append("<hr>")
# Scroll to bottom
scrollbar = self.status_display.verticalScrollBar()
scrollbar.setValue(scrollbar.maximum())
[docs]
def main():
"""Run the example application"""
app = QtWidgets.QApplication(sys.argv)
window = ActionManagerExample()
window.show()
sys.exit(app.exec())
if __name__ == '__main__':
main()