Coverage for pyPhotoAlbum/dialogs/style_dialogs.py: 0%

182 statements  

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

1""" 

2Style dialogs for pyPhotoAlbum 

3 

4Dialogs for configuring image styling options: 

5- Corner radius 

6- Border (width and color) 

7- Drop shadow 

8""" 

9 

10from typing import Tuple 

11from PyQt6.QtWidgets import ( 

12 QDialog, 

13 QVBoxLayout, 

14 QHBoxLayout, 

15 QLabel, 

16 QSlider, 

17 QSpinBox, 

18 QDoubleSpinBox, 

19 QPushButton, 

20 QCheckBox, 

21 QColorDialog, 

22 QGroupBox, 

23 QFormLayout, 

24 QWidget, 

25) 

26from PyQt6.QtCore import Qt 

27from PyQt6.QtGui import QColor 

28 

29 

30class CornerRadiusDialog(QDialog): 

31 """Dialog for setting corner radius""" 

32 

33 def __init__(self, parent, current_radius: float = 0.0): 

34 super().__init__(parent) 

35 self.setWindowTitle("Corner Radius") 

36 self.setMinimumWidth(300) 

37 

38 layout = QVBoxLayout(self) 

39 

40 # Slider with label 

41 slider_layout = QHBoxLayout() 

42 slider_layout.addWidget(QLabel("Radius:")) 

43 

44 self.slider = QSlider(Qt.Orientation.Horizontal) 

45 self.slider.setMinimum(0) 

46 self.slider.setMaximum(50) 

47 self.slider.setValue(int(current_radius)) 

48 self.slider.valueChanged.connect(self._on_slider_changed) 

49 slider_layout.addWidget(self.slider) 

50 

51 self.value_label = QLabel(f"{int(current_radius)}%") 

52 self.value_label.setMinimumWidth(40) 

53 slider_layout.addWidget(self.value_label) 

54 

55 layout.addLayout(slider_layout) 

56 

57 # Preset buttons 

58 preset_layout = QHBoxLayout() 

59 for value, label in [(0, "None"), (5, "Slight"), (15, "Medium"), (25, "Large"), (50, "Circle")]: 

60 btn = QPushButton(label) 

61 btn.clicked.connect(lambda checked, v=value: self.slider.setValue(v)) 

62 preset_layout.addWidget(btn) 

63 layout.addLayout(preset_layout) 

64 

65 # OK/Cancel buttons 

66 button_layout = QHBoxLayout() 

67 ok_btn = QPushButton("OK") 

68 ok_btn.clicked.connect(self.accept) 

69 cancel_btn = QPushButton("Cancel") 

70 cancel_btn.clicked.connect(self.reject) 

71 button_layout.addStretch() 

72 button_layout.addWidget(ok_btn) 

73 button_layout.addWidget(cancel_btn) 

74 layout.addLayout(button_layout) 

75 

76 def _on_slider_changed(self, value): 

77 self.value_label.setText(f"{value}%") 

78 

79 def get_value(self) -> float: 

80 return float(self.slider.value()) 

81 

82 

83class ColorButton(QPushButton): 

84 """Button that shows a color and opens color picker on click""" 

85 

86 def __init__(self, color: Tuple[int, int, int], parent=None): 

87 super().__init__(parent) 

88 self.setFixedSize(40, 25) 

89 self._color = color 

90 self._update_style() 

91 self.clicked.connect(self._pick_color) 

92 

93 def _update_style(self): 

94 r, g, b = self._color 

95 self.setStyleSheet(f"background-color: rgb({r}, {g}, {b}); border: 1px solid #666;") 

96 

97 def _pick_color(self): 

98 r, g, b = self._color 

99 initial = QColor(r, g, b) 

100 color = QColorDialog.getColor(initial, self, "Select Color") 

101 if color.isValid(): 

102 self._color = (color.red(), color.green(), color.blue()) 

103 self._update_style() 

104 

105 def get_color(self) -> Tuple[int, int, int]: 

106 return self._color 

107 

108 

109class BorderDialog(QDialog): 

110 """Dialog for configuring border""" 

