[libcamera-devel] [PATCH v2 16/19] py: Re-structure the controls API
Laurent Pinchart
laurent.pinchart at ideasonboard.com
Fri May 27 12:52:34 CEST 2022
Hi Tomi,
Thank you for the patch.
On Tue, May 24, 2022 at 02:46:07PM +0300, Tomi Valkeinen wrote:
> Add ControlInfo class and change the controls related methods to
> resemble the C++ API (e.g. no more string based control methods).
>
> We don't implement ControlList or ControlInfoMap but just expose the
> same data via standard Python dict.
>
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen at ideasonboard.com>
> ---
> src/py/cam/cam.py | 8 +--
> src/py/cam/cam_qt.py | 9 ++--
> src/py/libcamera/py_main.cpp | 100 ++++++++++++++++++-----------------
> 3 files changed, 61 insertions(+), 56 deletions(-)
>
> diff --git a/src/py/cam/cam.py b/src/py/cam/cam.py
> index f6e8232c..f464bd01 100755
> --- a/src/py/cam/cam.py
> +++ b/src/py/cam/cam.py
> @@ -48,16 +48,16 @@ class CameraContext:
>
> print('Properties for', self.id)
>
> - for name, prop in camera.properties.items():
> - print('\t{}: {}'.format(name, prop))
> + for cid, val in camera.properties.items():
> + print('\t{}: {}'.format(cid, val))
>
> def do_cmd_list_controls(self):
> camera = self.camera
>
> print('Controls for', self.id)
>
> - for name, prop in camera.controls.items():
> - print('\t{}: {}'.format(name, prop))
> + for cid, info in camera.controls.items():
> + print('\t{}: {}'.format(cid, info))
>
> def do_cmd_info(self):
> camera = self.camera
> diff --git a/src/py/cam/cam_qt.py b/src/py/cam/cam_qt.py
> index d638e9cc..739e9749 100644
> --- a/src/py/cam/cam_qt.py
> +++ b/src/py/cam/cam_qt.py
> @@ -267,9 +267,9 @@ class MainWindow(QtWidgets.QWidget):
>
> camera = ctx.camera
>
> - for k, v in camera.properties.items():
> + for cid, cv in camera.properties.items():
> lab = QtWidgets.QLabel()
> - lab.setText(k + ' = ' + str(v))
> + lab.setText("{} = {}".format(cid, cv))
s/"/'/g
> groupLayout.addWidget(lab)
>
> group = QtWidgets.QGroupBox('Controls')
> @@ -277,9 +277,10 @@ class MainWindow(QtWidgets.QWidget):
> group.setLayout(groupLayout)
> controlsLayout.addWidget(group)
>
> - for k, (min, max, default) in camera.controls.items():
> + for cid, cinfo in camera.controls.items():
> lab = QtWidgets.QLabel()
> - lab.setText('{} = {}/{}/{}'.format(k, min, max, default))
> + lab.setText('{} = {}/{}/{}'
> + .format(cid, cinfo.min, cinfo.max, cinfo.default))
> groupLayout.addWidget(lab)
>
> controlsLayout.addStretch()
> diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp
> index 33ecc1cd..80f26c12 100644
> --- a/src/py/libcamera/py_main.cpp
> +++ b/src/py/libcamera/py_main.cpp
> @@ -159,6 +159,7 @@ PYBIND11_MODULE(_libcamera, m)
> auto pyFrameBuffer = py::class_<FrameBuffer>(m, "FrameBuffer");
> auto pyStream = py::class_<Stream>(m, "Stream");
> auto pyControlId = py::class_<ControlId>(m, "ControlId");
> + auto pyControlInfo = py::class_<ControlInfo>(m, "ControlInfo");
> auto pyRequest = py::class_<Request>(m, "Request");
> auto pyRequestStatus = py::enum_<Request::Status>(pyRequest, "Status");
> auto pyRequestReuse = py::enum_<Request::ReuseFlag>(pyRequest, "Reuse");
> @@ -260,28 +261,17 @@ PYBIND11_MODULE(_libcamera, m)
> .def_property_readonly("id", &Camera::id)
> .def("acquire", &Camera::acquire)
> .def("release", &Camera::release)
> - .def("start", [](Camera &self, py::dict controls) {
> + .def("start", [](Camera &self,
> + const std::unordered_map<const ControlId *, py::object> &controls) {
> /* \todo What happens if someone calls start() multiple times? */
>
> self.requestCompleted.connect(handleRequestCompleted);
>
> - const ControlInfoMap &controlMap = self.controls();
> - ControlList controlList(controlMap);
> - for (const auto& [hkey, hval]: controls) {
> - auto key = hkey.cast<std::string>();
> + ControlList controlList(self.controls());
>
> - auto it = std::find_if(controlMap.begin(), controlMap.end(),
> - [&key](const auto &kvp) {
> - return kvp.first->name() == key;
> - });
> -
> - if (it == controlMap.end())
> - throw std::runtime_error("Control " + key + " not found");
> -
> - const auto &id = it->first;
> - auto obj = py::cast<py::object>(hval);
> -
> - controlList.set(id->id(), pyToControlValue(obj, id->type()));
> + for (const auto& [id, obj]: controls) {
> + auto val = pyToControlValue(obj, id->type());
> + controlList.set(id->id(), val);
> }
>
> int ret = self.start(&controlList);
> @@ -291,7 +281,7 @@ PYBIND11_MODULE(_libcamera, m)
> }
>
> return 0;
> - }, py::arg("controls") = py::dict())
> + }, py::arg("controls") = std::unordered_map<const ControlId *, py::object>())
>
> .def("stop", [](Camera &self) {
> int ret = self.stop();
> @@ -341,40 +331,26 @@ PYBIND11_MODULE(_libcamera, m)
> return set;
> })
>
> - .def("find_control", [](Camera &self, const std::string &name) {
> - const auto &controls = self.controls();
> -
> - auto it = std::find_if(controls.begin(), controls.end(),
> - [&name](const auto &kvp) {
> - return kvp.first->name() == name;
> - });
> -
> - if (it == controls.end())
> - throw std::runtime_error("Control '" + name + "' not found");
> -
> - return it->first;
> - }, py::return_value_policy::reference_internal)
> -
> .def_property_readonly("controls", [](Camera &self) {
> - py::dict ret;
> + /* Convert ControlInfoMap to std container */
>
> - for (const auto &[id, ci] : self.controls()) {
> - ret[id->name().c_str()] = std::make_tuple<py::object>(controlValueToPy(ci.min()),
> - controlValueToPy(ci.max()),
> - controlValueToPy(ci.def()));
> - }
> + std::unordered_map<const ControlId *, ControlInfo> ret;
> +
> + for (const auto &[k, cv] : self.controls())
> + ret[k] = cv;
>
> return ret;
> })
>
> .def_property_readonly("properties", [](Camera &self) {
> - py::dict ret;
> + /* Convert ControlList to std container */
>
> - for (const auto &[key, cv] : self.properties()) {
> - const ControlId *id = properties::properties.at(key);
> - py::object ob = controlValueToPy(cv);
> + std::unordered_map<const ControlId *, py::object> ret;
>
> - ret[id->name().c_str()] = ob;
> + for (const auto &[k, cv] : self.properties()) {
> + const ControlId *id = properties::properties.at(k);
> + py::object ob = controlValueToPy(cv);
> + ret[id] = ob;
> }
>
> return ret;
> @@ -464,7 +440,34 @@ PYBIND11_MODULE(_libcamera, m)
> pyControlId
> .def_property_readonly("id", &ControlId::id)
> .def_property_readonly("name", &ControlId::name)
> - .def_property_readonly("type", &ControlId::type);
> + .def_property_readonly("type", &ControlId::type)
> + .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());
Should this be py::str("libcamera.controls.{}").format(self.name()) ?
That may not work for draft controls though.
> + });
> +
> + pyControlInfo
> + .def_property_readonly("min", [](const ControlInfo& self) {
s/& self/ &self/
Same below.
Reviewed-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> + return controlValueToPy(self.min());
> + })
> + .def_property_readonly("max", [](const ControlInfo& self) {
> + return controlValueToPy(self.max());
> + })
> + .def_property_readonly("default", [](const ControlInfo& self) {
> + return controlValueToPy(self.def());
> + })
> + .def_property_readonly("values", [](const ControlInfo& self) {
> + py::list l;
> + for (const auto &v : self.values())
> + l.append(controlValueToPy(v));
> + return l;
> + })
> + .def("__str__", &ControlInfo::toString)
> + .def("__repr__", [](const ControlInfo& self) {
> + return py::str("libcamera.ControlInfo({})")
> + .format(self.toString());
> + });
>
> pyRequest
> /* \todo Fence is not supported, so we cannot expose addBuffer() directly */
> @@ -475,17 +478,18 @@ PYBIND11_MODULE(_libcamera, m)
> .def_property_readonly("buffers", &Request::buffers)
> .def_property_readonly("cookie", &Request::cookie)
> .def_property_readonly("has_pending_buffers", &Request::hasPendingBuffers)
> - .def("set_control", [](Request &self, ControlId &id, py::object value) {
> + .def("set_control", [](Request &self, const ControlId &id, py::object value) {
> self.controls().set(id.id(), pyToControlValue(value, id.type()));
> })
> .def_property_readonly("metadata", [](Request &self) {
> - py::dict ret;
> + /* Convert ControlList to std container */
> +
> + std::unordered_map<const ControlId *, py::object> ret;
>
> for (const auto &[key, cv] : self.metadata()) {
> const ControlId *id = controls::controls.at(key);
> py::object ob = controlValueToPy(cv);
> -
> - ret[id->name().c_str()] = ob;
> + ret[id] = ob;
> }
>
> return ret;
--
Regards,
Laurent Pinchart
More information about the libcamera-devel
mailing list