Exemple #1
0
class Node(Widget, ItemWidget):
    """A :class:`.NodeEditor` node.

    Should only contain :class:`.NodeAttribute` objects, any other kind of widget will not be
    displayed. Note that :class:`.NodeAttribute` objects may contain any kind or number of widget
    though."""

    label: str = ConfigProperty()
    draggable: bool = ConfigProperty()

    def __init__(self, label: str = None, *, name_id: str = None, **config):
        super().__init__(label=label, name_id=name_id, **config)

    def _setup_add_widget(self, dpg_args) -> None:
        dpgcore.add_node(self.id, **dpg_args)

    def __enter__(self) -> Node:
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        dpgcore.end()
Exemple #2
0
class TabButton(Widget, ItemWidget):
    """A button that can be added to a :class:`TabBar`.

    Note:
        This widget must be placed inside a :class:`.TabBar` to be visible.
    """

    label: str = ConfigProperty()

    #: Create a button on the tab that can hide the tab.
    closable: bool = ConfigProperty()

    order_mode: TabOrderMode

    @ConfigProperty()
    def order_mode(self) -> TabOrderMode:
        config = self.get_config()
        if config.get('leading'):
            return TabOrderMode.Leading
        if config.get('trailing'):
            return TabOrderMode.Trailing
        if config.get('no_reorder'):
            return TabOrderMode.Fixed
        return TabOrderMode.Reorderable

    @order_mode.getconfig
    def order_mode(self, value: TabOrderMode):
        return {
            mode.value: (mode == value)
            for mode in TabOrderMode if mode.value is not None
        }

    #: Disable tooltip
    no_tooltip: bool = ConfigProperty()

    def __init__(self, label: str = None, *, name_id: str = None, **config):
        super().__init__(label=label, name_id=name_id, **config)

    def _setup_add_widget(self, dpg_args) -> None:
        dpgcore.add_tab_button(self.id, **dpg_args)
Exemple #3
0
class TabItem(Widget, ItemWidgetMx, ContainerWidgetMx['TabItem']):
    """A container whose contents will be displayed when selected in a :class:`.TabBar`.

    Note:
        This widget must be placed inside a :class:`.TabBar` to be visible.
    """

    label: str = ConfigProperty()

    #: Create a button on the tab that can hide the tab.
    closable: bool = ConfigProperty()

    order_mode: TabOrderMode

    @ConfigProperty()
    def order_mode(self) -> TabOrderMode:
        config = self.get_config()
        if config.get('leading'):
            return TabOrderMode.Leading
        if config.get('trailing'):
            return TabOrderMode.Trailing
        if config.get('no_reorder'):
            return TabOrderMode.Fixed
        return TabOrderMode.Reorderable

    @order_mode.getconfig
    def order_mode(self, value: TabOrderMode):
        return {
            mode.value: (mode == value)
            for mode in TabOrderMode if mode.value is not None
        }

    #: Disable tooltip
    no_tooltip: bool = ConfigProperty()

    def __init__(self, label: str = None, **config):
        super().__init__(label=label, **config)

    def __setup_add_widget__(self, dpg_args) -> None:
        dpgcore.add_tab(self.id, **dpg_args)
Exemple #4
0
class IndentLayout(Widget, ItemWidgetMx, ContainerWidgetMx['IndentLayout']):
    """Adds an indent to contained items."""

    offset: float = ConfigProperty()

    def __init__(self, **config):
        super().__init__(**config)

    def __setup_add_widget__(self, dpg_args) -> None:
        dpgcore.add_indent(name=self.id, **dpg_args)

    def __finalize__(self) -> None:
        dpgcore.unindent()  # doesn't use dpgcore.end()
Exemple #5
0
class Menu(Widget, ItemWidgetMx, ContainerWidgetMx['Menu']):
    """A menu containing :class:`.MenuItem` objects.

    While they are often found inside a :class:`.MenuBar`, they are actually a general container
    that can be added anywhere and contain other kinds of widgets (e.g. buttons and text),
    even if it is unusual."""

    label: str = ConfigProperty()

    def __init__(self, label: str = None, **config):
        super().__init__(label=label, **config)

    def __setup_add_widget__(self, dpg_args) -> None:
        dpgcore.add_menu(self.id, **dpg_args)
Exemple #6
0
class MenuItem(Widget, ItemWidget):
    """An item for a :class:`.Menu`."""

    label: str = ConfigProperty()

    #: Keyboard shortcut, e.g. `'CTRL+M'`.
    shortcut: str = ConfigProperty()

    #: If ``True``, a checkmark is shown if the item's :attr:`value` is ``True``.
    enable_check: bool = ConfigProperty(key='check')

    def __init__(self,
                 label: str = None,
                 value: bool = None,
                 *,
                 name_id: str = None,
                 **config):
        super().__init__(label=label, name_id=name_id, **config)
        if value is not None:
            self.value = value

    def _setup_add_widget(self, dpg_args) -> None:
        dpgcore.add_menu_item(self.id, **dpg_args)
Exemple #7
0
class TabBar(Widget, ItemWidgetMx, ContainerWidgetMx['TabBar']):
    """A container that allows switching between different tabs.

    Note:
        This container should only contain :class:`.TabItem` or :class:`.TabButton` elements.
    """

    reorderable: bool = ConfigProperty()

    def __init__(self, **config):
        super().__init__(**config)

    def __setup_add_widget__(self, dpg_args) -> None:
        dpgcore.add_tab_bar(self.id, **dpg_args)
