Skip to content

sunflare.presenter module

Presenters come in two forms:

Protocols

Protocol classes - no inheritance required, allows full customization of the controller:

  • Users will have to implement each interface on their own in a controller class
  • Available protocols:

Mixin Classes

Boilerplate mixin classes - minimal, reusable components to provide a specific functionality:

API Reference

Connection

Bases: NamedTuple

Connection tuple.

Provides a type-hinted helper for describing a connection between a virtual bus signal and a local slot.

Usable with sunflare.presenter.Receiver and sunflare.presenter.SenderReceiver to set the connection_map parameter.

Attributes:

Name Type Description
signal ForwardRef('str')

Signal name.

slot ForwardRef('Callable[..., None]')

Slot to connect to.

Source code in src/sunflare/presenter/_base.py
class Connection(NamedTuple):
    """Connection tuple.

    Provides a type-hinted helper
    for describing a connection between
    a virtual bus signal and a local slot.

    Usable with [`sunflare.presenter.Receiver`]()
    and [`sunflare.presenter.SenderReceiver`]()
    to set the `connection_map` parameter.

    Attributes
    ----------
    signal : ``str``
        Signal name.
    slot : ``Callable[..., None]``
        Slot to connect to.
    """

    signal: str
    slot: Callable[..., None]

PPresenter

Bases: Protocol

Presenter protocol class.

Provides the interface for a class that Redsun can recognize as a controller by implementing the defined attributes.

Attributes:

Name Type Description
ctrl_info PPresenterInfo

Presenter configuration information.

models Mapping[str, PModel]

Reference to the models used in the controller.

virtual_bus VirtualBus

Reference to the virtual bus.

Source code in src/sunflare/presenter/_base.py
@runtime_checkable
class PPresenter(Protocol):  # pragma: no cover
    """Presenter protocol class.

    Provides the interface for a class
    that Redsun can recognize as a controller by
    implementing the defined attributes.

    Attributes
    ----------
    ctrl_info : PPresenterInfo
        Presenter configuration information.
    models : Mapping[str, PModel]
        Reference to the models used in the controller.
    virtual_bus : VirtualBus
        Reference to the virtual bus.
    """

    ctrl_info: PPresenterInfo
    virtual_bus: VirtualBus
    models: Mapping[str, PModel]

Presenter

Bases: PPresenter, Generic[CI]

A boilerplate base class for quick development.

Users may subclass from this controller and provide their custom sunflare.config.PresenterInfo implementation.

Example usage:

from sunflare.presenter import Presenter
from sunflare.config import PresenterInfo
from attrs import define


@define
class MyControllerInfo(PresenterInfo):
    str_param: str
    bool_param: bool
    # any other parameters...


class MyController(Presenter[MyControllerInfo]):
    def __init__(
        self,
        ctrl_info: MyControllerInfo,
        models: Mapping[str, PModel],
        virtual_bus: VirtualBus,
    ) -> None:
        super().__init__(ctrl_info, models, virtual_bus)
        # any other initialization code...

Parameters:

Name Type Description Default
ctrl_info ``CI``

Instance of sunflare.config.PresenterInfo subclass.

required
models ``Mapping[str, PModel]``

Reference to the models used in the controller.

required
virtual_bus `sunflare.virtual.VirtualBus`

Reference to the virtual bus.

required
Source code in src/sunflare/presenter/_base.py
class Presenter(PPresenter, Generic[CI]):
    """A boilerplate base class for quick development.

    Users may subclass from this controller and provide their custom
    `sunflare.config.PresenterInfo` implementation.

    Example usage:

    ```python
    from sunflare.presenter import Presenter
    from sunflare.config import PresenterInfo
    from attrs import define


    @define
    class MyControllerInfo(PresenterInfo):
        str_param: str
        bool_param: bool
        # any other parameters...


    class MyController(Presenter[MyControllerInfo]):
        def __init__(
            self,
            ctrl_info: MyControllerInfo,
            models: Mapping[str, PModel],
            virtual_bus: VirtualBus,
        ) -> None:
            super().__init__(ctrl_info, models, virtual_bus)
            # any other initialization code...
    ```

    Parameters
    ----------
    ctrl_info : ``CI``
        Instance of `sunflare.config.PresenterInfo` subclass.
    models : ``Mapping[str, PModel]``
        Reference to the models used in the controller.
    virtual_bus : `sunflare.virtual.VirtualBus`
        Reference to the virtual bus.
    """

    def __init__(
        self,
        ctrl_info: CI,
        models: Mapping[str, PModel],
        virtual_bus: VirtualBus,
    ) -> None:
        self.ctrl_info = ctrl_info
        self.models = models
        self.virtual_bus = virtual_bus

