Package organization
The pygame_menu.widgets
package contains the widget support for the Menu.
Its structure consists of several sub-packages:
pygame_menu/
widgets/
core/ Main object classes for Widget and Selector
selection/ Selection effect applied to widgets
widget/ Menu widget objects
Create a widget
All widget classes shall inherit from pygame_menu.widgets.core.widget.Widget
,
and they must be located in the pygame_menu.widgets.widget
package. The
most basic widget should contain this code:
from pygame_menu.widgets.core.widget import Widget
from pygame_menu._types import EventVectorType
class MyWidget(Widget):
def __init__(self, params):
super(MyWidget, self).__init__(params)
...
def _apply_font(self) -> None:
"""
Function triggered after a font is applied to the widget
by Menu._configure_widget() method.
"""
...
def _draw(self, surface: 'pygame.Surface') -> None:
"""
Draw the widget on a given surface.
This method must be overridden by all classes.
"""
# Draw the self._surface pygame object on the given surface
surface.blit(self._surface, self._rect.topleft)
def _render(self) -> Optional[bool]:
"""
Render the Widget surface.
This method shall update the attribute ``_surface`` with a :py:class:`pygame.Surface`
object representing the outer borders of the widget.
.. note::
Before rendering, check out if the widget font/title/values are
set. If not, it is probable that a zero-size surface is set.
.. note::
Render methods should call
:py:meth:`pygame_menu.widgets.core.widget.Widget.force_menu_surface_update`
to force Menu to update the drawing surface.
:return: ``True`` if widget has rendered a new state, ``None`` if the widget has not changed, so render used a cache
"""
...
# Generate widget surface
self._surface = pygame.surface....
# Update the width and height of the Widget
self._rect.width, self._rect.height = self._surface.get_size() + SomeConstants
# Force menu to update its surface on next Menu.render() call
self.force_menu_surface_update()
def update(self, events: EventVectorType) -> bool:
"""
Update according to the given events list and fire the callbacks. This
method must return ``True`` if it updated (the internal variables
changed during user input).
.. note::
Update is not performed if the Widget is in ``readonly`` state or
it's hidden. However, ``apply_update_callbacks`` method is called
in most widgets, except :py:class:`pygame_menu.widgets.NoneWidget`.
:param events: List/Tuple of pygame events
:return: ``True`` if updated
"""
...
return False
Warning
- After creating the widget, it must be added to the
__init__.py
file of the pygame_menu.widgets
package.
from pygame_menu.widgets.widget.mywidget import MyWidget
To add the widget to the pygame_menu.menu.Menu
class, a public method
add_mywidget()
must be added to the pygame_menu._widgetmanager.WidgetManager
class with the following structure. Or pygame_menu._widgetmanager.WidgetManager.generic_widget()
can be used.
import pygame_menu.widgets
class WidgetManager(object):
...
def mywidget(self, params, **kwargs):
"""
Add MyWidget to the menu.
"""
attributes = self._filter_widget_attributes(kwargs)
# Create your widget
widget = pygame_menu.widgets.MyWidget(..., **kwargs)
self._configure_widget(widget=widget, **attributes)
widget.set_default_value(default) # May add the default value
self._append_widget(widget)
return widget
...
Note
This method uses the kwargs parameter for defining the settings of the
Widget, such as the background, margin, etc. This is applied automatically
by the Menu widget addition class (WidgetManager
). If MyWidget needs
additional parameters, please use some that are not named as the default kwargs
used by the Menu Widget system.
The function must return the created widget object.
Note
The widget _render
method should always call
pygame_menu.widgets.core.widget.Widget.force_menu_surface_update()
method, this ensures that Menu updates the surface and the positioning.
Note
From v4
menu introduced a cache state for the draw surface. This cache
is updated if any widget update its status (update()
returned True) or
the surface was rendered. Anyway, execution-time elements that changes over
time (outside _render
) should force cache rendering (for example the
blinking cursor of text). If your widget has any property like this, the method
pygame_menu.widgets.core.widget.Widget.force_menu_surface_cache_update()
must be called within your Widget.
Create a selection effect
The widgets in Menu are drawn with the following idea:
Each time a new Widget is added, regenerate their position.
Widgets can either be active or inactive. The active widget will catch user events as keyboard or mouse.
Active widgets have a decoration, named Selection
The drawing process is:
Draw Menu background color/image
Draw all widgets
Draw Selection decoration on selected widget surface area
Draw menubar
Draw scrollbar
For defining a new selection effect, a new pygame_menu.widgets.core.Selection
subclass must be added to the pygame_menu.widgets.selection
package. A
basic class must contain the following code:
from pygame_menu.widgets.core.selection import Selection
class MySelection(Selection):
def __init__(self):
# Call the constructor of the Selection providing the left, right, top and bottom margins
# of your Selection effect box.
#
# --------------------------
# | ^ top | In this example, XXXX represents the
# | left XXXXXXXXXXXX right | Widget to be Selected.
# |<----> XXXXXXXXXXXX<----->| left, right, top and bottom must be described
# | v bottom | in pixels
# --------------------------
#
super(MySelection, self).__init__(margin_left, margin_right, margin_top, margin_bottom)
self.your_params = ...
def draw(self, surface, widget):
"""
This method receives the surface to draw the selection and the
widget itself. For retrieving the Selection coordinates the rect
object from widget should be used.
"""
surface.draw(.....)
Warning
After creating the selection effect, it must be added to __init__.py
file
of the pygame_menu.widgets
package.
from pygame_menu.widgets.selection.myselection import MySelection
Finally, this new selection effect can be set by following one of these two instructions:
Pass it when adding a new widget to the menu
import pygame_menu menu = pygame_menu.Menu(...) menu.add.button(..., selection_effect=pygame_menu.widgets.MySelection(...))
To apply it on all menus and widgets (and avoid passing it for each added widget), a theme can be created
import pygame_menu MY_THEME = pygame_menu.themes.Theme( ..., widget_selection_effect=pygame_menu.widgets.MySelection(...) ) menu = pygame_menu.Menu(..., theme=MY_THEME)