Exemple #8
0
class RadioButtons(Widget, ItemWidget, ValueWidget[int], MutableSequence[str]):
    """A set of radio buttons.

    This widget can be used as a mutable sequence of labels. Changing the sequence will
    change the radio buttons in the group and their labels."""

    value: int  #: The **index** of the selected item.

    horizontal: bool = ConfigProperty()

    items: Sequence[str]
    @ConfigProperty()
    def items(self) -> Sequence[str]:
        """Get or set this widget's items as a sequence."""
        return tuple(self._get_items())

    @items.getconfig
    def items(self, items: Sequence[str]):
        return {'items':list(items)}

    def __init__(self, items: Iterable[str], value: int = 0, *, name_id: str = None, **config):
        super().__init__(items=items, default_value=value, name_id=name_id, **config)

    def _setup_add_widget(self, dpg_args) -> None:
        dpgcore.add_radio_button(self.id, **dpg_args)

    def _get_items(self) -> List[str]:
        return self.get_config()['items']

    def __len__(self) -> int:
        return len(self._get_items())

    def __getitem__(self, idx: int) -> str:
        return self._get_items()[idx]

    def __setitem__(self, idx: int, label: str) -> None:
        items = self._get_items()
        items[idx] = label
        self.set_config(items=items)

    def __delitem__(self, idx: int) -> None:
        items = self._get_items()
        del items[idx]
        self.set_config(items=items)

    def insert(self, idx: int, label: str) -> None:
        items = self._get_items()
        items.insert(idx, label)
        self.set_config(items=items)
Exemple #9
0
class ColorButton(Widget, ItemWidget):
    """A button that displays and enables copying of color data.

    Clicking and draging the color square will copy the color to be applied on any other color widget.

    While it has color "value", this is not a :class:`.ValueWidget`!
    """
    color: ColorRGBA = ConfigPropertyColorRGBA(
        no_init=True)  #: The color to copy on drag-and-drop.
    no_border: bool = ConfigProperty()
    no_alpha: bool = ConfigProperty()  #: Don't include alpha channel.
    no_drag_drop: bool = ConfigProperty()

    def __init__(self,
                 color: ColorRGBA = ColorRGBA(1, 0, 1),
                 *,
                 name_id: str = None,
                 **config):
        super().__init__(color=dpg_export_color(color),
                         name_id=name_id,
                         **config)

    def _setup_add_widget(self, dpg_args) -> None:
        dpgcore.add_color_button(self.id, **dpg_args)
Exemple #10
0
class LabelText(Widget, ItemWidgetMx, ValueWidgetMx[str]):
    """Display text with a label.

    Useful for output values when used with a :attr:`~.Widget.data_source`.
    The text is linked to the data source, while the label remains unchanged."""

    value: str  #: The text to display (separate from the :attr:`label`).

    label: str = ConfigProperty()
    color: ColorRGBA = ConfigPropertyColorRGBA()

    def __init__(self, label: str = None, value: str = '', **config):
        super().__init__(label=label, default_value=value, **config)

    def __setup_add_widget__(self, dpg_args) -> None:
        dpgcore.add_label_text(self.id, **dpg_args)
Exemple #11
0
class LayoutIndent(Widget, ItemWidget):
    """Adds an indent to contained items."""

    offset: float = ConfigProperty()

    def __init__(self, *, name_id: str = None, **config):
        super().__init__(name_id=name_id, **config)

    def _setup_add_widget(self, dpg_args) -> None:
        dpgcore.add_indent(name=self.id, **dpg_args)

    def __enter__(self) -> LayoutIndent:
        return self

    def __exit__(self, exc_type, exc_val, exc_tb) -> None:
        dpgcore.unindent()
Exemple #12
0
class NumberInput(Widget, ItemWidget, ValueWidget[_TInput], Generic[_TElem,
                                                                    _TInput]):
    """Base class for number input boxes."""
    value: _TInput  #: The inputted value.
    _default_value: _TInput

    format: str = ConfigProperty()
    on_enter: bool = ConfigProperty()
    step: _TElem = ConfigProperty()
    step_fast: _TElem = ConfigProperty()
    readonly: bool = ConfigProperty()
    label: str = ConfigProperty()

    min_value: Optional[_TElem]

    @ConfigProperty()
    def min_value(self) -> Optional[_TElem]:
        config = self.get_config()
        if not config.get('min_clamped'):
            return None
        return config['min_value']

    @min_value.getconfig
    def min_value(self, value: Optional[_TElem]):
        if value is None:
            return {'min_clamped': False}
        return {'min_clamped': True, 'min_value': value}

    max_value: Optional[_TElem]

    @ConfigProperty()
    def max_value(self) -> Optional[_TElem]:
        config = self.get_config()
        if not config.get('max_clamped'):
            return None
        return config['max_value']

    @max_value.getconfig
    def max_value(self, value: Optional[_TElem]):
        if value is None:
            return {'max_clamped': False}
        return {'max_clamped': True, 'max_value': value}

    def __init__(self,
                 label: str = None,
                 value: _TInput = None,
                 *,
                 name_id: str = None,
                 **config):
        value = value or self._default_value
        super().__init__(label=label,
                         default_value=value,
                         name_id=name_id,
                         **config)
