Patch Detail
Show a patch.
GET /api/1.1/patches/17270/?format=api
{ "id": 17270, "url": "https://patchwork.libcamera.org/api/1.1/patches/17270/?format=api", "web_url": "https://patchwork.libcamera.org/patch/17270/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api", "name": "libcamera", "link_name": "libcamera", "list_id": "libcamera_core", "list_email": "libcamera-devel@lists.libcamera.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20220831054938.21617-7-utkarsh02t@gmail.com>", "date": "2022-08-31T05:49:37", "name": "[libcamera-devel,v9,6/7] qcam: CamSelectDialog: Add capture script button", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "202c057261c6246166f04e1dd2f5ebae952584df", "submitter": { "id": 114, "url": "https://patchwork.libcamera.org/api/1.1/people/114/?format=api", "name": "Utkarsh Tiwari", "email": "utkarsh02t@gmail.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/17270/mbox/", "series": [ { "id": 3457, "url": "https://patchwork.libcamera.org/api/1.1/series/3457/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=3457", "date": "2022-08-31T05:49:31", "name": "Introduce capture scripts to qcam", "version": 9, "mbox": "https://patchwork.libcamera.org/series/3457/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/17270/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/17270/checks/", "tags": {}, "headers": { "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>", "X-Original-To": "parsemail@patchwork.libcamera.org", "Delivered-To": "parsemail@patchwork.libcamera.org", "Received": [ "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id D9E2CC3272\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 31 Aug 2022 05:50:10 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 9284061FC0;\n\tWed, 31 Aug 2022 07:50:10 +0200 (CEST)", "from mail-pl1-x62a.google.com (mail-pl1-x62a.google.com\n\t[IPv6:2607:f8b0:4864:20::62a])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 48CE561FBC\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 31 Aug 2022 07:50:09 +0200 (CEST)", "by mail-pl1-x62a.google.com with SMTP id v5so6975835plo.9\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 30 Aug 2022 22:50:09 -0700 (PDT)", "from devut-HP-Laptop-14q-cs0xxx.. ([103.36.82.122])\n\tby smtp.gmail.com with ESMTPSA id\n\tq19-20020aa79833000000b00537eacc8fa6sm8307333pfl.40.2022.08.30.22.50.05\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tTue, 30 Aug 2022 22:50:07 -0700 (PDT)" ], "DKIM-Signature": [ "v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1661925010;\n\tbh=A9VDPAbsuWT4PSFI3gmdY8a8KPlIC/WDS7qw2qTMhWw=;\n\th=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:\n\tFrom;\n\tb=deeBSB8E3nEAoNWLE0Oy8DU6olr7uLiuyheOMYV0jcgDP75A6SeVEQeIaaqQWuxLt\n\tDSa83ApVlKtPsZYKrROy4WtdHeHk+tCbOBMEbY8esse6irUm3HWIL+PaqM0dgXus8V\n\ttmMmbmP2SfguEK/kpkvZE7kWAgEiX741WL9Uh/sqPvMUEZYpFlOCKWDQLIvJaBeagj\n\tZTwBz1uvhTAJD42Nkpk3ezBPG5mRIzDnoJ+YVyZCYhzrUO/0raVylOhRZYa5INWOyR\n\tGg6hzJPhHSkfUayFTdRwtiqsww0iAPsIEdrfqWj1Uei/Op8WbZWCIPoMtRWkM3ZCG0\n\tdzRLFwtG+oucQ==", "v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112;\n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:from:to:cc;\n\tbh=7oZU/s9Vw1tSQFJ3JtJmVKSNaVPLKq0zm5Ljm6xj+b0=;\n\tb=QrQlWXXEeUVW793k2fq9uuQ9DCrMOQXfup1KNfcUv+NSZijKjFo7i8qLLLJPlUi+lB\n\txblbIzGTm1JbraYRO93R4jb3NMkb7zyMzPUatbcJ4wMbX7rGebBnDOw3m0YffqdzSsor\n\tpH4blkAZQzGghe8FabNtcRVA83vrPoapGSswyYDpLqCvvN/DAYh9mfp+lshGcItvCufk\n\tyYDatk5AyDHdQVwzla0Yxno8FA90ZouMZhWfUL9z4mB5jzuj0lkFymiG8cWO8XB9Oden\n\t33fPXhXaZJ1SUEj7f8gsHIA/HMRK+0u6sbCZDpwgHAaQ/5uozFIzYx1M2Q6Ug+CbAy0e\n\tRO5Q==" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (2048-bit key; \n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"QrQlWXXE\"; dkim-atps=neutral", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20210112;\n\th=content-transfer-encoding:mime-version:references:in-reply-to\n\t:message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc;\n\tbh=7oZU/s9Vw1tSQFJ3JtJmVKSNaVPLKq0zm5Ljm6xj+b0=;\n\tb=sOjSvqgR31ck/Ut7YSYdmMGVuEhybw335FwVCKaT6aPPw4mSUoAbU1OOgd4nJJHnAz\n\tSmUq6ELHUqmsRGz0CfpKwdnxUCQC2mEOwO+jvmZJLAvb47iP4Fu6Cc/0eZ+bEAcbkVvI\n\t6MqGsgDS8Z9wUfzC+LwC7/tDlrleju7fN6Znzg5nTFkbq79msAK4Ra228qo9fEknL6IO\n\tTZQCwCrzTFi3y/dsE0WA0Xu6nazOhhfFWXwXfMRMAzWIP4DYvrLT+NCDw4uo9sMqVIC7\n\txF6n+dEaOHkrvc6Y/hL5Jbz78Wie59+aN2CUWYOwRbXBUBN8PCGbueiw2ynAS0SenH/H\n\tX+lw==", "X-Gm-Message-State": "ACgBeo21TWoWvNveWVWf+S9lBPV7WLACgtnFxA/H3eLQx32gshhMOt5T\n\t52U5In5GCXTFcPiHX5VHbcvoOFX756Y=", "X-Google-Smtp-Source": "AA6agR6BRxXCjD9zDkfwqlkQx5qThUjIu0r73f7Xucc2bNvalQL5R5Yr5Yhz5Wxod4qhIUUBXUKCkw==", "X-Received": "by 2002:a17:902:a60f:b0:172:e476:fde with SMTP id\n\tu15-20020a170902a60f00b00172e4760fdemr24122143plq.104.1661925007485; \n\tTue, 30 Aug 2022 22:50:07 -0700 (PDT)", "To": "libcamera-devel@lists.libcamera.org", "Date": "Wed, 31 Aug 2022 11:19:37 +0530", "Message-Id": "<20220831054938.21617-7-utkarsh02t@gmail.com>", "X-Mailer": "git-send-email 2.34.1", "In-Reply-To": "<20220831054938.21617-1-utkarsh02t@gmail.com>", "References": "<20220831054938.21617-1-utkarsh02t@gmail.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Subject": "[libcamera-devel] [PATCH v9 6/7] qcam: CamSelectDialog: Add capture\n\tscript button", "X-BeenThere": "libcamera-devel@lists.libcamera.org", "X-Mailman-Version": "2.1.29", "Precedence": "list", "List-Id": "<libcamera-devel.lists.libcamera.org>", "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>", "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>", "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>", "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>", "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>", "From": "Utkarsh Tiwari via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>", "Reply-To": "Utkarsh Tiwari <utkarsh02t@gmail.com>", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "Display a QLabel which is readonly, and displays the currently\nselected capture script. The tooltip of the QLabel displays the file\npath of the script.\n\nImplement a capture script button which is a QToolButton which when\nclicked opens a QFileDialog this allows to select a capture script\n(*.yaml) file.\n\nNext to the capture scipt button, show a QToolButton which stops the\ncapture script.\n\nIf an invalid script has been selected show a QMesssageBox::critical and\ncontinue with the capture's previous state.\n\nIntroduce a queueCount_ to keep track of the requests queued.\n\nWhen stopping the execution of the capture script the queueCount_ is not\nreset and the capture continues as it is (i.e it is not stopped or\nrestarted).\n\nRequests are queued with any controls the script matching the current\nqueueCount_.\n---\nDiffernce from v8:\n 1. Now display a QLabel with the fileName and filePath with button\n on the side.\n 2. infromScriptReset() informScriptRunning() are removed\n 3. Local script makes handling of invalid scripts easy.\n src/qcam/assets/feathericons/feathericons.qrc | 2 +\n src/qcam/cam_select_dialog.cpp | 88 ++++++++++++++++++-\n src/qcam/cam_select_dialog.h | 20 ++++-\n src/qcam/main_window.cpp | 67 +++++++++++++-\n src/qcam/main_window.h | 7 ++\n src/qcam/meson.build | 2 +\n 6 files changed, 181 insertions(+), 5 deletions(-)", "diff": "diff --git a/src/qcam/assets/feathericons/feathericons.qrc b/src/qcam/assets/feathericons/feathericons.qrc\nindex c5302040..0ea0c2d5 100644\n--- a/src/qcam/assets/feathericons/feathericons.qrc\n+++ b/src/qcam/assets/feathericons/feathericons.qrc\n@@ -3,7 +3,9 @@\n <qresource>\n \t<file>aperture.svg</file>\n \t<file>camera-off.svg</file>\n+ <file>delete.svg</file>\n \t<file>play-circle.svg</file>\n+ <file>upload.svg</file>\n \t<file>save.svg</file>\n \t<file>stop-circle.svg</file>\n \t<file>x-circle.svg</file>\ndiff --git a/src/qcam/cam_select_dialog.cpp b/src/qcam/cam_select_dialog.cpp\nindex 6543228a..99405cc1 100644\n--- a/src/qcam/cam_select_dialog.cpp\n+++ b/src/qcam/cam_select_dialog.cpp\n@@ -14,13 +14,18 @@\n \n #include <QComboBox>\n #include <QDialogButtonBox>\n+#include <QFileDialog>\n #include <QFormLayout>\n+#include <QHBoxLayout>\n+#include <QIcon>\n #include <QLabel>\n #include <QString>\n+#include <QToolButton>\n+#include <QWidget>\n \n CameraSelectorDialog::CameraSelectorDialog(libcamera::CameraManager *cameraManager,\n-\t\t\t\t\t QWidget *parent)\n-\t: QDialog(parent), cm_(cameraManager)\n+\t\t\t\t\t std::string scriptPath, QWidget *parent)\n+\t: QDialog(parent), cm_(cameraManager), scriptPath_(scriptPath)\n {\n \t/* Use a QFormLayout for the dialog. */\n \tQFormLayout *layout = new QFormLayout(this);\n@@ -38,6 +43,41 @@ CameraSelectorDialog::CameraSelectorDialog(libcamera::CameraManager *cameraManag\n \tconnect(cameraIdComboBox_, &QComboBox::currentTextChanged,\n \t\tthis, &CameraSelectorDialog::updateCamInfo);\n \n+\t/* Set capture script selection / removal button. */\n+\tQWidget *captureWidget = new QWidget(this);\n+\tQHBoxLayout *captureLayout = new QHBoxLayout(captureWidget);\n+\n+\tscriptFileLine_ = new QLabel;\n+\tscriptFileLine_->setFrameStyle(QFrame::Sunken | QFrame::StyledPanel);\n+\n+\tchooseCaptureScriptButton_ = new QToolButton;\n+\tchooseCaptureScriptButton_->setIcon(QIcon::fromTheme(\"document-open\",\n+\t\t\t\t\t\t\t QIcon(\":upload.svg\")));\n+\tchooseCaptureScriptButton_->setStyleSheet(\"border:none\");\n+\tconnect(chooseCaptureScriptButton_, &QToolButton::clicked,\n+\t\tthis, &CameraSelectorDialog::selectCaptureScript);\n+\n+\tQToolButton *stopCaptureScriptButton = new QToolButton;\n+\tstopCaptureScriptButton->setIcon(QIcon::fromTheme(\"edit-clear\",\n+\t\t\t\t\t\t\t QIcon(\":delete.svg\")));\n+\tstopCaptureScriptButton->setStyleSheet(\"border:node;\");\n+\tconnect(stopCaptureScriptButton, &QToolButton::clicked,\n+\t\tthis, &CameraSelectorDialog::resetCaptureScript);\n+\n+\tcaptureLayout->addWidget(scriptFileLine_);\n+\tcaptureLayout->addWidget(chooseCaptureScriptButton_);\n+\tcaptureLayout->addWidget(stopCaptureScriptButton);\n+\tcaptureLayout->setMargin(0);\n+\n+\t/* Set the file name of the capture script. */\n+\tif (scriptPath_.empty()) {\n+\t\tscriptFileLine_->setText(\"No File Selected\");\n+\t} else {\n+\t\tscriptFileInfo_.setFile(QString::fromStdString(scriptPath_));\n+\t\tscriptFileLine_->setText(scriptFileInfo_.fileName());\n+\t\tscriptFileLine_->setToolTip(scriptFileInfo_.filePath());\n+\t}\n+\n \t/* Setup the QDialogButton Box */\n \tQDialogButtonBox *buttonBox =\n \t\tnew QDialogButtonBox(QDialogButtonBox::Ok |\n@@ -52,6 +92,7 @@ CameraSelectorDialog::CameraSelectorDialog(libcamera::CameraManager *cameraManag\n \tlayout->addRow(\"Camera:\", cameraIdComboBox_);\n \tlayout->addRow(\"Location:\", cameraLocation_);\n \tlayout->addRow(\"Model:\", cameraModel_);\n+\tlayout->addRow(\"Capture Script:\", captureWidget);\n \tlayout->addWidget(buttonBox);\n }\n \n@@ -110,3 +151,46 @@ void CameraSelectorDialog::updateCamInfo(QString cameraId)\n \n \tcameraModel_->setText(QString::fromStdString(model));\n }\n+\n+/* Capture script support. */\n+void CameraSelectorDialog::selectCaptureScript()\n+{\n+\tselectedScriptPath_ = QFileDialog::getOpenFileName(this,\n+\t\t\t\t\t\t\t \"Run Capture Script\", QDir::currentPath(),\n+\t\t\t\t\t\t\t \"Capture Script (*.yaml)\")\n+\t\t\t\t .toStdString();\n+\n+\tif (!selectedScriptPath_.empty()) {\n+\t\tscriptFileInfo_.setFile(QString::fromStdString(selectedScriptPath_));\n+\t\tscriptFileLine_->setText(scriptFileInfo_.fileName());\n+\t\tscriptFileLine_->setToolTip(scriptFileInfo_.filePath());\n+\t} else {\n+\t\tselectedScriptPath_ = scriptPath_;\n+\t}\n+}\n+\n+void CameraSelectorDialog::resetCaptureScript()\n+{\n+\tQ_EMIT stopCaptureScript();\n+\tscriptPath_.clear();\n+\tselectedScriptPath_.clear();\n+\tscriptFileLine_->setText(\"No File Selected\");\n+}\n+\n+void CameraSelectorDialog::accept()\n+{\n+\tscriptPath_ = selectedScriptPath_;\n+\tQDialog::accept();\n+}\n+\n+void CameraSelectorDialog::reject()\n+{\n+\tif (scriptPath_.empty()) {\n+\t\tscriptFileLine_->setText(\"No File Selected\");\n+\t} else {\n+\t\tscriptFileInfo_.setFile(QString::fromStdString(scriptPath_));\n+\t\tscriptFileLine_->setText(scriptFileInfo_.fileName());\n+\t\tscriptFileLine_->setToolTip(scriptFileInfo_.filePath());\n+\t}\n+\tQDialog::reject();\n+}\ndiff --git a/src/qcam/cam_select_dialog.h b/src/qcam/cam_select_dialog.h\nindex c91b7ebe..377faebc 100644\n--- a/src/qcam/cam_select_dialog.h\n+++ b/src/qcam/cam_select_dialog.h\n@@ -15,21 +15,24 @@\n #include <libcamera/property_ids.h>\n \n #include <QDialog>\n+#include <QFileInfo>\n #include <QString>\n \n class QComboBox;\n class QLabel;\n+class QToolButton;\n \n class CameraSelectorDialog : public QDialog\n {\n \tQ_OBJECT\n public:\n \tCameraSelectorDialog(libcamera::CameraManager *cameraManager,\n-\t\t\t QWidget *parent);\n+\t\t\t std::string scriptPath, QWidget *parent);\n \n \t~CameraSelectorDialog();\n \n \tstd::string getCameraId();\n+\tstd::string getCaptureScript() { return scriptPath_; };\n \n \t/* Hotplug / Unplug Support. */\n \tvoid addCamera(QString cameraId);\n@@ -38,11 +41,26 @@ public:\n \t/* Camera Information */\n \tvoid updateCamInfo(QString cameraId);\n \n+\t/* Capture script support. */\n+\tvoid selectCaptureScript();\n+\tvoid resetCaptureScript();\n+\n+\tvoid accept() override;\n+\tvoid reject() override;\n+\n+Q_SIGNALS:\n+\tvoid stopCaptureScript();\n+\n private:\n \tlibcamera::CameraManager *cm_;\n \n+\tstd::string scriptPath_;\n+\tstd::string selectedScriptPath_;\n+\tQFileInfo scriptFileInfo_;\n \t/* UI elements. */\n \tQComboBox *cameraIdComboBox_;\n \tQLabel *cameraLocation_;\n \tQLabel *cameraModel_;\n+\tQLabel *scriptFileLine_;\n+\tQToolButton *chooseCaptureScriptButton_;\n };\ndiff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp\nindex 2a9ca830..af992b94 100644\n--- a/src/qcam/main_window.cpp\n+++ b/src/qcam/main_window.cpp\n@@ -9,6 +9,7 @@\n \n #include <assert.h>\n #include <iomanip>\n+#include <memory>\n #include <string>\n \n #include <libcamera/camera_manager.h>\n@@ -18,6 +19,7 @@\n #include <QFileDialog>\n #include <QImage>\n #include <QImageWriter>\n+#include <QMessageBox>\n #include <QMutexLocker>\n #include <QStandardPaths>\n #include <QStringList>\n@@ -143,7 +145,9 @@ MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options)\n \tcm_->cameraAdded.connect(this, &MainWindow::addCamera);\n \tcm_->cameraRemoved.connect(this, &MainWindow::removeCamera);\n \n-\tcameraSelectorDialog_ = new CameraSelectorDialog(cm_, this);\n+\tcameraSelectorDialog_ = new CameraSelectorDialog(cm_, scriptPath_, this);\n+\tconnect(cameraSelectorDialog_, &CameraSelectorDialog::stopCaptureScript,\n+\t\tthis, &MainWindow::stopCaptureScript);\n \n \t/* Open the camera and start capture. */\n \tret = openCamera();\n@@ -152,6 +156,10 @@ MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options)\n \t\treturn;\n \t}\n \n+\t/* Start capture script. */\n+\tif (!scriptPath_.empty())\n+\t\tret = loadCaptureScript();\n+\n \tstartStopAction_->setChecked(true);\n }\n \n@@ -266,8 +274,11 @@ void MainWindow::switchCamera()\n \tif (newCameraId.empty())\n \t\treturn;\n \n-\tif (camera_ && newCameraId == camera_->id())\n+\tif (camera_ && newCameraId == camera_->id()) {\n+\t\t// When user opens camera selection dialog for CaptureScript selection\n+\t\tloadCaptureScript();\n \t\treturn;\n+\t}\n \n \tconst std::shared_ptr<Camera> &cam = cm_->get(newCameraId);\n \n@@ -287,17 +298,63 @@ void MainWindow::switchCamera()\n \tcamera_->release();\n \tcamera_ = cam;\n \n+\tloadCaptureScript();\n+\n \tstartStopAction_->setChecked(true);\n \n \t/* Display the current cameraId in the toolbar .*/\n \tcameraSelectButton_->setText(QString::fromStdString(newCameraId));\n }\n \n+void MainWindow::stopCaptureScript()\n+{\n+\tif (script_)\n+\t\tscript_.reset();\n+}\n+/**\n+ * \\brief Loads and validates the current capture script\n+ *\n+ * returns -EINVAL on failure and 0 on success\n+ */\n+int MainWindow::loadCaptureScript()\n+{\n+\tif (scriptPath_.empty() || camera_ == nullptr)\n+\t\treturn -EINVAL;\n+\n+\tauto script = std::make_unique<CaptureScript>(camera_, scriptPath_);\n+\n+\tif (!script->valid()) {\n+\t\tscript.reset();\n+\n+\t\tQMessageBox::critical(this, \"Invalid Script\",\n+\t\t\t\t \"Couldn't load the capture script\");\n+\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/*\n+\t * If we are already capturing, stop so we don't have stuck image\n+\t * in viewfinder.\n+\t */\n+\tbool wasCapturing = isCapturing_;\n+\tif (isCapturing_)\n+\t\ttoggleCapture(false);\n+\n+\tscript_ = std::move(script);\n+\n+\t/* Start capture again if we were capturing before. */\n+\tif (wasCapturing)\n+\t\ttoggleCapture(true);\n+\treturn 0;\n+}\n+\n std::string MainWindow::chooseCamera()\n {\n \tif (cameraSelectorDialog_->exec() != QDialog::Accepted)\n \t\treturn std::string();\n \n+\tscriptPath_ = cameraSelectorDialog_->getCaptureScript();\n+\n \treturn cameraSelectorDialog_->getCameraId();\n }\n \n@@ -499,6 +556,7 @@ int MainWindow::startCapture()\n \tpreviousFrames_ = 0;\n \tframesCaptured_ = 0;\n \tlastBufferTime_ = 0;\n+\tqueueCount_ = 0;\n \n \tret = camera_->start();\n \tif (ret) {\n@@ -777,5 +835,10 @@ void MainWindow::renderComplete(FrameBuffer *buffer)\n \n int MainWindow::queueRequest(Request *request)\n {\n+\tif (script_)\n+\t\trequest->controls() = script_->frameControls(queueCount_);\n+\n+\tqueueCount_++;\n+\n \treturn camera_->queueRequest(request);\n }\ndiff --git a/src/qcam/main_window.h b/src/qcam/main_window.h\nindex 22c85247..7c877ae1 100644\n--- a/src/qcam/main_window.h\n+++ b/src/qcam/main_window.h\n@@ -27,6 +27,7 @@\n #include <QQueue>\n #include <QTimer>\n \n+#include \"../cam/capture_script.h\"\n #include \"../cam/stream_options.h\"\n \n #include \"viewfinder.h\"\n@@ -89,6 +90,9 @@ private:\n \tvoid processHotplug(HotplugEvent *e);\n \tvoid processViewfinder(libcamera::FrameBuffer *buffer);\n \n+\tint loadCaptureScript();\n+\tvoid stopCaptureScript();\n+\n \t/* UI elements */\n \tQToolBar *toolbar_;\n \tQAction *startStopAction_;\n@@ -129,6 +133,9 @@ private:\n \tQElapsedTimer frameRateInterval_;\n \tuint32_t previousFrames_;\n \tuint32_t framesCaptured_;\n+\tuint32_t queueCount_;\n \n \tstd::vector<std::unique_ptr<libcamera::Request>> requests_;\n+\tstd::unique_ptr<CaptureScript> script_;\n+\tstd::string scriptPath_;\n };\ndiff --git a/src/qcam/meson.build b/src/qcam/meson.build\nindex 61861ea6..70a18d7e 100644\n--- a/src/qcam/meson.build\n+++ b/src/qcam/meson.build\n@@ -15,6 +15,7 @@ endif\n qcam_enabled = true\n \n qcam_sources = files([\n+ '../cam/capture_script.cpp',\n '../cam/image.cpp',\n '../cam/options.cpp',\n '../cam/stream_options.cpp',\n@@ -39,6 +40,7 @@ qcam_resources = files([\n qcam_deps = [\n libatomic,\n libcamera_public,\n+ libyaml,\n qt5_dep,\n ]\n \n", "prefixes": [ "libcamera-devel", "v9", "6/7" ] }