toon
Description
Additional tools for neuroscience experiments, including:
- A framework for polling input devices on a separate process.
- A framework for animating things.
Everything should work on Windows/Mac/Linux.
See requirements.txt for dependencies.
Install
Current release:
pip install toon
Development version:
pip install git+https://github.com/aforren1/toon
For full install (including dependencies of included devices):
pip install toon[full]
See setup.py for a list of those dependencies, as well as device-specific subdivisions.
See the demos/ folder for usage examples.
Overview
Input
toon provides a framework for polling from input devices, including common peripherals like mice and keyboards, with the flexibility to handle less-common devices like eyetrackers, motion trackers, and custom devices (see toon/input/ for examples). The goal is to make it easier to use a wide variety of devices, including those with sampling rates >1kHz, with minimal performance impact on the main process.
We use the built-in multiprocessing module to control a separate process that hosts the device, and, in concert with numpy, to move data to the main process via shared memory. It seems that under typical conditions, we can expect single read() operations to take less than 500 microseconds (and more often < 100 us). See demos/bench.py for an example of measuring read performance.
Typical use looks like:
from toon.input import MpDevice
from toon.input.mouse import Mouse
# NB: pass the *class*, not an object
device = MpDevice(Mouse)
with device:
data = device.read()
# alternatively, unpack
# clicks, pos, scroll = device.read()
if data.pos is not None:
# N-D array of data (0th dim is time)
print(data.pos)
# time is 1D array of timestamps
print(data.pos.time)
print(data.pos[-1].time) # most recent timestamp
Creating a custom device is relatively straightforward, though there are a few boxes to check.
from toon.input import BaseDevice, Obs
from ctypes import c_double
# Obs is a class that manages observations
class MyDevice(BaseDevice):
# optional: give a hint for the buffer size
sampling_frequency = 500
# required: each data source gets its own Obs
# this should be defined in the scope of the device
class Pos(Obs):
shape = (3,)
ctype = float # python type, numpy dtype, or ctype
# can have multiple Obs per device
class RotMat(Obs):
shape = (3, 3)
ctype = c_double
# optional: prefer starting communication in __enter__
def __init__(self, **kwargs):
# kwargs are passed in through MpDevice
pass
# optional: configure the device, start communicating
def __enter__(self):
# remember to return self, or else the context manager won't work
return self
# optional: clean up resources, close device
def __exit__(self, *args):
pass
# required (and picky)
def read(self):
# See demos/ for examples of sharing a time source between the processes
time = self.clock()
# store new data with a timestamp
pos = self.Pos(time, (1, 2, 3))
rotmat = self.RotMat(time, [[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# self.Returns is a dynamically-created named tuple
# names derived from any Obs defined in the Device
# prefer using keyword arguments, names are alphabetically sorted!
return self.Returns(pos=pos, rotmat=rotmat)
This device can then be passed to a toon.input.MpDevice, which preallocates the shared memory and handles other details.
A few things to be aware of for data returned by MpDevice:
- If a device only has a single
Obs,MpDevicereturns a singleTsArray(a numpy array with atimeattribute). Otherwise,MpDevicereturns a named tuple of observations, where the names are alphabetically-sorted, lowercased versions of the pre-definedObs. - If the data returned by a single read is scalar (e.g. a 1D force sensor),
MpDevicewill drop the 1st dimension. - If there's no data for a given observation,
Noneis returned. The named tuple has a method for checking all members at once (data.any()).
Other notes:
- The returned data is a view of the local copy of the data.
toon.input.TsArrays have acopymethod, which may be useful if e.g. appending to a list for later concatenation. - If receiving batches of data when
read()ing from the device, you can return a list ofReturns(seetests/input/mockdevices.pyfor an example) - Can optionally use
device.start()/device.stop()instead of a context manager - Can check for remote errors at any point using
device.check_error(), though this automatically happens immediately after entering the context manager and whenread()ing.
Animation
This is still a work in progress, though I think it has some utility as-is. It's a port of the animation component in the Magnum framework, though lacking some of the features (e.g. Track extrapolation, proper handling of time scaling).
Example:
from time import sleep
from timeit import default_timer
import matplotlib.pyplot as plt
from toon.anim import Track, Player
# see toon/anim/easing.py for all easings available
from toon.anim.easing import linear, elastic_in_out
class Circle(object):
x = 0
y = 0
circle = Circle()
# list of (time, value)
keyframes = [(0.2, -0.5), (0.5, 0), (3, 0.5)]
x_track = Track(keyframes, easing=linear)
# we can reuse keyframes
y_track = Track(keyframes, easing=elastic_in_out)
player = Player()
# directly modify an attribute
player.add(x_track, 'x', obj=circle)
def y_cb(val, obj):
obj.y = val
# modify via callback
player.add(y_track, y_cb, obj=circle)
t0 = default_timer()
player.start(t0)
vals = []
while default_timer() < t0 + 3.2:
player.advance(default_timer())
vals.append([circle.x, circle.y])
sleep(1/60)
plt.plot(vals)
plt.show()
Other notes:
- Non-numeric attributes, like color strings, can also be modified in this framework (easing is ignored).
- The
Timelineclass (intoon.anim) can be used to get the time between frames, or the time since some origin time, taken attimeline.start(). - The
Playercan also be used as a mixin, in which case theobjargument can be omitted fromplayer.add()(see thedemos/folder for examples). - Multiple objects can be modified simultaneously by feeding a list of objects into
player.add().
Release files for toon 0.11.5
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| toon-0.11.5.tar.gz | 22.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| toon-0.11.5-py2.py3-none-any.whl | Python 2, Python 3 | none | any | Details |
Total release size: 51.8 kB
Release files / toon-0.11.5.tar.gz
| Download URL | toon-0.11.5.tar.gz |
|---|---|
| Size | 22.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4e77f8816b8dc89648ccd517b0beedca65463ed9d65e1af51ada34598a78c8b8
|
|
BLAKE2b-256 checksum How to use checksums |
a775b04c744539ed612e91ad4e42adb7b485afe2b5c6dd804f823e45bdae04dc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/1.12.1 pkginfo/1.4.2 requests/2.19.1 setuptools/40.2.0 requests-toolbelt/0.8.0 tqdm/4.26.0 CPython/3.6.6
|
Release files / toon-0.11.5-py2.py3-none-any.whl
| Download URL | toon-0.11.5-py2.py3-none-any.whl |
|---|---|
| Size | 29.5 kB |
| Tags | Python 2 Python 3 |
|
SHA-256 checksum How to use checksums |
c1a3fcb74e1f68e5db42e4127fc7855a6124fafbbc5f410aabcc4159e4772280
|
|
BLAKE2b-256 checksum How to use checksums |
8208543dc96635adf6cb580106c514671c7cd782ea57dfd2c5c1f333324ef439
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/1.12.1 pkginfo/1.4.2 requests/2.19.1 setuptools/40.2.0 requests-toolbelt/0.8.0 tqdm/4.26.0 CPython/3.6.6
|