Exemple #13
0
class Popup(Widget):
    """A container that appears when a :class:`.ItemWidget` is interacted with."""

    trigger: PopupInteraction  #: The interaction that will trigger the popup.

    @ConfigProperty(key='mousebutton')
    def trigger(self) -> PopupInteraction:
        config = self.get_config()
        return PopupInteraction(config['mousebutton'])

    @trigger.getconfig
    def trigger(self, trigger: PopupInteraction):
        return {'mousebutton': trigger.value}

    #: Prevent the user from interacting with other windows until the popup is closed.
    modal: bool = ConfigProperty()

    def __init__(self, parent: ItemWidget, *, name_id: str = None, **config):
        self._popup_parent = parent
        super().__init__(name_id=name_id, **config)

    def __enter__(self) -> Popup:
        return self

    def __exit__(self, exc_type, exc_val, exc_tb) -> None:
        dpgcore.end()

    def _setup_add_widget(self, dpg_args) -> None:
        dpgcore.add_popup(self._popup_parent.id, self.id, **dpg_args)

    parent: ItemWidget

    @property
    def parent(self) -> ItemWidget:
        """The :class:`.ItemWidget` that the popup is attached to. Cannot be changed."""
        return self._popup_parent

    def close(self) -> None:
        """Closes the popup.

        Node:
            Modal popups cannot be closed except by using this method.
        """
        dpgcore.close_popup(self.id)
Exemple #14
0
class TabBar(Widget, ItemWidget):
    """A container that allows switching between different tabs.

    Note:
        This container should only contain :class:`.TabItem` or :class:`.TabButton` elements.
    """

    reorderable: bool = ConfigProperty()

    def __init__(self, *, name_id: str = None, **config):
        super().__init__(name_id=name_id, **config)

    def _setup_add_widget(self, dpg_args) -> None:
        dpgcore.add_tab_bar(self.id, **dpg_args)

    def __enter__(self) -> TabBar:
        return self

    def __exit__(self, exc_type, exc_val, exc_tb) -> None:
        dpgcore.end()
Exemple #15
0
class Menu(Widget, ItemWidget):
    """A menu containing :class:`.MenuItem` objects.

    While they are often found inside a :class:`.MenuBar`, they are actually a general container
    that can be added anywhere and contain other kinds of widgets (e.g. buttons and text),
    even if it is unusual."""

    label: str = ConfigProperty()

    def __init__(self, label: str = None, *, name_id: str = None, **config):
        super().__init__(label=label, name_id=name_id, **config)

    def _setup_add_widget(self, dpg_args) -> None:
        dpgcore.add_menu(self.id, **dpg_args)

    def __enter__(self) -> Menu:
        return self

    def __exit__(self, exc_type, exc_val, exc_tb) -> None:
        dpgcore.end()
Exemple #16
0
class ChildView(Widget, ItemWidgetMx, ContainerWidgetMx['ChildView']):
    """Adds an embedded child window with optional scollbars."""

    border: bool = ConfigProperty()
    autosize_x: bool = ConfigProperty()
    autosize_y: bool = ConfigProperty()
    menubar: bool = ConfigProperty()

    #: Disable scrollbars (can still scroll with mouse or programmatically).
    no_scrollbar: bool = ConfigProperty()

    #: Allow horizontal scrollbar to appear.
    horizontal_scrollbar: bool = ConfigProperty()

    def __setup_add_widget__(self, dpg_args) -> None:
        dpgcore.add_child(self.id, **dpg_args)
Exemple #17
0
class SliderInput(Generic[_TElem, _TInput], Widget, ItemWidgetMx,
                  ValueWidgetMx[_TInput]):
    """Base class for slider types."""
    value: _TInput  #: The inputted value.
    _default_value: _TInput

    label: str = ConfigProperty()
    min_value: _TElem = ConfigProperty()
    max_value: _TElem = ConfigProperty()
    format: str = ConfigProperty()  #: number format
    vertical: bool = ConfigProperty()

    #: Control whether a value can be manually entered using CTRL+Click
    no_input: bool = ConfigProperty()

    #: Whether to clamp the value when using manual input. By default CTRL+Click allows going out of bounds.
    clamped: bool = ConfigProperty()

    def __init__(self, label: str = None, value: _TElem = None, **config):
        value = value or self._default_value
        super().__init__(label=label, default_value=value, **config)
Exemple #18
0
class ChildView(Widget, ItemWidget):
    """Adds an embedded child window with optional scollbars."""

    border: bool = ConfigProperty()
    autosize_x: bool = ConfigProperty()
    autosize_y: bool = ConfigProperty()
    menubar: bool = ConfigProperty()

    #: Disable scrollbars (can still scroll with mouse or programmatically).
    no_scrollbar: bool = ConfigProperty()

    #: Allow horizontal scrollbar to appear.
    horizontal_scrollbar: bool = ConfigProperty()

    def _setup_add_widget(self, dpg_args) -> None:
        dpgcore.add_child(self.id, **dpg_args)

    def __enter__(self) -> ChildView:
        return self

    def __exit__(self, exc_type, exc_val, exc_tb) -> None:
        dpgcore.end()
