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

1""" 

2Ribbon widget for pyPhotoAlbum 

3""" 

4 

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

6from PyQt6.QtCore import Qt 

7 

8 

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