from __future__ import print_function
from threading import Event
[docs]class ExitHelper(object):
"""
A simple helper for loops, to allow exiting them on pressing KEY_LEFT (or other keys).
You need to make sure that, while the loop is running, no other UI element
sets its callbacks. with Printer UI elements, you can usually pass None
instead of ``i`` to achieve that.
Arguments:
* ``i``: input device
* ``keys``: all the keys that should trigger an exit. You can also pass "*" so that
it catches all of the keys (allowing you to make an "exit on any key" action).
* ``cb``: the callback that should be executed once one of the keys is pressed.
By default, sets an internal flag that you can check with ``do_exit`` and
``do_run``.
"""
started = False
callback = None
last_key = None
def __init__(self, i, keys=["KEY_LEFT"], cb=None):
self.i = i
self.keys = keys
self._do_exit = Event()
self.set_callback(cb)
[docs] def start(self):
"""
Sets up the input listener, then returns ``self`` (allowing for shortened usage,
like ``eh = ExitHelper(i).start()``.
"""
self.setup_input()
self.started = True
return self
def setup_input(self):
"""
Clears input device keymap, registers callbacks and enables input listener.
"""
self.i.stop_listen()
self.i.clear_keymap()
if "*" in self.keys:
self.i.set_streaming(self.save_key_and_callback)
else:
keymap = {key:lambda x=key:self.save_key_and_callback(x) for key in self.keys}
self.i.set_keymap(keymap)
self.i.listen()
def save_key_and_callback(self, key):
"""
Saves the key to the ``last_key`` attribute before calling the callback
(not passing the ``key`` argument).
"""
self.last_key = key
self.callback()
def set_callback(self, callback=None):
"""
Allows you to define a custom callback (which will still set the internal flag).
If no callback is passed, simply uses the internal one (so calling this function
with no arguments clears whatever callback you might've set previously).
"""
if callback is None:
self.callback = self.signal_exit
elif not callable(callback):
raise ArgumentError("set_callback expected a callable, received {}!".format(type(callback)))
else:
def wrapper():
self._do_exit.set()
callback()
self.callback = wrapper
[docs] def do_exit(self):
"""Returns ``True`` once exit flag has been set, ``False`` otherwise."""
return self._do_exit.isSet()
[docs] def do_run(self):
"""Returns ``False`` once exit flag has been set, ``True`` otherwise."""
return not self._do_exit.isSet()
def signal_exit(self):
"""Internal function to set the exit flag."""
self._do_exit.set()
[docs] def reset(self):
"""Clears the exit flag."""
self._do_exit.clear()
[docs] def stop(self):
"""Stop input listener and remove the created keymap. Shouldn't usually be necessary,
since all other UI elements are supposed to make sure their callbacks are set."""
self.started = False
self.i.clear_keymap()
self.i.stop_listen()
def remove_left_failsafe(i):
"""
Removes the "exit context on LEFT unless a callback for KEY_LEFT is set"
failsafe that's added to all ``InputProxy`` objects by default.
"""
if "KEY_LEFT" in i.maskable_keymap:
i.remove_maskable_callback("KEY_LEFT")