Exemple #19
0
class ColorPicker(Widget, ItemWidgetMx, ValueWidgetMx[ColorRGBA]):
    """A color picking widget.

    Clicking and draging the color square will copy the color to be applied on any other color widget.
    Right-click allows the style of the color picker to be changed."""

    value: ColorRGBA  #: The picked color.

    label: str = ConfigProperty()
    no_alpha: bool = ConfigProperty()
    no_small_preview: bool = ConfigProperty()
    no_inputs: bool = ConfigProperty()
    no_tooltip: bool = ConfigProperty()
    no_label: bool = ConfigProperty()
    no_side_preview: bool = ConfigProperty()
    alpha_bar: bool = ConfigProperty()
    alpha_preview: bool = ConfigProperty()
    alpha_preview_half: bool = ConfigProperty()
    display_rgb: bool = ConfigProperty()
    display_hsv: bool = ConfigProperty()
    display_hex: bool = ConfigProperty()
    picker_hue_bar: bool = ConfigProperty()
    picker_hue_wheel: bool = ConfigProperty()
    input_rgb: bool = ConfigProperty()
    input_hsv: bool = ConfigProperty()

    color_format: ColorFormatMode

    @ConfigProperty()
    def color_format(self) -> ColorFormatMode:
        config = self.get_config()
        if config['floats'] and not config['uint8']:
            return ColorFormatMode.Float
        return ColorFormatMode.UInt8

    @color_format.getconfig
    def color_format(self, value: ColorFormatMode):
        if value == ColorFormatMode.UInt8:
            return {'uint8': False, 'floats': True}
        if value == ColorFormatMode.Float:
            return {'uint8': True, 'floats': False}
        raise ValueError('invalid color format mode')

    def __init__(self,
                 label: str = None,
                 value: ColorRGBA = ColorRGBA(1, 0, 1),
                 **config):
        super().__init__(label=label,
                         default_value=export_color_to_dpg(value),
                         **config)

    def __setup_add_widget__(self, dpg_args) -> None:
        dpgcore.add_color_picker4(self.id, **dpg_args)

    def __get_value__(self) -> ColorRGBA:
        return import_color_from_dpg(super().__get_value__())

    def __set_value__(self, color: ColorRGBA) -> None:
        super().__set_value__(export_color_to_dpg(color))
Exemple #20
0
class Plot(Widget, ItemWidgetMx):
    """A rich plot widget."""

    ## Plot Axes

    xaxis: PlotXAxisConfig = PlotXAxis()  #: The X-axis
    yaxis: PlotYAxisConfig = PlotYAxis(0)  #: The Y-axis
    y2axis: PlotOptYAxisConfig = PlotYAxis(1, 'yaxis2')  #: Optional Y-axis 2
    y3axis: PlotOptYAxisConfig = PlotYAxis(2, 'yaxis3')  #: Optional Y-axis 3

    ## Config Properties

    label: str = ConfigProperty()
    x_axis_label: str = ConfigProperty(key='x_axis_name')
    y_axis_label: str = ConfigProperty(key='y_axis_name')

    show_annotations: bool = ConfigProperty()
    show_drag_lines: bool = ConfigProperty()
    show_drag_points: bool = ConfigProperty()
    show_color_scale: bool = ConfigProperty()

    scale_min: float = ConfigProperty()
    scale_max: float = ConfigProperty()
    scale_height: int = ConfigProperty()
    equal_aspects: bool = ConfigProperty()

    query: bool = ConfigProperty()
    crosshairs: bool = ConfigProperty()
    no_legend: bool = ConfigProperty()
    no_menus: bool = ConfigProperty()
    no_box_select: bool = ConfigProperty()
    no_mouse_pos: bool = ConfigProperty()
    no_highlight: bool = ConfigProperty()
    no_child: bool = ConfigProperty()

    anti_aliased: bool = ConfigProperty()

    def __init__(self, **config):
        # not super happy that we have to resort to typing.cast() here, but it works
        self._xaxis_config = PlotXAxisConfig(self, cast(PlotXAxis, Plot.xaxis))
        self._yaxis_config = PlotYAxisConfig(self, cast(PlotYAxis, Plot.yaxis))
        self._y2axis_config = PlotOptYAxisConfig(self,
                                                 cast(PlotYAxis, Plot.y2axis))
        self._y3axis_config = PlotOptYAxisConfig(self,
                                                 cast(PlotYAxis, Plot.y3axis))

        super().__init__(**config)

    def __setup_add_widget__(self, dpg_args) -> None:
        dpgcore.add_plot(self.id, **dpg_args)

    def add_dataseries(self,
                       series: DataSeries,
                       *,
                       update_bounds: bool = True) -> None:
        """Add a :class:`.DataSeries` to this plot (or update it).

        Updates the data series if it has already been added."""
        series.update(self, update_bounds)

    def remove_dataseries(self, series: DataSeries) -> None:
        """Remove a :class:`.DataSeries` from this plot if it has been added."""
        dpgcore.delete_series(self.id, series.id)

    def clear(self) -> None:
        dpgcore.clear_plot(self.id)

    def set_xlimits(self, limits: Optional[PlotLimits]) -> None:
        """Set the ``(min, max)`` limits for the x-axis, or pass ``None`` to use automatic limits."""
        if limits is None:
            dpgcore.set_plot_xlimits_auto(self.id)
        else:
            dpgcore.set_plot_xlimits(self.id, *limits)

    def set_ylimits(self, limits: Optional[PlotLimits]) -> None:
        """Set the ``(min, max)`` limits for the y-axis, or pass ``None`` to use automatic limits."""
        if limits is None:
            dpgcore.set_plot_ylimits_auto(self.id)
        else:
            dpgcore.set_plot_ylimits(self.id, *limits)

    def set_xticks(self, ticks: Optional[Iterable[TickLabel]]) -> None:
        """Set the tick labels for the x-axis, or pass ``None`` to use automatic ticks."""
        if ticks is None:
            dpgcore.reset_xticks(self.id)
        else:
            dpgcore.set_xticks(self.id, ticks)

    def set_yticks(self, ticks: Optional[Iterable[TickLabel]]) -> None:
        """Set the tick labels for the y-axis, or pass ``None`` to use automatic ticks."""
        if ticks is None:
            dpgcore.reset_yticks(self.id)
        else:
            dpgcore.set_yticks(self.id, ticks)

    def add_annotation(self,
                       text: str,
                       pos: Tuple[float, float],
                       offset: Tuple[float, float],
                       *,
                       color: ColorRGBA = None,
                       clamped: bool = True) -> PlotAnnotation:
        """Creates a :class:`.PlotAnnotation` and adds it to the plot."""
        return PlotAnnotation(self,
                              text,
                              pos,
                              offset,
                              color=color,
                              clamped=clamped)

    def get_mouse_pos(self) -> Optional[Tuple[float, float]]:
        """Returns the ``(x, y)`` mouse position in the plot if it is hovered, or ``None``."""
        if not self.is_hovered():
            return None
        return dpgcore.get_plot_mouse_pos()

    def get_selected_query_area(self) -> Optional[PlotQueryArea]:
        """Returns a :class:`.PlotQueryArea` for the selected query area if there is one.

        A query area can be selected by the user by holding control and right-click dragging in
        a plot. If a query area has not been selected, this will return ``None``."""
        if not dpgcore.is_plot_queried(self.id):
            return None
        return PlotQueryArea(*dpgcore.get_plot_query_area(self.id))