Receiver

Bases: Presenter[CI]

A controller capable of connecting signals exposed in the virtual bus to local slots.

Provides an additional connection_map parameter to the constructor to optionally connect a group of the controller's defined signals to local slots.

Users may subclass from this controller and provide their custom :class:~sunflare.config.PresenterInfo implementation.

Example usage:

from sunflare.presenter import ReceiverController
from sunflare.config import PresenterInfo
from attrs import define

@define
class MyControllerInfo(PresenterInfo):
    str_param: str
    bool_param: bool
    # any other parameters...

class MyController(ReceiverController[MyControllerInfo]):
    def __init__(
        self,
        ctrl_info: MyControllerInfo,
        models: Mapping[str, PModel],
        virtual_bus: VirtualBus,
    ) -> None:
        connection_map = {
            "WidgetEmitter": [
                Connection("sigWidgetSignal1", self.widget_slot),
                Connection("sigWidgetSignal2", self.other_widget_slot)
            ],
            "ControllerEmitter": [
                Connection("sigControllerSignal", self.controller_slot),
            ],
        }
        super().__init__(ctrl_info, models, virtual_bus, connection_map)
        # any other initialization code...

    def widget_slot(self, *args, **kwargs) -> None:
        # your logic here...

    def other_widget_slot(self, *args, **kwargs) -> None:
        # your logic here...

    def controller_slot(self, *args, **kwargs) -> None:
        # your logic here...

Note

The controller slots should respect the signals' signatures in order to correctly receive the emitted signals. Make sure to review any provided documentation about the nature of the signals being connected.

See sunflare.presenter.Presenter for parent information.

Parameters:

Name Type Description Default
ctrl_info ``CI``

Instance of a sunflare.config.PresenterInfo subclass.

required
models ``Mapping[str, PModel]``

Reference to the models used in the controller.

required
virtual_bus `sunflare.virtual.VirtualBus`

Reference to the virtual bus.

required
connection_map ``Mapping[str, list[Connection]]``

Mapping of emitters to a list of connections. Default is None (no connections).

None

Attributes:

Name Type Description
connection_map ``Mapping[str, list[Connection]]``, keyword-only, optional

Mapping of emitters to a list of connections.

Source code in src/sunflare/presenter/_base.py
class Receiver(Presenter[CI]):
    """A controller capable of connecting signals exposed in the virtual bus to local slots.

    Provides an additional `connection_map` parameter to the constructor
    to optionally connect a group of the controller's defined signals to local slots.

    Users may subclass from this controller and provide their custom
    :class:`~sunflare.config.PresenterInfo` implementation.

    Example usage:

    ```python
    from sunflare.presenter import ReceiverController
    from sunflare.config import PresenterInfo
    from attrs import define

    @define
    class MyControllerInfo(PresenterInfo):
        str_param: str
        bool_param: bool
        # any other parameters...

    class MyController(ReceiverController[MyControllerInfo]):
        def __init__(
            self,
            ctrl_info: MyControllerInfo,
            models: Mapping[str, PModel],
            virtual_bus: VirtualBus,
        ) -> None:
            connection_map = {
                "WidgetEmitter": [
                    Connection("sigWidgetSignal1", self.widget_slot),
                    Connection("sigWidgetSignal2", self.other_widget_slot)
                ],
                "ControllerEmitter": [
                    Connection("sigControllerSignal", self.controller_slot),
                ],
            }
            super().__init__(ctrl_info, models, virtual_bus, connection_map)
            # any other initialization code...

        def widget_slot(self, *args, **kwargs) -> None:
            # your logic here...

        def other_widget_slot(self, *args, **kwargs) -> None:
            # your logic here...

        def controller_slot(self, *args, **kwargs) -> None:
            # your logic here...
    ```

    !!! note
        The controller slots should respect the signals' signatures
        in order to correctly receive the emitted signals.
        Make sure to review any provided documentation
        about the nature of the signals being connected.

    See [`sunflare.presenter.Presenter`]() for parent information.

    Parameters
    ----------
    ctrl_info : ``CI``
        Instance of a `sunflare.config.PresenterInfo` subclass.
    models : ``Mapping[str, PModel]``
        Reference to the models used in the controller.
    virtual_bus : `sunflare.virtual.VirtualBus`
        Reference to the virtual bus.
    connection_map : ``Mapping[str, list[Connection]]``, optional
        Mapping of emitters to a list of connections.
        Default is ``None`` (no connections).

    Attributes
    ----------
    connection_map : ``Mapping[str, list[Connection]]``, keyword-only, optional
        Mapping of emitters to a list of connections.
    """

    def __init__(
        self,
        ctrl_info: CI,
        models: Mapping[str, PModel],
        virtual_bus: VirtualBus,
        *,
        connection_map: Mapping[str, list[Connection]] | None = None,
    ) -> None:
        self.connection_map = connection_map
        super().__init__(ctrl_info, models, virtual_bus)

    def connection_phase(self) -> None:
        """Connect the signals defined in ``self.connection_map``.

        This method is called from Redsun during
        application initialization.
        """
        if self.connection_map is not None:
            for emitter, connections in self.connection_map.items():
                for connection in connections:
                    self.virtual_bus.signals[emitter][connection.signal].connect(
                        connection.slot
                    )

