From 3c82ae3821798794873be939bb46c507d4ebde7c Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Fri, 27 May 2022 17:44:29 +0300 Subject: py: Re-implement controls geneneration The Python bindings controls generation was not very good. It only covered the enums and they were in the main namespace. This adds the controls somewhat similarly to the C++ side. We will have e.g.: libcamera.controls.Brightness libcamera.controls.AeMeteringModeEnum.CentreWeighted Signed-off-by: Tomi Valkeinen Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart --- src/py/libcamera/gen-py-control-enums.py | 98 -------------------- src/py/libcamera/gen-py-controls.py | 102 +++++++++++++++++++++ src/py/libcamera/meson.build | 16 ++-- src/py/libcamera/py_control_enums_generated.cpp.in | 19 ---- src/py/libcamera/py_controls_generated.cpp.in | 30 ++++++ src/py/libcamera/py_main.cpp | 4 +- 6 files changed, 144 insertions(+), 125 deletions(-) delete mode 100755 src/py/libcamera/gen-py-control-enums.py create mode 100755 src/py/libcamera/gen-py-controls.py delete mode 100644 src/py/libcamera/py_control_enums_generated.cpp.in create mode 100644 src/py/libcamera/py_controls_generated.cpp.in (limited to 'src/py') diff --git a/src/py/libcamera/gen-py-control-enums.py b/src/py/libcamera/gen-py-control-enums.py deleted file mode 100755 index 6b2b5362..00000000 --- a/src/py/libcamera/gen-py-control-enums.py +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env python3 -# SPDX-License-Identifier: GPL-2.0-or-later -# -# Generate Python bindings enums for controls from YAML - -import argparse -import string -import sys -import yaml - - -def find_common_prefix(strings): - prefix = strings[0] - - for string in strings[1:]: - while string[:len(prefix)] != prefix and prefix: - prefix = prefix[:len(prefix) - 1] - if not prefix: - break - - return prefix - - -def generate_py(controls): - out = '' - - for ctrl in controls: - name, ctrl = ctrl.popitem() - - enum = ctrl.get('enum') - if not enum: - continue - - if ctrl.get('draft'): - ns = 'libcamera::controls::draft::' - else: - ns = 'libcamera::controls::' - - cpp_enum = name + 'Enum' - - out += '\tpy::enum_<{}{}>(m, \"{}\")\n'.format(ns, cpp_enum, name) - - if name == 'LensShadingMapMode': - prefix = 'LensShadingMapMode' - elif name == 'SceneFlicker': - # If we strip the prefix, we would get '50Hz', which is illegal name - prefix = '' - else: - 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' - - return {'enums': out} - - -def fill_template(template, data): - template = open(template, 'rb').read() - template = template.decode('utf-8') - template = string.Template(template) - return template.substitute(data) - - -def main(argv): - # Parse command line arguments - parser = argparse.ArgumentParser() - parser.add_argument('-o', dest='output', metavar='file', type=str, - help='Output file name. Defaults to standard output if not specified.') - parser.add_argument('input', type=str, - help='Input file name.') - parser.add_argument('template', type=str, - help='Template file name.') - args = parser.parse_args(argv[1:]) - - data = open(args.input, 'rb').read() - controls = yaml.safe_load(data)['controls'] - - data = generate_py(controls) - - data = fill_template(args.template, data) - - if args.output: - output = open(args.output, 'wb') - output.write(data.encode('utf-8')) - output.close() - else: - sys.stdout.write(data) - - return 0 - - -if __name__ == '__main__': - sys.exit(main(sys.argv)) diff --git a/src/py/libcamera/gen-py-controls.py b/src/py/libcamera/gen-py-controls.py new file mode 100755 index 00000000..4c072e60 --- /dev/null +++ b/src/py/libcamera/gen-py-controls.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Generate Python bindings controls from YAML + +import argparse +import string +import sys +import yaml + + +def find_common_prefix(strings): + prefix = strings[0] + + for string in strings[1:]: + while string[:len(prefix)] != prefix and prefix: + prefix = prefix[:len(prefix) - 1] + if not prefix: + break + + return prefix + + +def generate_py(controls): + out = '' + + for ctrl in controls: + name, ctrl = ctrl.popitem() + + if ctrl.get('draft'): + ns = 'libcamera::controls::draft::' + container = 'draft' + else: + ns = 'libcamera::controls::' + container = 'controls' + + out += f'\t{container}.def_readonly_static("{name}", static_cast(&{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 name == 'LensShadingMapMode': + prefix = 'LensShadingMapMode' + elif name == 'SceneFlicker': + # If we strip the prefix, we would get '50Hz', which is illegal name + prefix = '' + else: + 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} + + +def fill_template(template, data): + template = open(template, 'rb').read() + template = template.decode('utf-8') + template = string.Template(template) + return template.substitute(data) + + +def main(argv): + # Parse command line arguments + parser = argparse.ArgumentParser() + parser.add_argument('-o', dest='output', metavar='file', type=str, + help='Output file name. Defaults to standard output if not specified.') + parser.add_argument('input', type=str, + help='Input file name.') + parser.add_argument('template', type=str, + help='Template file name.') + args = parser.parse_args(argv[1:]) + + data = open(args.input, 'rb').read() + controls = yaml.safe_load(data)['controls'] + + data = generate_py(controls) + + data = fill_template(args.template, data) + + if args.output: + output = open(args.output, 'wb') + output.write(data.encode('utf-8')) + output.close() + else: + sys.stdout.write(data) + + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/src/py/libcamera/meson.build b/src/py/libcamera/meson.build index b705ac1f..e8010846 100644 --- a/src/py/libcamera/meson.build +++ b/src/py/libcamera/meson.build @@ -18,17 +18,21 @@ pycamera_sources = files([ 'py_main.cpp', ]) -gen_py_control_enums_input_files = files([ +# Generate controls + +gen_py_controls_input_files = files([ '../../libcamera/control_ids.yaml', - 'py_control_enums_generated.cpp.in', + 'py_controls_generated.cpp.in', ]) -gen_py_control_enums = files('gen-py-control-enums.py') +gen_py_controls = files('gen-py-controls.py') pycamera_sources += custom_target('py_gen_controls', - input : gen_py_control_enums_input_files, - output : ['py_control_enums_generated.cpp'], - command : [gen_py_control_enums, '-o', '@OUTPUT@', '@INPUT@']) + input : gen_py_controls_input_files, + output : ['py_controls_generated.cpp'], + command : [gen_py_controls, '-o', '@OUTPUT@', '@INPUT@']) + +# Generate formats gen_py_formats_input_files = files([ '../../libcamera/formats.yaml', diff --git a/src/py/libcamera/py_control_enums_generated.cpp.in b/src/py/libcamera/py_control_enums_generated.cpp.in deleted file mode 100644 index ed81fbe7..00000000 --- a/src/py/libcamera/py_control_enums_generated.cpp.in +++ /dev/null @@ -1,19 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2022, Tomi Valkeinen - * - * Python bindings - Auto-generated control enums - * - * This file is auto-generated. Do not edit. - */ - -#include - -#include - -namespace py = pybind11; - -void init_py_control_enums_generated(py::module& m) -{ -${enums} -} diff --git a/src/py/libcamera/py_controls_generated.cpp.in b/src/py/libcamera/py_controls_generated.cpp.in new file mode 100644 index 00000000..cb8442ba --- /dev/null +++ b/src/py/libcamera/py_controls_generated.cpp.in @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2022, Tomi Valkeinen + * + * Python bindings - Auto-generated controls + * + * This file is auto-generated. Do not edit. + */ + +#include + +#include + +namespace py = pybind11; + +class PyControls +{ +}; + +class PyDraftControls +{ +}; + +void init_py_controls_generated(py::module& m) +{ + auto controls = py::class_(m, "controls"); + auto draft = py::class_(controls, "draft"); + +${controls} +} diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp index 5d389942..33ecc1cd 100644 --- a/src/py/libcamera/py_main.cpp +++ b/src/py/libcamera/py_main.cpp @@ -131,14 +131,14 @@ static void handleRequestCompleted(Request *req) } void init_py_enums(py::module &m); -void init_py_control_enums_generated(py::module &m); +void init_py_controls_generated(py::module &m); void init_py_formats_generated(py::module &m); void init_py_geometry(py::module &m); PYBIND11_MODULE(_libcamera, m) { init_py_enums(m); - init_py_control_enums_generated(m); + init_py_controls_generated(m); init_py_geometry(m); /* Forward declarations */ -- cgit v1.2.1