Coverage for main/components/circular_progress_bar.py: 100%
43 statements
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-03 15:51 +0000
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-03 15:51 +0000
1from PyQt6.QtCore import Qt, QRectF
2from PyQt6.QtGui import QColor, QPainter, QPen, QFont
3from PyQt6.QtWidgets import QWidget
6class CircularProgress(QWidget):
7 """
8 A customizable **circular progress bar widget** for PyQt6 applications.
10 This widget visually represents a percentage-based progress value (0–100)
11 as a circular arc with a numeric label at its center. It is lightweight,
12 anti-aliased, and supports dynamic color changes.
14 ---
15 **Features**
16 - Smooth circular progress rendering with anti-aliasing
17 - Adjustable color via `setColor()`
18 - Automatically scales with parent widget size
19 - Displays numeric percentage text in the center
20 ---
22 **Parameters**
23 - `color (str | QColor)`: Initial color for the progress arc.
24 Accepts either a hex string (e.g., "#3498DB") or a `QColor` instance.
26 **Example**
27 ```python
28 progress = CircularProgress("#00B894")
29 progress.setValue(75)
30 ```
31 """
33 def __init__(self, color: str = "#3498DB"):
34 """
35 Initialize the circular progress widget.
37 Args:
38 color (str): Hex color string for the progress arc (default: "#3498DB").
39 """
40 super().__init__()
41 self.value = 0 # Current progress value (0–100)
42 self.color = QColor(color) # Active progress color
44 self.setMaximumSize(300, 300)
46 def setValue(self, val: int):
47 """
48 Update the progress value and refresh the widget.
50 Args:
51 val (int): New progress value (0–100). Values outside this range are clamped.
53 Notes:
54 This method triggers a repaint using `update()`.
55 """
56 self.value = max(0, min(100, int(val)))
57 self.update()
59 def setColor(self, color: str | QColor):
60 """
61 Change the color of the progress arc.
63 Args:
64 color (str | QColor): The new color for the progress indicator.
65 Can be a hex string or a `QColor` instance.
67 Example:
68 ```python
69 progress.setColor("#E74C3C") # Set to red
70 ```
71 """
72 if isinstance(color, str):
73 self.color = QColor(color)
74 else:
75 self.color = color
76 self.update()
78 def sizeHint(self):
79 """
80 Suggest an appropriate default size for the widget.
82 Returns:
83 QSize: The preferred size of the widget, matching its parent if available.
84 """
85 return self.parentWidget().size() if self.parentWidget() else super().sizeHint()
87 def paintEvent(self, event):
88 """
89 Handle the paint event to draw the circular progress indicator.
91 The widget draws:
92 1. A **light gray background circle**
93 2. A **colored progress arc** proportional to the current value
94 3. A **percentage label** centered within the circle
96 Args:
97 event (QPaintEvent): The paint event object automatically provided by Qt.
98 """
99 width = self.width()
100 height = self.height()
101 size = min(width, height) # Ensure square aspect ratio
103 # Center the circle in the available space
104 x_offset = int((width - size) / 2)
105 y_offset = int((height - size) / 2)
107 painter = QPainter(self)
108 painter.setRenderHint(QPainter.RenderHint.Antialiasing)
110 # --- Draw background circle ---
111 painter.setPen(Qt.PenStyle.NoPen)
112 painter.setBrush(QColor("#f0f0f0")) # Light gray background
113 painter.drawEllipse(x_offset, y_offset, int(size), int(size))
115 # --- Draw progress arc ---
116 pen_width = max(5, int(size / 12)) # Adaptive thickness
117 pen = QPen(self.color)
118 pen.setWidth(pen_width)
119 pen.setCapStyle(Qt.PenCapStyle.RoundCap)
120 painter.setPen(pen)
121 painter.setBrush(Qt.BrushStyle.NoBrush)
123 # Define drawing area for the arc
124 rect = QRectF(
125 x_offset + pen_width / 2,
126 y_offset + pen_width / 2,
127 size - pen_width,
128 size - pen_width
129 )
130 angle_span = int(360 * self.value / 100)
131 painter.drawArc(rect, -90 * 16, -angle_span * 16) # Start from top center
133 # --- Draw percentage text ---
134 font_size = max(5, int(size / 9))
135 painter.setPen(QColor("#2C3E50")) # Dark blue-gray text
136 painter.setFont(QFont("Arial", font_size, QFont.Weight.Bold))
137 painter.drawText(rect, Qt.AlignmentFlag.AlignCenter, f"{self.value}%")