connection_phase

connection_phase() -> None

Connect the signals defined in self.connection_map.

This method is called from Redsun during application initialization.

Source code in src/sunflare/presenter/_base.py
def connection_phase(self) -> None:
    """Connect the signals defined in ``self.connection_map``.

    This method is called from Redsun during
    application initialization.
    """
    if self.connection_map is not None:
        for emitter, connections in self.connection_map.items():
            for connection in connections:
                self.virtual_bus.signals[emitter][connection.signal].connect(
                    connection.slot
                )

Sender

Bases: Presenter[CI]

A controller capable of emitting signals to the virtual bus.

Provides an additional signals parameter to the constructor to optionally register a group of the controller's defined signals.

Users may subclass from this controller and provide their custom sunflare.config.PresenterInfo implementation.

Example usage:

from sunflare.presenter import SignalerController
from sunflare.config import PresenterInfo
from attrs import define


@define
class MyControllerInfo(PresenterInfo):
    str_param: str
    bool_param: bool
    # any other parameters...


class MyController(SignalerController[MyControllerInfo]):
    sigRegisteredSignal = Signal(str)
    sigUnregisteredSignal = Signal(int)

    def __init__(
        self,
        ctrl_info: MyControllerInfo,
        models: Mapping[str, PModel],
        virtual_bus: VirtualBus,
    ) -> None:
        signals = ["sigRegisteredSignal"]
        super().__init__(ctrl_info, models, virtual_bus, signals)
        # any other initialization code...

See sunflare.presenter.Presenter for parent information.

Note

Users should carefully document the data transmitted by the signals to ensure other endpoints can easily connect to them.

Parameters:

Name Type Description Default
ctrl_info ``CI``

Instance of a sunflare.config.PresenterInfo subclass.

required
models ``Mapping[str, PModel]``

Reference to the models used in the controller.

required
virtual_bus `sunflare.virtual.VirtualBus`

Reference to the virtual bus.

required
signals ``Sequence[str]``, keyword-only

Sequence of signals to register. Default is None (no signals registered).

None

Attributes:

Name Type Description
signals ``Sequence[str]``, optional

Sequence of registered signals.

