diff options
Diffstat (limited to 'src/py')
-rwxr-xr-x | src/py/libcamera/gen-py-controls.py | 126 | ||||
-rw-r--r-- | src/py/libcamera/meson.build | 27 | ||||
-rw-r--r-- | src/py/libcamera/py_camera_manager.h | 2 | ||||
-rw-r--r-- | src/py/libcamera/py_controls_generated.cpp.in | 35 | ||||
-rw-r--r-- | src/py/libcamera/py_enums.cpp | 3 | ||||
-rw-r--r-- | src/py/libcamera/py_helpers.cpp | 16 | ||||
-rw-r--r-- | src/py/libcamera/py_main.cpp | 21 | ||||
-rw-r--r-- | src/py/libcamera/py_properties_generated.cpp.in | 30 |
8 files changed, 120 insertions, 140 deletions
diff --git a/src/py/libcamera/gen-py-controls.py b/src/py/libcamera/gen-py-controls.py index 8efbf95b..d43a7c1c 100755 --- a/src/py/libcamera/gen-py-controls.py +++ b/src/py/libcamera/gen-py-controls.py @@ -4,10 +4,12 @@ # Generate Python bindings controls from YAML import argparse -import string +import jinja2 import sys import yaml +from controls import Control + def find_common_prefix(strings): prefix = strings[0] @@ -21,70 +23,39 @@ def find_common_prefix(strings): return prefix -def generate_py(controls, mode): - out = '' - - vendors_class_def = [] - vendor_defs = [] - vendors = [] - for vendor, ctrl_list in controls.items(): - for ctrls in ctrl_list: - name, ctrl = ctrls.popitem() - - if vendor not in vendors and vendor != 'libcamera': - vendor_mode_str = f'{vendor.capitalize()}{mode.capitalize()}' - vendors_class_def.append('class Py{}\n{{\n}};\n'.format(vendor_mode_str)) - vendor_defs.append('\tauto {} = py::class_<Py{}>(controls, \"{}\");'.format(vendor, vendor_mode_str, vendor)) - vendors.append(vendor) - - if vendor != 'libcamera': - ns = 'libcamera::{}::{}::'.format(mode, vendor) - container = vendor - else: - ns = 'libcamera::{}::'.format(mode) - container = 'controls' - - out += f'\t{container}.def_readonly_static("{name}", static_cast<const libcamera::ControlId *>(&{ns}{name}));\n\n' - - enum = ctrl.get('enum') - if not enum: - continue - - cpp_enum = name + 'Enum' - - out += '\tpy::enum_<{}{}>({}, \"{}\")\n'.format(ns, cpp_enum, container, cpp_enum) - - if mode == 'controls': - # Adjustments for controls - if name == 'LensShadingMapMode': - prefix = 'LensShadingMapMode' - else: - prefix = find_common_prefix([e['name'] for e in enum]) - else: - # Adjustments for properties - prefix = find_common_prefix([e['name'] for e in enum]) - - for entry in enum: - cpp_enum = entry['name'] - py_enum = entry['name'][len(prefix):] - - out += '\t\t.value(\"{}\", {}{})\n'.format(py_enum, ns, cpp_enum) - - out += '\t;\n\n' - - return {'controls': out, - 'vendors_class_def': '\n'.join(vendors_class_def), - 'vendors_defs': '\n'.join(vendor_defs)} +def extend_control(ctrl, mode): + if ctrl.vendor != 'libcamera': + ctrl.klass = ctrl.vendor + ctrl.namespace = f'{ctrl.vendor}::' + else: + ctrl.klass = mode + ctrl.namespace = '' + + if not ctrl.is_enum: + return ctrl + + if mode == 'controls': + # Adjustments for controls + if ctrl.name == 'LensShadingMapMode': + prefix = 'LensShadingMapMode' + else: + prefix = find_common_prefix([e.name for e in ctrl.enum_values]) + else: + # Adjustments for properties + prefix = find_common_prefix([e.name for e in ctrl.enum_values]) + for enum in ctrl.enum_values: + enum.py_name = enum.name[len(prefix):] -def fill_template(template, data): - template = open(template, 'rb').read() - template = template.decode('utf-8') - template = string.Template(template) - return template.substitute(data) + return ctrl def main(argv): + headers = { + 'controls': 'control_ids.h', + 'properties': 'property_ids.h', + } + # Parse command line arguments parser = argparse.ArgumentParser() parser.add_argument('--mode', '-m', type=str, required=True, @@ -97,26 +68,41 @@ def main(argv): help='Input file name.') args = parser.parse_args(argv[1:]) - if args.mode not in ['controls', 'properties']: + if not headers.get(args.mode): print(f'Invalid mode option "{args.mode}"', file=sys.stderr) return -1 - controls = {} + controls = [] + vendors = [] + for input in args.input: - data = open(input, 'rb').read() - vendor = yaml.safe_load(data)['vendor'] - controls[vendor] = yaml.safe_load(data)['controls'] + data = yaml.safe_load(open(input, 'rb').read()) + + vendor = data['vendor'] + if vendor != 'libcamera': + vendors.append(vendor) + + for ctrl in data['controls']: + ctrl = Control(*ctrl.popitem(), vendor, args.mode) + controls.append(extend_control(ctrl, args.mode)) - data = generate_py(controls, args.mode) + data = { + 'mode': args.mode, + 'header': headers[args.mode], + 'vendors': vendors, + 'controls': controls, + } - data = fill_template(args.template, data) + env = jinja2.Environment() + template = env.from_string(open(args.template, 'r', encoding='utf-8').read()) + string = template.render(data) if args.output: - output = open(args.output, 'wb') - output.write(data.encode('utf-8')) + output = open(args.output, 'w', encoding='utf-8') + output.write(string) output.close() else: - sys.stdout.write(data) + sys.stdout.write(string) return 0 diff --git a/src/py/libcamera/meson.build b/src/py/libcamera/meson.build index 4807ca7d..596a203c 100644 --- a/src/py/libcamera/meson.build +++ b/src/py/libcamera/meson.build @@ -26,37 +26,24 @@ pycamera_sources = files([ 'py_transform.cpp', ]) -# Generate controls +# Generate controls and properties -gen_py_controls_input_files = [] gen_py_controls_template = files('py_controls_generated.cpp.in') - gen_py_controls = files('gen-py-controls.py') -foreach file : controls_files - gen_py_controls_input_files += files('../../libcamera/' + file) -endforeach - pycamera_sources += custom_target('py_gen_controls', - input : gen_py_controls_input_files, + input : controls_files, output : ['py_controls_generated.cpp'], command : [gen_py_controls, '--mode', 'controls', '-o', '@OUTPUT@', - '-t', gen_py_controls_template, '@INPUT@']) - -# Generate properties - -gen_py_property_enums_input_files = [] -gen_py_properties_template = files('py_properties_generated.cpp.in') - -foreach file : properties_files - gen_py_property_enums_input_files += files('../../libcamera/' + file) -endforeach + '-t', gen_py_controls_template, '@INPUT@'], + env : py_build_env) pycamera_sources += custom_target('py_gen_properties', - input : gen_py_property_enums_input_files, + input : properties_files, output : ['py_properties_generated.cpp'], command : [gen_py_controls, '--mode', 'properties', '-o', '@OUTPUT@', - '-t', gen_py_properties_template, '@INPUT@']) + '-t', gen_py_controls_template, '@INPUT@'], + env : py_build_env) # Generate formats diff --git a/src/py/libcamera/py_camera_manager.h b/src/py/libcamera/py_camera_manager.h index 3574db23..af69b915 100644 --- a/src/py/libcamera/py_camera_manager.h +++ b/src/py/libcamera/py_camera_manager.h @@ -20,7 +20,7 @@ public: ~PyCameraManager(); pybind11::list cameras(); - std::shared_ptr<Camera> get(const std::string &name) { return cameraManager_->get(name); } + std::shared_ptr<Camera> get(std::string_view name) { return cameraManager_->get(name); } static const std::string &version() { return CameraManager::version(); } diff --git a/src/py/libcamera/py_controls_generated.cpp.in b/src/py/libcamera/py_controls_generated.cpp.in index 26d5a104..22a132d1 100644 --- a/src/py/libcamera/py_controls_generated.cpp.in +++ b/src/py/libcamera/py_controls_generated.cpp.in @@ -2,12 +2,12 @@ /* * Copyright (C) 2022, Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> * - * Python bindings - Auto-generated controls + * Python bindings - Auto-generated {{mode}} * * This file is auto-generated. Do not edit. */ -#include <libcamera/control_ids.h> +#include <libcamera/{{header}}> #include <pybind11/pybind11.h> @@ -15,16 +15,33 @@ namespace py = pybind11; -class PyControls +class Py{{mode|capitalize}} { }; -${vendors_class_def} - -void init_py_controls_generated(py::module& m) +{% for vendor in vendors -%} +class Py{{vendor|capitalize}}{{mode|capitalize}} { - auto controls = py::class_<PyControls>(m, "controls"); -${vendors_defs} +}; -${controls} +{% endfor -%} + +void init_py_{{mode}}_generated(py::module& m) +{ + auto {{mode}} = py::class_<Py{{mode|capitalize}}>(m, "{{mode}}"); +{%- for vendor in vendors %} + auto {{vendor}} = py::class_<Py{{vendor|capitalize}}{{mode|capitalize}}>({{mode}}, "{{vendor}}"); +{%- endfor %} + +{% for ctrl in controls %} + {{ctrl.klass}}.def_readonly_static("{{ctrl.name}}", static_cast<const libcamera::ControlId *>(&libcamera::{{mode}}::{{ctrl.namespace}}{{ctrl.name}})); +{%- if ctrl.is_enum %} + + py::enum_<libcamera::{{mode}}::{{ctrl.namespace}}{{ctrl.name}}Enum>({{ctrl.klass}}, "{{ctrl.name}}Enum") +{%- for enum in ctrl.enum_values %} + .value("{{enum.py_name}}", libcamera::{{mode}}::{{ctrl.namespace}}{{enum.name}}) +{%- endfor %} + ; +{%- endif %} +{% endfor -%} } diff --git a/src/py/libcamera/py_enums.cpp b/src/py/libcamera/py_enums.cpp index ca6aeb86..9e75ec1a 100644 --- a/src/py/libcamera/py_enums.cpp +++ b/src/py/libcamera/py_enums.cpp @@ -32,7 +32,8 @@ void init_py_enums(py::module &m) .value("Float", ControlType::ControlTypeFloat) .value("String", ControlType::ControlTypeString) .value("Rectangle", ControlType::ControlTypeRectangle) - .value("Size", ControlType::ControlTypeSize); + .value("Size", ControlType::ControlTypeSize) + .value("Point", ControlType::ControlTypePoint); py::enum_<Orientation>(m, "Orientation") .value("Rotate0", Orientation::Rotate0) diff --git a/src/py/libcamera/py_helpers.cpp b/src/py/libcamera/py_helpers.cpp index 79891ab6..1ad1d4c1 100644 --- a/src/py/libcamera/py_helpers.cpp +++ b/src/py/libcamera/py_helpers.cpp @@ -34,6 +34,8 @@ static py::object valueOrTuple(const ControlValue &cv) py::object controlValueToPy(const ControlValue &cv) { switch (cv.type()) { + case ControlTypeNone: + return py::none(); case ControlTypeBool: return valueOrTuple<bool>(cv); case ControlTypeByte: @@ -46,14 +48,14 @@ py::object controlValueToPy(const ControlValue &cv) return valueOrTuple<float>(cv); case ControlTypeString: return py::cast(cv.get<std::string>()); - case ControlTypeRectangle: - return valueOrTuple<Rectangle>(cv); case ControlTypeSize: { const Size *v = reinterpret_cast<const Size *>(cv.data().data()); return py::cast(v); } - case ControlTypeNone: - return py::none(); + case ControlTypeRectangle: + return valueOrTuple<Rectangle>(cv); + case ControlTypePoint: + return valueOrTuple<Point>(cv); default: throw std::runtime_error("Unsupported ControlValue type"); } @@ -73,6 +75,8 @@ static ControlValue controlValueMaybeArray(const py::object &ob) ControlValue pyToControlValue(const py::object &ob, ControlType type) { switch (type) { + case ControlTypeNone: + return ControlValue(); case ControlTypeBool: return ControlValue(ob.cast<bool>()); case ControlTypeByte: @@ -89,8 +93,8 @@ ControlValue pyToControlValue(const py::object &ob, ControlType type) return controlValueMaybeArray<Rectangle>(ob); case ControlTypeSize: return ControlValue(ob.cast<Size>()); - case ControlTypeNone: - return ControlValue(); + case ControlTypePoint: + return controlValueMaybeArray<Point>(ob); default: throw std::runtime_error("Control type not implemented"); } diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp index ab33f38a..441a70ab 100644 --- a/src/py/libcamera/py_main.cpp +++ b/src/py/libcamera/py_main.cpp @@ -7,6 +7,7 @@ #include "py_main.h" +#include <limits> #include <memory> #include <stdexcept> #include <string> @@ -399,12 +400,26 @@ PYBIND11_MODULE(_libcamera, m) pyControlId .def_property_readonly("id", &ControlId::id) .def_property_readonly("name", &ControlId::name) + .def_property_readonly("vendor", &ControlId::vendor) .def_property_readonly("type", &ControlId::type) + .def_property_readonly("isArray", &ControlId::isArray) + .def_property_readonly("size", &ControlId::size) .def("__str__", [](const ControlId &self) { return self.name(); }) .def("__repr__", [](const ControlId &self) { - return py::str("libcamera.ControlId({}, {}, {})") - .format(self.id(), self.name(), self.type()); - }); + std::string sizeStr = ""; + if (self.isArray()) { + sizeStr = "["; + size_t size = self.size(); + if (size == std::numeric_limits<size_t>::max()) + sizeStr += "n"; + else + sizeStr += std::to_string(size); + sizeStr += "]"; + } + return py::str("libcamera.ControlId({}, {}.{}{}, {})") + .format(self.id(), self.vendor(), self.name(), sizeStr, self.type()); + }) + .def("enumerators", &ControlId::enumerators); pyControlInfo .def_property_readonly("min", [](const ControlInfo &self) { diff --git a/src/py/libcamera/py_properties_generated.cpp.in b/src/py/libcamera/py_properties_generated.cpp.in deleted file mode 100644 index d28f1ab8..00000000 --- a/src/py/libcamera/py_properties_generated.cpp.in +++ /dev/null @@ -1,30 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2022, Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> - * - * Python bindings - Auto-generated properties - * - * This file is auto-generated. Do not edit. - */ - -#include <libcamera/property_ids.h> - -#include <pybind11/pybind11.h> - -#include "py_main.h" - -namespace py = pybind11; - -class PyProperties -{ -}; - -${vendors_class_def} - -void init_py_properties_generated(py::module& m) -{ - auto controls = py::class_<PyProperties>(m, "properties"); -${vendors_defs} - -${controls} -} |