From patchwork Fri May 27 14:44:30 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomi Valkeinen X-Patchwork-Id: 16084 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id B30CDC3256 for ; Fri, 27 May 2022 14:45:25 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 4EBA165697; Fri, 27 May 2022 16:45:25 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1653662725; bh=KjPMCtIKmKLBPOuGyREMU890hdHrQ1ucqHg7xWG38eo=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=m4x8yLKb0TNSfQwx8e0qIOWgX+4BaktUO7LvB+QPfhy5a4Rrq5Q6xsruPlq4w/w8B LRjwXU/8HToef2L/OkrMau8lf+R1J5OppQYBH6U8OFEfq2FxAzvGHvQIKhWJZwrVJm 5xAYrMG1RYZ1UzEFeeFyyiq3tM28Ni3fcqwiBLkmBdNP/dfYcOlP2bWpxT5KaVA6F5 JqGzTUrlViTiWIzJ6+kK7vXnipurjfANCulN2nEsMjKIm+vfc1SNC1YIKW41Jn3Nx7 XXyYqucMkwpkPPRHBky/FMbgKjkKebLUXHtkiF+GQPcFt1QrVKoTFr16hICgvQ2PP+ kFhZrfdxP2uhA== Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 410E865644 for ; Fri, 27 May 2022 16:45:11 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="tE2Xh/B8"; dkim-atps=neutral Received: from deskari.lan (91-156-85-209.elisa-laajakaista.fi [91.156.85.209]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id AA0B81440; Fri, 27 May 2022 16:45:10 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1653662711; bh=KjPMCtIKmKLBPOuGyREMU890hdHrQ1ucqHg7xWG38eo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=tE2Xh/B88ZhbKPJypIhMcEM2UCgdg4RAeHo243iQYH7gITNuehi9P3+ilqdHo3gU/ C0Tf6Dery1F2T/EoYMA1z6oB2kVKZEH6STMb2rBtSMUP/9L2h3QoP+Fu6mXJ8dqKcn ZqK2NU8XwjFCrrmq+YoMiyXR8beCXKlGrkWXyEBo= To: libcamera-devel@lists.libcamera.org, David Plowman , Kieran Bingham , Laurent Pinchart , Jacopo Mondi Date: Fri, 27 May 2022 17:44:30 +0300 Message-Id: <20220527144447.94891-14-tomi.valkeinen@ideasonboard.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220527144447.94891-1-tomi.valkeinen@ideasonboard.com> References: <20220527144447.94891-1-tomi.valkeinen@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 13/30] py: Re-structure the controls API X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Tomi Valkeinen via libcamera-devel From: Tomi Valkeinen Reply-To: Tomi Valkeinen Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" 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 Reviewed-by: Laurent Pinchart --- src/py/cam/cam.py | 8 +-- src/py/cam/cam_qt.py | 9 +-- src/py/libcamera/py_main.cpp | 104 +++++++++++++++++------------------ 3 files changed, 61 insertions(+), 60 deletions(-) diff --git a/src/py/cam/cam.py b/src/py/cam/cam.py index c8ffb084..677dd680 100755 --- a/src/py/cam/cam.py +++ b/src/py/cam/cam.py @@ -46,14 +46,14 @@ class CameraContext: def do_cmd_list_props(self): print('Properties for', self.id) - for name, prop in self.camera.properties.items(): - print('\t{}: {}'.format(name, prop)) + for cid, val in self.camera.properties.items(): + print('\t{}: {}'.format(cid, val)) def do_cmd_list_controls(self): print('Controls for', self.id) - for name, prop in self.camera.controls.items(): - print('\t{}: {}'.format(name, prop)) + for cid, info in self.camera.controls.items(): + print('\t{}: {}'.format(cid, info)) def do_cmd_info(self): print('Stream info for', self.id) diff --git a/src/py/cam/cam_qt.py b/src/py/cam/cam_qt.py index d638e9cc..e2395c4b 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)) 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..4fb66866 100644 --- a/src/py/libcamera/py_main.cpp +++ b/src/py/libcamera/py_main.cpp @@ -5,10 +5,6 @@ * Python bindings */ -/* - * \todo Add bindings for the ControlInfo class - */ - #include #include #include @@ -159,6 +155,7 @@ PYBIND11_MODULE(_libcamera, m) auto pyFrameBuffer = py::class_(m, "FrameBuffer"); auto pyStream = py::class_(m, "Stream"); auto pyControlId = py::class_(m, "ControlId"); + auto pyControlInfo = py::class_(m, "ControlInfo"); auto pyRequest = py::class_(m, "Request"); auto pyRequestStatus = py::enum_(pyRequest, "Status"); auto pyRequestReuse = py::enum_(pyRequest, "Reuse"); @@ -260,28 +257,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 &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(); - - auto it = std::find_if(controlMap.begin(), controlMap.end(), - [&key](const auto &kvp) { - return kvp.first->name() == key; - }); + ControlList controlList(self.controls()); - if (it == controlMap.end()) - throw std::runtime_error("Control " + key + " not found"); - - const auto &id = it->first; - auto obj = py::cast(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 +277,7 @@ PYBIND11_MODULE(_libcamera, m) } return 0; - }, py::arg("controls") = py::dict()) + }, py::arg("controls") = std::unordered_map()) .def("stop", [](Camera &self) { int ret = self.stop(); @@ -341,40 +327,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(controlValueToPy(ci.min()), - controlValueToPy(ci.max()), - controlValueToPy(ci.def())); - } + std::unordered_map 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 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 +436,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()); + }); + + pyControlInfo + .def_property_readonly("min", [](const ControlInfo &self) { + 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 +474,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 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;