Merge pull request #370 from lurch/ButtonBoard

Initial version of ButtonBoard for testing...
This commit is contained in:
Dave Jones
2016-08-30 23:19:00 +01:00
committed by GitHub
2 changed files with 111 additions and 2 deletions

View File

@@ -106,6 +106,7 @@ from .output_devices import (
) )
from .boards import ( from .boards import (
CompositeOutputDevice, CompositeOutputDevice,
ButtonBoard,
LEDCollection, LEDCollection,
LEDBoard, LEDBoard,
LEDBarGraph, LEDBarGraph,

View File

@@ -32,7 +32,7 @@ from .output_devices import (
) )
from .threads import GPIOThread from .threads import GPIOThread
from .devices import Device, CompositeDevice from .devices import Device, CompositeDevice
from .mixins import SharedMixin, SourceMixin from .mixins import SharedMixin, SourceMixin, HoldMixin
class CompositeOutputDevice(SourceMixin, CompositeDevice): class CompositeOutputDevice(SourceMixin, CompositeDevice):
@@ -90,12 +90,120 @@ class CompositeOutputDevice(SourceMixin, CompositeDevice):
# Simply ignore values for non-output devices # Simply ignore values for non-output devices
class ButtonBoard(HoldMixin, CompositeDevice):
"""
Extends :class:`CompositeDevice` and represents a generic button board or
collection of buttons.
:param int \*pins:
Specify the GPIO pins that the buttons of the board are attached to.
You can designate as many pins as necessary.
:param bool pull_up:
If ``True`` (the default), the GPIO pins will be pulled high by
default. In this case, connect the other side of the buttons to
ground. If ``False``, the GPIO pins will be pulled low by default. In
this case, connect the other side of the buttons to 3V3. This
parameter can only be specified as a keyword parameter.
:param float bounce_time:
If ``None`` (the default), no software bounce compensation will be
performed. Otherwise, this is the length of time (in seconds) that the
buttons will ignore changes in state after an initial change. This
parameter can only be specified as a keyword parameter.
:param float hold_time:
The length of time (in seconds) to wait after any button is pushed,
until executing the :attr:`when_held` handler. Defaults to ``1``. This
parameter can only be specified as a keyword parameter.
:param bool hold_repeat:
If ``True``, the :attr:`when_held` handler will be repeatedly executed
as long as any buttons remain held, every *hold_time* seconds. If
``False`` (the default) the :attr:`when_held` handler will be only be
executed once per hold. This parameter can only be specified as a
keyword parameter.
:param \*\*named_pins:
Specify GPIO pins that buttons of the board are attached to,
associating each button with a property name. You can designate as
many pins as necessary and use any names, provided they're not already
in use by something else.
"""
def __init__(self, *args, **kwargs):
pull_up = kwargs.pop('pull_up', True)
bounce_time = kwargs.pop('bounce_time', None)
hold_time = kwargs.pop('hold_time', 1)
hold_repeat = kwargs.pop('hold_repeat', False)
order = kwargs.pop('_order', None)
super(ButtonBoard, self).__init__(
*(
Button(pin, pull_up, bounce_time, hold_time, hold_repeat)
for pin in args
),
_order=order,
**{
name: Button(pin, pull_up, bounce_time, hold_time, hold_repeat)
for name, pin in kwargs.items()
})
def get_new_handler(device):
def fire_both_events():
device._fire_events()
self._fire_events()
return fire_both_events
for button in self:
button.pin.when_changed = get_new_handler(button)
self._when_changed = None
self._last_value = None
# Call _fire_events once to set initial state of events
self._fire_events()
self.hold_time = hold_time
self.hold_repeat = hold_repeat
@property
def pull_up(self):
"""
If ``True``, the device uses a pull-up resistor to set the GPIO pin
"high" by default.
"""
return self[0].pull_up
@property
def when_changed(self):
return self._when_changed
@when_changed.setter
def when_changed(self, value):
self._when_changed = self._wrap_callback(value)
def _fire_changed(self):
if self.when_changed:
self.when_changed()
def _fire_events(self):
super(ButtonBoard, self)._fire_events()
old_value = self._last_value
new_value = self._last_value = self.value
if old_value is None:
# Initial "indeterminate" value; don't do anything
pass
elif old_value != new_value:
self._fire_changed()
ButtonBoard.is_pressed = ButtonBoard.is_active
ButtonBoard.pressed_time = ButtonBoard.active_time
ButtonBoard.when_pressed = ButtonBoard.when_activated
ButtonBoard.when_released = ButtonBoard.when_deactivated
ButtonBoard.wait_for_press = ButtonBoard.wait_for_active
ButtonBoard.wait_for_release = ButtonBoard.wait_for_inactive
class LEDCollection(CompositeOutputDevice): class LEDCollection(CompositeOutputDevice):
""" """
Extends :class:`CompositeOutputDevice`. Abstract base class for Extends :class:`CompositeOutputDevice`. Abstract base class for
:class:`LEDBoard` and :class:`LEDBarGraph`. :class:`LEDBoard` and :class:`LEDBarGraph`.
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self._blink_thread = None self._blink_thread = None
pwm = kwargs.pop('pwm', False) pwm = kwargs.pop('pwm', False)