"""
pygame-menu
https://github.com/ppizarror/pygame-menu
PROGRESS BAR
Progress bar widget.
"""
__all__ = [
# Class
'ProgressBar',
# Types
'ProgressBarTextFormatType'
]
import pygame
import pygame_menu
from pygame_menu.font import FontType, assert_font
from pygame_menu.locals import ALIGN_LEFT, ALIGN_CENTER
from pygame_menu.utils import assert_color, assert_vector, make_surface, \
is_callable, assert_alignment, parse_padding
from pygame_menu.widgets.core.widget import Widget, WidgetTransformationNotImplemented
from pygame_menu._types import Any, CallbackType, Optional, ColorType, NumberType, \
Tuple2IntType, NumberInstance, ColorInputType, EventVectorType, Callable, \
PaddingType, Tuple4IntType
ProgressBarTextFormatType = Callable[[NumberType], str]
# noinspection PyMissingOrEmptyDocstring
[docs]class ProgressBar(Widget):
"""
Progress bar widget, offers a bar that accepts a percentage from ``0`` to ``100``.
.. note::
ProgressBar only accepts translation transformation.
:param title: Progressbar title
:param progressbar_id: ProgressBar ID
:param default: Default value of the progressbar, from ``0`` to ``100``
:param width: Progress bar width in px
:param onselect: Function when selecting the widget
:param box_background_color: Background color of the box
:param box_border_color: Border color of the box
:param box_border_width: Border width of the box in px
:param box_margin: Box margin on x-axis and y-axis (x, y) respect to the title of the widget in px
:param box_progress_color: Box progress color
:param box_progress_padding: Box progress padding
:param progress_text_align: Align of the progress text, can be CENTER, LEFT or RIGHT. See :py:mod:`pygame_menu.locals`
:param progress_text_enabled: Enables the progress text over box
:param progress_text_font: Progress font. If ``None`` uses the same as the widget font
:param progress_text_font_color: Progress font color. If ``None`` uses the same as the widget font
:param progress_text_font_hfactor: Height factor of the font height relative to the widget font height
:param progress_text_format: Format function of the progress text, which considers as input the progress value (0-100)
:param progress_text_margin: Margin of the progress box on x-axis and y-axis in px
:param progress_text_placeholder: Placeholder of the progress text, which considers as format the output of ``progress_text_format``
:param args: Optional arguments for callbacks
:param kwargs: Optional keyword arguments
"""
_box: 'pygame.Surface'
_box_background_color: ColorType
_box_border_color: ColorType
_box_border_width: int
_box_height: int
_box_margin: Tuple2IntType
_box_pos: int
_box_progress_color: ColorType
_box_progress_padding: Tuple4IntType
_progress: NumberType
_progress_font: FontType
_progress_text_align: str
_progress_text_enabled: bool
_progress_text_font: Optional[FontType]
_progress_text_font_color: ColorType
_progress_text_font_height: int
_progress_text_font_height_factor: float
_progress_text_format: ProgressBarTextFormatType
_progress_text_margin: Tuple2IntType
_progress_text_placeholder: str
_width: int
def __init__(
self,
title: Any,
progressbar_id: str = '',
default: NumberType = 0,
width: int = 150,
onselect: CallbackType = None,
box_background_color: ColorInputType = (255, 255, 255),
box_border_color: ColorInputType = (0, 0, 0),
box_border_width: int = 1,
box_margin: Tuple2IntType = (25, 0),
box_progress_color: ColorInputType = (0, 255, 0),
box_progress_padding: PaddingType = (1, 1),
progress_text_align: str = ALIGN_CENTER,
progress_text_enabled: bool = True,
progress_text_font: Optional[FontType] = None,
progress_text_font_color: ColorInputType = (0, 0, 0),
progress_text_font_hfactor: float = 0.8,
progress_text_format: ProgressBarTextFormatType = lambda x: str(round(x, 1)),
progress_text_margin: Tuple2IntType = (0, 0),
progress_text_placeholder: str = '{0} %',
*args,
**kwargs
) -> None:
super(ProgressBar, self).__init__(
args=args,
kwargs=kwargs,
onselect=onselect,
title=title,
widget_id=progressbar_id
)
# Check the value
assert isinstance(default, NumberInstance)
assert 0 <= default <= 100, 'default value must range from 0 to 100'
# Check fonts
if progress_text_font is not None:
assert_font(progress_text_font)
assert isinstance(progress_text_font_hfactor, NumberInstance)
assert progress_text_font_hfactor > 0, \
'progress text font height factor must be greater than zero'
# Check colors
box_background_color = assert_color(box_background_color)
box_border_color = assert_color(box_border_color)
box_progress_color = assert_color(box_progress_color)
progress_text_font_color = assert_color(progress_text_font_color)
# Check dimensions and sizes
assert isinstance(box_border_width, int)
assert box_border_width >= 0, \
'box border width must be equal or greater than zero'
assert_vector(box_margin, 2, int)
assert_vector(progress_text_margin, 2, int)
assert isinstance(width, int)
assert width > 0, 'width must be greater than zero'
box_progress_padding = parse_padding(box_progress_padding)
self._box_progress_padding = box_progress_padding
# Check progress text
assert isinstance(progress_text_enabled, bool)
assert is_callable(progress_text_format)
assert isinstance(progress_text_format(0), str)
assert isinstance(progress_text_placeholder, str)
assert_alignment(progress_text_align)
# Store properties
self._default_value = default
self._box_background_color = box_background_color
self._box_border_color = box_border_color
self._box_border_width = box_border_width
self._box_margin = box_margin
self._box_progress_color = box_progress_color
self._progress = default
self._progress_text_align = progress_text_align
self._progress_text_enabled = progress_text_enabled
self._progress_text_font = progress_text_font
self._progress_text_font_color = progress_text_font_color
self._progress_text_font_height = 0
self._progress_text_font_height_factor = progress_text_font_hfactor
self._progress_text_format = progress_text_format
self._progress_text_margin = progress_text_margin
self._progress_text_placeholder = progress_text_placeholder
self._width = width
[docs] def set_value(self, value: NumberType) -> None:
assert isinstance(value, NumberInstance), 'progress value must be numeric'
assert 0 <= value <= 100, 'value must be between 0 and 100'
self._progress = value
self._render()
def scale(self, *args, **kwargs) -> 'ProgressBar':
raise WidgetTransformationNotImplemented()
def resize(self, *args, **kwargs) -> 'ProgressBar':
raise WidgetTransformationNotImplemented()
def set_max_width(self, *args, **kwargs) -> 'ProgressBar':
raise WidgetTransformationNotImplemented()
def set_max_height(self, *args, **kwargs) -> 'ProgressBar':
raise WidgetTransformationNotImplemented()
def rotate(self, *args, **kwargs) -> 'ProgressBar':
raise WidgetTransformationNotImplemented()
def flip(self, *args, **kwargs) -> 'ProgressBar':
raise WidgetTransformationNotImplemented()
[docs] def get_value(self) -> NumberType:
return self._progress
def _apply_font(self) -> None:
if self._progress_text_font is None:
self._progress_text_font = self._font_name
self._progress_text_font_height = int(self._font_size * self._progress_text_font_height_factor)
self._progress_font = pygame_menu.font.get_font(
self._progress_text_font, self._progress_text_font_height
)
self._box_height = self._font_render_string('TEST').get_height()
def _draw(self, surface: 'pygame.Surface') -> None:
# Draw title
surface.blit(self._surface, (self._rect.x, self._rect.y))
# Draw box
box_rect = self._box.get_rect()
box_rect.x += self._rect.x + self._box_margin[0] + self._box_pos
box_rect.y += self._rect.y + self._box_margin[1]
surface.blit(self._box, box_rect)
# Draw box border
if self._box_border_width > 0:
pygame.draw.rect(surface, self._box_border_color, box_rect, self._box_border_width)
def _render(self) -> Optional[bool]:
if not hasattr(self, '_progress_font'):
return False
if not self._render_hash_changed(
self._selected, self._title, self._visible, self.readonly,
self._progress):
return True
# Create basic title
self._surface = self._render_string(self._title, self.get_font_color_status())
self._rect.width, self._rect.height = self._surface.get_size()
# Create box
self._box = make_surface(self._width, self._box_height,
fill_color=self._box_background_color)
box_progress = make_surface(int(self._width * self._progress / 100),
self._box_height - self._box_progress_padding[0] - self._box_progress_padding[2],
fill_color=self._box_progress_color)
self._box.blit(box_progress, (self._box_progress_padding[1], self._box_progress_padding[0]))
self._box_pos = self._rect.width
# Create progress text
text = self._progress_font.render(
self._progress_text_placeholder.format(self._progress_text_format(self._progress)),
self._font_antialias,
self._progress_text_font_color
)
text_y = int((self._box_height - text.get_height()) / 2)
if self._progress_text_align == ALIGN_LEFT:
text_x = self._box_progress_padding[1]
elif self._progress_text_align == ALIGN_CENTER:
text_x = int((self._width - text.get_width()) / 2)
else:
text_x = self._width - self._box_progress_padding[3] - text.get_width()
text_x += self._progress_text_margin[0]
text_y += self._progress_text_margin[1]
if self._progress_text_enabled:
self._box.blit(text, (text_x, text_y))
# Update maximum rect height
self._rect.height = max(self._rect.height, self._box.get_height())
self._rect.width += self._width + self._box_margin[0]
# Finals
self.force_menu_surface_update()
[docs] def update(self, events: EventVectorType) -> bool:
self.apply_update_callbacks(events)
return False