111 

112 def __init__( 

113 self, 

114 parent, 

115 current_width: float = 0.0, 

116 current_color: Tuple[int, int, int] = (0, 0, 0), 

117 ): 

118 super().__init__(parent) 

119 self.setWindowTitle("Border Settings") 

120 self.setMinimumWidth(300) 

121 

122 layout = QVBoxLayout(self) 

123 

124 # Border width 

125 width_layout = QHBoxLayout() 

126 width_layout.addWidget(QLabel("Width (mm):")) 

127 self.width_spin = QDoubleSpinBox() 

128 self.width_spin.setRange(0, 20) 

129 self.width_spin.setSingleStep(0.5) 

130 self.width_spin.setValue(current_width) 

131 self.width_spin.setDecimals(1) 

132 width_layout.addWidget(self.width_spin) 

133 layout.addLayout(width_layout) 

134 

135 # Border color 

136 color_layout = QHBoxLayout() 

137 color_layout.addWidget(QLabel("Color:")) 

138 self.color_btn = ColorButton(current_color) 

139 color_layout.addWidget(self.color_btn) 

140 color_layout.addStretch() 

141 layout.addLayout(color_layout) 

142 

143 # Preset buttons 

144 preset_layout = QHBoxLayout() 

145 presets = [ 

146 ("None", 0, (0, 0, 0)), 

147 ("Thin Black", 0.5, (0, 0, 0)), 

148 ("White", 2, (255, 255, 255)), 

149 ("Gold", 1.5, (212, 175, 55)), 

150 ] 

151 for label, width, color in presets: 

152 btn = QPushButton(label) 

153 btn.clicked.connect(lambda checked, w=width, c=color: self._apply_preset(w, c)) 

154 preset_layout.addWidget(btn) 

155 layout.addLayout(preset_layout) 

156 

157 # OK/Cancel buttons 

158 button_layout = QHBoxLayout() 

159 ok_btn = QPushButton("OK") 

160 ok_btn.clicked.connect(self.accept) 

161 cancel_btn = QPushButton("Cancel") 

162 cancel_btn.clicked.connect(self.reject) 

163 button_layout.addStretch() 

164 button_layout.addWidget(ok_btn) 

165 button_layout.addWidget(cancel_btn) 

166 layout.addLayout(button_layout) 

167 

168 def _apply_preset(self, width, color): 

169 self.width_spin.setValue(width) 

170 self.color_btn._color = color 

171 self.color_btn._update_style() 

172 

173 def get_values(self) -> Tuple[float, Tuple[int, int, int]]: 

174 return self.width_spin.value(), self.color_btn.get_color() 

175 

176 

177class ShadowDialog(QDialog): 

178 """Dialog for configuring drop shadow""" 

179 

180 def __init__( 

181 self, 

182 parent, 

183 enabled: bool = False, 

184 offset: Tuple[float, float] = (2.0, 2.0), 

185 blur: float = 3.0, 

186 color: Tuple[int, int, int, int] = (0, 0, 0, 128), 

187 ): 

188 super().__init__(parent) 

189 self.setWindowTitle("Shadow Settings") 

190 self.setMinimumWidth(350) 

191 

192 layout = QVBoxLayout(self) 

193 

194 # Enable checkbox 

195 self.enabled_check = QCheckBox("Enable Drop Shadow") 

196 self.enabled_check.setChecked(enabled) 

197 self.enabled_check.stateChanged.connect(self._update_controls) 

198 layout.addWidget(self.enabled_check) 

199 

200 # Settings group 

201 self.settings_group = QGroupBox("Shadow Settings") 

202 form = QFormLayout(self.settings_group) 

203 

204 # Offset X 

205 self.offset_x = QDoubleSpinBox() 

206 self.offset_x.setRange(-20, 20) 

207 self.offset_x.setSingleStep(0.5) 

208 self.offset_x.setValue(offset[0]) 

209 self.offset_x.setDecimals(1) 

210 form.addRow("Offset X (mm):", self.offset_x) 

211 

212 # Offset Y 

213 self.offset_y = QDoubleSpinBox() 

