Controls API ============ Quarklib provides high-level access to V4L2 camera controls through a hierarchy of semi-automatically defined types that take care of value conversion and validation. Finding controls ---------------- The entry-point to this api is the :attr:`quarklib.camera.Camera.controls` property available on any :class:`quarklib.camera.Camera` instance. To print an overview of the available controls, their formats, and their current values, run the following code snippet: .. code-block:: python from quarklib.camera import Camera cam = Camera() print(cam.controls) The output should look similar to the following: .. code-block:: text qtec_default_controls: digital_gain: red (scalar, FloatFormatter): 1.0 green (scalar, FloatFormatter): 1.0 blue (scalar, FloatFormatter): 1.0 ... flip: horizontal (scalar, BoolFormatter): False vertical (scalar, BoolFormatter): False binning: horizontal (scalar, IntFormatter): 1 vertical (scalar, IntFormatter): 1 pattern_generator (scalar, MenuFormatter): Off To locate a specific control, either find it in the dynamic overview or look at :doc:`/reference/qtec_controls` which contains a detailed reference of the full set of controls. .. caution:: Depending on your use of camera family and bitstream, all controls may not be available for your setup. Application code has the responsibility of ensuring that invalid access is handled appropriately. Accessing a non-existent control results in an :class:`AttributeError`. Using controls -------------- All controls inherit from :class:`quarklib.controls.IControl` which defines an interface for reading/writing values to that control. Implementations are grouped into two categories, :class:`quarklib.controls.DriverControl` and :class:`quarklib.controls.GroupControl`. The former covers controls that have a V4L2 equivalent while the latter covers groupings of other controls. Driver Controls ^^^^^^^^^^^^^^^ A driver control wraps a specific V4L2 control but provides automatic format conversion and validation as part of its definition. The :attr:`quarklib.controls.DriverControl.formatter` attribute, which is an instance of the :class:`quarklib.controls.formatters.AbstractFormatter` class, determines what values are accepted by the control and how they are converted to/from the format expected by the underlying V4L2 control. The currently supported formatters are: * :class:`quarklib.controls.formatters.IntFormatter`: Integer values that may be signed/unsigned and with defined limits. * :class:`quarklib.controls.formatters.FloatFormatter`: Float values that are converted to fixed-point decimal numbers internally. * :class:`quarklib.controls.formatters.MenuFormatter`: An enumeration of strings that map to indices internally. * :class:`quarklib.controls.formatters.BoolFormatter`: A boolean value that maps to an integer internally. Group Controls ^^^^^^^^^^^^^^ Control groups provide high-level and hierarchical access to other controls. Groups contain multiple child controls that can be accessed either through attributes (e.g. `cam.controls.digital_gain.red`) or through the group's own read/write methods. The value type and structure returned from :meth:`quarklib.controls.GroupControl.read` and expected as the argument in :meth:`quarklib.controls.GroupControl.write` depends on the specific class of group used. Quarklib provides the following built-in group types (custom ones can be made by subclassing :class:`quarklib.controls.GroupControl`): * :class:`quarklib.controls.GroupControl`: The base type of group allows reading/writing to its children using a dictionary of values. * :class:`quarklib.controls.GroupArrayControl`: Concatenates child values into a single array that can be accessed using the *NumPy* slicing syntax. * :class:`quarklib.controls.GroupSplitControl`: Writing to this control writes the same value to all its children. Reading returns a dictionary like its parent class. Value validation ---------------- All controls perform value validation when writing values. For groups, this is delegated to its children and for :class:`quarklib.controls.DriverControl` instances, it is performed by the type's :class:`quarklib.controls.formatters.AbstractFormatter` instance. Since validation is performed when calling :meth:`quarklib.controls.IControl.write`, it may throw a :class:`ValueError`. Reading does not need to validate values, since all written values are assumed to be validated, but may still throw :class:`qamlib.V4L2Exception` when IO-related errors occur. Implementation details ---------------------- The base type is built at runtime when a new :class:`quarklib.camera.Camera` instance is created. The exact contents depend on the capabilities of the camera (sensor family, bitstream, and driver version) but the default configuration using the `draco-tokyo` bitstream is documented as :class:`quarklib.controls.definitions.QtecDefaultControls`. The main reasons for creating a new class for each control is clarity and for safety. Making a type for each V4L2 control makes it more readily identifiable without having to look at instance variables and it also makes it less likely that controls are used incorrectly since it is possible to specify its type at the usage site. Since Python is dynamically typed, it is of course possible to violate type hints but this is now the responsibility of the caller instead of the callee, thus eliminating the need to verify control formats at each interface point. The reason the classes are created dynamically is that many of the control parameters can be read from the V4L2 driver itself thus eliminating the need to manually copy these parameters. Although this approach makes it impossible to inspect the source of each created class, the documentation still provides documentation for generated classes in :doc:`/reference/qtec_controls`. These standard control types are created using V4L2 parameters saved from a specific camera configuration (currently the IMX421 with the draco-tokyo bitstream). Building custom controls ------------------------ You can easily replace the default Qtec control hierarchy in the :class:`quarklib.camera.Camera` constructor with a custom :class:`quarklib.controls.GroupControl` class. This allows you to build a custom interface to a camera. This guide will also show you how to extend the default type such that the resulting `Camera` instance is still compatible with the rest of the quarklib API. Controls are built using the helper classes provided in :mod:`quarklib.controls.builders`. These each provide a set of class methods that simplify the creation of new control hierarchies. For examples of how to use them, check the :mod:`quarklib.controls.definitions` source code.