Пример #1
0
    def _add_decor(self, decortype: int, prev: bool, data: Any) -> str:
        """
        Add decoration.

        :param decortype: Decoration type
        :param prev: To prev or post
        :param data: Data of the decoration
        :return: ID of the decoration
        """
        decor_id = uuid4()

        if prev:
            assert self._prev_enabled, 'prev decorators are not enabled'
            self._decor[DECOR_TYPE_PREV].append((decortype, decor_id, data))
            self._decor_prev_id.append(decor_id)
        else:
            assert self._post_enabled, 'post decorators are not enabled'
            self._decor[DECOR_TYPE_POST].append((decortype, decor_id, data))

        # Force surface cache update
        if hasattr(self._obj, 'force_menu_surface_cache_update'):
            self._obj.force_menu_surface_cache_update()

        # Forces cache update
        self._cache_needs_update[DECOR_TYPE_PREV if prev else DECOR_TYPE_POST] = True

        # Check sizes
        if self._total_decor() >= 300 and not self.cache:
            warnings.warn('cache is recommended if the total number of decorations exceeds 300')

        # Set automatically as enabled
        self._decor_enabled[decor_id] = True

        return decor_id
Пример #2
0
    def __init__(self, object_id: str) -> None:
        """
        Base constructor.

        :param object_id: Object ID
        """
        assert isinstance(object_id, str)
        if len(object_id) == 0:
            object_id = uuid4()
        self._attributes = {}
        self._id = object_id
Пример #3
0
    def __init__(self, object_id: str) -> None:
        """
        Base constructor.

        :param object_id: Object ID
        """
        assert isinstance(object_id, str)
        if len(object_id) == 0:
            object_id = uuid4()
        self._attributes = {}
        self._class_id__repr__ = False  # If True, repr/str of the object is class id
        self._id = object_id
        self._id__repr__ = False  # If True, repr/str of the object adds object id
