Source code for fastplotlib.ui._base

from typing import Literal
import numpy as np

from imgui_bundle import imgui

from ..layouts._figure import Figure


GUI_EDGES = ["top", "right", "bottom", "left"]


[docs] class BaseGUI: """ Base class for all ImGUI based GUIs, windows and popups The main purpose of this base is for setting a unique ID between multiple figs with identical UI elements This ID can be pushed in subclasses within the `update()` method """ ID_COUNTER: int = 0 def __init__(self): BaseGUI.ID_COUNTER += 1 self._id_counter = BaseGUI.ID_COUNTER
[docs] def update(self): """must be implemented in subclass""" raise NotImplementedError
[docs] class Window(BaseGUI): """Base class for imgui windows drawn within Figures""" pass
[docs] class EdgeWindow(Window): def __init__( self, figure: Figure, size: int, location: Literal["top", "bottom", "left", "right"], title: str, window_flags: int = imgui.WindowFlags_.no_collapse | imgui.WindowFlags_.no_resize, *args, **kwargs, ): """ A base class for imgui windows displayed at one of the four edges of a Figure Parameters ---------- figure: Figure Figure instance that this window will be placed in size: int width or height of the window, depending on its location location: str, "top" | "bottom" | "left" | "right" location of the window title: str window title window_flags: int window flag enum, valid flags are: .. code-block:: py imgui.WindowFlags_.no_title_bar imgui.WindowFlags_.no_resize imgui.WindowFlags_.no_move imgui.WindowFlags_.no_scrollbar imgui.WindowFlags_.no_scroll_with_mouse imgui.WindowFlags_.no_collapse imgui.WindowFlags_.always_auto_resize imgui.WindowFlags_.no_background imgui.WindowFlags_.no_saved_settings imgui.WindowFlags_.no_mouse_inputs imgui.WindowFlags_.menu_bar imgui.WindowFlags_.horizontal_scrollbar imgui.WindowFlags_.no_focus_on_appearing imgui.WindowFlags_.no_bring_to_front_on_focus imgui.WindowFlags_.always_vertical_scrollbar imgui.WindowFlags_.always_horizontal_scrollbar imgui.WindowFlags_.no_nav_inputs imgui.WindowFlags_.no_nav_focus imgui.WindowFlags_.unsaved_document imgui.WindowFlags_.no_docking imgui.WindowFlags_.no_nav imgui.WindowFlags_.no_decoration imgui.WindowFlags_.no_inputs *args additional args for the GUI **kwargs additional kwargs for teh GUI """ super().__init__() if location not in GUI_EDGES: f"GUI does not have a valid location, valid locations are: {GUI_EDGES}, you have passed: {location}" self._figure = figure self._size = size self._location = location self._title = title self._window_flags = window_flags self._fa_icons = self._figure._fa_icons self._x, self._y, self._width, self._height = self.get_rect() self._figure.canvas.add_event_handler(self._set_rect, "resize") @property def size(self) -> int | None: """width or height of the edge window""" return self._size @size.setter def size(self, value): if not isinstance(value, int): raise TypeError self._size = value @property def location(self) -> str: """location of the window""" return self._location @property def x(self) -> int: """canvas x position of the window""" return self._x @property def y(self) -> int: """canvas y position of the window""" return self._y @property def width(self) -> int: """with the window""" return self._width @property def height(self) -> int: """height of the window""" return self._height def _set_rect(self, *args): self._x, self._y, self._width, self._height = self.get_rect()
[docs] def get_rect(self) -> tuple[int, int, int, int]: """ Compute the rect that defines the area this GUI is drawn to Returns ------- int, int, int, int x_pos, y_pos, width, height """ width_canvas, height_canvas = self._figure.canvas.get_logical_size() match self._location: case "top": x_pos, y_pos = (0, 0) width, height = (width_canvas, self.size) case "bottom": x_pos = 0 y_pos = height_canvas - self.size width, height = (width_canvas, self.size) case "right": x_pos, y_pos = (width_canvas - self.size, 0) if self._figure.guis["top"]: # if there is a GUI in the top edge, make this one below y_pos += self._figure.guis["top"].size width, height = (self.size, height_canvas) if self._figure.guis["bottom"] is not None: height -= self._figure.guis["bottom"].size case "left": x_pos, y_pos = (0, 0) if self._figure.guis["top"]: # if there is a GUI in the top edge, make this one below y_pos += self._figure.guis["top"].size width, height = (self.size, height_canvas) if self._figure.guis["bottom"] is not None: height -= self._figure.guis["bottom"].size return x_pos, y_pos, width, height
[docs] def draw_window(self): """helps simplify using imgui by managing window creation & position, and pushing/popping the ID""" # window position & size imgui.set_next_window_size((self.width, self.height)) imgui.set_next_window_pos((self.x, self.y)) flags = self._window_flags # begin window imgui.begin(self._title, p_open=None, flags=flags) # push ID to prevent conflict between multiple figs with same UI imgui.push_id(self._id_counter) # draw stuff from subclass into window self.update() # pop ID imgui.pop_id() # end the window imgui.end()
[docs] def update(self): """Implement your GUI here and it will be drawn within the window. See the GUI examples""" raise NotImplementedError