Source code in src/sunflare/presenter/_base.py
class Sender(Presenter[CI]):
    """A controller capable of emitting signals to the virtual bus.

    Provides an additional `signals` parameter to the constructor
    to optionally register a group of the controller's defined
    signals.

    Users may subclass from this controller and provide their custom
    `sunflare.config.PresenterInfo` implementation.

    Example usage:

    ```python
    from sunflare.presenter import SignalerController
    from sunflare.config import PresenterInfo
    from attrs import define


    @define
    class MyControllerInfo(PresenterInfo):
        str_param: str
        bool_param: bool
        # any other parameters...


    class MyController(SignalerController[MyControllerInfo]):
        sigRegisteredSignal = Signal(str)
        sigUnregisteredSignal = Signal(int)

        def __init__(
            self,
            ctrl_info: MyControllerInfo,
            models: Mapping[str, PModel],
            virtual_bus: VirtualBus,
        ) -> None:
            signals = ["sigRegisteredSignal"]
            super().__init__(ctrl_info, models, virtual_bus, signals)
            # any other initialization code...
    ```

    See [`sunflare.presenter.Presenter`]() for parent information.

    !!! note
        Users should carefully document the data transmitted by the signals
        to ensure other endpoints can easily connect to them.

    Parameters
    ----------
    ctrl_info : ``CI``
        Instance of a `sunflare.config.PresenterInfo` subclass.
    models : ``Mapping[str, PModel]``
        Reference to the models used in the controller.
    virtual_bus : `sunflare.virtual.VirtualBus`
        Reference to the virtual bus.
    signals : ``Sequence[str]``, keyword-only, optional
        Sequence of signals to register.
        Default is ``None`` (no signals registered).

    Attributes
    ----------
    signals : ``Sequence[str]``, optional
        Sequence of registered signals.
    """

    def __init__(
        self,
        ctrl_info: CI,
        models: Mapping[str, PModel],
        virtual_bus: VirtualBus,
        *,
        signals: Sequence[str] | None = None,
    ) -> None:
        self.signals = signals
        super().__init__(ctrl_info, models, virtual_bus)

    def registration_phase(self) -> None:
        """Register the signals defined in ``self.signals``.

        This method is called from Redsun during
        application initialization.
        """
        self.virtual_bus.register_signals(self, self.signals)

registration_phase

registration_phase() -> None

Register the signals defined in self.signals.

This method is called from Redsun during application initialization.

Source code in src/sunflare/presenter/_base.py
def registration_phase(self) -> None:
    """Register the signals defined in ``self.signals``.

    This method is called from Redsun during
    application initialization.
    """
    self.virtual_bus.register_signals(self, self.signals)

SenderReceiver

Bases: Presenter[CI]

Combines the functionality of sunflare.presenter.Sender and sunflare.presenter.Receiver.

Users may subclass from this controller and provide their custom sunflare.config.PresenterInfo implementation.

Example usage:

.. code-block:: python

from sunflare.presenter import SenderReceiverController
from sunflare.config import PresenterInfo
from attrs import define

@define
class MyControllerInfo(PresenterInfo):
    str_param: str
    bool_param: bool
    # any other parameters...

class MyController(SenderReceiverController[MyControllerInfo]):
    sigRegisteredSignal = Signal(str)
    sigUnregisteredSignal = Signal(int)

    def __init__(
        self,
        ctrl_info: MyControllerInfo,
        models: Mapping[str, PModel],
        virtual_bus: VirtualBus,
    ) -> None:
        signals = ["sigRegisteredSignal"]
        connection_map = {
            "WidgetEmitter": [
                Connection("sigWidgetSignal1", self.widget_slot),
                # you can recover the slot with "getattr(self, <slot_name>)" too
                Connection("sigWidgetSignal2", getattr(self, "other_widget_slot"))
            ],
            "ControllerEmitter": [
                Connection("sigControllerSignal", self.controller_slot),
            ],
        }
        super().__init__(ctrl_info, models, virtual_bus, signals, connection_map)
        # any other initialization code...

    def widget_slot(self, *args, **kwargs) -> None:
        # your logic here...

    def other_widget_slot(self, *args, **kwargs) -> None:
        # your logic here...

    def controller_slot(self, *args, **kwargs) -> None:
        # your logic here...

.. note::

    The controller slots should respect the signals' signatures
    in order to correctly receive the emitted signals.
    Make sure to review any provided documentation
    about the nature of the signals being connected.

Parameters:

Name Type Description Default
ctrl_info ``CI``

Instance of a sunflare.config.PresenterInfo subclass.

required
models ``Mapping[str, PModel]``

Reference to the models used in the controller.

required
virtual_bus `sunflare.virtual.VirtualBus`

Reference to the virtual bus.

required
signals ``Sequence[str]``

Sequence of signals to register. Default is None (no signals registered).

None
connection_map ``Mapping[str, list[Connection]]``

Mapping of emitters to a list of connections. Default is None (no connections).

None

Attributes:

Name Type Description
signals ``Sequence[str]``, optional

Sequence of registered signals.

connection_map ``Mapping[str, list[Connection]]``, optional

Mapping of emitters to a list of connections.