Exemple #21
0
class ColorEdit(Widget, ItemWidget, ValueWidget[ColorRGBA]):
    """A color editing widget.

    Clicking and draging the color square will copy the color to be applied on any other color widget."""

    value: ColorRGBA  #: The inputted color.

    label: str = ConfigProperty()
    no_alpha: bool = ConfigProperty()  #: Don't include alpha channel.
    no_picker: bool = ConfigProperty()
    no_options: bool = ConfigProperty()
    no_small_preview: bool = ConfigProperty()
    no_inputs: bool = ConfigProperty()
    no_tooltip: bool = ConfigProperty()
    no_label: bool = ConfigProperty()
    no_drag_drop: bool = ConfigProperty()
    alpha_bar: bool = ConfigProperty()
    alpha_preview: bool = ConfigProperty()
    alpha_preview_half: bool = ConfigProperty()
    display_rgb: bool = ConfigProperty()
    display_hsv: bool = ConfigProperty()
    display_hex: bool = ConfigProperty()
    input_rgb: bool = ConfigProperty()
    input_hsv: bool = ConfigProperty()

    color_format: ColorFormatMode

    @ConfigProperty()
    def color_format(self) -> ColorFormatMode:
        config = self.get_config()
        if config['floats'] and not config['uint8']:
            return ColorFormatMode.Float
        return ColorFormatMode.UInt8

    @color_format.getconfig
    def color_format(self, value: ColorFormatMode):
        if value == ColorFormatMode.UInt8:
            return {'uint8': False, 'floats': True}
        if value == ColorFormatMode.Float:
            return {'uint8': True, 'floats': False}
        raise ValueError('invalid color format mode')

    def __init__(self,
                 label: str = None,
                 value: ColorRGBA = ColorRGBA(1, 0, 1),
                 *,
                 name_id: str = None,
                 **config):
        super().__init__(label=label,
                         default_value=dpg_export_color(value),
                         name_id=name_id,
                         **config)

    def _setup_add_widget(self, dpg_args) -> None:
        dpgcore.add_color_edit4(self.id, **dpg_args)

    def _get_value(self) -> ColorRGBA:
        return dpg_import_color(super()._get_value())

    def _set_value(self, color: ColorRGBA) -> None:
        super()._set_value(dpg_export_color(color))
Exemple #22
0
class ListBox(Widget, ItemWidgetMx, ValueWidgetMx[int], MutableSequence[str]):
    """A scrollable box containing a selection of items."""

    value: int  #: The **index** of the selected item.

    label: str = ConfigProperty()
    num_visible: int = ConfigProperty(
        key='num_items')  #: The number of items to show.

    items: Sequence[str]

    @ConfigProperty()
    def items(self) -> Sequence[str]:
        """Get or set this widget's items as a sequence."""
        return tuple(self._get_items())

    @items.getconfig
    def items(self, items: Sequence[str]):
        return {'items': list(items)}

    def __init__(self,
                 label: str = None,
                 items: Iterable[str] = (),
                 value: int = 0,
                 **config):
        super().__init__(label=label,
                         items=items,
                         default_value=value,
                         **config)

    def __setup_add_widget__(self, dpg_args) -> None:
        dpgcore.add_listbox(self.id, **dpg_args)

    def _get_items(self) -> List[str]:
        return self.get_config()['items']

    def __len__(self) -> int:
        return len(self._get_items())

    @overload
    def __getitem__(self, idx: int) -> str:
        ...

    @overload
    def __getitem__(self, idx: slice) -> Iterable[str]:
        ...

    def __getitem__(self, idx):
        return self._get_items()[idx]

    @overload
    def __setitem__(self, idx: int, label: str) -> None:
        ...

    @overload
    def __setitem__(self, idx: slice, label: Iterable[str]) -> None:
        ...

    def __setitem__(self, idx, label):
        items = self._get_items()
        items[idx] = label
        self.set_config(items=items)

    @overload
    def __delitem__(self, idx: int) -> None:
        ...

    @overload
    def __delitem__(self, idx: slice) -> None:
        ...

    def __delitem__(self, idx):
        items = self._get_items()
        del items[idx]
        self.set_config(items=items)

    def insert(self, idx: int, label: str) -> None:
        items = self._get_items()
        items.insert(idx, label)
        self.set_config(items=items)
