Inputs¶
The Inputs API is built as a thin wrapper over the identically-named library (inputs). If you are having issues, check whether they are coming from RTCBot or from the underlying library.
There are three input devices exposed. A Keyboard
, a Mouse
and a Gamepad
.
Note
To get access to Keyboard and Mouse, you might need to either
run as adminsitrator, or on Linux, add your user to the input
group.
Warning
Keyboard support is experimental - it only works in certain environments. You can try it, but don’t be surprised if no events show up.
Mouse¶
To get mouse events, you can run the following:
import asyncio
from rtcbot import Mouse
m = Mouse()
@m.subscribe
def onkey(key):
print(key)
try:
asyncio.get_event_loop().run_forever()
finally:
m.close()
This code gives the following results:
{'timestamp': 1552629001.833567, 'code': 'REL_X', 'state': 1, 'event': 'Relative'}
{'timestamp': 1552629001.833567, 'code': 'REL_Y', 'state': 1, 'event': 'Relative'}
{'timestamp': 1552629001.841518, 'code': 'REL_X', 'state': 2, 'event': 'Relative'}
{'timestamp': 1552629001.889522, 'code': 'REL_X', 'state': 2, 'event': 'Relative'}
{'timestamp': 1552629001.905525, 'code': 'REL_X', 'state': 3, 'event': 'Relative'}
{'timestamp': 1552629001.905525, 'code': 'REL_Y', 'state': -2, 'event': 'Relative'}
{'timestamp': 1552629002.16957, 'code': 'REL_X', 'state': 2, 'event': 'Relative'}
{'timestamp': 1552629004.233588, 'code': 'MSC_SCAN', 'state': 589825, 'event': 'Misc'}
{'timestamp': 1552629004.233588, 'code': 'BTN_LEFT', 'state': 1, 'event': 'Key'}
{'timestamp': 1552629004.361593, 'code': 'MSC_SCAN', 'state': 589825, 'event': 'Misc'}
{'timestamp': 1552629004.361593, 'code': 'BTN_LEFT', 'state': 0, 'event': 'Key'}
{'timestamp': 1552629005.361596, 'code': 'MSC_SCAN', 'state': 589826, 'event': 'Misc'}
{'timestamp': 1552629005.361596, 'code': 'BTN_RIGHT', 'state': 1, 'event': 'Key'}
The REL_X and REL_Y codes refer to relative mouse motion. Here, the mouse started by moving 1 unit to the right (REL_X).
Gamepad¶
The Gamepad usually refers to a wired Xbox controller. Connect it to your computer through USB. To use the gamepad, you probably don’t need administrator access:
import asyncio
from rtcbot import Gamepad
g = Gamepad()
@g.subscribe
def onkey(key):
print(key)
try:
asyncio.get_event_loop().run_forever()
finally:
g.close()
This code gives the following results:
{'timestamp': 1552629513.7494, 'code': 'BTN_SOUTH', 'state': 1, 'event': 'Key'}
{'timestamp': 1552629513.7494, 'code': 'ABS_Y', 'state': -1, 'event': 'Absolute'}
{'timestamp': 1552629513.969403, 'code': 'BTN_SOUTH', 'state': 0, 'event': 'Key'}
{'timestamp': 1552629517.089424, 'code': 'ABS_X', 'state': -253, 'event': 'Absolute'}
{'timestamp': 1552629517.097385, 'code': 'ABS_X', 'state': -64, 'event': 'Absolute'}
{'timestamp': 1552629517.109388, 'code': 'ABS_X', 'state': -211, 'event': 'Absolute'}
{'timestamp': 1552629517.117379, 'code': 'ABS_X', 'state': -242, 'event': 'Absolute'}
The resulting events are all button presses and joystick control. For example, ABS_X here refers to the horizontal position of the right joystick on a wired Xbox controller.
API¶
-
class
rtcbot.inputs.
Gamepad
(eventFilter=<function <lambda>>, loop=None)[source]¶ Bases:
rtcbot.inputs.InputDevice
-
close
()¶ Shuts down data gathering, and closes all subscriptions. Note that it is not recommended to call this in an async function, since it waits until the background thread joins.
The object is meant to be used as a singleton, which is initialized at the start of your code, and is closed when shutting down.
-
property
closed
¶ Returns whether the object was closed. This includes both thrown exceptions, and clean exits.
-
property
error
¶ If there is an error that causes the underlying process to crash, this property will hold the actual
Exception
that was thrown:if myobject.error is not None: print("Oh no! There was an error:",myobject.error)
This property is offered for convenience, but usually, you will want to subscribe to the error by using
onError()
, which will notify your app when the issue happens.Note
If the error is not None, the object is considered crashed, and no longer processing data.
-
async
get
()¶ Behaves similarly to
subscribe().get()
. On the first call, creates a default subscription, and all subsequent calls toget()
use that subscription.If
unsubscribe()
is called, the subscription is deleted, so a subsequent call toget()
will create a new one:data = await myobj.get() # Creates subscription on first call data = await myobj.get() # Same subscription myobj.unsubscribe() data2 = await myobj.get() # A new subscription
The above code is equivalent to the following:
defaultSubscription = myobj.subscribe() data = await defaultSubscription.get() data = await defaultSubscription.get() myobj.unsubscribe(defaultSubscription) newDefaultSubscription = myobj.subscribe() data = await newDefaultSubscription.get()
-
onClose
(subscription=None)¶ This is mainly useful for connections - they can be closed remotely. This allows handling the close event.
@myobj.onClose def closeCallback(): print("Closed!)
Be aware that this is equivalent to explicitly awaiting the object:
await myobj
-
onError
(subscription=None)¶ Since most data processing happens in the background, the object might encounter an error, and the data processing might crash. If there is a crash, the object is considered dead, and no longer gathering data.
To catch these errors, when an unhandled exception happens, the error event is fired, with the associated
Exception
. This function allows you to subscribe to these events:@myobj.onError def error_happened(err): print("Crap, stuff just crashed: ",err)
The
onError()
function behaves in the same way as asubscribe()
, which means that you can pass it a coroutine, or even directly await it:err = await myobj.onError()
-
onReady
(subscription=None)¶ Creating the class does not mean that the object is ready to process data. When created, the object starts an initialization procedure in the background, and once this procedure is complete, and any spawned background workers are ready to process data, it fires a ready event.
This function allows you to listen for this event:
@myobj.onReady def readyCallback(): print("Ready!)
The function works in exactly the same way as a
subscribe()
, meaning that you can pass it a coroutine, or even await it directly:await myobj.onReady()
Note
The object will automatically handle any subscriptions or inserts that happen while it is initializing, so you generally don’t need to worry about the ready event, unless you need exact control.
-
property
ready
¶ This is True when the class has been fully initialized, and is ready to process data:
if not myobject.ready: print("Not ready to process data")
This property is offered for convenience, but if you want to be notifed when ready to process data, you will want to use the
onReady()
function, which will allow you to set up a callback/coroutine to wait until initialized.Note
You usually don’t need to check the ready state, since all functions for getting/putting data will work even if the class is still starting up in the background.
-
subscribe
(subscription=None)¶ Allows subscribing to new data as it comes in, returning a subscription (see Subscriptions):
s = myobj.subscribe() while True: data = await s.get() print(data)
There can be multiple subscriptions active at the same time, each of which get identical data. Each call to
subscribe()
returns a new, independent subscription:s1 = myobj.subscribe() s2 = myobj.subscribe() while True: assert await s1.get()== await s2.get()
This function can also be used as a callback:
@myobj.subscribe def newData(data): print("Got data:",data)
If passed an argument, it attempts to use the given callback/coroutine/subscription to notify of incoming data.
- Parameters
subscription (optional) –
- An optional existing subscription to subscribe to. This can be one of 3 things:
An object which has the method put_nowait (see Subscriptions):
q = asyncio.Queue() myobj.subscribe(q) while True: data = await q.get() print(data)
A callback function - this will be called the moment new data is inserted:
@myobj.subscribe def myfunction(data): print(data)
An coroutine callback - A future of this coroutine is created on each insert:
@myobj.subscribe async def myfunction(data): await asyncio.sleep(5) print(data)
- Returns
A subscription. If one was passed in, returns the passed in subscription:
q = asyncio.Queue() ret = thing.subscribe(q) assert ret==q
-
unsubscribe
(subscription=None)¶ Removes the given subscription, so that it no longer gets updated:
subs = myobj.subscribe() myobj.unsubscribe(subs)
If no argument is given, removes the default subscription created by get(). If none exists, then does nothing.
- Parameters
subscription (optional) – Anything that was passed into/returned from
subscribe()
.
-
unsubscribeAll
()¶ Removes all currently active subscriptions, including the default one if it was intialized.
-
-
class
rtcbot.inputs.
InputDevice
(device, eventFilter=<function <lambda>>, loop=None)[source]¶ Bases:
rtcbot.base.multiprocess.ProcessSubscriptionProducer
A thin wrapper over
inputs
, which permits getting events in an asynchronous manner.-
close
()¶ Shuts down data gathering, and closes all subscriptions. Note that it is not recommended to call this in an async function, since it waits until the background thread joins.
The object is meant to be used as a singleton, which is initialized at the start of your code, and is closed when shutting down.
-
property
closed
¶ Returns whether the object was closed. This includes both thrown exceptions, and clean exits.
-
property
error
¶ If there is an error that causes the underlying process to crash, this property will hold the actual
Exception
that was thrown:if myobject.error is not None: print("Oh no! There was an error:",myobject.error)
This property is offered for convenience, but usually, you will want to subscribe to the error by using
onError()
, which will notify your app when the issue happens.Note
If the error is not None, the object is considered crashed, and no longer processing data.
-
async
get
()¶ Behaves similarly to
subscribe().get()
. On the first call, creates a default subscription, and all subsequent calls toget()
use that subscription.If
unsubscribe()
is called, the subscription is deleted, so a subsequent call toget()
will create a new one:data = await myobj.get() # Creates subscription on first call data = await myobj.get() # Same subscription myobj.unsubscribe() data2 = await myobj.get() # A new subscription
The above code is equivalent to the following:
defaultSubscription = myobj.subscribe() data = await defaultSubscription.get() data = await defaultSubscription.get() myobj.unsubscribe(defaultSubscription) newDefaultSubscription = myobj.subscribe() data = await newDefaultSubscription.get()
-
onClose
(subscription=None)¶ This is mainly useful for connections - they can be closed remotely. This allows handling the close event.
@myobj.onClose def closeCallback(): print("Closed!)
Be aware that this is equivalent to explicitly awaiting the object:
await myobj
-
onError
(subscription=None)¶ Since most data processing happens in the background, the object might encounter an error, and the data processing might crash. If there is a crash, the object is considered dead, and no longer gathering data.
To catch these errors, when an unhandled exception happens, the error event is fired, with the associated
Exception
. This function allows you to subscribe to these events:@myobj.onError def error_happened(err): print("Crap, stuff just crashed: ",err)
The
onError()
function behaves in the same way as asubscribe()
, which means that you can pass it a coroutine, or even directly await it:err = await myobj.onError()
-
onReady
(subscription=None)¶ Creating the class does not mean that the object is ready to process data. When created, the object starts an initialization procedure in the background, and once this procedure is complete, and any spawned background workers are ready to process data, it fires a ready event.
This function allows you to listen for this event:
@myobj.onReady def readyCallback(): print("Ready!)
The function works in exactly the same way as a
subscribe()
, meaning that you can pass it a coroutine, or even await it directly:await myobj.onReady()
Note
The object will automatically handle any subscriptions or inserts that happen while it is initializing, so you generally don’t need to worry about the ready event, unless you need exact control.
-
property
ready
¶ This is True when the class has been fully initialized, and is ready to process data:
if not myobject.ready: print("Not ready to process data")
This property is offered for convenience, but if you want to be notifed when ready to process data, you will want to use the
onReady()
function, which will allow you to set up a callback/coroutine to wait until initialized.Note
You usually don’t need to check the ready state, since all functions for getting/putting data will work even if the class is still starting up in the background.
-
subscribe
(subscription=None)¶ Allows subscribing to new data as it comes in, returning a subscription (see Subscriptions):
s = myobj.subscribe() while True: data = await s.get() print(data)
There can be multiple subscriptions active at the same time, each of which get identical data. Each call to
subscribe()
returns a new, independent subscription:s1 = myobj.subscribe() s2 = myobj.subscribe() while True: assert await s1.get()== await s2.get()
This function can also be used as a callback:
@myobj.subscribe def newData(data): print("Got data:",data)
If passed an argument, it attempts to use the given callback/coroutine/subscription to notify of incoming data.
- Parameters
subscription (optional) –
- An optional existing subscription to subscribe to. This can be one of 3 things:
An object which has the method put_nowait (see Subscriptions):
q = asyncio.Queue() myobj.subscribe(q) while True: data = await q.get() print(data)
A callback function - this will be called the moment new data is inserted:
@myobj.subscribe def myfunction(data): print(data)
An coroutine callback - A future of this coroutine is created on each insert:
@myobj.subscribe async def myfunction(data): await asyncio.sleep(5) print(data)
- Returns
A subscription. If one was passed in, returns the passed in subscription:
q = asyncio.Queue() ret = thing.subscribe(q) assert ret==q
-
unsubscribe
(subscription=None)¶ Removes the given subscription, so that it no longer gets updated:
subs = myobj.subscribe() myobj.unsubscribe(subs)
If no argument is given, removes the default subscription created by get(). If none exists, then does nothing.
- Parameters
subscription (optional) – Anything that was passed into/returned from
subscribe()
.
-
unsubscribeAll
()¶ Removes all currently active subscriptions, including the default one if it was intialized.
-
-
class
rtcbot.inputs.
Keyboard
(eventFilter=<function <lambda>>, loop=None)[source]¶ Bases:
rtcbot.inputs.InputDevice
-
close
()¶ Shuts down data gathering, and closes all subscriptions. Note that it is not recommended to call this in an async function, since it waits until the background thread joins.
The object is meant to be used as a singleton, which is initialized at the start of your code, and is closed when shutting down.
-
property
closed
¶ Returns whether the object was closed. This includes both thrown exceptions, and clean exits.
-
property
error
¶ If there is an error that causes the underlying process to crash, this property will hold the actual
Exception
that was thrown:if myobject.error is not None: print("Oh no! There was an error:",myobject.error)
This property is offered for convenience, but usually, you will want to subscribe to the error by using
onError()
, which will notify your app when the issue happens.Note
If the error is not None, the object is considered crashed, and no longer processing data.
-
async
get
()¶ Behaves similarly to
subscribe().get()
. On the first call, creates a default subscription, and all subsequent calls toget()
use that subscription.If
unsubscribe()
is called, the subscription is deleted, so a subsequent call toget()
will create a new one:data = await myobj.get() # Creates subscription on first call data = await myobj.get() # Same subscription myobj.unsubscribe() data2 = await myobj.get() # A new subscription
The above code is equivalent to the following:
defaultSubscription = myobj.subscribe() data = await defaultSubscription.get() data = await defaultSubscription.get() myobj.unsubscribe(defaultSubscription) newDefaultSubscription = myobj.subscribe() data = await newDefaultSubscription.get()
-
onClose
(subscription=None)¶ This is mainly useful for connections - they can be closed remotely. This allows handling the close event.
@myobj.onClose def closeCallback(): print("Closed!)
Be aware that this is equivalent to explicitly awaiting the object:
await myobj
-
onError
(subscription=None)¶ Since most data processing happens in the background, the object might encounter an error, and the data processing might crash. If there is a crash, the object is considered dead, and no longer gathering data.
To catch these errors, when an unhandled exception happens, the error event is fired, with the associated
Exception
. This function allows you to subscribe to these events:@myobj.onError def error_happened(err): print("Crap, stuff just crashed: ",err)
The
onError()
function behaves in the same way as asubscribe()
, which means that you can pass it a coroutine, or even directly await it:err = await myobj.onError()
-
onReady
(subscription=None)¶ Creating the class does not mean that the object is ready to process data. When created, the object starts an initialization procedure in the background, and once this procedure is complete, and any spawned background workers are ready to process data, it fires a ready event.
This function allows you to listen for this event:
@myobj.onReady def readyCallback(): print("Ready!)
The function works in exactly the same way as a
subscribe()
, meaning that you can pass it a coroutine, or even await it directly:await myobj.onReady()
Note
The object will automatically handle any subscriptions or inserts that happen while it is initializing, so you generally don’t need to worry about the ready event, unless you need exact control.
-
property
ready
¶ This is True when the class has been fully initialized, and is ready to process data:
if not myobject.ready: print("Not ready to process data")
This property is offered for convenience, but if you want to be notifed when ready to process data, you will want to use the
onReady()
function, which will allow you to set up a callback/coroutine to wait until initialized.Note
You usually don’t need to check the ready state, since all functions for getting/putting data will work even if the class is still starting up in the background.
-
subscribe
(subscription=None)¶ Allows subscribing to new data as it comes in, returning a subscription (see Subscriptions):
s = myobj.subscribe() while True: data = await s.get() print(data)
There can be multiple subscriptions active at the same time, each of which get identical data. Each call to
subscribe()
returns a new, independent subscription:s1 = myobj.subscribe() s2 = myobj.subscribe() while True: assert await s1.get()== await s2.get()
This function can also be used as a callback:
@myobj.subscribe def newData(data): print("Got data:",data)
If passed an argument, it attempts to use the given callback/coroutine/subscription to notify of incoming data.
- Parameters
subscription (optional) –
- An optional existing subscription to subscribe to. This can be one of 3 things:
An object which has the method put_nowait (see Subscriptions):
q = asyncio.Queue() myobj.subscribe(q) while True: data = await q.get() print(data)
A callback function - this will be called the moment new data is inserted:
@myobj.subscribe def myfunction(data): print(data)
An coroutine callback - A future of this coroutine is created on each insert:
@myobj.subscribe async def myfunction(data): await asyncio.sleep(5) print(data)
- Returns
A subscription. If one was passed in, returns the passed in subscription:
q = asyncio.Queue() ret = thing.subscribe(q) assert ret==q
-
unsubscribe
(subscription=None)¶ Removes the given subscription, so that it no longer gets updated:
subs = myobj.subscribe() myobj.unsubscribe(subs)
If no argument is given, removes the default subscription created by get(). If none exists, then does nothing.
- Parameters
subscription (optional) – Anything that was passed into/returned from
subscribe()
.
-
unsubscribeAll
()¶ Removes all currently active subscriptions, including the default one if it was intialized.
-
-
class
rtcbot.inputs.
Mouse
(eventFilter=<function <lambda>>, loop=None)[source]¶ Bases:
rtcbot.inputs.InputDevice
-
close
()¶ Shuts down data gathering, and closes all subscriptions. Note that it is not recommended to call this in an async function, since it waits until the background thread joins.
The object is meant to be used as a singleton, which is initialized at the start of your code, and is closed when shutting down.
-
property
closed
¶ Returns whether the object was closed. This includes both thrown exceptions, and clean exits.
-
property
error
¶ If there is an error that causes the underlying process to crash, this property will hold the actual
Exception
that was thrown:if myobject.error is not None: print("Oh no! There was an error:",myobject.error)
This property is offered for convenience, but usually, you will want to subscribe to the error by using
onError()
, which will notify your app when the issue happens.Note
If the error is not None, the object is considered crashed, and no longer processing data.
-
async
get
()¶ Behaves similarly to
subscribe().get()
. On the first call, creates a default subscription, and all subsequent calls toget()
use that subscription.If
unsubscribe()
is called, the subscription is deleted, so a subsequent call toget()
will create a new one:data = await myobj.get() # Creates subscription on first call data = await myobj.get() # Same subscription myobj.unsubscribe() data2 = await myobj.get() # A new subscription
The above code is equivalent to the following:
defaultSubscription = myobj.subscribe() data = await defaultSubscription.get() data = await defaultSubscription.get() myobj.unsubscribe(defaultSubscription) newDefaultSubscription = myobj.subscribe() data = await newDefaultSubscription.get()
-
onClose
(subscription=None)¶ This is mainly useful for connections - they can be closed remotely. This allows handling the close event.
@myobj.onClose def closeCallback(): print("Closed!)
Be aware that this is equivalent to explicitly awaiting the object:
await myobj
-
onError
(subscription=None)¶ Since most data processing happens in the background, the object might encounter an error, and the data processing might crash. If there is a crash, the object is considered dead, and no longer gathering data.
To catch these errors, when an unhandled exception happens, the error event is fired, with the associated
Exception
. This function allows you to subscribe to these events:@myobj.onError def error_happened(err): print("Crap, stuff just crashed: ",err)
The
onError()
function behaves in the same way as asubscribe()
, which means that you can pass it a coroutine, or even directly await it:err = await myobj.onError()
-
onReady
(subscription=None)¶ Creating the class does not mean that the object is ready to process data. When created, the object starts an initialization procedure in the background, and once this procedure is complete, and any spawned background workers are ready to process data, it fires a ready event.
This function allows you to listen for this event:
@myobj.onReady def readyCallback(): print("Ready!)
The function works in exactly the same way as a
subscribe()
, meaning that you can pass it a coroutine, or even await it directly:await myobj.onReady()
Note
The object will automatically handle any subscriptions or inserts that happen while it is initializing, so you generally don’t need to worry about the ready event, unless you need exact control.
-
property
ready
¶ This is True when the class has been fully initialized, and is ready to process data:
if not myobject.ready: print("Not ready to process data")
This property is offered for convenience, but if you want to be notifed when ready to process data, you will want to use the
onReady()
function, which will allow you to set up a callback/coroutine to wait until initialized.Note
You usually don’t need to check the ready state, since all functions for getting/putting data will work even if the class is still starting up in the background.
-
subscribe
(subscription=None)¶ Allows subscribing to new data as it comes in, returning a subscription (see Subscriptions):
s = myobj.subscribe() while True: data = await s.get() print(data)
There can be multiple subscriptions active at the same time, each of which get identical data. Each call to
subscribe()
returns a new, independent subscription:s1 = myobj.subscribe() s2 = myobj.subscribe() while True: assert await s1.get()== await s2.get()
This function can also be used as a callback:
@myobj.subscribe def newData(data): print("Got data:",data)
If passed an argument, it attempts to use the given callback/coroutine/subscription to notify of incoming data.
- Parameters
subscription (optional) –
- An optional existing subscription to subscribe to. This can be one of 3 things:
An object which has the method put_nowait (see Subscriptions):
q = asyncio.Queue() myobj.subscribe(q) while True: data = await q.get() print(data)
A callback function - this will be called the moment new data is inserted:
@myobj.subscribe def myfunction(data): print(data)
An coroutine callback - A future of this coroutine is created on each insert:
@myobj.subscribe async def myfunction(data): await asyncio.sleep(5) print(data)
- Returns
A subscription. If one was passed in, returns the passed in subscription:
q = asyncio.Queue() ret = thing.subscribe(q) assert ret==q
-
unsubscribe
(subscription=None)¶ Removes the given subscription, so that it no longer gets updated:
subs = myobj.subscribe() myobj.unsubscribe(subs)
If no argument is given, removes the default subscription created by get(). If none exists, then does nothing.
- Parameters
subscription (optional) – Anything that was passed into/returned from
subscribe()
.
-
unsubscribeAll
()¶ Removes all currently active subscriptions, including the default one if it was intialized.
-
-
rtcbot.inputs.
defaultFilter
(x)¶