Coverage for pyPhotoAlbum/mixins/operations/alignment_ops.py: 72%

50 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-20 12:55 +0000

1""" 

2Alignment operations mixin for pyPhotoAlbum 

3""" 

4 

5from pyPhotoAlbum.decorators import ribbon_action 

6from pyPhotoAlbum.alignment import AlignmentManager 

7from pyPhotoAlbum.commands import AlignElementsCommand, ResizeElementsCommand 

8 

9 

10class AlignmentOperationsMixin: 

11 """Mixin providing element alignment operations""" 

12 

13 def _get_selected_elements_list(self): 

14 """Get list of selected elements for alignment operations""" 

15 return list(self.gl_widget.selected_elements) if self.gl_widget.selected_elements else [] 

16 

17 def _execute_alignment(self, alignment_func, status_msg: str): 

18 """ 

19 Execute an alignment operation with common boilerplate. 

20 

21 Args: 

22 alignment_func: AlignmentManager method to call with elements 

23 status_msg: Status message format string (will receive element count) 

24 """ 

25 elements = self._get_selected_elements_list() 

26 if not self.require_selection(min_count=2): # type: ignore[attr-defined] 

27 return 

28 

29 changes = alignment_func(elements) 

30 if changes: 

31 cmd = AlignElementsCommand(changes) 

32 self.project.history.execute(cmd) # type: ignore[attr-defined] 

33 self.update_view() # type: ignore[attr-defined] 

34 self.show_status(status_msg.format(len(elements)), 2000) # type: ignore[attr-defined] 

35 

36 @ribbon_action( 

37 label="Align Left", 

38 tooltip="Align selected elements to the left", 

39 tab="Arrange", 

40 group="Align", 

41 requires_selection=True, 

42 min_selection=2, 

43 ) 

44 def align_left(self): 

45 """Align selected elements to the left""" 

46 self._execute_alignment(AlignmentManager.align_left, "Aligned {} elements to left") 

47 

48 @ribbon_action( 

49 label="Align Right", 

50 tooltip="Align selected elements to the right", 

51 tab="Arrange", 

52 group="Align", 

53 requires_selection=True, 

54 min_selection=2, 

55 ) 

56 def align_right(self): 

57 """Align selected elements to the right""" 

58 self._execute_alignment(AlignmentManager.align_right, "Aligned {} elements to right") 

59 

60 @ribbon_action( 

61 label="Align Top", 

62 tooltip="Align selected elements to the top", 

63 tab="Arrange", 

64 group="Align", 

65 requires_selection=True, 

66 min_selection=2, 

67 ) 

68 def align_top(self): 

69 """Align selected elements to the top""" 

70 self._execute_alignment(AlignmentManager.align_top, "Aligned {} elements to top") 

71 

72 @ribbon_action( 

73 label="Align Bottom", 

74 tooltip="Align selected elements to the bottom", 

75 tab="Arrange", 

76 group="Align", 

77 requires_selection=True, 

78 min_selection=2, 

79 ) 

80 def align_bottom(self): 

81 """Align selected elements to the bottom""" 

82 self._execute_alignment(AlignmentManager.align_bottom, "Aligned {} elements to bottom") 

83 

84 @ribbon_action( 

85 label="Align H-Center", 

86 tooltip="Align selected elements to horizontal center", 

87 tab="Arrange", 

88 group="Align", 

89 requires_selection=True, 

90 min_selection=2, 

91 ) 

92 def align_horizontal_center(self): 

93 """Align selected elements to horizontal center""" 

94 self._execute_alignment(AlignmentManager.align_horizontal_center, "Aligned {} elements to horizontal center") 

95 

96 @ribbon_action( 

97 label="Align V-Center", 

98 tooltip="Align selected elements to vertical center", 

99 tab="Arrange", 

100 group="Align", 

101 requires_selection=True, 

102 min_selection=2, 

103 ) 

104 def align_vertical_center(self): 

105 """Align selected elements to vertical center""" 

106 self._execute_alignment(AlignmentManager.align_vertical_center, "Aligned {} elements to vertical center") 

107 

108 @ribbon_action( 

109 label="Maximize Pattern", 

110 tooltip="Maximize selected elements using crystal growth algorithm", 

111 tab="Arrange", 

112 group="Size", 

113 requires_selection=True, 

114 min_selection=1, 

115 ) 

116 def maximize_pattern(self): 

117 """Maximize selected elements until they are close to borders or each other""" 

118 elements = self._get_selected_elements_list() 

119 if not self.require_selection(min_count=1): 

120 return 

121 

122 # Get page size from current page 

123 page = self.get_current_page() 

124 if not page: 

125 self.show_warning("No Page", "Please create a page first.") 

126 return 

127 

128 page_size = page.layout.size 

129 

130 changes = AlignmentManager.maximize_pattern(elements, page_size) 

131 if changes: 

132 cmd = ResizeElementsCommand(changes) 

133 self.project.history.execute(cmd) 

134 self.update_view() 

135 self.show_status(f"Maximized {len(elements)} element(s) using pattern growth", 2000)