214 self.offset_y.setRange(-20, 20) 

215 self.offset_y.setSingleStep(0.5) 

216 self.offset_y.setValue(offset[1]) 

217 self.offset_y.setDecimals(1) 

218 form.addRow("Offset Y (mm):", self.offset_y) 

219 

220 # Blur 

221 self.blur_spin = QDoubleSpinBox() 

222 self.blur_spin.setRange(0, 20) 

223 self.blur_spin.setSingleStep(0.5) 

224 self.blur_spin.setValue(blur) 

225 self.blur_spin.setDecimals(1) 

226 form.addRow("Blur (mm):", self.blur_spin) 

227 

228 # Color 

229 color_widget = QWidget() 

230 color_layout = QHBoxLayout(color_widget) 

231 color_layout.setContentsMargins(0, 0, 0, 0) 

232 self.color_btn = ColorButton(color[:3]) 

233 color_layout.addWidget(self.color_btn) 

234 color_layout.addStretch() 

235 form.addRow("Color:", color_widget) 

236 

237 # Opacity 

238 self.opacity_slider = QSlider(Qt.Orientation.Horizontal) 

239 self.opacity_slider.setRange(0, 255) 

240 self.opacity_slider.setValue(color[3] if len(color) > 3 else 128) 

241 opacity_layout = QHBoxLayout() 

242 opacity_layout.addWidget(self.opacity_slider) 

243 self.opacity_label = QLabel(f"{self.opacity_slider.value()}") 

244 self.opacity_label.setMinimumWidth(30) 

245 opacity_layout.addWidget(self.opacity_label) 

246 self.opacity_slider.valueChanged.connect(lambda v: self.opacity_label.setText(str(v))) 

247 form.addRow("Opacity:", opacity_layout) 

248 

249 layout.addWidget(self.settings_group) 

250 

251 # Preset buttons 

252 preset_layout = QHBoxLayout() 

253 presets = [ 

254 ("Subtle", True, (1.0, 1.0), 2.0, (0, 0, 0, 60)), 

255 ("Normal", True, (2.0, 2.0), 3.0, (0, 0, 0, 100)), 

256 ("Strong", True, (3.0, 3.0), 5.0, (0, 0, 0, 150)), 

257 ] 

258 for label, en, off, bl, col in presets: 

259 btn = QPushButton(label) 

260 btn.clicked.connect(lambda checked, e=en, o=off, b=bl, c=col: self._apply_preset(e, o, b, c)) 

261 preset_layout.addWidget(btn) 

262 layout.addLayout(preset_layout) 

263 

264 # OK/Cancel buttons 

265 button_layout = QHBoxLayout() 

266 ok_btn = QPushButton("OK") 

267 ok_btn.clicked.connect(self.accept) 

268 cancel_btn = QPushButton("Cancel") 

269 cancel_btn.clicked.connect(self.reject) 

270 button_layout.addStretch() 

271 button_layout.addWidget(ok_btn) 

272 button_layout.addWidget(cancel_btn) 

273 layout.addLayout(button_layout) 

274 

275 self._update_controls() 

276 

277 def _update_controls(self): 

278 self.settings_group.setEnabled(self.enabled_check.isChecked()) 

279 

280 def _apply_preset(self, enabled, offset, blur, color): 

281 self.enabled_check.setChecked(enabled) 

282 self.offset_x.setValue(offset[0]) 

283 self.offset_y.setValue(offset[1]) 

284 self.blur_spin.setValue(blur) 

285 self.color_btn._color = color[:3] 

286 self.color_btn._update_style() 

287 self.opacity_slider.setValue(color[3] if len(color) > 3 else 128) 

288 

289 def get_values(self) -> Tuple[bool, Tuple[float, float], float, Tuple[int, int, int, int]]: 

290 color_rgb = self.color_btn.get_color() 

291 color_rgba = color_rgb + (self.opacity_slider.value(),) 

292 return ( 

293 self.enabled_check.isChecked(), 

294 (self.offset_x.value(), self.offset_y.value()), 

295 self.blur_spin.value(), 

296 color_rgba, 

297 )