Exemple #23
0
class Combo(Widget, ItemWidgetMx, ValueWidgetMx[str], MutableSequence[str]):
    """A combo box (drop down).

    Unlike :class:`.RadioButtons`, the :attr:`value` of a Combo is one of the item strings,
    not the index.

    Unless specified, none of the items are initially selected and :attr:`value` is an empty string.
    """
    value: str  #: The string **value** of the selected item.

    label: str = ConfigProperty()
    popup_align_left: bool = ConfigProperty()
    no_arrow_button: bool = ConfigProperty(
    )  #: Don't display the arrow button.
    no_preview: bool = ConfigProperty(
    )  #: Don't display the preview box showing the selected item.

    items: Sequence[str]

    @ConfigProperty()
    def items(self) -> Sequence[str]:
        """Get or set this widget's items as a sequence."""
        return tuple(self._get_items())

    @items.getconfig
    def items(self, items: Sequence[str]):
        return {'items': list(items)}

    height_mode: ComboHeightMode

    @ConfigProperty(key='height')
    def height_mode(self) -> ComboHeightMode:
        config = self.get_config()
        for mode in ComboHeightMode:
            if config.get(mode.value):
                return mode
        warn('could not determine height_mode')
        return ComboHeightMode.Regular  # its supposedly the default?

    @height_mode.getconfig
    def height_mode(self, value: ComboHeightMode) -> ItemConfigData:
        return {mode.value: (mode == value) for mode in ComboHeightMode}

    def __init__(self,
                 label: str = None,
                 items: Iterable[str] = (),
                 value: str = '',
                 **config):
        super().__init__(label=label,
                         items=items,
                         default_value=value,
                         **config)

    def __setup_add_widget__(self, dpg_args) -> None:
        dpgcore.add_combo(self.id, **dpg_args)

    def _get_items(self) -> List[str]:
        return self.get_config()['items']

    def __len__(self) -> int:
        return len(self._get_items())

    @overload
    def __getitem__(self, idx: int) -> str:
        ...

    @overload
    def __getitem__(self, idx: slice) -> Iterable[str]:
        ...

    def __getitem__(self, idx):
        return self._get_items()[idx]

    @overload
    def __setitem__(self, idx: int, label: str) -> None:
        ...

    @overload
    def __setitem__(self, idx: slice, label: Iterable[str]) -> None:
        ...

    def __setitem__(self, idx, label):
        items = self._get_items()
        items[idx] = label
        self.set_config(items=items)

    @overload
    def __delitem__(self, idx: int) -> None:
        ...

    @overload
    def __delitem__(self, idx: slice) -> None:
        ...

    def __delitem__(self, idx):
        items = self._get_items()
        del items[idx]
        self.set_config(items=items)

    def insert(self, idx: int, label: str) -> None:
        items = self._get_items()
        items.insert(idx, label)
        self.set_config(items=items)
Exemple #24
0
class Window(Widget):
    """Creates a new window."""

    label: str = ConfigProperty()
    x_pos: int = ConfigProperty()
    y_pos: int = ConfigProperty()
    autosize: bool = ConfigProperty()

    no_resize: bool = ConfigProperty()
    no_title_bar: bool = ConfigProperty()
    no_move: bool = ConfigProperty()
    no_collapse: bool = ConfigProperty()
    no_focus_on_appearing: bool = ConfigProperty()
    no_bring_to_front_on_focus: bool = ConfigProperty()
    no_close: bool = ConfigProperty()
    no_background: bool = ConfigProperty()

    show_menubar: bool = ConfigProperty(key='menubar')

    #: Disable scrollbars (can still scroll with mouse or programmatically).
    no_scrollbar: bool = ConfigProperty()

    #: Allow horizontal scrollbar to appear.
    horizontal_scrollbar: bool = ConfigProperty()

    pos: Tuple[int, int]

    @ConfigProperty()
    def pos(self) -> Tuple[int, int]:
        """Get or set (x_pos, y_pos) as a tuple."""
        config = self.get_config()
        return config['x_pos'], config['y_pos']

    @pos.getconfig
    def pos(self, value: Tuple[int, int]) -> ItemConfigData:
        width, height = value
        return {'x_pos': width, 'y_pos': height}

    def __init__(self, label: str = None, *, name_id: str = None, **config):
        """
        Parameters:
             label: window label.
        """
        super().__init__(label=label, name_id=name_id, **config)

    def _setup_add_widget(self, dpg_args) -> None:
        dpgcore.add_window(self.id, on_close=self._on_close, **dpg_args)

    def __enter__(self) -> Window:
        return self

    def __exit__(self, exc_type, exc_val, exc_tb) -> None:
        dpgcore.end()

    ## workaround for the fact that you can't set the on_close callback in DPG
    _on_close_callback: Optional[Callable] = None

    def _on_close(self, sender, data) -> None:
        if self._on_close_callback is not None:
            self._on_close_callback(sender, data)

    def on_close(self, callback: Optional[PyGuiCallback]) -> Callable:
        """Set on_close callback, can be used as a decorator."""
        if callback is not None:
            callback = wrap_callback(callback)
        self._on_close_callback = callback
        return callback

    def resized(self, callback: PyGuiCallback) -> Callable:
        """Set resized callback, can be used as a decorator."""
        dpgcore.set_resize_callback(wrap_callback(callback), handler=self.id)
        return callback
