Source code for fastplotlib.layouts._subplot

from typing import Literal, Union

import pygfx

from rendercanvas import BaseRenderCanvas

from ..graphics import TextGraphic
from ._utils import create_camera, create_controller
from ._plot_area import PlotArea
from ._graphic_methods_mixin import GraphicMethodsMixin
from ..graphics._axes import Axes


[docs] class Subplot(PlotArea, GraphicMethodsMixin): def __init__( self, parent: Union["Figure"], camera: Literal["2d", "3d"] | pygfx.PerspectiveCamera, controller: pygfx.Controller, canvas: BaseRenderCanvas | pygfx.Texture, renderer: pygfx.WgpuRenderer = None, name: str = None, ): """ General plot object is found within a ``Figure``. Each ``Figure`` instance will have [n rows, n columns] of subplots. .. important:: ``Subplot`` is not meant to be constructed directly, it only exists as part of a ``Figure`` Parameters ---------- parent: 'Figure' | None parent Figure instance position: (int, int), optional corresponds to the [row, column] position of the subplot within a ``Figure`` camera: str or pygfx.PerspectiveCamera, default '2d' indicates the FOV for the camera, '2d' sets ``fov = 0``, '3d' sets ``fov = 50``. ``fov`` can be changed at any time. controller: str or pygfx.Controller, optional | if ``None``, uses a PanZoomController for "2d" camera or FlyController for "3d" camera. | if ``str``, must be one of: `"panzoom", "fly", "trackball", or "orbit"`. | also accepts a pygfx.Controller instance canvas: BaseRenderCanvas, or a pygfx.Texture Provides surface on which a scene will be rendered. renderer: WgpuRenderer object used to render scenes using wgpu name: str, optional name of the subplot, will appear as ``TextGraphic`` above the subplot """ super(GraphicMethodsMixin, self).__init__() camera = create_camera(camera) controller = create_controller(controller_type=controller, camera=camera) self._docks = dict() self._title_graphic: TextGraphic = None self._toolbar = True super(Subplot, self).__init__( parent=parent, camera=camera, controller=controller, scene=pygfx.Scene(), canvas=canvas, renderer=renderer, name=name, ) for pos in ["left", "top", "right", "bottom"]: dv = Dock(self, pos, size=0) dv.name = pos self.docks[pos] = dv self.children.append(dv) if self.name is not None: self.set_title(self.name) self._axes = Axes(self) self.scene.add(self.axes.world_object) @property def axes(self) -> Axes: return self._axes @property def name(self) -> str: return self._name @name.setter def name(self, name: str): if name is None: self._name = None return for subplot in self.get_figure(self): if (subplot is self) or (subplot is None): continue if subplot.name == name: raise ValueError("subplot names must be unique") self._name = name @property def docks(self) -> dict: """ The docks of this plot area. Each ``dock`` is basically just a PlotArea too. The docks are: ["left", "top", "right", "bottom"] Returns ------- Dict[str, Dock] {dock_name: Dock} """ return self._docks @property def toolbar(self) -> bool: """show/hide toolbar""" return self._toolbar @toolbar.setter def toolbar(self, visible: bool): self._toolbar = bool(visible) self.get_figure()._fpl_set_subplot_viewport_rect(self) def _render(self): self.axes.update_using_camera() super()._render()
[docs] def set_title(self, text: str): """Sets the plot title, stored as a ``TextGraphic`` in the "top" dock area""" if text is None: return text = str(text) if self._title_graphic is not None: self._title_graphic.text = text else: tg = TextGraphic(text=text, font_size=18) self._title_graphic = tg self.docks["top"].size = 35 self.docks["top"].add_graphic(tg) self.center_title()
[docs] def center_title(self): """Centers name of subplot.""" if self._title_graphic is None: raise AttributeError("No title graphic is set") self._title_graphic.world_object.position = (0, 0, 0) self.docks["top"].center_graphic(self._title_graphic, zoom=1.5) self._title_graphic.world_object.position_y = -3.5
class Dock(PlotArea): _valid_positions = ["right", "left", "top", "bottom"] def __init__( self, parent: Subplot, position: str, size: int, ): if position not in self._valid_positions: raise ValueError( f"the `position` of an AnchoredViewport must be one of: {self._valid_positions}" ) self._size = size self._position = position super().__init__( parent=parent, camera=pygfx.OrthographicCamera(), controller=pygfx.PanZoomController(), scene=pygfx.Scene(), canvas=parent.canvas, renderer=parent.renderer, ) @property def position(self) -> str: return self._position @property def size(self) -> int: """Get or set the size of this dock""" return self._size @size.setter def size(self, s: int): self._size = s if self.position == "top": # TODO: treat title dock separately, do not allow user to change viewport stuff return self.get_figure(self.parent)._fpl_set_subplot_viewport_rect(self.parent) self.get_figure(self.parent)._fpl_set_subplot_dock_viewport_rect( self.parent, self._position ) def _render(self): if self.size == 0: return super()._render()