Пример #4
0
    def add_row(
            self,
            cells: Union[ColumnInputType, 'Widget'],
            cell_align: Optional[str] = None,
            cell_border_color: Optional[ColorInputType] = None,
            cell_border_position: Optional[WidgetBorderPositionType] = None,
            cell_border_width: Optional[int] = None,
            cell_font: Optional[FontType] = None,
            cell_font_color: Optional[ColorInputType] = None,
            cell_font_size: Optional[int] = None,
            cell_padding: PaddingType = None,
            cell_vertical_position: Optional[str] = None,
            row_background_color: Optional[ColorInputType] = None) -> 'Frame':
        """
        Add row to table.

        .. note::

            By default, if ``None`` each cell style uses the table defaults "cell"
            styles.

        .. note::

            By default, the cell font is the same as the table font style.

        .. warning::

            Currently, only static widgets work properly, that is, widgets that
            does not accept events. Buttons or TextInputs can be added, but them
            will not accept any event, also, these widgets cannot be selected.
            That's because the Table architecture relies on Frame widget.

        :param cells: Cells to add. This can be a tuple or list of widgets, string, numbers, boolean values or images. Also, a Frame row can be added
        :param cell_align: Horizontal align of each cell. See :py:mod:`pygame_menu.locals`
        :param cell_border_color: Border color of each cell
        :param cell_border_position: Border position of each cell. Valid only: north, south, east, and west. See :py:mod:`pygame_menu.locals`
        :param cell_border_width: Border width in px of each cell
        :param cell_font: Font name or path
        :param cell_font_color: Font color
        :param cell_font_size: Font size
        :param cell_padding: Padding of each cell according to CSS rules. General shape: (top, right, bottom, left)
        :param cell_vertical_position: Vertical position of each cell. Only valid: north, center, and south. See :py:mod:`pygame_menu.locals`
        :param row_background_color: Row background color
        :return:
        """
        assert self.configured, 'table must be configured before adding rows'

        # Use defaults
        if cell_align is None:
            cell_align = self.default_cell_align
        if cell_border_color is None:
            cell_border_color = self.default_cell_border_color
        if cell_border_position is None:
            cell_border_position = self.default_cell_border_position
        if cell_border_width is None:
            cell_border_width = self.default_cell_border_width
        if cell_font is None:
            cell_font = self._font_name
        if cell_font_color is None:
            cell_font_color = self._font_color
        if cell_font_size is None:
            cell_font_size = self._font_size
        if cell_padding is None:
            cell_padding = self.default_cell_padding
        if cell_vertical_position is None:
            cell_vertical_position = self.default_cell_vertical_position
        if row_background_color is None:
            row_background_color = self.default_row_background_color

        # If cells is a previous table row
        if isinstance(cells, Frame) and cells.has_attribute('is_row'):
            row_cells = list(cells.get_widgets(unpack_subframes=False))
            cells.clear()
            cells = row_cells
        if isinstance(cells, Widget):
            cells = [cells]

        assert isinstance(cells, VectorInstance)

        # Check cell styles
        self._check_cell_style(align=cell_align,
                               background_color=row_background_color,
                               border_color=cell_border_color,
                               border_position=cell_border_position,
                               border_width=cell_border_width,
                               padding=cell_padding,
                               vertical_position=cell_vertical_position)
        cell_padding = parse_padding(cell_padding)
        if cell_border_color is not None:
            cell_border_color = assert_color(cell_border_color)
        if isinstance(cell_border_position, str):
            cell_border_position = [cell_border_position]

        # Check positioning
        if cell_border_width == 0:
            cell_border_position = WIDGET_BORDER_POSITION_NONE

        if row_background_color is not None:
            row_background_color = assert_color(row_background_color)

        # Create frame row
        row = Frame(1,
                    1,
                    ORIENTATION_HORIZONTAL,
                    frame_id=self._id + '+cell-row-' + uuid4(short=True))
        row._accepts_scrollarea = False
        row._accepts_title = False
        row._menu_can_be_none_pack = True
        row._update__repr___(self)
        row.configured = True
        row.relax()
        row.set_background_color(row_background_color)
        row.set_menu(self._menu)
        row.set_scrollarea(self._scrollarea)
        row.set_attribute('is_row')
        row.set_controls(joystick=self._joystick_enabled,
                         mouse=self._mouse_enabled,
                         touchscreen=self._touchscreen_enabled,
                         keyboard=self._keyboard_enabled)
        # row.set_frame(self) This cannot be executed as row is packed within

        # Create widgets
        row_cells: List['Widget'] = []
        cell: 'Widget'
        j = 0

        for c in cells:
            cell_widget_type = False

            if isinstance(c, (str, int, float, bool)):
                cell = Label(c,
                             label_id=self._id + '+cell-label-' +
                             uuid4(short=True))
                cell.set_font(
                    antialias=self._font_antialias,
                    background_color=None,
                    color=cell_font_color,
                    font=cell_font,
                    font_size=cell_font_size,
                    readonly_color=self._font_readonly_color,
                    readonly_selected_color=self._font_readonly_selected_color,
                    selected_color=self._font_selected_color)
                cell.set_padding(0)
                cell.set_tab_size(self._tab_size)

            elif isinstance(c, BaseImage):
                cell = Image(c,
                             image_id=self._id + '+cell-image-' +
                             uuid4(short=True))

            elif isinstance(c, pygame.Surface):
                cell = SurfaceWidget(c,
                                     surface_id=self._id + '+cell-surface-' +
                                     uuid4(short=True))

            else:
                assert isinstance(c, Widget)
                assert c != self, f'{self.get_class_id()} cannot be appended to itself'

                # Check if Frame not recursive
                if isinstance(c, Frame):
                    print(self, c.get_widgets())
                    assert self not in c.get_widgets(unpack_subframes_include_frame=True), \
                        f'{self.get_class_id()} cannot be packed within {c.get_class_id()},' \
                        f' recursive packing is not allowed (Table is within Frame' \
                        f' to be inserted as row cell)'

                cell = c
                if c._accept_events:
                    cell_widget_type = True
                    warn(
                        f'{self.get_class_id()} does not accept events in current'
                        f' pygame-menu v{ver}; thus appended cell row widget '
                        f'{c.get_class_id()} (pos {j}) would not work properly, '
                        f'as it will ignore all inputs. Also, widgets within Tables'
                        f' cannot be selected. Consider Tables as visual-only')
                # self._append_menu_update_frame(self)

            # Configure cell
            cell.set_attribute('accept_events', cell_widget_type)
            cell.set_attribute('align', cell_align)
            cell.set_attribute('background_color', row_background_color)
            cell.set_attribute('border_color', cell_border_color)
            cell.set_attribute('border_position', cell_border_position)
            cell.set_attribute('border_width', cell_border_width)
            cell.set_attribute('column', j + 1)
            cell.set_attribute('padding', cell_padding)
            cell.set_attribute('row', len(self._rows) + 1)
            cell.set_attribute('row_frame', row)
            cell.set_attribute('table', self)
            cell.set_attribute('vertical_position', cell_vertical_position)
            cell.set_float(False)
            cell._update__repr___(self)
            cell.configured = True

            # If cell is within a menu, remove from it
            if cell.get_menu() is not None:
                try:
                    cell.get_menu().remove_widget(cell)
                except ValueError:
                    pass

            # Check the cell frame is None
            assert cell.get_frame() != self, \
                f'{cell.get_class_id()} cannot be added as it already exists in table'
            assert cell.get_frame() is None, \
                f'{cell.get_class_id()} is already packed in ' \
                f'{cell.get_frame().get_class_id()}, it cannot be added to {self.get_class_id()}'

            # If cell is frame and scrollable
            if isinstance(cell, Frame):
                self._append_menu_update_frame(cell)

            # Add to cells
            row_cells.append(cell)
            j += 1

        # Pack cells to row
        for c in row_cells:
            row.pack(c)

        # Pack rows to self
        super(Table, self).pack(row)
        self._rows.append(row)

        # Update size
        self._update_row_sizing()
        self._update_event_widgets()

        return row