Exemple #25
0
class Table(Widget, ItemWidgetMx):
    """Adds a simple table that can hold text.

    A Table's data consists of a sequence of rows, each row being a sequence of strings.

    Note that a Table has two different kinds of "columns". A Table will have a number of *data*
    columns and a number of *header* columns.

    These won't always match. If you have more data columns than header columns, only a subsection
    of the data will actually get shown. This will be the case even if :attr:`hide_headers` is
    ``True``.

    Parameters:
        headers: can be an iterable of header strings or an integer. If an integer is used,
            it will set the number of header columns and the :attr:`hide_headers` property
            will be set to ``True``.

    To get/set values in the table, indexing syntax can be used. For example:

    .. code-block:: python

        table[2, 3] = 'cell content'
        table[3, :] = ['sets', 'an', 'entire', 'row']
        table[:, 4] = ['sets', 'an', 'entire', 'column']
        table[:, :] = [['first', row'], ['second', 'row'], ['third', 'row]]

    Cell selection state can also be modified in a similar manner.

    .. code-block:: python

        table.selection[1, :] = True  # selects the entire second row.

    """

    hide_headers: bool = ConfigProperty(
    )  #: If ``True``, table headers will not be displayed.

    #: A :class:`.TableSelection` instance that can be used to get or modify the table's cell
    #: selection state.
    selected: TableSelection

    def __init__(self, headers: Union[int, Iterable[str]] = 2, **config: Any):
        if isinstance(headers, int):
            super().__init__(headers=['' for i in range(headers)],
                             hide_headers=True,
                             **config)
        else:
            super().__init__(headers=headers, **config)
        self.selected = TableSelection(self)

    def __setup_add_widget__(self, dpg_args: MutableMapping[str, Any]) -> None:
        dpgcore.add_table(self.id, **dpg_args)

    def set_headers(self, headers: Union[Iterable[str], int]) -> None:
        """Set the table headers.

        This determines the number of displayed columns (distinct from the number of data columns!).
        If an integer is passed, the headers will be replaced with empty strings and hidden."""
        if isinstance(headers, int):
            headers = ['' for i in range(headers)]
            self.hide_headers = True
        dpgcore.set_headers(self.id, headers)

    def _get_data(self) -> List[List[str]]:
        return dpgcore.get_table_data(self.id)

    @property
    def rows(self) -> int:
        """The number of data rows."""
        return len(self._get_data())

    @property
    def columns(self) -> int:
        """The number of data columns."""
        data = self._get_data()
        if len(data):
            return len(data[0])
        return 0

    @overload
    def __getitem__(self, indices: Tuple[int, int]) -> str:
        ...

    @overload
    def __getitem__(self, indices: Tuple[int, slice]) -> Sequence[str]:
        ...

    @overload
    def __getitem__(self, indices: Tuple[slice, int]) -> Sequence[str]:
        ...

    @overload
    def __getitem__(self, indices: Tuple[slice,
                                         slice]) -> Sequence[Sequence[str]]:
        ...

    def __getitem__(self, indices):
        """Get table data using indices or slices."""
        row_idx, col_idx = indices
        if isinstance(row_idx, slice) and isinstance(col_idx, slice):
            return tuple(
                tuple(row[col_idx]) for row in self._get_data()[row_idx])
        elif isinstance(row_idx, slice):
            return tuple(row[col_idx] for row in self._get_data()[row_idx])
        elif isinstance(col_idx, slice):
            return tuple(self._get_data()[row_idx][col_idx])
        else:
            return dpgcore.get_table_item(self.id, row_idx, col_idx)

    @overload
    def __setitem__(self, indices: Tuple[int, int], value: str) -> None:
        ...

    @overload
    def __setitem__(self, indices: Tuple[int, slice],
                    value: Iterable[str]) -> None:
        ...

    @overload
    def __setitem__(self, indices: Tuple[slice, int],
                    value: Iterable[str]) -> None:
        ...

    @overload
    def __setitem__(self, indices: Tuple[slice, slice],
                    value: Iterable[Iterable[str]]) -> None:
        ...

    def __setitem__(self, indices, value):
        """Set table data using indices or slices.
        The shape of the **value** argument must match the provided indices/slices."""
        row_idx, col_idx = indices

        ## both row_idx and col_idx are slices. value is an iterable of iterables
        if isinstance(row_idx, slice) and isinstance(col_idx, slice):
            if row_idx == slice(None) and col_idx == slice(None):
                data = value  # overwrite entire table data
            else:
                data = self._get_data(
                )  # overwrite just sliced rows and columns
                for data_row, set_row in zip(data[row_idx], value):
                    data_row[col_idx] = set_row
            dpgcore.set_table_data(self.id, data)

        ## just row_idx is a slice. value is an iterable
        elif isinstance(row_idx, slice):
            data = self._get_data()
            for row, s in zip(data[row_idx], value):
                row[col_idx] = s
            dpgcore.set_table_data(self.id, data)

        ## just col_idx is a slice. value is an iterable
        elif isinstance(col_idx, slice):
            data = self._get_data()
            data[row_idx][col_idx] = value
            dpgcore.set_table_data(self.id, data)

        ## neither are slices
        else:
            dpgcore.set_table_item(self.id, row_idx, col_idx, value)

    def clear(self) -> None:
        """Clear the table.

        This will remove all rows from the table.
        It does NOT change the table headers and therefore the number of visible columns."""
        dpgcore.clear_table(self.id)

    def append_row(self, row: Iterable[str]) -> None:
        dpgcore.add_row(self.id, list(row))

    def insert_row(self, row_idx: int, row: Iterable[str]) -> None:
        dpgcore.insert_row(self.id, row_idx, list(row))

    def remove_row(self, row_idx: int) -> None:
        dpgcore.delete_row(self.id, row_idx)

    def append_column(self, header: str, column: Iterable[str]) -> None:
        dpgcore.add_column(self.id, header, list(column))

    def insert_column(self, col_idx: int, header: str,
                      column: Iterable[str]) -> None:
        dpgcore.insert_column(self.id, col_idx, header, list(column))

    def remove_column(self, col_idx: int) -> None:
        dpgcore.delete_column(self.id, col_idx)
