def set_alignment(self, align): """ Set the alignment of the widget. :param align: Widget align, see locals :type align: str :return: None """ assert_alignment(align) self._alignment = align
def _get(params, key, allowed_types=None, default=None): """ Return a value from a dictionary. :param params: parameters dictionary :type params: dict :param key: key to look for :type key: str :param allowed_types: list of allowed types :type allowed_types: any :param default: default value to return :type default: any :return: The value associated to the key :rtype: any """ if key not in params: return default value = params.pop(key) if allowed_types: if not isinstance(allowed_types, (tuple, list)): allowed_types = (allowed_types, ) for valtype in allowed_types: if valtype == 'color': _utils.assert_color(value) elif valtype == 'color_none': if value is None: return value _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: return value elif isinstance(value, BaseImage): return value _utils.assert_color(value) elif valtype == 'position': _utils.assert_position(value) elif valtype == 'alignment': _utils.assert_alignment(value) elif valtype == 'tuple2': _utils.assert_vector2(value) all_types = ('color', 'color_none', 'color_image', 'color_image_none', 'position', 'alignment', 'tuple2') others = tuple(t for t in allowed_types if t not in all_types) if others: msg = 'Theme.{} type shall be in {} types (got {})'.format( key, others, type(value)) assert isinstance(value, others), msg return value
def _check_cell_style(align: str, background_color: ColorInputType, border_color: ColorInputType, border_position: WidgetBorderPositionType, border_width: int, padding: PaddingType, vertical_position: str) -> None: """ Assert cell style. :param align: Horizontal align of each cell. See :py:mod:`pygame_menu.locals` :param background_color: Background color :param border_color: Border color of each cell :param border_position: Border position of each cell. Valid only: north, south, east, and west. See :py:mod:`pygame_menu.locals` :param border_width: Border width in px of each cell :param padding: Cell padding according to CSS rules. General shape: (top, right, bottom, left) :param vertical_position: Vertical position of each cell. Only valid: north, center, and south. See :py:mod:`pygame_menu.locals` :return: None """ # Alignment assert_alignment(align) # Background color if background_color is not None: assert_color(background_color) # Padding parse_padding(padding) # Vertical position assert_position(vertical_position) assert vertical_position in (POSITION_NORTH, POSITION_CENTER, POSITION_SOUTH), \ 'cell vertical position must be NORTH, CENTER, or SOUTH' # Border color assert isinstance(border_width, int) and border_width >= 0 if border_color is not None: assert_color(border_color) # Border position assert isinstance(border_position, (str, VectorInstance)) if isinstance(border_position, str): border_position = [border_position] # Border positioning for pos in border_position: assert pos in (POSITION_NORTH, POSITION_SOUTH, POSITION_EAST, POSITION_WEST), \ 'only north, south, east, and west border positions are valid, ' \ 'but received "{0}"'.format(pos)
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 validate(self) -> 'Theme': """ Validate the values of the theme. If there's a invalid parameter throws an ``AssertionError``. This function also converts all lists to tuples. This is done because lists are mutable. :return: Self reference """ if self._disable_validation: return self # Boolean asserts assert isinstance(self.scrollbar_shadow, bool) assert isinstance(self.title_bar_modify_scrollarea, bool) assert isinstance(self.title_close_button, bool) assert isinstance(self.title_font_antialias, bool) assert isinstance(self.title_font_shadow, bool) assert isinstance(self.widget_font_antialias, bool) assert isinstance(self.widget_font_background_color_from_menu, bool) assert isinstance(self.widget_font_shadow, bool) # Value type checks assert_alignment(self.widget_alignment) assert_cursor(self.scrollbar_cursor) assert_cursor(self.title_close_button_cursor) assert_cursor(self.widget_cursor) assert_font(self.title_font) assert_font(self.widget_font) assert_position(self.scrollbar_shadow_position) assert_position(self.title_font_shadow_position) assert_position(self.widget_font_shadow_position) assert_position_vector(self.widget_border_position) assert _check_menubar_style(self.title_bar_style) assert get_scrollbars_from_position( self.scrollarea_position) is not None # Check selection effect if None if self.widget_selection_effect is None: self.widget_selection_effect = NoneSelection() assert isinstance(self.cursor_switch_ms, NumberInstance) assert isinstance(self.fps, NumberInstance) assert isinstance(self.scrollbar_shadow_offset, int) assert isinstance(self.scrollbar_slider_pad, NumberInstance) assert isinstance(self.scrollbar_thick, int) assert isinstance(self.title, bool) assert isinstance(self.title_fixed, bool) assert isinstance(self.title_floating, bool) assert isinstance(self.title_font_shadow_offset, int) assert isinstance(self.title_font_size, int) assert isinstance(self.title_updates_pygame_display, bool) assert isinstance(self.widget_background_inflate_to_selection, bool) assert isinstance(self.widget_border_width, int) assert isinstance(self.widget_box_border_width, int) assert isinstance(self.widget_font_shadow_offset, int) assert isinstance(self.widget_font_size, int) assert isinstance(self.widget_padding, PaddingInstance) assert isinstance(self.widget_selection_effect, Selection) assert isinstance(self.widget_tab_size, int) # Format colors, this converts all color lists to tuples automatically, # if image, return the same object self.background_color = self._format_color_opacity( self.background_color) self.cursor_color = self._format_color_opacity(self.cursor_color) self.cursor_selection_color = self._format_color_opacity( self.cursor_selection_color) self.focus_background_color = self._format_color_opacity( self.focus_background_color) self.readonly_color = self._format_color_opacity(self.readonly_color) self.readonly_selected_color = self._format_color_opacity( self.readonly_selected_color) self.scrollbar_color = self._format_color_opacity(self.scrollbar_color) self.scrollbar_shadow_color = self._format_color_opacity( self.scrollbar_shadow_color) self.scrollbar_slider_color = self._format_color_opacity( self.scrollbar_slider_color) self.scrollbar_slider_hover_color = self._format_color_opacity( self.scrollbar_slider_hover_color) self.selection_color = self._format_color_opacity(self.selection_color) self.surface_clear_color = self._format_color_opacity( self.surface_clear_color) self.title_background_color = self._format_color_opacity( self.title_background_color) self.title_close_button_background_color = self._format_color_opacity( self.title_close_button_background_color) self.title_font_color = self._format_color_opacity( self.title_font_color) self.title_font_shadow_color = self._format_color_opacity( self.title_font_shadow_color) self.widget_background_color = self._format_color_opacity( self.widget_background_color, none=True) self.widget_border_color = self._format_color_opacity( self.widget_border_color) self.widget_box_arrow_color = self._format_color_opacity( self.widget_box_arrow_color) self.widget_box_background_color = self._format_color_opacity( self.widget_box_background_color) self.widget_box_border_color = self._format_color_opacity( self.widget_box_border_color) self.widget_font_background_color = self._format_color_opacity( self.widget_font_background_color, none=True) self.widget_font_color = self._format_color_opacity( self.widget_font_color) self.widget_font_shadow_color = self._format_color_opacity( self.widget_font_shadow_color) self.widget_url_color = self._format_color_opacity( self.widget_url_color) # List to tuple self.scrollarea_outer_margin = self._vec_to_tuple( self.scrollarea_outer_margin, 2, NumberInstance) self.title_offset = self._vec_to_tuple(self.title_offset, 2, NumberInstance) self.widget_background_inflate = self._vec_to_tuple( self.widget_background_inflate, 2, int) self.widget_border_inflate = self._vec_to_tuple( self.widget_border_inflate, 2, int) self.widget_box_arrow_margin = self._vec_to_tuple( self.widget_box_arrow_margin, 3, int) self.widget_box_inflate = self._vec_to_tuple(self.widget_box_inflate, 2, int) self.widget_box_margin = self._vec_to_tuple(self.widget_box_margin, 2, NumberInstance) self.widget_margin = self._vec_to_tuple(self.widget_margin, 2, NumberInstance) if isinstance(self.widget_padding, VectorInstance): self.widget_padding = self._vec_to_tuple(self.widget_padding) assert 2 <= len(self.widget_padding) <= 4, \ 'widget padding tuple length must be 2, 3 or 4' for p in self.widget_padding: assert isinstance(p, NumberInstance), \ 'each padding element must be numeric (integer or float)' assert p >= 0, \ 'all padding elements must be equal or greater than zero' else: assert self.widget_padding >= 0, 'padding cannot be a negative number' self.widget_offset = self._vec_to_tuple(self.widget_offset, 2, NumberInstance) # Check sizes assert self.scrollarea_outer_margin[0] >= 0 and self.scrollarea_outer_margin[1] >= 0, \ 'scroll area outer margin must be equal or greater than zero on both axis' assert self.widget_offset[0] >= 0 and self.widget_offset[1] >= 0, \ 'widget offset must be equal or greater than zero' assert self.widget_background_inflate[0] >= 0 and self.widget_background_inflate[1] >= 0, \ 'widget background inflate must be equal or greater than zero on both axis' assert self.widget_border_inflate[0] >= 0 and self.widget_border_inflate[1] >= 0, \ 'widget border inflate must be equal or greater than zero on both axis' assert self.widget_box_inflate[0] >= 0 and self.widget_box_inflate[1] >= 0, \ 'widget box inflate inflate must be equal or greater than zero on both axis' assert self.cursor_switch_ms > 0, 'cursor switch ms must be greater than zero' assert self.fps >= 0, 'fps must be equal or greater than zero' assert self.scrollbar_shadow_offset > 0, 'scrollbar shadow offset must be greater than zero' assert self.scrollbar_slider_pad >= 0, 'slider pad must be equal or greater than zero' assert self.scrollbar_thick > 0, 'scrollbar thickness must be greater than zero' assert self.title_font_size > 0, 'title font size must be greater than zero' assert self.widget_border_width >= 0, 'widget border width must be equal or greater than zero' assert self.widget_box_border_width >= 0, 'widget border box width must be equal or greater than zero' assert self.widget_font_shadow_offset > 0, 'widget font shadow offset must be greater than zero' assert self.widget_font_size > 0, 'widget font size must be greater than zero' assert self.widget_tab_size >= 0, 'widget tab size must be equal or greater than zero' # Color asserts assert self.focus_background_color[3] != 0, \ 'focus background color cannot be fully transparent, suggested opacity between 1 and 255' return self
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 validate(self) -> 'Theme': """ Validate the values of the theme. If there's a invalid parameter throws an ``AssertionError``. This function also converts all lists to tuples. This is done because lists are mutable. :return: Self reference """ if self._disable_validation: return self # Boolean asserts assert isinstance(self.title_close_button, bool) assert isinstance(self.title_bar_modify_scrollarea, bool) assert isinstance(self.title_font_antialias, bool) assert isinstance(self.title_shadow, bool) assert isinstance(self.scrollbar_shadow, bool) assert isinstance(self.widget_font_antialias, bool) assert isinstance(self.widget_font_background_color_from_menu, bool) assert isinstance(self.widget_shadow, bool) # Value type checks _utils.assert_alignment(self.widget_alignment) _utils.assert_position(self.scrollbar_shadow_position) _utils.assert_position(self.title_shadow_position) _utils.assert_position(self.widget_shadow_position) assert _check_menubar_style(self.title_bar_style) assert get_scrollbars_from_position(self.scrollarea_position) is not None assert isinstance(self.cursor_switch_ms, (int, float)) assert isinstance(self.fps, (int, float)) assert isinstance(self.scrollbar_shadow_offset, (int, float)) assert isinstance(self.scrollbar_slider_pad, (int, float)) assert isinstance(self.scrollbar_thick, (int, float)) assert isinstance(self.title_floating, bool) assert isinstance(self.title_font, str) assert isinstance(self.title_font_size, int) assert isinstance(self.title_shadow_offset, (int, float)) assert isinstance(self.title_updates_pygame_display, bool) assert isinstance(self.widget_border_width, int) assert isinstance(self.widget_font, str) assert isinstance(self.widget_font_size, int) assert isinstance(self.widget_padding, (int, float, tuple, list)) assert isinstance(self.widget_selection_effect, _widgets.core.Selection) assert isinstance(self.widget_shadow_offset, (int, float)) # Format colors, this converts all color lists to tuples automatically self.background_color = self._format_opacity(self.background_color) self.cursor_color = self._format_opacity(self.cursor_color) self.cursor_selection_color = self._format_opacity(self.cursor_selection_color) self.focus_background_color = self._format_opacity(self.focus_background_color) self.readonly_color = self._format_opacity(self.readonly_color) self.readonly_selected_color = self._format_opacity(self.readonly_selected_color) self.scrollbar_color = self._format_opacity(self.scrollbar_color) self.scrollbar_shadow_color = self._format_opacity(self.scrollbar_shadow_color) self.scrollbar_slider_color = self._format_opacity(self.scrollbar_slider_color) self.selection_color = self._format_opacity(self.selection_color) self.surface_clear_color = self._format_opacity(self.surface_clear_color) self.title_background_color = self._format_opacity(self.title_background_color) self.widget_border_color = self._format_opacity(self.widget_border_color) self.title_font_color = self._format_opacity(self.title_font_color) self.title_shadow_color = self._format_opacity(self.title_shadow_color) self.widget_background_color = self._format_opacity(self.widget_background_color) self.widget_font_background_color = self._format_opacity(self.widget_font_background_color) self.widget_font_color = self._format_opacity(self.widget_font_color) # List to tuple self.scrollarea_outer_margin = self._vec_to_tuple(self.scrollarea_outer_margin, 2) self.title_offset = self._vec_to_tuple(self.title_offset, 2) self.widget_background_inflate = self._vec_to_tuple(self.widget_background_inflate, 2) self.widget_border_inflate = self._vec_to_tuple(self.widget_border_inflate, 2) self.widget_margin = self._vec_to_tuple(self.widget_margin, 2) if isinstance(self.widget_padding, (tuple, list)): self.widget_padding = self._vec_to_tuple(self.widget_padding) assert 2 <= len(self.widget_padding) <= 4, 'widget padding tuple length must be 2, 3 or 4' for p in self.widget_padding: assert p >= 0, 'all padding elements must be equal or greater than zero' else: assert self.widget_padding >= 0, 'padding cannot be a negative number' self.widget_offset = self._vec_to_tuple(self.widget_offset, 2) # Check sizes assert self.scrollarea_outer_margin[0] >= 0 and self.scrollarea_outer_margin[1] >= 0, \ 'scroll area outer margin must be equal or greater than zero in both axis' assert self.widget_offset[0] >= 0 and self.widget_offset[1] >= 0, \ 'widget offset must be equal or greater than zero' assert self.widget_border_inflate[0] >= 0 and self.widget_border_inflate[1] >= 0, \ 'widget border inflate must be equal or greater than zero in both axis' assert self.cursor_switch_ms > 0, 'cursor switch ms must be greater than zero' assert self.fps >= 0, 'fps must be equal or greater than zero' assert self.scrollbar_shadow_offset > 0, 'scrollbar shadow offset must be greater than zero' assert self.scrollbar_slider_pad >= 0, 'slider pad must be equal or greater than zero' assert self.scrollbar_thick > 0, 'scrollbar thickness must be greater than zero' assert self.title_font_size > 0, 'title font size must be greater than zero' assert self.widget_font_size > 0, 'widget font size must be greater than zero' assert self.widget_shadow_offset > 0, 'widget shadow offset must be greater than zero' # Configs self.widget_selection_effect.set_color(self.selection_color) # Color asserts assert self.focus_background_color[3] != 0, \ 'focus background color cannot be fully transparent, suggested opacity between 1 and 255' return self
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
def pack( self, widget: Union['Widget', List['Widget'], Tuple['Widget', ...]], alignment: str = _locals.ALIGN_LEFT, vertical_position: str = _locals.POSITION_NORTH, margin: Vector2NumberType = (0, 0) ) -> Union['Widget', List['Widget'], Tuple['Widget', ...], Any]: """ Packs widget in the frame line. To pack a widget it has to be already appended to Menu, and the Menu must be the same as the frame. Packing is added to the same line, for example if three LEFT widgets are added: .. code-block:: python <frame horizontal> frame.pack(W1, alignment=ALIGN_LEFT, vertical_position=POSITION_NORTH) frame.pack(W2, alignment=ALIGN_LEFT, vertical_position=POSITION_CENTER) frame.pack(W3, alignment=ALIGN_LEFT, vertical_position=POSITION_SOUTH) ---------------- |W1 | | W2 | | W3 | ---------------- Another example: .. code-block:: python <frame horizontal> frame.pack(W1, alignment=ALIGN_LEFT) frame.pack(W2, alignment=ALIGN_CENTER) frame.pack(W3, alignment=ALIGN_RIGHT) ---------------- |W1 W2 W3| ---------------- .. code-block:: python <frame vertical> frame.pack(W1, alignment=ALIGN_LEFT) frame.pack(W2, alignment=ALIGN_CENTER) frame.pack(W3, alignment=ALIGN_RIGHT) -------- |W1 | | W2 | | W3| -------- .. note:: Frame does not consider previous widget margin. For such purpose, use ``margin`` pack parameter. .. note:: It is recommended to force menu rendering after packing all widgets. .. note:: Packing applies a virtual translation to the widget, previous translation is not modified. .. note:: Widget floating is also considered within frames. If a widget is floating, it does not add any size to the respective positioning. :param widget: Widget to be packed :param alignment: Widget alignment :param vertical_position: Vertical position of the widget within frame. See :py:mod:`pygame_menu.locals` :param margin: (left, top) margin of added widget in px. It overrides the previous widget margin :return: Added widget references """ assert self._menu is not None, \ 'frame menu must be set before packing widgets' if isinstance(widget, (tuple, list)): for w in widget: self.pack(widget=w, alignment=alignment, vertical_position=vertical_position) return widget assert isinstance(widget, Widget) if isinstance(widget, Frame): assert widget.get_menu() is not None, \ '{0} menu cannot be None'.format(widget.get_class_id()) assert widget.get_id() not in self._widgets.keys(), \ '{0} already exists in {1}'.format(widget.get_class_id(), self.get_class_id()) assert widget.get_menu() == self._menu or widget.get_menu() is None, \ 'widget menu to be added to frame must be in same menu as frame, or it can have any Menu instance' assert widget.get_frame() is None, \ '{0} is already packed in {1}'.format(widget.get_class_id(), widget.get_frame().get_class_id()) assert_alignment(alignment) assert vertical_position in (_locals.POSITION_NORTH, _locals.POSITION_CENTER, _locals.POSITION_SOUTH), \ 'vertical position must be NORTH, CENTER, or SOUTH' assert_vector(margin, 2) assert widget.configured, 'widget must be configured before packing' if widget.get_margin() != (0, 0) and self._pack_margin_warning: msg = '{0} margin should be (0, 0) if packed, but received {1}; {2}.pack() does not consider ' \ 'previous widget margin. Set frame._pack_margin_warning=False to hide this warning' \ ''.format(widget.get_class_id(), widget.get_margin(), self.get_class_id()) warnings.warn(msg) if isinstance(widget, Frame): widget.update_indices() widget.set_frame(self) widget.set_margin(*margin) if self._frame_scrollarea is not None: widget.set_scrollarea(self._frame_scrollarea) self._sort_menu_scrollable_frames() else: widget.set_scrollarea(self._scrollarea) self._widgets[widget.get_id()] = widget self._widgets_props[widget.get_id()] = (alignment, vertical_position) # Sort widgets to keep selection order menu_widgets = self._menu._widgets if widget.get_menu() is not None and widget in menu_widgets: self._menu._validate_frame_widgetmove = False widgets_list = list(self._widgets.values()) # Move frame to last if len(self._widgets) > 1: wlast = widgets_list[-2] # -1 is the last added for i in range(2, len(self._widgets)): if wlast.get_menu() is None and len(self._widgets) > 2: wlast = widgets_list[-(i + 1)] else: break # Check for last if wlast is frame while True: if not (isinstance(wlast, Frame) and wlast.get_indices() != (-1, -1)) or wlast.get_menu() is None: break wlast = menu_widgets[wlast.last_index] if wlast.get_menu() == self._menu: self._menu.move_widget_index(self, wlast, render=False) # Swap self._menu.move_widget_index(widget, self, render=False) if isinstance(widget, Frame): reverse = menu_widgets.index(widget) == len(menu_widgets) - 1 widgs = widget.get_widgets(unpack_subframes_include_frame=True, reverse=reverse) for w in widgs: if w.get_menu() is None: continue self._menu.move_widget_index(w, self, render=False) if len(widgs) >= 1: swap_target = widgs[-1] if not reverse: swap_target = widgs[0] menu_widgets.remove(widget) menu_widgets.insert(menu_widgets.index(swap_target), widget) # Move widget to first menu_widgets.remove(self) for k in range(len(widgets_list)): if widgets_list[k].get_menu() == self._menu: menu_widgets.insert(menu_widgets.index(widgets_list[k]), self) break self._menu._validate_frame_widgetmove = True # Update control widget if self._control_widget is None: self._control_widget = widget self._control_widget_last_pos = self._control_widget.get_position( ) if isinstance(widget, Frame): self._has_frames = True # Update menu selected widget self._menu.move_widget_index(None, update_selected_index=True) # Render is mandatory as it modifies row/column layout try: self.update_position() self._menu._render() except _FrameSizeException: self.unpack(widget) raise # Request scroll if widget is selected if widget.is_selected(): widget.scroll_to_widget() widget.scroll_to_widget() return widget
def pack( self, widget: Union['Widget', List['Widget'], Tuple['Widget', ...]], alignment: str = _locals.ALIGN_LEFT, vertical_position: PackPositionTypes = _locals.POSITION_NORTH, margin: Vector2NumberType = (0, 0) ) -> Union['Widget', List['Widget'], Tuple['Widget', ...]]: """ Packs widget in the frame line. To pack a widget it has to be already appended to Menu, and the Menu must be the same as the frame. Packing is added to the same line, for example if three LEFT widgets are added: .. code-block:: python <frame horizontal> frame.pack(W1, alignment=ALIGN_LEFT, vertical_position=POSITION_NORTH) frame.pack(W2, alignment=ALIGN_LEFT, vertical_position=POSITION_CENTER) frame.pack(W3, alignment=ALIGN_LEFT, vertical_position=POSITION_SOUTH) ---------------- |W1 | | W2 | | W3 | ---------------- Another example: .. code-block:: python <frame horizontal> frame.pack(W1, alignment=ALIGN_LEFT) frame.pack(W2, alignment=ALIGN_CENTER) frame.pack(W3, alignment=ALIGN_RIGHT) ---------------- |W1 W2 W3| ---------------- .. code-block:: python <frame vertical> frame.pack(W1, alignment=ALIGN_LEFT) frame.pack(W2, alignment=ALIGN_CENTER) frame.pack(W3, alignment=ALIGN_RIGHT) -------- |W1 | | W2 | | W3| -------- .. note:: It is recommended to force menu rendering after packing all widgets. :param widget: Widget to be packed :param alignment: Widget alignment :param vertical_position: Vertical position of the widget :param margin: *(left, top)* margin of added widget in px. It overrides the previous widget margin :return: Added widget reference """ menu = self.get_menu() assert menu is not None, \ 'menu must be set before packing widgets' if isinstance(widget, (list, tuple)): for w in widget: self.pack(widget=w, alignment=alignment, vertical_position=vertical_position) return widget assert isinstance(widget, Widget) assert widget.get_id() not in self._widgets.keys(), \ 'widget already in frame' assert widget.get_menu() == menu, \ 'widget menu to be added to frame must be in same menu as frame' assert widget.get_frame() is None, \ 'widget already is in another frame' assert_alignment(alignment) assert vertical_position in (_locals.POSITION_NORTH, _locals.POSITION_CENTER, _locals.POSITION_SOUTH), \ 'vertical position must be NORTH, CENTER, or SOUTH' assert widget._translate[0] == 0 and widget._translate[1] == 0, \ 'widget cannot have a previous translation if appended. Frame overrides translation' assert_vector(margin, 2) widget.set_frame(self) widget.set_float() widget.set_margin(*margin) self._widgets[widget.get_id()] = (widget, alignment, vertical_position) # Notify menu and sort widgets to keep selection order # noinspection PyProtectedMember menu_widgets = menu._widgets frame_index = menu_widgets.index(self) widgt_index = menu_widgets.index(widget) assert widgt_index > frame_index, 'widget cannot be appended before frame' menu_widgets.pop(widgt_index) menu_widgets.insert(frame_index, widget) if widget.is_selected(): widget.select(False) menu.select_widget(widget) if self._control_widget is None: self._control_widget = widget self._control_widget_last_pos = self._control_widget.get_position() # Render is mandatory as it modifies row/column layout try: menu.render() except _FrameSizeException: self.unpack(widget) raise self.update_indices() return widget