2025-03-07 08:03:18 +01:00

96 lines
3.1 KiB
Python

# SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus <wojtryb@gmail.com>
# SPDX-License-Identifier: GPL-3.0-or-later
import math
from typing import Optional
from PyQt5.QtGui import QPainter, QPainterPath, QColor, QPixmap, QPaintEvent
from PyQt5.QtCore import QPoint, QRectF
from PyQt5.QtWidgets import QWidget
class Painter:
"""
Wraps `QPainter` to extend it with custom shapes to paint.
Allows to paint:
- wheel of given thickness, color and radius
- pie being a part of a wheel
- pixmap providing a center instead of top-left corner
Unlike original painter, can be used with context manager.
"""
def __init__(self, widget: QWidget, event: QPaintEvent) -> None:
self._painter = QPainter(widget)
self._painter.eraseRect(event.rect())
self._painter.setRenderHints(QPainter.Antialiasing)
def paint_wheel(
self,
center: QPoint,
outer_radius: float,
color: QColor,
thickness: Optional[float] = None,
) -> None:
"""
Paint a wheel at center providing its radius, color and thickness.
Not providing thickness results in fully filled circle.
"""
path = QPainterPath()
path.addEllipse(center, outer_radius, outer_radius)
if thickness:
inner_radius = outer_radius - thickness
path.addEllipse(center, inner_radius, inner_radius)
self._painter.fillPath(path, color)
def paint_pie(
self,
center: QPoint,
outer_radius: int,
angle: int,
span: int,
color: QColor,
thickness: Optional[float] = None,
) -> None:
"""Paint part of wheel a, that spans left and right by span/2."""
angle = -angle + 90
path = QPainterPath()
path.moveTo(center)
outer_rectangle = self._square(center, outer_radius*2)
path.arcTo(outer_rectangle, angle-math.floor(span/2), span)
if thickness:
inner_radius = outer_radius-thickness
inner_rectangle = self._square(center, round(inner_radius*2))
path.arcTo(inner_rectangle, angle+math.ceil(span/2), -span)
self._painter.fillPath(path, color)
def paint_pixmap(self, center: QPoint, pixmap: QPixmap) -> None:
"""Paint pixmap providing a center instead of top-left corner."""
self._painter.drawPixmap(
center.x() - pixmap.width()//2,
center.y() - pixmap.height()//2,
pixmap.width(),
pixmap.height(),
pixmap
)
def _square(self, center: QPoint, width: int) -> QRectF:
"""Return a square of given `width` at `center` point."""
return QRectF(center.x()-width//2, center.y()-width//2, width, width)
def end(self) -> None:
"""End painting a widget provided in __init__."""
self._painter.end()
def __enter__(self) -> 'Painter':
"""Start using a painter using context manager."""
return self
def __exit__(self, *_) -> None:
"""End using a painter using context manager."""
self.end()