Exemple #26
0
class Plot(Widget, ItemWidget):
    """A rich plot widget."""

    ## Plot Axes

    xaxis: PlotXAxisConfig = PlotXAxis()  #: The X-axis
    yaxis: PlotYAxisConfig = PlotYAxis(0)  #: The Y-axis
    y2axis: PlotOptYAxisConfig = PlotYAxis(1, 'yaxis2')  #: Optional Y-axis 2
    y3axis: PlotOptYAxisConfig = PlotYAxis(2, 'yaxis3')  #: Optional Y-axis 3

    ## Config Properties

    label: str = ConfigProperty()
    x_axis_label: str = ConfigProperty(key='x_axis_name')
    y_axis_label: str = ConfigProperty(key='y_axis_name')

    show_annotations: bool = ConfigProperty()
    show_drag_lines: bool = ConfigProperty()
    show_drag_points: bool = ConfigProperty()
    show_color_scale: bool = ConfigProperty()

    scale_min: float = ConfigProperty()
    scale_max: float = ConfigProperty()
    scale_height: int = ConfigProperty()
    equal_aspects: bool = ConfigProperty()

    query: bool = ConfigProperty()
    crosshairs: bool = ConfigProperty()
    no_legend: bool = ConfigProperty()
    no_menus: bool = ConfigProperty()
    no_box_select: bool = ConfigProperty()
    no_mouse_pos: bool = ConfigProperty()
    no_highlight: bool = ConfigProperty()
    no_child: bool = ConfigProperty()

    anti_aliased: bool = ConfigProperty()

    def __init__(self, *, name_id: str = None, **config):
        # not super happy that we have to resort to typing.cast() here, but it works
        self._xaxis_config = PlotXAxisConfig(self, cast(PlotXAxis, Plot.xaxis))
        self._yaxis_config = PlotYAxisConfig(self, cast(PlotYAxis, Plot.yaxis))
        self._y2axis_config = PlotOptYAxisConfig(self,
                                                 cast(PlotYAxis, Plot.y2axis))
        self._y3axis_config = PlotOptYAxisConfig(self,
                                                 cast(PlotYAxis, Plot.y3axis))

        super().__init__(name_id=name_id, **config)

    def _setup_add_widget(self, dpg_args) -> None:
        dpgcore.add_plot(self.id, **dpg_args)

    def add_dataseries(self,
                       series: DataSeries,
                       *,
                       update_bounds: bool = True) -> None:
        """Add a :class:`.DataSeries` to this plot (or update it).

        Updates the data series if it has already been added."""
        series.update_plot(self, update_bounds)

    def remove_dataseries(self, series: DataSeries) -> None:
        """Remove a :class:`.DataSeries` from this plot if it has been added."""
        dpgcore.delete_series(self.id, series.id)

    def clear(self) -> None:
        dpgcore.clear_plot(self.id)

    def set_xlimits(self, limits: Optional[PlotLimits]) -> None:
        """Set the ``(min, max)`` limits for the x-axis, or pass ``None`` to use automatic limits."""
        if limits is None:
            dpgcore.set_plot_xlimits_auto(self.id)
        else:
            dpgcore.set_plot_xlimits(self.id, *limits)

    def set_ylimits(self, limits: Optional[PlotLimits]) -> None:
        """Set the ``(min, max)`` limits for the y-axis, or pass ``None`` to use automatic limits."""
        if limits is None:
            dpgcore.set_plot_ylimits_auto(self.id)
        else:
            dpgcore.set_plot_ylimits(self.id, *limits)

    def set_xticks(self, ticks: Optional[Iterable[TickLabel]]) -> None:
        """Set the tick labels for the x-axis, or pass ``None`` to use automatic ticks."""
        if ticks is None:
            dpgcore.reset_xticks(self.id)
        else:
            dpgcore.set_xticks(self.id, ticks)

    def set_yticks(self, ticks: Optional[Iterable[TickLabel]]) -> None:
        """Set the tick labels for the y-axis, or pass ``None`` to use automatic ticks."""
        if ticks is None:
            dpgcore.reset_yticks(self.id)
        else:
            dpgcore.set_yticks(self.id, ticks)
Exemple #27
0
class InputText(Widget, ItemWidgetMx, ValueWidgetMx[str]):
    """A text input box."""

    value: str  #: The inputted text.

    hint: str = ConfigProperty()
    multiline: bool = ConfigProperty()
    no_spaces: bool = ConfigProperty()
    uppercase: bool = ConfigProperty()
    tab_input: bool = ConfigProperty()
    decimal: bool = ConfigProperty()
    hexadecimal: bool = ConfigProperty()
    readonly: bool = ConfigProperty()
    password: bool = ConfigProperty()
    scientific: bool = ConfigProperty()
    label: str = ConfigProperty()
    on_enter: bool = ConfigProperty()

    def __init__(self, label: str = None, value: str = '', **config):
        super().__init__(label=label, default_value=value, **config)

    def __setup_add_widget__(self, dpg_args) -> None:
        dpgcore.add_input_text(self.id, **dpg_args)