Пример #5
0
    def add_row(
            self,
            cells: Union[ColumnInputType, 'Widget'],
            cell_align: Optional[str] = None,
            cell_border_color: Optional[ColorInputType] = None,
            cell_border_position: Optional[WidgetBorderPositionType] = None,
            cell_border_width: Optional[int] = None,
            cell_font: Optional[FontType] = None,
            cell_font_color: Optional[ColorInputType] = None,
            cell_font_size: Optional[int] = None,
            cell_padding: PaddingType = None,
            cell_vertical_position: Optional[str] = None,
            row_background_color: Optional[ColorInputType] = None) -> 'Frame':
        """
        Add row to table.

        .. note::

            By default, if ``None`` each cell style uses the table defaults "cell"
            styles.

        .. note::

            By default, the cell font is the same as the table font style.

        :param cells: Cells to add. This can be a tuple or list of widgets, string, numbers, boolean values or images. Also a Frame row can be added
        :param cell_align: Horizontal align of each cell. See :py:mod:`pygame_menu.locals`
        :param cell_border_color: Border color of each cell
        :param cell_border_position: Border position of each cell. Valid only: north, south, east, and west. See :py:mod:`pygame_menu.locals`
        :param cell_border_width: Border width in px of each cell
        :param cell_font: Font name or path
        :param cell_font_color: Font color
        :param cell_font_size: Font size
        :param cell_padding: Padding of each cell according to CSS rules. General shape: (top, right, bottom, left)
        :param cell_vertical_position: Vertical position of each cell. Only valid: north, center, and south. See :py:mod:`pygame_menu.locals`
        :param row_background_color: Row background color
        :return:
        """
        assert self.configured, 'table must be configured before adding rows'

        # Use defaults
        if cell_align is None:
            cell_align = self.default_cell_align
        if cell_border_color is None:
            cell_border_color = self.default_cell_border_color
        if cell_border_position is None:
            cell_border_position = self.default_cell_border_position
        if cell_border_width is None:
            cell_border_width = self.default_cell_border_width
        if cell_font is None:
            cell_font = self._font_name
        if cell_font_color is None:
            cell_font_color = self._font_color
        if cell_font_size is None:
            cell_font_size = self._font_size
        if cell_padding is None:
            cell_padding = self.default_cell_padding
        if cell_vertical_position is None:
            cell_vertical_position = self.default_cell_vertical_position
        if row_background_color is None:
            row_background_color = self.default_row_background_color

        # If cells is a previous table row
        if isinstance(cells, Frame) and cells.has_attribute('is_row'):
            row_cells = list(cells.get_widgets(unpack_subframes=False))
            cells.clear()
            cells = row_cells
        if isinstance(cells, Widget):
            cells = [cells]

        assert isinstance(cells, VectorInstance)

        # Check cell styles
        self._check_cell_style(align=cell_align,
                               background_color=row_background_color,
                               border_color=cell_border_color,
                               border_position=cell_border_position,
                               border_width=cell_border_width,
                               padding=cell_padding,
                               vertical_position=cell_vertical_position)
        cell_padding = parse_padding(cell_padding)
        if cell_border_color is not None:
            cell_border_color = assert_color(cell_border_color)
        if isinstance(cell_border_position, str):
            cell_border_position = [cell_border_position]

        # Check positioning
        if cell_border_width == 0:
            cell_border_position = WIDGET_BORDER_POSITION_NONE

        if row_background_color is not None:
            row_background_color = assert_color(row_background_color)

        # Create frame row
        row = Frame(1,
                    1,
                    ORIENTATION_HORIZONTAL,
                    frame_id=self._id + '+cell-row-' + uuid4(short=True))
        row._accepts_scrollarea = False
        row._accepts_title = False
        row._menu_can_be_none_pack = True
        row._update__repr___(self)
        row.configured = True
        row.relax()
        row.set_background_color(row_background_color)
        row.set_menu(self._menu)
        row.set_scrollarea(self._scrollarea)
        row.set_attribute('is_row')
        # row.set_frame(self) This cannot be executed as row is packed within

        # Create widgets
        row_cells: List['Widget'] = []
        cell: 'Widget'
        j = 0
        for c in cells:
            if isinstance(c, (str, int, float, bool)):
                cell = Label(c,
                             label_id=self._id + '+cell-label-' +
                             uuid4(short=True))
                cell.set_font(
                    antialias=self._font_antialias,
                    background_color=None,
                    color=cell_font_color,
                    font=cell_font,
                    font_size=cell_font_size,
                    readonly_color=self._font_readonly_color,
                    readonly_selected_color=self._font_readonly_selected_color,
                    selected_color=self._font_selected_color)
                cell.set_padding(0)
                cell.set_tab_size(self._tab_size)
            elif isinstance(c, BaseImage):
                cell = Image(c,
                             image_id=self._id + '+cell-image-' +
                             uuid4(short=True))
            elif isinstance(c, pygame.Surface):
                cell = SurfaceWidget(c,
                                     surface_id=self._id + '+cell-surface-' +
                                     uuid4(short=True))
            else:
                assert isinstance(c, Widget)
                cell = c

            # Configure cell
            cell.set_attribute('align', cell_align)
            cell.set_attribute('background_color', row_background_color)
            cell.set_attribute('border_color', cell_border_color)
            cell.set_attribute('border_position', cell_border_position)
            cell.set_attribute('border_width', cell_border_width)
            cell.set_attribute('column', j + 1)
            cell.set_attribute('padding', cell_padding)
            cell.set_attribute('row', len(self._rows) + 1)
            cell.set_attribute('row_frame', row)
            cell.set_attribute('table', self)
            cell.set_attribute('vertical_position', cell_vertical_position)
            cell.set_float(False)
            cell._update__repr___(self)
            cell.configured = True

            # If cell is within a menu, remove from it
            if cell.get_menu() is not None:
                try:
                    cell.get_menu().remove_widget(cell)
                except ValueError:
                    pass

            # Check the cell frame is None
            assert cell.get_frame() != self, \
                '{0} cannot be added as it already exists in table' \
                ''.format(cell.get_class_id())
            assert cell.get_frame() is None, \
                '{0} is already packed in {1}, it cannot be added to {2}' \
                ''.format(cell.get_class_id(), cell.get_frame().get_class_id(),
                          self.get_class_id())

            # If cell is frame and scrollable
            if isinstance(cell, Frame) and self._menu is not None:
                menu_update_frames = self._get_menu_update_frames()
                if cell not in menu_update_frames:
                    menu_update_frames.append(cell)
                    self.sort_menu_update_frames()

            # Add to cells
            row_cells.append(cell)
            j += 1

        # Pack cells to row
        for c in row_cells:
            row.pack(c)

        # Pack row to self
        super(Table, self).pack(row)
        self._rows.append(row)

        # Update size
        self._update_row_sizing()

        return row
