def update_callback(self, callback, *args): """ Update function triggered by the button; ``callback`` cannot point to a Menu, that behaviour is only valid using ``Menu.add_button()`` method. .. note:: If button points to a submenu, and the callback is changed to a function, the submenu will be removed from the parent menu. Thus preserving the structure. :param callback: Function :type callback: callable :param args: Arguments used by the function once triggered :type args: any :return: None """ assert is_callable(callback), 'only function are allowed' # If return is a Menu object, remove it from submenus list if self._menu is not None and self._on_return is not None and self.to_menu: assert len(self._args) == 1 submenu = self._args[0] # Menu assert self._menu.in_submenu(submenu, recursive=False), \ 'pointed menu is not in submenu list of parent container' # noinspection PyProtectedMember assert self._menu._remove_submenu( submenu, recursive=False), 'submenu could not be removed' self.to_menu = False self._args = args or [] # type: list self._on_return = callback
def add_callable(self, fun: Union[Callable[['pygame.Surface', Any], Any], Callable[[], Any]], prev: bool = True, pass_args: bool = True) -> str: """ Adds a callable method. The function receives the surface and the object; for example, if adding to a widget: .. code-block:: python fun(surface, object) .. note:: If your callable function changes over time set ``decorator.cache=False`` or force cache manually by calling Decorator method :py:meth:`pygame_menu._decorator.Decorator.force_cache_update`. Also, the object should force the menu surface cache to update. :param fun: Function :param prev: If ``True`` draw previous the object, else draws post :param pass_args: If ``False`` function is called without (surface, object) as args :return: ID of the decoration """ assert is_callable(fun), 'fun must be a callable type' assert isinstance(pass_args, bool) if pass_args: return self._add_decor(DECORATION_CALLABLE, prev, fun) else: return self._add_decor(DECORATION_CALLABLE_NO_ARGS, prev, fun)
def add_draw_callback(self, draw_callback): """ Adds a function to the widget to be executed each time the widget is drawn. The function that this method receives receives two objects: the widget itself and the menu reference. .. code-block:: python import math def draw_update_function(widget, menu): t = widget.get_attribute('t', 0) t += menu.get_clock().get_time() widget.set_padding(10*(1 + math.sin(t)))) # Oscillating padding button = menu.add_button('This button updates its padding', None) button.set_draw_callback(draw_update_function) After creating a new callback, this functions returns the ID of the call. It can be removed anytime using ``widget.remove_draw_callback(id)``. :param draw_callback: Function :type draw_callback: callable :return: Callback ID :rtype: str """ assert is_callable( draw_callback), 'draw callback must be a function type' callback_id = str(uuid4()) self._draw_callbacks[callback_id] = draw_callback return callback_id
def update_callback(self, callback: Callable, *args) -> None: """ Update function triggered by the button; ``callback`` cannot point to a Menu, that behaviour is only valid using :py:meth:`pygame_menu.menu.Menu.add.button` method. .. note:: If button points to a submenu, and the callback is changed to a function, the submenu will be removed from the parent Menu. Thus preserving the structure. :param callback: Function :param args: Arguments used by the function once triggered :return: None """ assert is_callable(callback), \ 'only callable (function-type) are allowed' # If return is a Menu object, remove it from submenus list if self._menu is not None and self._onreturn is not None and self.to_menu: assert len(self._args) == 1 submenu = self._args[0] # Menu assert self._menu.in_submenu(submenu), \ 'pointed menu is not in submenu list of parent container' # noinspection PyProtectedMember assert self._menu._remove_submenu(submenu), \ 'submenu could not be removed' self.to_menu = False self._args = args or [] self._onreturn = callback
def _get_current_selected_text(self) -> str: if len(self._selected_indices) == 0: return self._placeholder # Apply selected format if self._selection_placeholder_format == DROPSELECT_MULTIPLE_SFORMAT_TOTAL: return self._placeholder_selected.format(len(self._selected_indices)) list_items = self._get_selected_items_list_str() if self._selection_placeholder_format == DROPSELECT_MULTIPLE_SFORMAT_LIST_COMMA: return self._placeholder_selected.format(','.join(list_items)) elif self._selection_placeholder_format == DROPSELECT_MULTIPLE_SFORMAT_LIST_HYPHEN: return self._placeholder_selected.format('-'.join(list_items)) elif isinstance(self._selection_placeholder_format, str): return self._placeholder_selected.format(self._selection_placeholder_format.join(list_items)) elif is_callable(self._selection_placeholder_format): try: o = self._selection_placeholder_format(list_items) except TypeError: raise ValueError('selection placeholder function receives only 1 ' 'argument (a list of the selected items string)' ' and must return a string') assert isinstance(o, str), \ 'output from selection placeholder format function must be a ' \ 'string (List[str]=>str), not {0} type ({1} returned)' \ ''.format(type(o), o) return self._placeholder_selected.format(o) else: raise ValueError('invalid selection placeholder format type')
def __init__(self, menu: 'pygame_menu.Menu', menu_opener_handler: Callable, link_id: str = '') -> None: assert isinstance(menu, pygame_menu.Menu) assert is_callable(menu_opener_handler), \ 'menu opener handler must be callable (a function)' super(MenuLink, self).__init__(widget_id=link_id) self.menu = menu self._onreturn = menu_opener_handler self._visible = False self.is_selectable = False
def set_selection_callback( self, callback: Optional[Callable[[bool, 'Widget', 'pygame_menu.Menu'], Any]] ) -> None: """ Update the button selection callback, once button is selected, the callback function is executed as follows: .. code-block:: python callback(selected, widget, menu) :param callback: Callback when selecting the widget, executed in :py:meth:`pygame_menu.widgets.core.widget.Widget.set_selected` :return: None """ if callback is not None: assert is_callable(callback), \ 'callback must be callable (function-type) or None' self._onselect = callback
def set_title_generator(self, generator: LabelTitleGeneratorType) -> 'Label': """ Set a title generator. This function is executed each time the label updates, returning a new title (string) which replaces the current label title. The generator does not take any input as argument. :param generator: Function which generates a new text status :return: Self reference """ if generator is not None: assert is_callable(generator) self._title_generator = generator # Update update widgets menu_update_widgets = self._get_menu_update_widgets() if generator is None and self in menu_update_widgets: menu_update_widgets.remove(self) if generator is not None and self not in menu_update_widgets: menu_update_widgets.append(self) return self
def add_update_callback(self, update_callback): """ Adds a function to the widget to be executed each time the widget is updated. The function that this method receives receives two objects: the widget itself and the menu reference. It is similar to ``add_draw_callback``. After creating a new callback, this functions returns the ID of the call. It can be removed anytime using ``widget.remove_update_callback(id)``. .. note:: Not all widgets are updated, so the provided function may never be executed. :param update_callback: Function :type update_callback: callable :return: Callback ID :rtype: str """ assert is_callable( update_callback), 'update callback must be a function type' callback_id = str(uuid4()) self._update_callbacks[callback_id] = update_callback return callback_id
def _get(params: Dict[str, Any], key: str, allowed_types: Optional[Union[Type, str, List[Type], Tuple[Type, ...]]] = None, default: Any = None) -> Any: """ Return a value from a dictionary. Custom types (str) - alignment – pygame-menu alignment (locals) - callable – Is callable type, same as ``"function"`` - color – Check color - color_image – Color or :py:class:`pygame_menu.baseimage.BaseImage` - color_image_none – Color, :py:class:`pygame_menu.baseimage.BaseImage`, or None - color_none – Color or None - cursor – Cursor object (pygame) - font – Font type - image – Value must be ``BaseImage`` - none – None only - position – pygame-menu position (locals) - position_vector – pygame-menu position (str or vector) - tuple2 – Only valid numeric tuples ``(x, y)`` or ``[x, y]`` - tuple2int – Only valid integer tuples ``(x, y)`` or ``[x, y]`` - tuple3 – Only valid numeric tuples ``(x, y, z)`` or ``[x, y, z]`` - tuple3int – Only valid integer tuples ``(x, y, z)`` or ``[x, y, z]`` - type – Type-class (bool, str, etc...) :param params: Parameters dictionary :param key: Key to look for :param allowed_types: List of allowed types :param default: Default value to return :return: The value associated to the key """ value = params.pop(key, default) if allowed_types is not None: other_types = [] # Contain other types to check from if not isinstance(allowed_types, VectorInstance): allowed_types = (allowed_types, ) for val_type in allowed_types: if val_type == 'alignment': assert_alignment(value) elif val_type == callable or val_type == 'function' or val_type == 'callable': assert is_callable(value), \ 'value must be callable type' elif val_type == 'color': value = assert_color(value) elif val_type == 'color_image': if not isinstance(value, BaseImage): value = assert_color(value) elif val_type == 'color_image_none': if not (value is None or isinstance(value, BaseImage)): value = assert_color(value) elif val_type == 'color_none': if value is not None: value = assert_color(value) elif val_type == 'cursor': assert_cursor(value) elif val_type == 'font': assert_font(value) elif val_type == 'image': assert isinstance(value, BaseImage), \ 'value must be BaseImage type' elif val_type == 'none': assert value is None elif val_type == 'position': assert_position(value) elif val_type == 'position_vector': assert_position_vector(value) elif val_type == 'type': assert isinstance(value, type), \ 'value is not type-class' elif val_type == 'tuple2': assert_vector(value, 2) elif val_type == 'tuple2int': assert_vector(value, 2, int) elif val_type == 'tuple3': assert_vector(value, 3) elif val_type == 'tuple3int': assert_vector(value, 3, int) else: # Unknown type assert isinstance(val_type, type), \ 'allowed type "{0}" is not a type-class'.format(val_type) other_types.append(val_type) # Check other types if len(other_types) > 0: others = tuple(other_types) assert isinstance(value, others), \ 'Theme.{} type shall be in {} types (got {})'.format(key, others, type(value)) return value
def __init__(self, title='', widget_id='', onchange=None, onreturn=None, args=None, kwargs=None): assert isinstance(widget_id, str), 'widget id must be a string' if onchange: assert is_callable(onchange), 'onchange must be callable or None' if onreturn: assert is_callable(onreturn), 'onreturn must be callable or None' # Store id, if None or empty create new ID based on UUID if widget_id is None or len(widget_id) == 0: widget_id = uuid4() self._alignment = _locals.ALIGN_CENTER self._attributes = {} # Stores widget attributes self._background_color = None self._background_inflate = (0, 0) self._events = [] # type: list self._id = str(widget_id) self._margin = (0.0, 0.0) # type: tuple self._max_width = None # type: (int,float) self._padding = (0, 0, 0, 0) # top, right, bottom, left self._selection_time = 0 # type: float self._title = to_string(title) # Widget transforms self._angle = 0 # Rotation angle (degrees) self._flip = (False, False) # x, y self._scale = [False, 1, 1, False, False] # do_scale, x, y, smooth, use_same_xy self._translate = (0.0, 0.0) # type: tuple # Widget rect. This object does not contain padding. For getting the widget+padding # use .get_rect() Widget method instead self._rect = pygame.Rect(0, 0, 0, 0) # type: pygame.Rect # Callbacks self._draw_callbacks = {} # type: dict self._update_callbacks = {} # type: dict self._args = args or [] # type: list self._kwargs = kwargs or {} # type: dict self._on_change = onchange # type: callable self._on_return = onreturn # type: callable # Surface of the widget self._surface = None # type: (pygame.Surface,None) # Menu reference self._menu = None # If this is True then the widget forces the Menu to update because the # widget render has changed self._menu_surface_needs_update = False # Modified in set_font() method self._font = None # type: (pygame.font.Font,None) self._font_antialias = True # type: bool self._font_background_color = None # type: (tuple, None) self._font_color = (0, 0, 0) # type: tuple self._font_name = '' # type: str self._font_selected_color = (255, 255, 255) # type: tuple self._font_size = 0 # type: int # Text shadow self._shadow = False # type: bool self._shadow_color = (0, 0, 0) # type: tuple self._shadow_offset = 2.0 # type: float self._shadow_position = _locals.POSITION_NORTHWEST self._shadow_tuple = None # (x px offset, y px offset) self._create_shadow_tuple() # Rendering, this variable may be used by render() method # If the hash of the variables change respect to the last render hash # (hash computed using self._hash_variables() method) # then the widget should render and update the hash self._last_render_hash = 0 # type: int # Selection effect, for avoiding exception while getting object rect, NullSelection # was created. Initially it was None self._selection_effect = _NullSelection() # type: Selection # Public attributes self.active = False # Widget requests focus self.is_selectable = True # Some widgets cannot be selected like labels self.joystick_enabled = True self.lock_position = False # If True, locks position after first call to .set_position(x,y) method self.mouse_enabled = True self.selected = False self.selection_effect_enabled = True # Some widgets cannot have selection effect self.selection_expand_background = False # If True, the widget background will inflate to match selection margin if selected self.sound = Sound() # type: Sound self.touchscreen_enabled = True self.visible = True # Use .show() or .hide() to modify this status
def _get(params: Dict[str, Any], key: str, allowed_types: Optional[Union[Type, str, List[Type], Tuple[Type, ...]]] = None, default: Any = None) -> Any: """ Return a value from a dictionary. Custom types (str) - alignment pygame-menu alignment (locals) - callable Is callable type, same as ``'function'`` - color Check color - color_image Color or :py:class:`pygame_menu.baseimage.BaseImage` - color_image_none Color, :py:class:`pygame_menu.baseimage.BaseImage`, or None - color_none Color or None - image Value must be ``BaseImage`` - none None only - position pygame-menu position (locals)} - type Type-class (bool, str, etc...) - tuple2 Only valid numeric tuples ``(x,y)`` or ``[x,y]`` - tuple3 Only valid numeric tuples ``(x,y,z)`` or ``[x,y,z]`` :param params: Parameters dictionary :param key: Key to look for :param allowed_types: List of allowed types :param default: Default value to return :return: The value associated to the key """ value = params.pop(key, default) if allowed_types is not None: other_types = [] # Contain other types to check from if not isinstance(allowed_types, (tuple, list)): allowed_types = (allowed_types,) for valtype in allowed_types: if valtype == 'alignment': _utils.assert_alignment(value) elif valtype == callable or valtype == 'function' or valtype == 'callable': assert _utils.is_callable(value), 'value must be callable type' elif valtype == 'color': _utils.assert_color(value) elif valtype == 'color_image': if isinstance(value, BaseImage): return value _utils.assert_color(value) elif valtype == 'color_image_none': if value is None or isinstance(value, BaseImage): return value _utils.assert_color(value) elif valtype == 'color_none': if value is None: return value _utils.assert_color(value) elif valtype == 'image': assert isinstance(value, BaseImage), 'value must be BaseImage type' elif valtype == 'none': assert value is None elif valtype == 'position': _utils.assert_position(value) elif valtype == 'type': assert isinstance(value, type), 'value is not type-class' elif valtype == 'tuple2': _utils.assert_vector(value, 2) elif valtype == 'tuple3': _utils.assert_vector(value, 3) else: # Unknown type assert isinstance(valtype, type), \ 'allowed type "{0}" is not a type-class'.format(valtype) other_types.append(valtype) # Check other types if len(other_types) > 0: others = tuple(other_types) msg = 'Theme.{} type shall be in {} types (got {})'.format(key, others, type(value)) assert isinstance(value, others), msg return value
def test_callable(self) -> None: """ Test is callable. """ self.assertTrue(ut.is_callable(bool)) self.assertFalse(ut.is_callable(1))
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