Coverage for pyPhotoAlbum/ribbon_widget.py: 97%
72 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-20 12:55 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-20 12:55 +0000
1"""
2Ribbon widget for pyPhotoAlbum
3"""
5from PyQt6.QtWidgets import QWidget, QTabWidget, QHBoxLayout, QVBoxLayout, QPushButton, QLabel, QFrame, QGridLayout
6from PyQt6.QtCore import Qt
9class RibbonWidget(QWidget):
10 """A ribbon-style toolbar using QTabWidget"""
12 def __init__(self, main_window, ribbon_config=None, buttons_per_row=4, parent=None):
13 super().__init__(parent)
14 self.main_window = main_window
15 self.buttons_per_row = buttons_per_row # Default to 4 buttons per row
17 # Use provided config or fall back to importing the old one
18 if ribbon_config is None:
19 from ribbon_config import RIBBON_CONFIG
21 self.ribbon_config = RIBBON_CONFIG
22 else:
23 self.ribbon_config = ribbon_config
25 # Main layout
26 main_layout = QVBoxLayout()
27 main_layout.setContentsMargins(0, 0, 0, 0)
28 main_layout.setSpacing(0)
29 self.setLayout(main_layout)
31 # Create tab widget
32 self.tab_widget = QTabWidget()
33 self.tab_widget.setDocumentMode(True)
34 main_layout.addWidget(self.tab_widget)
36 # Build ribbon from config
37 self._build_ribbon()
39 def _build_ribbon(self):
40 """Build the ribbon UI from configuration"""
41 for tab_name, tab_config in self.ribbon_config.items():
42 tab_widget = self._create_tab(tab_config)
43 self.tab_widget.addTab(tab_widget, tab_name)
45 def _create_tab(self, tab_config):
46 """Create a tab widget with groups and actions"""
47 tab_widget = QWidget()
48 tab_layout = QHBoxLayout()
49 tab_layout.setContentsMargins(5, 5, 5, 5)
50 tab_layout.setSpacing(10)
51 tab_widget.setLayout(tab_layout)
53 # Create groups
54 for group_config in tab_config.get("groups", []):
55 group_widget = self._create_group(group_config)
56 tab_layout.addWidget(group_widget)
58 # Add stretch to push groups to the left
59 tab_layout.addStretch()
61 return tab_widget
63 def _create_group(self, group_config):
64 """Create a group of actions"""
65 group_widget = QFrame()
66 group_layout = QVBoxLayout()
67 group_layout.setContentsMargins(5, 5, 5, 5)
68 group_layout.setSpacing(5)
69 group_widget.setLayout(group_layout)
71 # Create actions grid layout
72 actions_layout = QGridLayout()
73 actions_layout.setSpacing(5)
75 # Get buttons per row from group config or use default
76 buttons_per_row = group_config.get("buttons_per_row", self.buttons_per_row)
78 # Add buttons to grid
79 actions = group_config.get("actions", [])
80 for i, action_config in enumerate(actions):
81 button = self._create_action_button(action_config)
82 row = i // buttons_per_row
83 col = i % buttons_per_row
84 actions_layout.addWidget(button, row, col)
86 group_layout.addLayout(actions_layout)
88 # Add group label
89 group_label = QLabel(group_config.get("name", ""))
90 group_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
91 group_label.setStyleSheet("font-size: 10px; color: gray;")
92 group_layout.addWidget(group_label)
94 # Add separator frame
95 group_widget.setFrameShape(QFrame.Shape.Box)
96 group_widget.setFrameShadow(QFrame.Shadow.Sunken)
97 group_widget.setLineWidth(1)
99 return group_widget
101 def _create_action_button(self, action_config):
102 """Create a button for an action"""
103 button = QPushButton(action_config.get("label", ""))
104 button.setToolTip(action_config.get("tooltip", ""))
105 button.setMinimumSize(60, 40)
107 # Connect to action
108 action_name = action_config.get("action")
109 if action_name:
110 # Use default argument to capture action_name by value, not by reference
111 button.clicked.connect(lambda checked, name=action_name: self._execute_action(name))
113 return button
115 def _execute_action(self, action_name):
116 """Execute an action by calling the corresponding method on main window"""
117 if hasattr(self.main_window, action_name):
118 method = getattr(self.main_window, action_name)
119 if callable(method):
120 method()
121 else:
122 print(f"Warning: Action '{action_name}' not implemented in main window")