Source code in src/sunflare/presenter/_base.py
class SenderReceiver(Presenter[CI]):
    """Combines the functionality of [`sunflare.presenter.Sender`]() and [`sunflare.presenter.Receiver`]().

    Users may subclass from this controller and provide their custom
    `sunflare.config.PresenterInfo` implementation.

    Example usage:

    .. code-block:: python

        from sunflare.presenter import SenderReceiverController
        from sunflare.config import PresenterInfo
        from attrs import define

        @define
        class MyControllerInfo(PresenterInfo):
            str_param: str
            bool_param: bool
            # any other parameters...

        class MyController(SenderReceiverController[MyControllerInfo]):
            sigRegisteredSignal = Signal(str)
            sigUnregisteredSignal = Signal(int)

            def __init__(
                self,
                ctrl_info: MyControllerInfo,
                models: Mapping[str, PModel],
                virtual_bus: VirtualBus,
            ) -> None:
                signals = ["sigRegisteredSignal"]
                connection_map = {
                    "WidgetEmitter": [
                        Connection("sigWidgetSignal1", self.widget_slot),
                        # you can recover the slot with "getattr(self, <slot_name>)" too
                        Connection("sigWidgetSignal2", getattr(self, "other_widget_slot"))
                    ],
                    "ControllerEmitter": [
                        Connection("sigControllerSignal", self.controller_slot),
                    ],
                }
                super().__init__(ctrl_info, models, virtual_bus, signals, connection_map)
                # any other initialization code...

            def widget_slot(self, *args, **kwargs) -> None:
                # your logic here...

            def other_widget_slot(self, *args, **kwargs) -> None:
                # your logic here...

            def controller_slot(self, *args, **kwargs) -> None:
                # your logic here...

        .. note::

            The controller slots should respect the signals' signatures
            in order to correctly receive the emitted signals.
            Make sure to review any provided documentation
            about the nature of the signals being connected.

    Parameters
    ----------
    ctrl_info : ``CI``
        Instance of a `sunflare.config.PresenterInfo` subclass.
    models : ``Mapping[str, PModel]``
        Reference to the models used in the controller.
    virtual_bus : `sunflare.virtual.VirtualBus`
        Reference to the virtual bus.
    signals : ``Sequence[str]``, optional
        Sequence of signals to register.
        Default is ``None`` (no signals registered).
    connection_map : ``Mapping[str, list[Connection]]``, optional
        Mapping of emitters to a list of connections.
        Default is ``None`` (no connections).

    Attributes
    ----------
    signals : ``Sequence[str]``, optional
        Sequence of registered signals.
    connection_map : ``Mapping[str, list[Connection]]``, optional
        Mapping of emitters to a list of connections.
    """

    def __init__(
        self,
        ctrl_info: CI,
        models: Mapping[str, PModel],
        virtual_bus: VirtualBus,
        *,
        signals: Sequence[str] | None = None,
        connection_map: Mapping[str, list[Connection]] | None = None,
    ) -> None:
        self.signals = signals
        self.connection_map = connection_map
        super().__init__(ctrl_info, models, virtual_bus)

    def registration_phase(self) -> None:
        """Register the signals defined in ``self.signals``.

        This method is called from Redsun during
        application initialization.
        """
        self.virtual_bus.register_signals(self, self.signals)

    def connection_phase(self) -> None:
        """Connect the signals defined in ``self.connection_map``.

        This method is called from Redsun during
        application initialization.
        """
        if self.connection_map is not None:
            for emitter, connections in self.connection_map.items():
                for connection in connections:
                    self.virtual_bus.signals[emitter][connection.signal].connect(
                        connection.slot
                    )

registration_phase

registration_phase() -> None

Register the signals defined in self.signals.

This method is called from Redsun during application initialization.

Source code in src/sunflare/presenter/_base.py
def registration_phase(self) -> None:
    """Register the signals defined in ``self.signals``.

    This method is called from Redsun during
    application initialization.
    """
    self.virtual_bus.register_signals(self, self.signals)

connection_phase

connection_phase() -> None

Connect the signals defined in self.connection_map.

This method is called from Redsun during application initialization.

Source code in src/sunflare/presenter/_base.py
def connection_phase(self) -> None:
    """Connect the signals defined in ``self.connection_map``.

    This method is called from Redsun during
    application initialization.
    """
    if self.connection_map is not None:
        for emitter, connections in self.connection_map.items():
            for connection in connections:
                self.virtual_bus.signals[emitter][connection.signal].connect(
                    connection.slot
                )