Coverage for src / ezqt_app / widgets / extended / setting_widgets.py: 84.54%
278 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-06 13:12 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-06 13:12 +0000
1# ///////////////////////////////////////////////////////////////
2# WIDGETS.EXTENDED.SETTING_WIDGETS - Settings widget components
3# Project: ezqt_app
4# ///////////////////////////////////////////////////////////////
6"""Setting widget components: toggle, select, slider, text, checkbox."""
8from __future__ import annotations
10# ///////////////////////////////////////////////////////////////
11# IMPORTS
12# ///////////////////////////////////////////////////////////////
13# Third-party imports
14from ezqt_widgets import ToggleSwitch
15from PySide6.QtCore import QCoreApplication, Qt, Signal
16from PySide6.QtWidgets import (
17 QComboBox,
18 QHBoxLayout,
19 QLabel,
20 QLineEdit,
21 QSlider,
22 QVBoxLayout,
23 QWidget,
24)
26# Local imports
27from ...services.settings import get_settings_service
30# ///////////////////////////////////////////////////////////////
31# CLASSES
32# ///////////////////////////////////////////////////////////////
33class BaseSettingWidget(QWidget):
34 """Base class for all setting widgets."""
36 def __init__(self, label: str, description: str = ""):
37 super().__init__()
38 self._label_text = label
39 self._description_text = description
40 self._key = None
41 self.setObjectName("base_setting_widget")
43 def set_key(self, key: str):
44 """Set the setting key."""
45 self._key = key
47 def _tr(self, text: str) -> str:
48 """Shortcut for translation with global context."""
49 return QCoreApplication.translate("EzQt_App", text)
51 def retranslate_ui(self):
52 """To be implemented by subclasses."""
55class SettingToggle(BaseSettingWidget):
56 """Widget for toggle settings (on/off)."""
58 valueChanged = Signal(str, bool)
60 def __init__(self, label: str, description: str = "", default: bool = False):
61 super().__init__(label, description)
62 self._value = default
63 self.setObjectName("setting_toggle_container")
64 self.setProperty("type", "setting_toggle")
65 self._setup_ui()
67 def _setup_ui(self):
68 """Configure the user interface."""
69 self._layout = QVBoxLayout(self)
70 self._layout.setSpacing(4)
71 self._layout.setContentsMargins(0, 0, 0, 0)
73 self._control_layout = QHBoxLayout()
74 self._control_layout.setSpacing(8)
75 self._control_layout.setContentsMargins(0, 0, 0, 0)
77 self._label_widget = QLabel(self._tr(self._label_text))
78 self._label_widget.setObjectName("setting_label")
79 self._label_widget.setWordWrap(True)
80 self._control_layout.addWidget(self._label_widget, 1)
82 animation_enabled = get_settings_service().gui.TIME_ANIMATION > 0
83 self._control_widget = ToggleSwitch(animation=animation_enabled)
84 self._control_widget.setObjectName("setting_toggle")
85 self._control_widget.checked = self._value
86 self._control_widget.toggled.connect(self._on_toggled)
87 self._control_layout.addWidget(self._control_widget, 0)
89 self._layout.addLayout(self._control_layout)
91 self._description_label = None
92 if self._description_text:
93 self._description_label = QLabel(self._tr(self._description_text))
94 self._description_label.setObjectName("setting_description")
95 self._description_label.setWordWrap(True)
96 self._layout.addWidget(self._description_label)
98 def retranslate_ui(self):
99 """Update strings after language change."""
100 self._label_widget.setText(self._tr(self._label_text))
101 if self._description_label:
102 self._description_label.setText(self._tr(self._description_text))
104 def _on_toggled(self, checked: bool):
105 self._value = checked
106 self.valueChanged.emit(self._key, checked)
108 @property
109 def value(self) -> bool:
110 return self._value
112 @value.setter
113 def value(self, val: bool):
114 self._value = val
115 self._control_widget.checked = val
117 def get_value(self) -> bool:
118 return self._value
120 def set_value(self, val: bool):
121 self._value = val
122 self._control_widget.checked = val
125class SettingSelect(BaseSettingWidget):
126 """Widget for selection settings."""
128 valueChanged = Signal(str, str)
130 def __init__(
131 self,
132 label: str,
133 description: str = "",
134 options: list | None = None,
135 default: str | None = None,
136 ):
137 super().__init__(label, description)
138 self._options = options or []
139 self._value = default or (options[0] if options else "")
140 self.setObjectName("setting_select_container")
141 self.setProperty("type", "setting_select")
142 self._setup_ui()
144 def _setup_ui(self):
145 self._layout = QVBoxLayout(self)
146 self._layout.setSpacing(4)
147 self._layout.setContentsMargins(0, 0, 0, 0)
149 self._label_widget = QLabel(self._tr(self._label_text))
150 self._label_widget.setObjectName("setting_label")
151 self._label_widget.setWordWrap(True)
152 self._layout.addWidget(self._label_widget)
154 self._control_widget = QComboBox()
155 self._control_widget.setObjectName("setting_combo_box")
156 self._control_widget.addItems(self._options)
157 if self._value in self._options:
158 self._control_widget.setCurrentText(self._value)
159 self._control_widget.currentTextChanged.connect(self._on_text_changed)
160 self._layout.addWidget(self._control_widget)
162 self._description_label = None
163 if self._description_text:
164 self._description_label = QLabel(self._tr(self._description_text))
165 self._description_label.setObjectName("setting_description")
166 self._description_label.setWordWrap(True)
167 self._layout.addWidget(self._description_label)
169 def retranslate_ui(self):
170 self._label_widget.setText(self._tr(self._label_text))
171 if self._description_label:
172 self._description_label.setText(self._tr(self._description_text))
174 def _on_text_changed(self, text: str):
175 self._value = text
176 self.valueChanged.emit(self._key, text)
178 @property
179 def value(self) -> str:
180 return self._value
182 @value.setter
183 def value(self, val: str):
184 self._value = val
185 if val in self._options: 185 ↛ exitline 185 didn't return from function 'value' because the condition on line 185 was always true
186 self._control_widget.setCurrentText(val)
188 def get_value(self) -> str:
189 return self._value
191 def set_value(self, val: str):
192 self._value = val
193 if val in self._options: 193 ↛ exitline 193 didn't return from function 'set_value' because the condition on line 193 was always true
194 self._control_widget.setCurrentText(val)
197class SettingSlider(BaseSettingWidget):
198 """Widget for numeric settings with slider."""
200 valueChanged = Signal(str, int)
202 def __init__(
203 self,
204 label: str,
205 description: str = "",
206 min_val: int = 0,
207 max_val: int = 100,
208 default: int = 50,
209 unit: str = "",
210 ):
211 super().__init__(label, description)
212 self._min_val = min_val
213 self._max_val = max_val
214 self._value = default
215 self._unit = unit
216 self.setObjectName("setting_slider_container")
217 self.setProperty("type", "setting_slider")
218 self._setup_ui()
220 def _setup_ui(self):
221 self._layout = QVBoxLayout(self)
222 self._layout.setSpacing(4)
223 self._layout.setContentsMargins(0, 0, 0, 0)
225 self._header_layout = QHBoxLayout()
226 self._header_layout.setSpacing(8)
227 self._header_layout.setContentsMargins(0, 0, 0, 0)
229 self._label_widget = QLabel(self._tr(self._label_text))
230 self._label_widget.setObjectName("setting_label")
231 self._label_widget.setWordWrap(True)
232 self._header_layout.addWidget(self._label_widget, 1)
234 self._value_label = QLabel(f"{self._value}{self._unit}")
235 self._value_label.setObjectName("setting_value_label")
236 self._header_layout.addWidget(self._value_label, 0)
238 self._layout.addLayout(self._header_layout)
240 self._control_widget = QSlider(Qt.Orientation.Horizontal)
241 self._control_widget.setObjectName("setting_slider")
242 self._control_widget.setMinimum(self._min_val)
243 self._control_widget.setMaximum(self._max_val)
244 self._control_widget.setValue(self._value)
245 self._control_widget.valueChanged.connect(self._on_value_changed)
246 self._layout.addWidget(self._control_widget)
248 self._description_label = None
249 if self._description_text: 249 ↛ 250line 249 didn't jump to line 250 because the condition on line 249 was never true
250 self._description_label = QLabel(self._tr(self._description_text))
251 self._description_label.setObjectName("setting_description")
252 self._description_label.setWordWrap(True)
253 self._layout.addWidget(self._description_label)
255 def retranslate_ui(self):
256 self._label_widget.setText(self._tr(self._label_text))
257 if self._description_label:
258 self._description_label.setText(self._tr(self._description_text))
260 def _on_value_changed(self, value: int):
261 self._value = value
262 self._value_label.setText(f"{value}{self._unit}")
263 self.valueChanged.emit(self._key, value)
265 @property
266 def value(self) -> int:
267 return self._value
269 @value.setter
270 def value(self, val: int):
271 self._value = val
272 self._control_widget.setValue(val)
273 self._value_label.setText(f"{val}{self._unit}")
275 def get_value(self) -> int:
276 return self._value
278 def set_value(self, val: int):
279 self._value = val
280 self._control_widget.setValue(val)
281 self._value_label.setText(f"{val}{self._unit}")
284class SettingText(BaseSettingWidget):
285 """Widget for text settings."""
287 valueChanged = Signal(str, str)
289 def __init__(self, label: str, description: str = "", default: str = ""):
290 super().__init__(label, description)
291 self._value = default
292 self.setObjectName("setting_text_container")
293 self.setProperty("type", "setting_text")
294 self._setup_ui()
296 def _setup_ui(self):
297 self._layout = QVBoxLayout(self)
298 self._layout.setSpacing(4)
299 self._layout.setContentsMargins(0, 0, 0, 0)
301 self._label_widget = QLabel(self._tr(self._label_text))
302 self._label_widget.setObjectName("setting_label")
303 self._label_widget.setWordWrap(True)
304 self._layout.addWidget(self._label_widget)
306 self._control_widget = QLineEdit()
307 self._control_widget.setObjectName("setting_text_edit")
308 self._control_widget.setText(self._value)
309 self._control_widget.textChanged.connect(self._on_text_changed)
310 self._layout.addWidget(self._control_widget)
312 self._description_label = None
313 if self._description_text: 313 ↛ 314line 313 didn't jump to line 314 because the condition on line 313 was never true
314 self._description_label = QLabel(self._tr(self._description_text))
315 self._description_label.setObjectName("setting_description")
316 self._description_label.setWordWrap(True)
317 self._layout.addWidget(self._description_label)
319 def retranslate_ui(self):
320 self._label_widget.setText(self._tr(self._label_text))
321 if self._description_label:
322 self._description_label.setText(self._tr(self._description_text))
324 def _on_text_changed(self, text: str):
325 self._value = text
326 self.valueChanged.emit(self._key, text)
328 @property
329 def value(self) -> str:
330 return self._value
332 @value.setter
333 def value(self, val: str):
334 self._value = val
335 self._control_widget.setText(val)
337 def get_value(self) -> str:
338 return self._value
340 def set_value(self, val: str):
341 self._value = val
342 self._control_widget.setText(val)
345class SettingCheckbox(BaseSettingWidget):
346 """Widget for checkbox settings (on/off)."""
348 valueChanged = Signal(str, bool)
350 def __init__(self, label: str, description: str = "", default: bool = False):
351 super().__init__(label, description)
352 self._value = default
353 self.setObjectName("setting_checkbox_container")
354 self.setProperty("type", "setting_checkbox")
355 self._setup_ui()
357 def _setup_ui(self):
358 self._layout = QVBoxLayout(self)
359 self._layout.setSpacing(4)
360 self._layout.setContentsMargins(0, 0, 0, 0)
362 self._control_layout = QHBoxLayout()
363 self._control_layout.setSpacing(8)
364 self._control_layout.setContentsMargins(0, 0, 0, 0)
366 self._label_widget = QLabel(self._tr(self._label_text))
367 self._label_widget.setObjectName("setting_label")
368 self._label_widget.setWordWrap(True)
369 self._control_layout.addWidget(self._label_widget, 1)
371 animation_enabled = get_settings_service().gui.TIME_ANIMATION > 0
372 self._control_widget = ToggleSwitch(animation=animation_enabled)
373 self._control_widget.setObjectName("setting_toggle")
374 self._control_widget.checked = self._value
375 self._control_widget.toggled.connect(self._on_toggled)
376 self._control_layout.addWidget(self._control_widget, 0)
378 self._layout.addLayout(self._control_layout)
380 self._description_label = None
381 if self._description_text: 381 ↛ 382line 381 didn't jump to line 382 because the condition on line 381 was never true
382 self._description_label = QLabel(self._tr(self._description_text))
383 self._description_label.setObjectName("setting_description")
384 self._description_label.setWordWrap(True)
385 self._layout.addWidget(self._description_label)
387 def retranslate_ui(self):
388 self._label_widget.setText(self._tr(self._label_text))
389 if self._description_label:
390 self._description_label.setText(self._tr(self._description_text))
392 def _on_toggled(self, checked: bool):
393 self._value = checked
394 self.valueChanged.emit(self._key, checked)
396 @property
397 def value(self) -> bool:
398 return self._value
400 @value.setter
401 def value(self, val: bool):
402 self._value = val
403 self._control_widget.checked = val
405 def get_value(self) -> bool:
406 return self._value
408 def set_value(self, val: bool):
409 self._value = val
410 self._control_widget.checked = val
413# ///////////////////////////////////////////////////////////////
414# PUBLIC API
415# ///////////////////////////////////////////////////////////////
416__all__ = [
417 "BaseSettingWidget",
418 "SettingCheckbox",
419 "SettingSelect",
420 "SettingSlider",
421 "SettingText",
422 "SettingToggle",
423]