From 6aacbea1c59d928fcfacc2e7dba05f128f86fb5a Mon Sep 17 00:00:00 2001 From: James Carr Date: Thu, 27 May 2021 13:27:57 +0100 Subject: [PATCH 1/3] Update the PyPI and readthedocs links in README.rst --- README.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index a4c6a29..1fe7f0a 100644 --- a/README.rst +++ b/README.rst @@ -2,8 +2,8 @@ Introduction ============ -.. image:: https://readthedocs.org/projects/circuitpython-displayio_switchround/badge/?version=latest - :target: https://circuitpython-displayio_switchround.readthedocs.io/ +.. image:: https://readthedocs.org/projects/circuitpython-displayio-switchround/badge/?version=latest + :target: https://circuitpython-displayio-switchround.readthedocs.io/ :alt: Documentation Status @@ -39,8 +39,8 @@ or individual libraries can be installed using Installing from PyPI ===================== -On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from -PyPI `_. +On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally +`from PyPI `_. To install for current user: .. code-block:: shell From 25c4dee08047db231eb94627ce4a777ebcc1c3aa Mon Sep 17 00:00:00 2001 From: James Carr Date: Thu, 27 May 2021 22:36:29 +0100 Subject: [PATCH 2/3] Add cross-reference links to adafruit_displayio_layout Extract the guide out into a new file - overview.rst --- displayio_switchround.py | 411 +++++--------------------------------- docs/api.rst | 2 - docs/conf.py | 4 + docs/index.rst | 5 + docs/overview.rst | 340 +++++++++++++++++++++++++++++++ docs/overview.rst.license | 4 + 6 files changed, 406 insertions(+), 360 deletions(-) create mode 100644 docs/overview.rst create mode 100644 docs/overview.rst.license diff --git a/displayio_switchround.py b/displayio_switchround.py index 3045300..1bf92f7 100644 --- a/displayio_switchround.py +++ b/displayio_switchround.py @@ -19,6 +19,13 @@ * Adafruit CircuitPython firmware for the supported boards: https://github.com/adafruit/circuitpython/releases +Inheritance +----------- + +.. inheritance-diagram:: adafruit_displayio_layout.widgets.switch_round + +| + """ ################################ @@ -50,368 +57,54 @@ class SwitchRound(Widget, Control): - """ - .. note:: Jump directly to: - - - :ref:`SwitchRound: Input parameters ` - - :ref:`SwitchRound: Methods ` - - :ref:`SwitchRound: Description of implementation ` - - .. _parameters: + Create a SwitchRound. See :ref:`Overview` for more details. :param int x: pixel position, defaults to 0 :param int y: pixel position, defaults to 0 - :param int width: width of the switch in pixels, if set to None (**recommended**) - the width will auto-size relative to the height, defaults to None + :param None,int width: width of the switch in pixels, if set to + `None` (**recommended**) the width will auto-size relative to the height, defaults + to `None` :param int height: height of the switch in pixels, defaults to 40 pixels :param int touch_padding: the width of an additional border surrounding the switch that extends the touch response boundary, defaults to 0 - :param Boolean horizontal: To set vertical orientation, set ``horizontal`` - to False, defaults to True - :param Boolean flip: Setting ``flip`` to True will flip the on and off - direction, default is True - :param float anchor_point: (X,Y) values from 0.0 to 1.0 to define the anchor - point relative to the switch bounding box, default is None - :param int anchored_position: (x,y) pixel value for the location - of the anchor_point, default is None - :param fill_color_off: (*RGB tuple - or 24-bit hex value*) switch off-state fill color, default is ``(66, 44, 66)`` gray. - :param fill_color_on: (*RGB tuple - or 24-bit hex value*) switch on-state fill color, default is ``(0, 100, 0)`` green. - :param outline_color_off: (*RGB tuple - or 24-bit hex value*) switch off-state outline color, default is ``(30, 30, 30)`` - dark gray. - :param outline_color_on: (*RGB tuple - or 24-bit hex value*) switch on-state outline color, default is ``(0, 60, 0)`` green - :param background_color_off: (*RGB tuple - or 24-bit hex value*) background off-state color, default is ``(255, 255, 255)`` white - :param background_color_on: (*RGB tuple - or 24-bit hex value*) background on-state color, default is ``(0, 60, 0)`` dark green - :param background_outline_color_off: (*RGB tuple - or 24-bit hex value*) background outline color in off-state, if set to None this - will default to ``background_color_off``, default is None - :param background_outline_color_on: (*RGB tuple - or 24-bit hex value*) background outline color in on-state, if set to None this - will default to ``background_color_on``, default is None + :param bool horizontal: To set vertical orientation, set :attr:`horizontal` + to `False`, defaults to `True` + :param bool flip: If `True` the on and off direction will be flipped, default is + `True` + :param None,tuple(float,float) anchor_point: (X,Y) values from 0.0 to 1.0 to define + the anchor point relative to the switch bounding box, default is `None` + :param None,tuple(int,int) anchored_position: (x,y) pixel value for the location + of the anchor_point, default is `None` + :param tuple(int,int,int),int fill_color_off: switch off-state fill color, *as an + RGB-tuple or 24-bit hex*, default is :const:`(66, 44, 66)` gray. + :param tuple(int,int,int),int fill_color_on: switch on-state fill color, *as an + RGB-tuple or 24-bit hex*, default is :const:`(0, 100, 0)` green. + :param tuple(int,int,int),int outline_color_off: switch off-state outline color, *as + an RGB-tuple or 24-bit hex*, default is :const:`(30, 30, 30)` dark gray. + :param tuple(int,int,int),int outline_color_on: switch on-state outline color, *as + an RGB-tuple or 24-bit hex*, default is :const:`(0, 60, 0)` green + :param tuple(int,int,int),int background_color_off: background off-state color, *as + an RGB-tuple or 24-bit hex*, default is :const:`(255, 255, 255)` white + :param tuple(int,int,int),int background_color_on: background on-state color, *as an + RGB-tuple or 24-bit hex*, default is :const:`(0, 60, 0)` dark green + :param None,tuple(int,int,int),int background_outline_color_off: background outline + color in off-state, *as an RGB-tuple or 24-bit hex or `None`*. If set to `None` + this will default to :attr:`background_color_off`, default is `None` + :param None,tuple(int,int,int),int background_outline_color_on: background outline + color in on-state, *as an RGB-tuple or 24-bit hex or `None`*. If set to `None` this + will default to :attr:`background_color_on`, default is `None` :param int switch_stroke: outline stroke width for the switch and background, in pixels, default is 2 - :param int text_stroke: outline stroke width (in pixels) for the 0/1 text shape - outlines, if set to None it will use the value for ``switch_stroke``, default - value is None - :param Boolean display_button_text: Set True to display the 0/1 text shapes - on the sliding switch, set False to hide the 0/1 text shapes, default value is True + :param None,int text_stroke: outline stroke width (in pixels) for the 0/1 text shape + outlines, if set to `None` it will use the value for :attr:`switch_stroke`, default + value is `None` + :param bool display_button_text: If `True` display the 0/1 text shapes + on the sliding switch. If `False` hide the 0/1 text shapes, default value is + `True` :param float animation_time: time for the switching animation, in seconds, default value is 0.2 seconds. - :param Boolean value: the initial value for the switch, default is False - - .. _links: - - **Sections: Description of the SwitchRound widget** - - :ref:`Quickstart: Importing and using SwitchRound ` - - :ref:`Summary: SwitchRound Features and input variables ` - - :ref:`Description of features ` - - :ref:`Internal details: How the SwitchRound widget works ` - - :ref:`Group structure: Display elements that make up SwitchRound ` - - :ref:`Coordinate systems and use of anchor_point and anchored_position ` - - :ref:`The Widget construction sequence ` - - :ref:`Making it move ` - - :ref:`Orientation and a peculiarity of width and height definitions for - SwitchRound ` - - :ref:`Setting the touch response boundary ` - - :ref:`Summary and a final word ` - - - .. _switch_round_details: - - **Quickstart: Importing and using SwitchRound** - - Here is one way of importing the `SwitchRound` class so you can use it as - the name ``Switch``: - - .. code-block:: python - - from displayio_switchround import SwitchRound as Switch - - Now you can create a switch at pixel position x=20, y=30 using: - - .. code-block:: python - - my_switch=Switch(20, 30) # instance the switch at x=20, y=30 - - Once you setup your display, you can now add ``my_switch`` to your display using: - - .. code-block:: python - - display.show(my_switch) # add the group to the display - - If you want to have multiple display elements, you can create a group and then - append the switch and the other elements to the group. Then, you can add the full - group to the display as in this example: - - .. code-block:: python - - my_switch = Switch(20, 30) # instance the switch at x=20, y=30 - my_group = displayio.Group(max_size=10) # make a group that can hold 10 items - my_group.append(my_switch) # Add my_switch to the group - - # - # Append other display elements to the group - # - - display.show(my_group) # add the group to the display - - For a full example, including how to respond to screen touches, check out the - following examples in the `Adafruit_CircuitPython_DisplayIO_Layout - `_ library: - - - ``examples/displayio_layout_switch_simpletest.py`` and - - ``examples/displayio_layout_switch_multiple.py`` - - - .. _feature_summary: - - **Summary: SwitchRound Features and input variables** - - The `SwitchRound` widget has numerous options for controlling its position, visible appearance, - orientation, animation speed and value through a collection of input variables: - - - **position**: ``x``, ``y`` or ``anchor_point`` and ``anchored_position`` - - - - **size**: ``width`` and ``height`` (recommend to leave ``width`` = None to use - preferred aspect ratio) - - - **orientation and movement direction (on vs. off)**: ``horizontal`` and ``flip`` - - - **switch color**: ``fill_color_off``, ``fill_color_on``, ``outline_color_off`` and - ``outline_color_on`` - - - **background color**: ``background_color_off``, ``background_color_on``, - ``background_outline_color_off``, and ``background_outline_color_on`` - - - **linewidths**: ``switch_stroke`` and ``text_stroke`` - - - **0/1 display**: Set ``display_button_text`` = True if you want the 0/1 shapes - to show on the switch - - - **animation**: Set ``animation_time`` to the duration (in seconds) it will take - to transition the switch, set zero if you want it to snap into position immediately - (0.2 seconds is a good starting point, and larger values for bigger switches) - - - **value**: Set ``value`` to the initial value (True or False) - - - **touch boundaries**: ``touch_padding`` defines the number of additional pixels - surrounding the switch that should respond to a touch. (Note: The ``touch_padding`` - variable updates the ``touch_boundary`` Control class variable. The definition of - the ``touch_boundary`` is used to determine the region on the Widget that returns - `True` in the `contains` function.) - - .. _features: - - **Description of features** - - The `SwitchRound` widget is a sliding switch that changes state whenever it is - touched. The color gradually changes from the off-state color scheme to the - on-state color scheme as the switch transfers from off to the on position. - The switch has an optional display of "0" and "1" on the sliding switch. The - switch can be oriented using the ``horizontal`` input variable, and the sliding - direction can be changed using the ``flip`` input variable. - - Regarding switch sizing, it is recommended to set the height dimension but to - leave the ``width = None``. Setting ``width = None`` will allow the width to resize - to maintain a recommended aspect ratio of width/height. Alternately, the switch - can be resized using the `resize` command, and it will adjust the width and height - to the maximum size that will fit inside the requested width and height dimensions, - while keeping the preferred aspect ratio. To make the switch easier to be selected, - additional padding around the switch can be defined using the ``touch_padding`` input - variable to increase the touch-responsive area. The duration of - animation between on/off can be set using the ``animation_time`` input variable. - - .. _internals: - - **Internal details: How the SwitchRound widget works** - - The `SwitchRound` widget is a graphical element that responds to touch elements - to provide sliding switch on/off behavior. Whenever touched, the switch toggles - to its alternate value. The following sections describe the construction of the - `SwitchRound` widget, in the hopes that it will serve as a first example of the key - properties and responses for widgets. - - The `SwitchRound` widget inherits from two classes, it is a subclass of ``Widget`` (which - itself is a subclass of `displayio.Group`) and a subclass of ``Control``. The ``Widget`` - class helps define the positioning and sizing of the switch, while the ``Control`` class - helps define the touch-response behavior. - - The following several sections describe the structure and inner workings of `SwitchRound`. - - .. _group_structure: - - **Group structure: Display elements that make up SwitchRound** - - The ``Widget`` class is a subclass of `displayio.Group`, thus we can append graphical - elements to the Widget for displaying on the screen. The switch consists of the - following graphical elements: - - 0. switch_roundrect: The switch background - 1. switch_circle: The switch button that slides back and forth - 2. Optional: text_0: The "0" circle shape on the switch button - 3. Optional: text_1: The "1" rectangle shape on the switch button - - The optional text items can be displayed or hidden using the ``display_button_text`` - input variable. - - .. _coordinates: - - **Coordinate systems and use of anchor_point and anchored_position** - - See the ``Widget`` class definition for clarification on the methods for positioning - the switch, including the difference in the display coordinate system and the Widget's - local coordinate system. - - .. _construction: - - **The Widget construction sequence** - - Here is the set of steps used to define this sliding switch widget. - - 1. Initialize the stationary display items - 2. Initialize the moving display elements - 3. Store initial position of the moving display elements - 4. Define "keyframes" to determine the translation vector - 5. Define the ``_draw_position`` function between 0.0 to 1.0 (and slightly beyond) - 6. Select the motion "easing" function - 7. **Extra**. Go check out the ``_animate_switch`` method - - First, the stationary background rounded rectangle (RoundRect is created). Second, - the moving display elements are created, the circle for the switch, the circle for - the text "0" and the rectangle for the text "1". Note that either the "0" or "1" is - set as hidden, depending upon the switch value. Third, we store away the - initial position of the three moving elements, these initial values will be used in the - functions that move these display elements. Next, we define the motion of the - moving element, by setting the ``self._x_motion`` and ``self._y_motion`` values - that depending upon the ``horizontal`` and ``flip`` variables. These motion variables - set the two "keyframes" for the moving elements, basically the endpoints of the switch - motion. (Note: other widgets may need an ``_angle_motion`` variable if they require - some form of rotation.) Next, we define the ``_draw_function`` method. This method - takes an input between 0.0 and 1.0 and adjusts the position relative to the motion - variables, where 0.0 is the initial position and 1.0 represents the final position - (as defined by the ``_x_motion`` and ``_y_motion`` values). In the case of the - sliding switch, we also use this ``position`` value (0.0 to 1.0) to gradually - grade the color of the components between their "on" and "off" colors. - - .. _move: - - **Making it move** - - Everything above has set the ground rules for motion, but doesn't cause it to move. - However, you have set almost all the pieces in place to respond to requests to change - the position. All that is left is the **Extra** method that performs the animation, - called ``_animate_switch``. The ``_animate_switch`` method is triggered by a touch - event through the ``selected`` Control class method. Once triggered, this method - checks how much time has elapsed. Based on the elapsed time and the ``animation_time`` - input variable, the ``_animate_switch`` function calculates the ``position`` where - the switch should be. Then, it takes this ``position`` to call the ``_draw_position`` - method that will update the display elements based on the requested position. - - But there's even one more trick to the animation. The ``_animate_switch`` calculates - the target position based on a linear relationship between the time and the position. - However, to give the animation a better "feel", it is desirable to tweak the motion - function depending upon how this widget should behave or what suits your fancy. To - do this we can use an "easing" function. In short, this adjusts the constant speed - (linear) movement to a variable speed during the movement. Said another way, it - changes the position versus time function according to a specific waveform equation. - There are a lot of different "easing" functions that folks have used or you can make - up your own. Some common easing functions are provided in the ``easing.py`` file. - You can change the easing function based on changing which function is imported - at the top of this file. You can see where the position is tweaked by the easing - function in the line in the ``_animate_switch`` method: - - .. code-block:: python - - self._draw_position(easing(position)) # update the switch position - - Go play around with the different easing functions and observe how the motion - behavior changes. You can use these functions in multiple dimensions to get all - varieties of behavior that you can take advantage of. The website - `easings.net `_ can help you - visualize some of the behavior of the easing functions. - - .. note:: Some of the "springy" easing functions require position values - slightly below 0.0 and slightly above 1.0, so if you want to use these, be sure - to check that your ``_draw_position`` method behaves itself for that range - of position inputs. - - .. _orientation: - - **Orientation and a peculiarity of width and height definitions for SwitchRound** - - In setting the switch sizing, use height and width to set the narrow and wide - dimension of the switch. To try and reduce confusion, the orientation is modified - after the height and width are selected. That is, if the switch is set to vertical, - the height and still mean the "narrow" and the width will still mean the dimensions - in the direction of the sliding. - - If you need the switch to fit within a specific bounding box, it's preferred to use - the ``resize`` function. This will put the switch (in whatever orientation) at the - maximum size where it can fit within the bounding box that you specified. The switch - aspect ratio will remain at the "preferred" aspect ratio of of 2:1 (width to height) - after the resizing. - - .. _touch: - - **Setting the touch response boundary** - - The touch response area is defined by the Control class variable called - ``touch_boundary``. In the case of the `SwitchRound` widget, we provide an - ``touch_padding`` input variable. The use of ``touch_padding`` defines an - additional number of pixels surrounding the display elements that respond to touch - events. To achieve this additional space, the ``touch_boundary`` increases in size - in all dimensions by the number of pixels specified in the ``touch_padding`` parameter. - - The ``touch_boundary`` is used in the Control function ``contains`` that checks - whether any touch_points are within the boundary. Please pay particular attention to - the `SwitchRound` contains function, since it calls the ``Control.contains`` superclass - function with the touch_point value adjusted for the switch's ``.x`` and ``.y`` values. - This offset adjustment is required since the ``Control.contains`` function operates only - on the widget's local coordinate system. It's good to keep in mind which coordinate - system you are working in, to ensure your code responds to the right inputs! - - .. _summary: - - **Summary** - - The `SwitchRound` widget is an example to explain the use of the ``Widget`` and ``Control`` - class functions. The ``Widget`` class handles the overall sizing and positioning function - and is the group that holds all the graphical elements. The ``Control`` class is used to - define the response of the widget to touch events (or could be generalized to other - inputs). Anything that only displays (such as a graph or an indicator light) won't - need to inherit the ``Control`` class. But anything that responds to touch inputs should - inherit the ``Control`` class to define the ``touch_boundary`` and the touch response - functions. - - I hope this `SwitchRound` widget will help turn on some new ideas and highlight some - of the new capabilities of the ``Widget`` and ``Control`` classes. Now go see what else - you can create and extend from here! - - **A Final Word** - - The design of the Widget and Control classes are open for inputs. If you think - a additions or changes are useful, add it and please submit a pull request so - others can use it too! Also, keep in mind you don't even need to follow these classes - to get the job done. The Widget and Class definitions are designed to give guidance - about one way to make things work, and to try to share some code. If it's standing in - your way, do something else! If you want to use the ``grid_layout`` or other layout tools - in this library, you only *really* need to have methods for positioning and resizing. - - .. note:: **Never let any of these class definitions hold you back, let your imagination run - wild and make some cool widgets!** - - - .. _methods: - - **SwitchRound methods** - + :param bool value: the initial value for the switch, default is `False` """ # pylint: disable=too-many-instance-attributes, too-many-arguments, too-many-locals @@ -443,6 +136,7 @@ def __init__( value=False, # initial value **kwargs, ): + """""" # Blocks documentation from super-class # initialize the Widget superclass (x, y, scale) super().__init__(x=x, y=y, height=height, width=width, **kwargs, max_size=4) @@ -787,7 +481,8 @@ def selected(self, touch_point): """Response function when Switch is selected. When selected, the switch position and value is changed with an animation. - :param touch_point: x,y location of the screen, in absolute display coordinates. + :param tuple(int,int) touch_point: x,y location of the screen, in absolute + display coordinates. :return: None """ @@ -807,8 +502,9 @@ def contains(self, touch_point): # overrides, then calls Control.contains(x,y) """Checks if the Widget was touched. Returns True if the touch_point is within the Control's touch_boundary. - :param touch_point: x,y location of the screen, in absolute display coordinates. - :return: Boolean + :param tuple(int,int) touch_point: x,y location of the screen, in absolute + display coordinates. + :return: bool """ touch_x = ( @@ -820,9 +516,9 @@ def contains(self, touch_point): # overrides, then calls Control.contains(x,y) @property def value(self): - """The current switch value (Boolean). + """The current Switch value (Boolean). - :return: Boolean + :return: bool """ return self._value @@ -863,12 +559,11 @@ def height(self, new_height): self._create_switch() def resize(self, new_width, new_height): - """Resize the switch to a new requested width and height. + """Resize the Switch to a new requested width and height. :param int new_width: requested maximum width :param int new_height: requested maximum height :return: None - """ # Fit the new button size within the requested maximum width/height # dimensions, but keeping an aspect ratio of 2:1 (width:height) diff --git a/docs/api.rst b/docs/api.rst index f6a4887..f19b343 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -8,5 +8,3 @@ :members: :member-order: bysource :inherited-members: - -.. inheritance-diagram:: adafruit_displayio_layout.widgets.switch_round diff --git a/docs/conf.py b/docs/conf.py index b2b49e2..1b74d2e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -35,6 +35,10 @@ intersphinx_mapping = { "python": ("https://docs.python.org/3.4", None), "CircuitPython": ("https://circuitpython.readthedocs.io/en/latest/", None), + "Layout": ( + "https://circuitpython.readthedocs.io/projects/displayio-layout/en/latest/", + None, + ), } # Show the docstring from both the class and its __init__() method. diff --git a/docs/index.rst b/docs/index.rst index 8f75db2..348764c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -15,6 +15,11 @@ Table of Contents examples +.. toctree:: + :caption: Overview + + overview + .. toctree:: :caption: API Reference :maxdepth: 3 diff --git a/docs/overview.rst b/docs/overview.rst new file mode 100644 index 0000000..4e290cd --- /dev/null +++ b/docs/overview.rst @@ -0,0 +1,340 @@ +.. _overview: + +Overview of SwitchRound +======================= + +Quickstart: Importing and using SwitchRound +------------------------------------------- + +Here is one way of importing the `SwitchRound` class so you can use it as +the name :data:`Switch`: + +.. code-block:: python + + from displayio_switchround import SwitchRound as Switch + +Now you can create a switch at pixel position x=20, y=30 using: + +.. code-block:: python + + my_switch = Switch(20, 30) # create the switch at x=20, y=30 + +Once you setup your display, you can now add :data:`my_switch` to your display using: + +.. code-block:: python + + display.show(my_switch) # add the group to the display + +If you want to have multiple display elements, you can create a group and then +append the switch and the other elements to the group. Then, you can add the full +group to the display as in this example: + +.. code-block:: python + + my_switch = Switch(20, 30) # create the switch at x=20, y=30 + my_group = displayio.Group(max_size = 10) # make a group that can hold 10 items + my_group.append(my_switch) # Add my_switch to the group + + # + # Append other display elements to the group + # + + display.show(my_group) # add the group to the display + +For a full example, including how to respond to screen touches, check out the +following examples in the `Adafruit_CircuitPython_DisplayIO_Layout +`_ library: + + - `examples/displayio_layout_switch_simpletest.py + `_ + - `examples/displayio_layout_switch_multiple.py + `_ + +Summary: SwitchRound Features and input variables +------------------------------------------------- + +The `SwitchRound` widget has numerous options for controlling its position, visible appearance, +orientation, animation speed and value through a collection of input variables: + + - **position**: + :attr:`~displayio_switchround.SwitchRound.x` and + :attr:`~displayio_switchround.SwitchRound.y` or + :attr:`~displayio_switchround.SwitchRound.anchor_point` and + :attr:`~displayio_switchround.SwitchRound.anchored_position` + + + - **size**: + :attr:`~displayio_switchround.SwitchRound.width` and + :attr:`~displayio_switchround.SwitchRound.height` + + It is recommended to leave :data:`width = None` to use the preferred aspect + ratio. + + - **orientation and movement direction (on vs. off)**: + :attr:`~displayio_switchround.SwitchRound.horizontal` and + :attr:`~displayio_switchround.SwitchRound.flip` + + - **switch color**: + :attr:`~displayio_switchround.SwitchRound.fill_color_off`, + :attr:`~displayio_switchround.SwitchRound.fill_color_on`, + :attr:`~displayio_switchround.SwitchRound.outline_color_off` and + :attr:`~displayio_switchround.SwitchRound.outline_color_on` + + - **background color**: + :attr:`~displayio_switchround.SwitchRound.background_color_off`, + :attr:`~displayio_switchround.SwitchRound.background_color_on`, + :attr:`~displayio_switchround.SwitchRound.background_outline_color_off` and + :attr:`~displayio_switchround.SwitchRound.background_outline_color_on` + + - **linewidths**: + :attr:`~displayio_switchround.SwitchRound.switch_stroke` and + :attr:`~displayio_switchround.SwitchRound.text_stroke` + + - **0/1 display**: + :attr:`~displayio_switchround.SwitchRound.display_button_text` + + Set to `True` if you want the 0/1 shapes + to show on the switch + + - **animation**: + :attr:`~displayio_switchround.SwitchRound.animation_time` + + Set the duration (in seconds) it will take to transition the switch, use + :data:`0` if you want it to snap into position immediately. The default value + of :data:`0.2` seconds is a good starting point, and larger values for bigger + switches. + + - **value**: + :attr:`~displayio_switchround.SwitchRound.value` + + Set to the initial value (`True` or `False`) + + - **touch boundaries**: + :attr:`~displayio_switchround.SwitchRound.touch_padding` + + This defines the number of additional pixels surrounding the switch that should + respond to a touch. (Note: The :attr:`touch_padding` variable updates the + :attr:`touch_boundary` Control class variable. The definition of the + :attr:`touch_boundary` is used to determine the region on the Widget that returns + `True` in the :meth:`~displayio_switchround.SwitchRound.contains` method.) + +Description of features +----------------------- + +The `SwitchRound` widget is a sliding switch that changes state whenever it is touched. +The color gradually changes from the off-state color scheme to the on-state color +scheme as the switch transfers from off to the on position. The switch has an optional +display of "0" and "1" on the sliding switch. The switch can be oriented using the +:attr:`~displayio_switchround.SwitchRound.horizontal` input variable, and the sliding +direction can be changed using the :attr:`~displayio_switchround.SwitchRound.flip` +input variable. + +Regarding switch sizing, it is recommended to set the height dimension but to leave the +:data:`width = None`. Setting :data:`width = None` will allow the width to resize to +maintain a recommended aspect ratio of width/height. Alternately, the switch can be +resized using the :meth:`~displayio_switchround.SwitchRound.resize` method, and it will +adjust the width and height to the maximum size that will fit inside the requested +width and height dimensions, while keeping the preferred aspect ratio. To make the +switch easier to be selected, additional padding around the switch can be defined using +the :attr:`~displayio_switchround.SwitchRound.touch_padding` input variable to increase +the touch-responsive area. The duration of animation between on/off can be set using +the :attr:`~displayio_switchround.SwitchRound.animation_time` input variable. + +Internal details: How the SwitchRound widget works +-------------------------------------------------- + +The `SwitchRound` widget is a graphical element that responds to touch elements to +provide sliding switch on/off behavior. Whenever touched, the switch toggles to its +alternate value. The following sections describe the construction of the `SwitchRound` +widget, in the hopes that it will serve as a first example of the key properties and +responses for widgets. + +.. inheritance-diagram:: adafruit_displayio_layout.widgets.switch_round + +| + +The `SwitchRound` widget inherits from two classes, it is a subclass of +:class:`~adafruit_displayio_layout.widgets.widget.Widget`, which itself is a subclass +of `displayio.Group`, and a subclass of +:class:`~adafruit_displayio_layout.widgets.control.Control`. The +:class:`~adafruit_displayio_layout.widgets.widget.Widget` class helps define the +positioning and sizing of the switch, while th +:class:`~adafruit_displayio_layout.widgets.control.Control` class helps define the +touch-response behavior. + +The following sections describe the structure and inner workings of `SwitchRound`. + +Group structure: Display elements that make up SwitchRound +---------------------------------------------------------- + +The :class:`~adafruit_displayio_layout.widgets.widget.Widget` +class is a subclass of `displayio.Group`, thus we can append graphical +elements to the Widget for displaying on the screen. The switch consists of the +following graphical elements: + + 0. switch_roundrect: The switch background + 1. switch_circle: The switch button that slides back and forth + 2. text_0 [Optional]: The "0" circle shape on the switch button + 3. text_1 [Optional]: The "1" rectangle shape on the switch button + +The optional text items can be displayed or hidden using the +:attr:`~displayio_switchround.SwitchRound.display_button_text` input variable. + +Coordinate systems and use of anchor_point and anchored_position +---------------------------------------------------------------- + +See the :class:`~adafruit_displayio_layout.widgets.widget.Widget` class definition for +clarification on the methods for positioning the switch, including the difference in +the display coordinate system and the Widget's local coordinate system. + +The Widget construction sequence +-------------------------------- + +Here is the set of steps used to define this sliding switch widget. + + 1. Initialize the stationary display items + 2. Initialize the moving display elements + 3. Store initial position of the moving display elements + 4. Define "keyframes" to determine the translation vector + 5. Define the :meth:`SwitchRound._draw_position` method between 0.0 to 1.0 (and + slightly beyond) + 6. Select the motion "easing" function + 7. **Extra**. Go check out the :meth:`SwitchRound._animate_switch` method + +First, the stationary background rounded rectangle (RoundRect is created). Second, the +moving display elements are created, the circle for the switch, the circle for the text +"0" and the rectangle for the text "1". Note that either the "0" or "1" is set as +hidden, depending upon the switch value. Third, we store away the initial position of +the three moving elements, these initial values will be used in the functions that move +these display elements. Next, we define the motion of the moving element, by setting +the :data:`self._x_motion` and :data:`self._y_motion` values that depending upon the +:attr:`~SwitchRound.horizontal` and :attr:`~SwitchRound.flip` variables. These motion +variables set the two "keyframes" for the moving elements, basically the endpoints of +the switch motion. (Note: other widgets may need an :data:`_angle_motion` variable if +they require some form of rotation.) Next, we define the +:meth:`SwitchRound._draw_function` method. This method takes an input between 0.0 and +1.0 and adjusts the position relative to the motion variables, where 0.0 is the initial +position and 1.0 represents the final position (as defined by the :data:`_x_motion` and +:data:`_y_motion` values). In the case of the sliding switch, we also use this +:attr:`SwitchRound.position` value (0.0 to 1.0) to gradually grade the color of the +components between their "on" and "off" colors. + +Making it move +-------------- + +Everything above has set the ground rules for motion, but doesn't cause it to move. +However, you have set almost all the pieces in place to respond to requests to change +the position. All that is left is the **Extra** method that performs the animation, +called :meth:`SwitchRound._animate_switch`. The :meth:`SwitchRound._animate_switch` +method is triggered by a touch event through the +:meth:`~adafruit_displayio_layout.widgets.control.Control.selected` Control class +method. Once triggered, this method +checks how much time has elapsed. Based on the elapsed time and the +:attr:`SwitchRound.animation_time` input variable, the +:meth:`SwitchRound._animate_switch` method calculates the :attr:`SwitchRound.position` +where the switch should be. Then, it takes this :attr:`SwitchRound.position` to call +the :meth:`SwitchRound._draw_position` method that will update the display elements +based on the requested position. + +But there's even one more trick to the animation. The +:meth:`SwitchRound._animate_switch` calculates the target position based on a linear +relationship between the time and the position. However, to give the animation a better +"feel", it is desirable to tweak the motion function depending upon how this widget +should behave or what suits your fancy. To do this we can use an *"easing"* function. +In short, this adjusts the constant speed (linear) movement to a variable speed during +the movement. Said another way, it changes the position versus time function according +to a specific waveform equation. There are a lot of different "easing" functions that +folks have used or you can make up your own. Some common easing functions are provided +in the :mod:`adafruit_displayio_layout.widgets.easing` module. You can change the +easing function based on changing which function is imported at the top of this file. +You can see where the position is tweaked by the easing function in the line in the +:meth:`SwitchRound._animate_switch` method: + +.. code-block:: python + + self._draw_position(easing(position)) # update the switch position + +Go play around with the different easing functions and observe how the motion +behavior changes. You can use these functions in multiple dimensions to get all +varieties of behavior that you can take advantage of. The website +`easings.net `_ can help you +visualize some of the behavior of the easing functions. + +.. note:: Some of the "springy" easing functions require position values + slightly below 0.0 and slightly above 1.0, so if you want to use these, be sure + to check that your :meth:`_draw_position` method behaves itself for that range + of position inputs. + +Orientation and a peculiarity of width and height definitions for SwitchRound +----------------------------------------------------------------------------- + +In setting the switch sizing, use height and width to set the narrow and wide dimension +of the switch. To try and reduce confusion, the orientation is modified after the +height and width are selected. That is, if the switch is set to vertical, the height +and still mean the "narrow" and the width will still mean the dimensions +in the direction of the sliding. + +If you need the switch to fit within a specific bounding box, it's preferred to use +the :meth:`~displayio_switchround.SwitchRound.resize` function. This will put the switch (in whatever +orientation) at the maximum size where it can fit within the bounding box that you +specified. The Switch aspect ratio will remain at the "preferred" aspect ratio of 2:1 +(width:height) after the resizing. + +Setting the touch response boundary +----------------------------------- + +The touch response area is defined by the Control class variable called +:data:`touch_boundary`. In the case of the `SwitchRound` widget, we provide an +:attr:`SwitchRound.touch_padding` input variable. The use of +:attr:`SwitchRound.touch_padding` defines an additional number of pixels surrounding +the display elements that respond to touch events. To achieve this additional space, +the :data:`touch_boundary` increases in size in all dimensions by the number of pixels +specified in the :attr:`SwitchRound.touch_padding` parameter. + +The :data:`touch_boundary` is used in the Control function +:meth:`~displayio_switchround.SwitchRound.contains` that checks whether any +touch_points are within the boundary. Please pay particular attention to the +`SwitchRound` :meth:`~displayio_switchround.SwitchRound.contains` method, since it +calls the :meth:`~adafruit_displayio_layout.widgets.control.Control.contains` +superclass method with the touch_point value adjusted for the switch's +:attr:`~displayio_switchround.SwitchRound.x` and +:attr:`~displayio_switchround.SwitchRound.y` values. This offset adjustment is +required since the :meth:`~adafruit_displayio_layout.widgets.control.Control.contains` +function operates only on the widget's local coordinate system. It's good to keep in +mind which coordinate system you are working in, to ensure your code responds to the +right inputs! + +Summary +------- + +The `SwitchRound` widget is an example to explain the use of the +:class:`~adafruit_displayio_layout.widgets.widget.Widget` and +:class:`~adafruit_displayio_layout.widgets.control.Control` class methods. The +:class:`~adafruit_displayio_layout.widgets.widget.Widget` class handles the overall +sizing and positioning function and is the group that holds all the graphical elements. +The :class:`~adafruit_displayio_layout.widgets.control.Control` class is used to define +the response of the widget to touch events (or could be generalized to other inputs). +Anything that only displays (such as a graph or an indicator light) won't need to +inherit the :class:`~adafruit_displayio_layout.widgets.control.Control` class. But +anything that responds to touch inputs should inherit the +:class:`~adafruit_displayio_layout.widgets.control.Control` class to define the +:data:`touch_boundary` and the touch response functions. + +I hope this `SwitchRound` widget will help turn on some new ideas and highlight some +of the new capabilities of the :class:`~adafruit_displayio_layout.widgets.widget.Widget` +and :class:`~adafruit_displayio_layout.widgets.control.Control` classes. Now go see +what else you can create and extend from here! + +A Final Word +------------ + +The design of the Widget and Control classes are open for inputs. If you think any +additions or changes are useful, add it and please submit a pull request so others can +use it too! Also, keep in mind you don't even need to follow these classes to get the +job done. The Widget and Class definitions are designed to give guidance about one way +to make things work, and to try to share some code. If it's standing in your way, do +something else! If you want to use the ``grid_layout`` or other layout tools in this +library, you only *really* need to have methods for positioning and resizing. + +.. note:: **Never let any of these class definitions hold you back, let your imagination + run wild and make some cool widgets!** diff --git a/docs/overview.rst.license b/docs/overview.rst.license new file mode 100644 index 0000000..ebc5fe2 --- /dev/null +++ b/docs/overview.rst.license @@ -0,0 +1,4 @@ +SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +SPDX-FileCopyrightText: Copyright (c) 2021 Kevin Matocha for circuitpython + +SPDX-License-Identifier: MIT From 86c34083567de59d9b4ee203a7b2e86b51f61fed Mon Sep 17 00:00:00 2001 From: James Carr Date: Thu, 3 Jun 2021 21:01:05 +0100 Subject: [PATCH 3/3] Correct the Inheritance diagrams --- displayio_switchround.py | 2 +- docs/overview.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/displayio_switchround.py b/displayio_switchround.py index 1bf92f7..ffd3b57 100644 --- a/displayio_switchround.py +++ b/displayio_switchround.py @@ -22,7 +22,7 @@ Inheritance ----------- -.. inheritance-diagram:: adafruit_displayio_layout.widgets.switch_round +.. inheritance-diagram:: displayio_switchround | diff --git a/docs/overview.rst b/docs/overview.rst index 4e290cd..1fdf672 100644 --- a/docs/overview.rst +++ b/docs/overview.rst @@ -149,7 +149,7 @@ alternate value. The following sections describe the construction of the `Switch widget, in the hopes that it will serve as a first example of the key properties and responses for widgets. -.. inheritance-diagram:: adafruit_displayio_layout.widgets.switch_round +.. inheritance-diagram:: displayio_switchround |