Coverage for src / ezqt_widgets / widgets / label / framed_label.py: 98.63%
69 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-31 10:03 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-31 10:03 +0000
1# ///////////////////////////////////////////////////////////////
2# FRAMED_LABEL - Framed Label Widget
3# Project: ezqt_widgets
4# ///////////////////////////////////////////////////////////////
6"""
7Framed label widget module.
9Provides a flexible label widget based on QFrame for advanced styling and
10layout in PySide6 applications.
11"""
13from __future__ import annotations
15# ///////////////////////////////////////////////////////////////
16# IMPORTS
17# ///////////////////////////////////////////////////////////////
18# Standard library imports
19from typing import Any
21# Third-party imports
22from PySide6.QtCore import QSize, Qt, Signal
23from PySide6.QtWidgets import QFrame, QLabel, QSizePolicy, QVBoxLayout
25# Local imports
26from ...types import WidgetParent
28# ///////////////////////////////////////////////////////////////
29# CLASSES
30# ///////////////////////////////////////////////////////////////
33class FramedLabel(QFrame):
34 """Flexible label widget based on QFrame for advanced styling.
36 This widget encapsulates a QLabel inside a QFrame, allowing you to benefit
37 from QFrame's styling and layout capabilities while providing a simple
38 interface for text display, alignment, and dynamic style updates.
40 Features:
41 - Property-based access to the label text and alignment
42 - Emits a textChanged(str) signal when the text changes
43 - Allows custom stylesheet injection for advanced appearance
44 - Suitable for use as a header, section label, or any styled context
46 Args:
47 text: The initial text to display in the label (default: "").
48 parent: The parent widget (default: None).
49 alignment: The alignment of the label text
50 (default: Qt.AlignmentFlag.AlignCenter).
51 style_sheet: Custom stylesheet to apply to the QFrame
52 (default: None, uses transparent background).
53 min_width: Minimum width constraint for the widget (default: None).
54 min_height: Minimum height constraint for the widget (default: None).
55 *args: Additional arguments passed to QFrame.
56 **kwargs: Additional keyword arguments passed to QFrame.
58 Signals:
59 textChanged(str): Emitted when the label text changes.
61 Example:
62 >>> from ezqt_widgets import FramedLabel
63 >>> label = FramedLabel(text="Section Title", min_height=30)
64 >>> label.textChanged.connect(lambda t: print(f"New text: {t}"))
65 >>> label.text = "Updated Title"
66 >>> label.show()
67 """
69 textChanged = Signal(str)
71 # ///////////////////////////////////////////////////////////////
72 # INIT
73 # ///////////////////////////////////////////////////////////////
75 def __init__(
76 self,
77 text: str = "",
78 parent: WidgetParent = None,
79 alignment: Qt.AlignmentFlag = Qt.AlignmentFlag.AlignCenter,
80 style_sheet: str | None = None,
81 min_width: int | None = None,
82 min_height: int | None = None,
83 *args: Any,
84 **kwargs: Any,
85 ) -> None:
86 """Initialize the framed label."""
87 super().__init__(parent, *args, **kwargs)
88 self.setProperty("type", "FramedLabel")
90 # Initialize properties
91 self._min_width: int | None = min_width
92 self._min_height: int | None = min_height
93 self._alignment: Qt.AlignmentFlag = alignment
95 # Setup styling
96 self.setStyleSheet(style_sheet or "background-color: transparent;")
97 self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
99 # Setup layout
100 layout = QVBoxLayout(self)
101 layout.setSpacing(0)
102 layout.setContentsMargins(0, 0, 0, 0)
103 layout.setAlignment(alignment)
105 # Setup label
106 self._label = QLabel(text, self)
107 self._label.setAlignment(alignment)
108 layout.addWidget(self._label)
110 # ///////////////////////////////////////////////////////////////
111 # PROPERTIES
112 # ///////////////////////////////////////////////////////////////
114 @property
115 def text(self) -> str:
116 """Get or set the label text.
118 Returns:
119 The current label text.
120 """
121 return self._label.text()
123 @text.setter
124 def text(self, value: str) -> None:
125 """Set the label text.
127 Args:
128 value: The new label text.
129 """
130 str_value = str(value)
131 if str_value != self._label.text():
132 self._label.setText(str_value)
133 self.textChanged.emit(str_value)
135 @property
136 def alignment(self) -> Qt.AlignmentFlag:
137 """Get or set the alignment of the label.
139 Returns:
140 The current alignment.
141 """
142 return self._alignment
144 @alignment.setter
145 def alignment(self, value: Qt.AlignmentFlag) -> None:
146 """Set the alignment of the label.
148 Args:
149 value: The new alignment.
150 """
151 self._alignment = value
152 self._label.setAlignment(value)
153 layout = self.layout()
154 if layout is not None: 154 ↛ exitline 154 didn't return from function 'alignment' because the condition on line 154 was always true
155 layout.setAlignment(value)
157 @property
158 def min_width(self) -> int | None:
159 """Get or set the minimum width.
161 Returns:
162 The minimum width, or None if not set.
163 """
164 return self._min_width
166 @min_width.setter
167 def min_width(self, value: int | None) -> None:
168 """Set the minimum width.
170 Args:
171 value: The minimum width, or None to auto-calculate.
172 """
173 self._min_width = value
174 self.updateGeometry()
176 @property
177 def min_height(self) -> int | None:
178 """Get or set the minimum height.
180 Returns:
181 The minimum height, or None if not set.
182 """
183 return self._min_height
185 @min_height.setter
186 def min_height(self, value: int | None) -> None:
187 """Set the minimum height.
189 Args:
190 value: The minimum height, or None to auto-calculate.
191 """
192 self._min_height = value
193 self.updateGeometry()
195 # ///////////////////////////////////////////////////////////////
196 # OVERRIDE METHODS
197 # ///////////////////////////////////////////////////////////////
199 def minimumSizeHint(self) -> QSize:
200 """Get the minimum size hint for the widget.
202 Returns:
203 The minimum size hint.
204 """
205 font_metrics = self.fontMetrics()
206 text_width = font_metrics.horizontalAdvance(self.text)
207 text_height = font_metrics.height()
209 content_width = text_width + 16 # 8px padding on each side
210 content_height = text_height + 8 # 4px padding top/bottom
212 min_width = self._min_width if self._min_width is not None else content_width
213 min_height = (
214 self._min_height if self._min_height is not None else content_height
215 )
217 return QSize(max(min_width, content_width), max(min_height, content_height))
219 # ///////////////////////////////////////////////////////////////
220 # STYLE METHODS
221 # ///////////////////////////////////////////////////////////////
223 def refreshStyle(self) -> None:
224 """Refresh the widget's style.
226 Useful after dynamic stylesheet changes.
227 """
228 self.style().unpolish(self)
229 self.style().polish(self)
230 self.update()
233# ///////////////////////////////////////////////////////////////
234# PUBLIC API
235# ///////////////////////////////////////////////////////////////
237__all__ = ["FramedLabel"]