plesty.lib.monitor.plots ======================== .. py:module:: plesty.lib.monitor.plots .. autoapi-nested-parse:: What a view draws, and the one shape every view is expressed in. A plot is a title and a function from :class:`~plesty.lib.monitor.data.RunData` to a list of drawables. That is the whole contract: .. code-block:: python @viz.plot("Spectrum") def spectrum(data): row = data.latest return [Line(row["wavelength"], row["counts"], x_label="Wavelength (nm)", y_label="Counts")] What is *fixed* is only how a result is expressed — five kinds of drawable, which is the vocabulary a renderer can draw. What is *free* is everything computed on the way to one: fit it, filter it, stack it, annotate it. The contract constrains the output and nothing else, which is what lets an experiment draw something nobody anticipated without asking for a new kind. **A drawable carries its own style.** There is no declaration to keep matched against it. The two used to be separate — a spec declared once, values keyed to it per update — which bought the renderer the right to build its items once, and cost the developer a string correspondence across two methods that nothing checked. The renderer now reuses its items by position and rebuilds when the shape changes, which is an optimisation it can make for itself. **Three ways in, one contract.** A function for the ordinary case; a :class:`Plot` subclass when a view has to remember something between ticks; and :meth:`Plot.build` for a view that is not a plot at all — a table, an image browser — which hands back a widget and draws nothing. Attributes ---------- .. autoapisummary:: plesty.lib.monitor.plots.COLORS plesty.lib.monitor.plots.SCALES plesty.lib.monitor.plots.Drawing plesty.lib.monitor.plots.DrawFunc Exceptions ---------- .. autoapisummary:: plesty.lib.monitor.plots.PlotError Classes ------- .. autoapisummary:: plesty.lib.monitor.plots.Drawable plesty.lib.monitor.plots.Line plesty.lib.monitor.plots.Scatter plesty.lib.monitor.plots.Image plesty.lib.monitor.plots.Scalar plesty.lib.monitor.plots.Text plesty.lib.monitor.plots.Plot plesty.lib.monitor.plots.FunctionPlot Functions --------- .. autoapisummary:: plesty.lib.monitor.plots.shape_of Module Contents --------------- .. py:data:: COLORS :type: tuple[str, Ellipsis] :value: ('cyan', 'violet', 'mint', 'amber', 'rose', 'blue') .. py:data:: SCALES :type: tuple[str, Ellipsis] :value: ('linear', 'sqrt') .. py:exception:: PlotError Bases: :py:obj:`ValueError` Raised when a plot declares something that cannot be drawn. Initialize self. See help(type(self)) for accurate signature. .. py:class:: Drawable One element of a plot: what to draw, and how it should look. :ivar label: Legend label. One starting with ``_`` stays out of the legend, as in matplotlib — for a trace the legend would only repeat, such as a fitted curve in the colour of its points. :ivar color: A token from :data:`COLORS`. :ivar x_label: Axis label including the unit, ``"Wavelength (nm)"``. :ivar y_label: Axis label including the unit. .. py:attribute:: kind :type: ClassVar[str] :value: 'drawable' .. py:attribute:: label :type: str :value: '' .. py:attribute:: color :type: str :value: 'cyan' .. py:attribute:: x_label :type: str :value: '' .. py:attribute:: y_label :type: str :value: '' .. py:method:: __post_init__() -> None Check the colour token. :raises PlotError: If the colour is not one the theme defines. .. py:class:: Line Bases: :py:obj:`Drawable` A curve through ``(x, y)``. :ivar x: Sample positions. :ivar y: Sample values. :ivar width: Line width. :ivar marker: Draw a point marker at every sample as well. .. py:attribute:: kind :type: ClassVar[str] :value: 'line' .. py:attribute:: x :type: Any :value: None .. py:attribute:: y :type: Any :value: None .. py:attribute:: width :type: float :value: 1.5 .. py:attribute:: marker :type: bool :value: False .. py:class:: Scatter Bases: :py:obj:`Drawable` Points at ``(x, y)``, drawn without a line between them. :ivar x: Sample positions. :ivar y: Sample values. :ivar size: Marker size. .. py:attribute:: kind :type: ClassVar[str] :value: 'scatter' .. py:attribute:: x :type: Any :value: None .. py:attribute:: y :type: Any :value: None .. py:attribute:: size :type: float :value: 8.0 .. py:class:: Image Bases: :py:obj:`Drawable` A 2-D block of values, drawn as a map with a colour bar. :ivar z: The values, shape ``(rows, columns)``. :ivar x: Positions of the columns, for the axis. :ivar y: Positions of the rows, for the axis. :ivar scale: ``"linear"``, or ``"sqrt"`` — colours follow the square root of the value, so features ten times weaker than the strongest still stand out from the background. The bar stays labelled in the image's own values. .. py:attribute:: kind :type: ClassVar[str] :value: 'image' .. py:attribute:: z :type: Any :value: None .. py:attribute:: x :type: Any :value: None .. py:attribute:: y :type: Any :value: None .. py:attribute:: scale :type: str :value: 'linear' .. py:method:: __post_init__() -> None Check the colour token and the colour scale. :raises PlotError: If the scale is not one the renderer knows. .. py:class:: Scalar Bases: :py:obj:`Drawable` A single number, shown large. :ivar value: The number. :ivar unit: Shown after it. .. py:attribute:: kind :type: ClassVar[str] :value: 'scalar' .. py:attribute:: value :type: Optional[float] :value: None .. py:attribute:: unit :type: str :value: '' .. py:class:: Text Bases: :py:obj:`Drawable` A line of text placed in the plot — a result, a warning, a caption. :ivar text: What to show. .. py:attribute:: kind :type: ClassVar[str] :value: 'text' .. py:attribute:: text :type: str :value: '' .. py:data:: Drawing .. py:data:: DrawFunc .. py:class:: Plot(title: str = '', *, name: str = '', weight: Optional[float] = None, area: str = '') Bases: :py:obj:`abc.ABC` One view of a run: a title, and what to draw from the run's data. Subclass this when a view has to remember something between ticks — a line tracker, a running baseline. For a view that is a pure function of the data, :meth:`~plesty.lib.monitor.Viz.plot` takes the function directly and there is no class to write. :ivar title: What the view is called, on its dock tab. :ivar name: Stable identifier, used as the layout key so an arrangement survives a restart; defaults to the title. :ivar weight: Relative size when the panels are first laid out. :ivar area: Which side of the window it starts on — one of :data:`~plesty.lib.ui.panel.DOCK_AREAS`. Views sharing an area are stacked in one column, so this is how a viewer says which of its plots belong side by side. The operator can drag them anywhere afterwards, and the arrangement is remembered. Name the view. :param title: Overrides the class attribute. :param name: Layout key; defaults to the title. :param weight: Overrides the class attribute. :param area: Overrides the class attribute. .. py:attribute:: title :type: str :value: '' .. py:attribute:: name :type: str :value: '' .. py:attribute:: weight :type: float :value: 1.0 .. py:attribute:: area :type: str :value: 'right' .. py:method:: draw(data: Any) -> Drawing :abstractmethod: Return what to draw, given everything the run has produced. Called on every tick, on the GUI thread. It should be a projection of the data and nothing else — no device is reachable from here and none should be: watching never disturbs measuring. :param data: The run's :class:`~plesty.lib.monitor.data.RunData`. :returns: The drawables, in the order they should be layered. .. py:method:: build() -> Any Return a widget for this view instead of a plot, or ``None``. The escape hatch. A view that is not a plot at all — a table of the committed rows, an image browser, a strip of readouts — returns its own widget here and ignores :meth:`draw`. Anything Qt can show is allowed; the dock does not care what is inside it. :returns: ``None`` by default, meaning "draw me as a plot". .. py:method:: __repr__() -> str Return ````. .. py:class:: FunctionPlot(func: DrawFunc, title: str = '', *, name: str = '', weight: Optional[float] = None) Bases: :py:obj:`Plot` A plot written as a function — what :meth:`Viz.plot` wraps. Not usually built by hand: decorating a function with :meth:`~plesty.lib.monitor.Viz.plot` makes one of these. Wrap *func* as a plot. :param func: Takes the run's data, returns drawables. :param title: What the view is called; defaults to the function's name with underscores as spaces. :param name: Layout key. :param weight: Relative size. .. py:attribute:: func .. py:method:: draw(data: Any) -> Drawing Call the wrapped function. :param data: The run's data. :returns: Whatever the function returned; ``()`` when it returned nothing, so a plot with nothing to show yet is empty rather than an error. .. py:function:: shape_of(drawing: Drawing) -> tuple[Any, Ellipsis] Return what about a drawing decides how it is built. A renderer reuses its items while this is unchanged and rebuilds when it is not, which is what replaces the keys a plot used to have to declare. :param drawing: The drawables returned by a plot. :returns: its kind, label, colour and scale. :rtype: One entry per drawable