Пример #6
0
    def label(
        self,
        title: Any,
        label_id: str = '',
        max_char: int = 0,
        onselect: Optional[Callable[[bool, 'Widget', 'pygame_menu.Menu'],
                                    Any]] = None,
        selectable: bool = False,
        **kwargs
    ) -> Union['pygame_menu.widgets.Label', List['pygame_menu.widgets.Label']]:
        """
        Add a simple text to the Menu.

        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
            - ``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)
            - ``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. Applied only if ``selectable`` is ``True``
            - ``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
            - ``underline_color``               (tuple, list, str, int, :py:class:`pygame.Color`, None) – Color of the underline. If ``None`` use the same color of the text
            - ``underline_offset``              (int) – Vertical offset in px. ``2`` by default
            - ``underline_width``               (int) – Underline width in px. ``2`` by default
            - ``underline``                     (bool) – Enables text underline, using a properly placed decoration. ``False`` by default

        .. 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.

        :param title: Text to be displayed
        :param label_id: ID of the label
        :param max_char: Split the title in several labels if the string length exceeds ``max_char``; ``0``: don't split, ``-1``: split to Menu width
        :param onselect: Callback executed when selecting the widget; only executed if ``selectable`` is ``True``
        :param selectable: Label accepts user selection; useful to move along the Menu using label selection
        :param kwargs: Optional keyword arguments
        :return: Widget object, or List of widgets if the text overflows
        :rtype: :py:class:`pygame_menu.widgets.Label`, :py:class:`typing.List` [:py:class:`pygame_menu.widgets.Label`]
        """
        assert isinstance(label_id, str)
        assert isinstance(max_char, int)
        assert isinstance(selectable, bool)
        assert max_char >= -1

        title = str(title)
        if len(label_id) == 0:
            label_id = uuid4()

        # If newline detected, split in two new lines
        if '\n' in title:
            title = title.split('\n')
            widgets = []
            for t in title:
                wig = self.label(title=t,
                                 label_id=label_id + '+' +
                                 str(len(widgets) + 1),
                                 max_char=max_char,
                                 onselect=onselect,
                                 selectable=selectable,
                                 **kwargs)
                if isinstance(wig, list):
                    for w in wig:
                        widgets.append(w)
                else:
                    widgets.append(wig)
            return widgets

        # Wrap text to Menu width (imply additional calls to render functions)
        if max_char < 0:
            if len(title) > 0:
                dummy_attrs = self._filter_widget_attributes(kwargs.copy())
                dummy = pygame_menu.widgets.Label(title=title)
                self._configure_widget(dummy, **dummy_attrs)
                max_char = int(1.0 * self._menu.get_width(inner=True) *
                               len(title) / dummy.get_width())
            else:
                max_char = 0

        # If no overflow
        if len(title) <= max_char or max_char == 0:
            attributes = self._filter_widget_attributes(kwargs)

            # Filter additional parameters
            underline = kwargs.pop('underline', False)
            underline_color = kwargs.pop('underline_color',
                                         attributes['font_color'])
            underline_offset = kwargs.pop('underline_offset', 1)
            underline_width = kwargs.pop('underline_width', 1)

            widget = Label(label_id=label_id, onselect=onselect, title=title)
            widget.is_selectable = selectable
            self._check_kwargs(kwargs)
            self._configure_widget(widget=widget, **attributes)

            if underline:
                widget.add_underline(underline_color, underline_offset,
                                     underline_width)

            self._append_widget(widget)

        else:
            self._menu._check_id_duplicated(label_id)  # Before adding + LEN
            widget = []
            for line in textwrap.wrap(title, max_char):
                widget.append(
                    self.label(title=line,
                               label_id=label_id + '+' + str(len(widget) + 1),
                               max_char=max_char,
                               onselect=onselect,
                               selectable=selectable,
                               **kwargs))

        return widget