1 """

2 Ribbon widget for pyPhotoAlbum

3 """

4

5 from PyQt6.QtWidgets import QWidget, QTabWidget, QHBoxLayout, QVBoxLayout, QPushButton, QLabel, QFrame, QGridLayout

6 from PyQt6.QtCore import Qt

7

8

9 class RibbonWidget(QWidget):

10 """A ribbon-style toolbar using QTabWidget"""

11

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

16

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

20

21 self.ribbon_config = RIBBON_CONFIG

22 else:

23 self.ribbon_config = ribbon_config

24

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)

30

31 # Create tab widget

32 self.tab_widget = QTabWidget()

33 self.tab_widget.setDocumentMode(True)

34 main_layout.addWidget(self.tab_widget)

35

36 # Build ribbon from config

37 self._build_ribbon()

38

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)

44

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)

52

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)

57

58 # Add stretch to push groups to the left

59 tab_layout.addStretch()

60

61 return tab_widget

62

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)

70

71 # Create actions grid layout

72 actions_layout = QGridLayout()

73 actions_layout.setSpacing(5)

74

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)

77

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)

85

86 group_layout.addLayout(actions_layout)

87

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)

93

94 # Add separator frame

95 group_widget.setFrameShape(QFrame.Shape.Box)

96 group_widget.setFrameShadow(QFrame.Shadow.Sunken)

97 group_widget.setLineWidth(1)

98

99 return group_widget

100

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)

106

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))

112

113 return button

114

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")