"""
pygame-menu
https://github.com/ppizarror/pygame-menu
PROGRESS BAR
Progress bar widget.
"""
__all__ = [
# Class
'ProgressBar',
'ProgressBarManager',
# Types
'ProgressBarTextFormatType'
]
import pygame
import pygame_menu
from abc import ABC
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, \
assert_alignment, parse_padding
from pygame_menu.widgets.core.widget import Widget, WidgetTransformationNotImplemented, \
AbstractWidgetManager
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 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
elif 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
class ProgressBarManager(AbstractWidgetManager, ABC):
"""
ProgressBar manager.
"""
def progress_bar(
self,
title: Any,
default: NumberType = 0,
onselect: CallbackType = None,
progressbar_id: str = '',
progress_text_format: ProgressBarTextFormatType = lambda x: str(round(x, 1)),
selectable: bool = False,
width: int = 150,
**kwargs
) -> 'pygame_menu.widgets.ProgressBar':
"""
Add a progress bar, which offers a bar that accepts a percentage from
``0`` to ``100``.
If ``onselect`` is defined, the callback is executed as follows, where
``selected`` is a boolean representing the selected status:
.. code-block:: python
onselect(selected, widget, menu)
kwargs (Optional)
- ``align`` (str) – Widget `alignment <https://pygame-menu.readthedocs.io/en/latest/_source/themes.html#alignment>`_
- ``background_color`` (tuple, list, str, int, :py:class:`pygame.Color`, :py:class:`pygame_menu.baseimage.BaseImage`) – Color of the background. ``None`` for no-color
- ``background_inflate`` (tuple, list) – Inflate background on x-axis and y-axis (x, y) in px
- ``border_color`` (tuple, list, str, int, :py:class:`pygame.Color`) – Widget border color. ``None`` for no-color
- ``border_inflate`` (tuple, list) – Widget border inflate on x-axis and y-axis (x, y) in px
- ``border_position`` (str, tuple, list) – Widget border positioning. It can be a single position, or a tuple/list of positions. Only are accepted: north, south, east, and west. See :py:mod:`pygame_menu.locals`
- ``border_width`` (int) – Border width in px. If ``0`` disables the border
- ``box_background_color`` (tuple, list, str, int, :py:class:`pygame.Color`) – Background color of the box
- ``box_border_color`` (tuple, list, str, int, :py:class:`pygame.Color`) – Border color of the box
- ``box_border_width`` (int) - Border width of the box in px
- ``box_margin`` (tuple, list) - Box margin on x-axis and y-axis (x, y) respect to the title of the widget in px
- ``box_progress_color`` (tuple, list, str, int, :py:class:`pygame.Color`) – Box progress color
- ``box_progress_padding`` (int, float, tuple, list) – Box progress padding
- ``cursor`` (int, :py:class:`pygame.cursors.Cursor`, None) – Cursor of the widget if the mouse is placed over
- ``float`` (bool) - If ``True`` the widget don't contribute width/height to the Menu widget positioning computation, and don't add one unit to the rows
- ``float_origin_position`` (bool) - If ``True`` the widget position is set to the top-left position of the Menu if the widget is floating
- ``font_background_color`` (tuple, list, str, int, :py:class:`pygame.Color`, None) – Widget font background color
- ``font_color`` (tuple, list, str, int, :py:class:`pygame.Color`) – Widget font color
- ``font_name`` (str, :py:class:`pathlib.Path`, :py:class:`pygame.font.Font`) – Widget font path
- ``font_shadow_color`` (tuple, list, str, int, :py:class:`pygame.Color`) – Font shadow color
- ``font_shadow_offset`` (int) – Font shadow offset in px
- ``font_shadow_position`` (str) – Font shadow position, see locals for position
- ``font_shadow`` (bool) – Font shadow is enabled or disabled
- ``font_size`` (int) – Font size of the widget
- ``margin`` (tuple, list) – Widget (left, bottom) margin in px
- ``padding`` (int, float, tuple, list) – Widget padding according to CSS rules. General shape: (top, right, bottom, left)
- ``progress_text_align`` (str) - Align of the progress text, can be CENTER, LEFT or RIGHT. See :py:mod:`pygame_menu.locals`
- ``progress_text_enabled`` (bool) - Enables the progress text over box
- ``progress_text_font_color`` (tuple, list, str, int, :py:class:`pygame.Color`) – Progress font color. If ``None`` uses the same as the widget font
- ``progress_text_font_hfactor`` (int, float) - Height factor of the font height relative to the widget font height
- ``progress_text_font`` (str, :py:class:`pathlib.Path`, :py:class:`pygame.font.Font`) – Progress font. If ``None`` uses the same as the widget font
- ``progress_text_margin`` (tuple, list) - Margin of the progress box on x-axis and y-axis in px
- ``progress_text_placeholder`` (str) - Placeholder of the progress text, which considers as format the output of ``progress_text_format``. ``"{0} %"`` by default
- ``readonly_color`` (tuple, list, str, int, :py:class:`pygame.Color`) – Color of the widget if readonly mode
- ``readonly_selected_color`` (tuple, list, str, int, :py:class:`pygame.Color`) – Color of the widget if readonly mode and is selected
- ``selection_color`` (tuple, list, str, int, :py:class:`pygame.Color`) – Color of the selected widget; only affects the font color
- ``selection_effect`` (:py:class:`pygame_menu.widgets.core.Selection`) – Widget selection effect
- ``shadow_color`` (tuple, list, str, int, :py:class:`pygame.Color`) – Color of the widget shadow
- ``shadow_radius`` (int) - Border radius of the shadow
- ``shadow_type`` (str) - Shadow type, it can be ``'rectangular'`` or ``'ellipse'``
- ``shadow_width`` (int) - Width of the shadow. If ``0`` the shadow is disabled
- ``tab_size`` (int) – Width of a tab character
.. note::
All theme-related optional kwargs use the default Menu theme if not
defined.
.. note::
This is applied only to the base Menu (not the currently displayed,
stored in ``_current`` pointer); for such behaviour apply to
:py:meth:`pygame_menu.menu.Menu.get_current` object.
.. warning::
Be careful with kwargs collision. Consider that all optional documented
kwargs keys are removed from the object.
:param title: Title of the progress bar
:param default: Default value of the progressbar, from ``0`` to ``100``
:param onselect: Callback executed when selecting the widget
:param progressbar_id: ID of the progress bar
:param progress_text_format: Format function of the progress text, which considers as input the progress value (0-100)
:param selectable: Progress bar accepts user selection
:param width: Progress bar width in px
:param kwargs: Optional keyword arguments
:return: Widget object
:rtype: :py:class:`pygame_menu.widgets.ProgressBar`
"""
assert isinstance(selectable, bool)
# Filter widget attributes to avoid passing them to the callbacks
attributes = self._filter_widget_attributes(kwargs)
box_background_color = kwargs.pop('box_background_color', self._theme.widget_box_background_color)
box_border_color = kwargs.pop('box_border_color', self._theme.widget_box_border_color)
box_border_width = kwargs.pop('box_border_width', self._theme.widget_box_border_width)
box_margin = kwargs.pop('box_margin', self._theme.widget_box_margin)
box_progress_color = kwargs.pop('box_progress_color', (53, 172, 78))
progress_text_font_color = kwargs.pop('progress_text_font_color', self._theme.widget_font_color)
widget = pygame_menu.widgets.ProgressBar(
title=title,
progressbar_id=progressbar_id,
default=default,
width=width,
onselect=onselect,
box_background_color=box_background_color,
box_border_color=box_border_color,
box_border_width=box_border_width,
box_margin=box_margin,
box_progress_color=box_progress_color,
progress_text_font_color=progress_text_font_color,
progress_text_format=progress_text_format,
**kwargs
)
widget.is_selectable = selectable
self._configure_widget(widget=widget, **attributes)
self._append_widget(widget)
return widget