[{"id":24534,"web_url":"https://patchwork.libcamera.org/comment/24534/","msgid":"<20220811061628.ck7zwizwpucnxhgw@gmail.com>","date":"2022-08-11T06:16:28","subject":"Re: [libcamera-devel] [PATCH v8 6/8] qcam: CamSelectDialog: Add\n\tcapture script button","submitter":{"id":114,"url":"https://patchwork.libcamera.org/api/people/114/","name":"Utkarsh Tiwari","email":"utkarsh02t@gmail.com"},"content":"On Wed, Aug 10, 2022 at 08:33:47PM +0530, Utkarsh Tiwari wrote:\n> Implement an Capture Script in CamSelectDialog button which would allow\n> the user to open a Capture Script (*.yaml).\n> This button has three states :\n>     - Open Capture Script\n>     - Loaded\n>     - Stop the execution of current capture script\n> \n> When clicked in an open state, present the user with a QFileDialog to\n> allow selecting a single file. When the script is loaded the button\n> displays \"Loaded\", the script has not been verified yet. Verifying the\n> script and executing it happens after user presses Ok.\n> \n> Introduce a queueCount_ to keep track of the requests queued.\n> \n> When stopping the execution of the capture script the queueCount_ is not\n> reset and the capture continues as it is (i.e it is not stopped or\n> restarted).\n> \n> Requests are queued with any controls the script matching the current\n> queueCount_.\n> \n> Signed-off-by: Utkarsh Tiwari <utkarsh02t@gmail.com>\n> ---\n> Difference from v7:\n> \t1. Fix grammetical errors in the commit message\n> \t2. Intialize the cameraSelectorDialog_ to nullptr in Construct\n> \t\tso we construct the CameraSelectorDialog just once\n>  src/qcam/cam_select_dialog.cpp | 69 ++++++++++++++++++++++++++++++++--\n>  src/qcam/cam_select_dialog.h   | 20 +++++++++-\n>  src/qcam/main_window.cpp       | 59 ++++++++++++++++++++++++++++-\n>  src/qcam/main_window.h         |  7 ++++\n>  src/qcam/meson.build           |  2 +\n>  5 files changed, 152 insertions(+), 5 deletions(-)\n> \n> diff --git a/src/qcam/cam_select_dialog.cpp b/src/qcam/cam_select_dialog.cpp\n> index f97ad6eb..0db0a5bd 100644\n> --- a/src/qcam/cam_select_dialog.cpp\n> +++ b/src/qcam/cam_select_dialog.cpp\n> @@ -16,12 +16,13 @@\n>  #include <QComboBox>\n>  #include <QDialog>\n>  #include <QDialogButtonBox>\n> +#include <QFileDialog>\n>  #include <QFormLayout>\n>  #include <QString>\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   bool isScriptRunning, QWidget *parent)\n> +\t: QDialog(parent), cm_(cameraManager), isScriptRunning_(isScriptRunning)\n>  {\n>  \t/* Use a QFormLayout for the dialog. */\n>  \tQFormLayout *layout = new QFormLayout(this);\n> @@ -39,6 +40,16 @@ CameraSelectorDialog::CameraSelectorDialog(libcamera::CameraManager *cameraManag\n>  \tconnect(cameraIdComboBox_, &QComboBox::currentTextChanged,\n>  \t\tthis, &CameraSelectorDialog::handleCameraChange);\n>  \n> +\tcaptureScriptButton_ = new QPushButton;\n> +\tconnect(captureScriptButton_, &QPushButton::clicked,\n> +\t\tthis, &CameraSelectorDialog::handleCaptureScriptButton);\n> +\n> +\t/* Display the action that would be performed when button is clicked. */\n> +\tif (isScriptRunning_)\n> +\t\tcaptureScriptButton_->setText(\"Stop\");\n> +\telse\n> +\t\tcaptureScriptButton_->setText(\"Open\");\n> +\n>  \t/* Setup the QDialogButton Box */\n>  \tQDialogButtonBox *buttonBox =\n>  \t\tnew QDialogButtonBox(QDialogButtonBox::Ok |\n> @@ -50,10 +61,10 @@ CameraSelectorDialog::CameraSelectorDialog(libcamera::CameraManager *cameraManag\n>  \t\tthis, &QDialog::reject);\n>  \n>  \t/* Set the layout. */\n> -\n>  \tlayout->addRow(\"Camera:\", cameraIdComboBox_);\n>  \tlayout->addRow(\"Location:\", cameraLocation_);\n>  \tlayout->addRow(\"Model:\", cameraModel_);\n> +\tlayout->addRow(\"Capture Script:\", captureScriptButton_);\n>  \tlayout->addWidget(buttonBox);\n>  }\n>  \n> @@ -62,6 +73,11 @@ std::string CameraSelectorDialog::getCameraId()\n>  \treturn cameraIdComboBox_->currentText().toStdString();\n>  }\n>  \n> +std::string CameraSelectorDialog::getCaptureScript()\n> +{\n> +\treturn scriptPath_;\n> +}\n> +\n>  /* Hotplug / Unplug Support. */\n>  void CameraSelectorDialog::cameraAdded(libcamera::Camera *camera)\n>  {\n> @@ -115,3 +131,50 @@ void CameraSelectorDialog::updateCamInfo(const std::shared_ptr<libcamera::Camera\n>  \n>  \tcameraModel_->setText(QString::fromStdString(model));\n>  }\n> +\n> +/* Capture script support. */\n> +void CameraSelectorDialog::handleCaptureScriptButton()\n> +{\n> +\tif (isScriptRunning_) {\n> +\t\tQ_EMIT stopCaptureScript();\n> +\t\tisScriptRunning_ = false;\n> +\t\tcaptureScriptButton_->setText(\"Open\");\n> +\t} else {\n> +\t\tselectedScriptPath_ = QFileDialog::getOpenFileName(this,\n> +\t\t\t\t\t\t\t\t   \"Run Capture Script\", QDir::currentPath(),\n> +\t\t\t\t\t\t\t\t   \"Capture Script (*.yaml)\")\n> +\t\t\t\t\t      .toStdString();\n> +\n> +\t\tif (!selectedScriptPath_.empty())\n> +\t\t\tcaptureScriptButton_->setText(\"Loaded\");\n> +\t\telse\n> +\t\t\tcaptureScriptButton_->setText(\"Open\");\n> +\t}\n> +}\n> +\n> +void CameraSelectorDialog::accept()\n> +{\n> +\tscriptPath_ = selectedScriptPath_;\n> +\tQDialog::accept();\n> +}\n> +\n> +void CameraSelectorDialog::reject()\n> +{\n> +\tif (isScriptRunning_)\n> +\t\tselectedScriptPath_ = scriptPath_;\n> +\tQDialog::reject();\n> +}\n> +\n> +void CameraSelectorDialog::informScriptReset()\n> +{\n> +\tisScriptRunning_ = false;\n> +\tscriptPath_.clear();\n\nadd this to also reset any selected files already this \nis necessary when dealing with interactive controls\n\tselectedScriptPath_.clear();\n\tcaptureWidgetLayout_->removeWidget(scriptPathLabel_);\n\n> +\tcaptureScriptButton_->setText(\"Open\");\n> +}\n> +\n> +void CameraSelectorDialog::informScriptRunning(std::string scriptPath)\n> +{\n> +\tisScriptRunning_ = true;\n> +\tscriptPath_ = scriptPath;\n> +\tcaptureScriptButton_->setText(\"Stop\");\n> +}\n> diff --git a/src/qcam/cam_select_dialog.h b/src/qcam/cam_select_dialog.h\n> index 16475af6..bbdf897e 100644\n> --- a/src/qcam/cam_select_dialog.h\n> +++ b/src/qcam/cam_select_dialog.h\n> @@ -17,18 +17,21 @@\n>  #include <QComboBox>\n>  #include <QDialog>\n>  #include <QLabel>\n> +#include <QPushButton>\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     bool isScriptRunning, QWidget *parent);\n>  \n>  \t~CameraSelectorDialog() = default;\n>  \n>  \tstd::string getCameraId();\n>  \n> +\tstd::string getCaptureScript();\n> +\n>  \t/* Hotplug / Unplug Support. */\n>  \tvoid cameraAdded(libcamera::Camera *camera);\n>  \n> @@ -38,11 +41,26 @@ public:\n>  \tvoid updateCamInfo(const std::shared_ptr<libcamera::Camera> &camera);\n>  \tvoid handleCameraChange();\n>  \n> +\t/* Capture script support. */\n> +\tvoid handleCaptureScriptButton();\n> +\tvoid informScriptReset();\n> +\tvoid informScriptRunning(std::string scriptPath);\n> +\tvoid accept() override;\n> +\tvoid reject() override;\n> +\n> +Q_SIGNALS:\n> +\tvoid stopCaptureScript();\n> +\n>  private:\n>  \tlibcamera::CameraManager *cm_;\n>  \n> +\tbool isScriptRunning_;\n> +\tstd::string scriptPath_;\n> +\tstd::string selectedScriptPath_;\n> +\n>  \t/* UI elements. */\n>  \tQComboBox *cameraIdComboBox_;\n>  \tQLabel *cameraLocation_;\n>  \tQLabel *cameraModel_;\n> +\tQPushButton *captureScriptButton_;\n>  };\n> diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp\n> index bf40572a..3c7c3173 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> @@ -19,6 +20,7 @@\n>  #include <QFileDialog>\n>  #include <QImage>\n>  #include <QImageWriter>\n> +#include <QMessageBox>\n>  #include <QMutexLocker>\n>  #include <QStandardPaths>\n>  #include <QStringList>\n> @@ -152,6 +154,9 @@ MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options)\n>  \t\treturn;\n>  \t}\n>  \n> +\t/* Start capture script. */\n> +\tloadCaptureScript();\n> +\n>  \tstartStopAction_->setChecked(true);\n>  }\n>  \n> @@ -290,11 +295,54 @@ void MainWindow::switchCamera()\n>  \tstartStopAction_->setChecked(true);\n>  }\n>  \n> +void MainWindow::stopCaptureScript()\n> +{\n> +\tif (script_) {\n> +\t\tscript_.reset();\n> +\t\tcameraSelectorDialog_->informScriptReset();\n> +\t}\n> +}\n> +\n> +void MainWindow::loadCaptureScript()\n> +{\n> +\tif (scriptPath_.empty() || camera_ == nullptr)\n> +\t\treturn;\n> +\n> +\tscript_ = std::make_unique<CaptureScript>(camera_, scriptPath_);\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> +\tif (!script_->valid()) {\n> +\t\tscript_.reset();\n> +\t\tcameraSelectorDialog_->informScriptReset();\n> +\n> +\t\tQMessageBox::critical(this, \"Invalid Script\",\n> +\t\t\t\t      \"Couldn't load the capture script\");\n> +\n> +\t} else\n> +\t\tcameraSelectorDialog_->informScriptRunning(scriptPath_);\n> +\n> +\t/* Start capture again if we were capturing before. */\n> +\tif (wasCapturing)\n> +\t\ttoggleCapture(true);\n> +}\n> +\n>  std::string MainWindow::chooseCamera()\n>  {\n> +\tbool scriptRunning = script_ != nullptr;\n> +\n>  \t/* Construct the selection dialog, only the first time. */\n>  \tif (!cameraSelectorDialog_)\n> -\t\tcameraSelectorDialog_ = new CameraSelectorDialog(cm_, this);\n> +\t\tcameraSelectorDialog_ = new CameraSelectorDialog(cm_, scriptRunning, this);\n> +\n> +\tconnect(cameraSelectorDialog_, &CameraSelectorDialog::stopCaptureScript,\n> +\t\tthis, &MainWindow::stopCaptureScript);\n>  \n>  \t/*\n>  \t * Use the camera specified on the command line, if any, or display the\n> @@ -309,6 +357,9 @@ std::string MainWindow::chooseCamera()\n>  \t\tstd::string cameraId = cameraSelectorDialog_->getCameraId();\n>  \t\tcameraSelectButton_->setText(QString::fromStdString(cameraId));\n>  \n> +\t\tscriptPath_ = cameraSelectorDialog_->getCaptureScript();\n> +\t\tloadCaptureScript();\n> +\n>  \t\treturn cameraId;\n>  \t} else\n>  \t\treturn std::string();\n> @@ -506,6 +557,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> @@ -783,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>  }\n> diff --git a/src/qcam/main_window.h b/src/qcam/main_window.h\n> index d161365a..10994b67 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 \"cam_select_dialog.h\"\n> @@ -89,6 +90,9 @@ private:\n>  \tvoid processHotplug(HotplugEvent *e);\n>  \tvoid processViewfinder(libcamera::FrameBuffer *buffer);\n>  \n> +\tvoid loadCaptureScript();\n> +\tvoid stopCaptureScript();\n> +\n>  \t/* UI elements */\n>  \tQToolBar *toolbar_;\n>  \tQAction *startStopAction_;\n> @@ -130,6 +134,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>  };\n> diff --git a/src/qcam/meson.build b/src/qcam/meson.build\n> index 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> -- \n> 2.25.1\n>","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 92239C3272\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 11 Aug 2022 06:16:38 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 0BF556332B;\n\tThu, 11 Aug 2022 08:16:38 +0200 (CEST)","from mail-pf1-x42b.google.com (mail-pf1-x42b.google.com\n\t[IPv6:2607:f8b0:4864:20::42b])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 0B5F461FA8\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 11 Aug 2022 08:16:36 +0200 (CEST)","by mail-pf1-x42b.google.com with SMTP id q19so15684920pfg.8\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 10 Aug 2022 23:16:35 -0700 (PDT)","from gmail.com ([2404:bd00:3:d2bc:3b87:b94e:5889:b674])\n\tby smtp.gmail.com with ESMTPSA id\n\tp8-20020a170902e74800b0016b865ea2ddsm14049501plf.85.2022.08.10.23.16.32\n\tfor <libcamera-devel@lists.libcamera.org>\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tWed, 10 Aug 2022 23:16:33 -0700 (PDT)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1660198598;\n\tbh=fK10cQpc+P2CtE9Zi6QxxcvgjrsR2Sr1hqGMMz6OWlY=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:\n\tFrom;\n\tb=H6lqHZ+xq2n2VmVuoh9Z4aV4erYNpxOYYle/CWsmaj/lNi+IPwDoBLGeKTeA4LU6A\n\tsBPF9KBsyxrUWQPX8oDYWLjOWTJ2fiKof1KSYXVzr9or9xymY59R/hIoPRm1yXnRVI\n\tZHJMwaaunGx3NQ0lRf0yQttP5IF61Z1mXKpIwH85bsssTTNLsEirw3JJdJEVUd82kY\n\teXBYI88sj5O1M2W9ELgSCk7PrtX4ns84uVESNcyvoXeW/AdDdX2avpDG7TRzq/wfau\n\t13ncXkqBSen87Ax2H2Rl35RA/6+BrjC2NuZI3SPjbfYsCGM8mREwhY46l5WDjUmf3c\n\tBhKzl193zoDiA==","v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112;\n\th=in-reply-to:content-disposition:mime-version:references\n\t:mail-followup-to:message-id:subject:to:from:date:from:to:cc;\n\tbh=NLmrFGdoMqo3+ybpfGHC5z/s1uVnjn3Du0gwjOBXWZM=;\n\tb=DqP74OOhvd6XB0CC7wWWyn1w/jR1Ra1PPGLrobKmh9H97EB8wr7+Op7hJArqg+Bvca\n\tWN/bJfACLQDJnrWEt2vpjzw5ITspeQh5aovER47wP80ygN0wMfzdCfrX5hCEANAhTUNt\n\t/il9qbzsFOyFcsUnYclhzbHxYQwQISZAJ+inEBrSddkuHhc/ZjPss1uTcgIb81AZMpB5\n\tEqlNcodKtDXmcJSz2Gwx/XJXSmGu2sUqo6rdh93iOdLWoi2zFVSx5Xm3CIJkAGn/jJ0I\n\tbfSqT/XrNh0gDX6TuDcf3lwloUG7+Aux66R45ERK6auTxcQCqo7MMwtEV+W5zOECdzE2\n\tdMxw=="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key; \n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"DqP74OOh\"; dkim-atps=neutral","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20210112;\n\th=in-reply-to:content-disposition:mime-version:references\n\t:mail-followup-to:message-id:subject:to:from:date:x-gm-message-state\n\t:from:to:cc;\n\tbh=NLmrFGdoMqo3+ybpfGHC5z/s1uVnjn3Du0gwjOBXWZM=;\n\tb=Nff9kdKamVbXNAbxJKy+gkPgMVCTUQd9Xt8+5+dJu1tGhedftm44giMUoRDnpnqE2g\n\tGKR+chzMKAGlLM3UosJ0y4OY7w/CaUCMksyogZUkVmD3ywqrqIsdGIvcUruNugSJMJiX\n\tdIDORgBDVRk3TyJq00NcSMWkrPvFl1e6gdbchmc8ZqncdiVAxmlvHqnbD4GplHQUGMuf\n\tM7RgMTI0zitKMEY3tevX5uY7H3xdPCbV8dh4Q39daV0xmgQgw4IokvcF3VxWFKPsY16N\n\tT0FZaiHTKiXKG2w/SNkFF5MS5O9iyKCVK9EgfYwQjytcLlPK6378GhhYrtUW7/JOzxuq\n\toNtw==","X-Gm-Message-State":"ACgBeo1P369gxbek7ERBsfqsOGg4/rx4FEIEDfrJZtcXiL4rrRG/S4pK\n\toTTJiqeEa2Rjfkb+U1Kgt9Pqzc8ZJ2c=","X-Google-Smtp-Source":"AA6agR7fr0GbE8tboPhiZW6iRUZbZAhY7STy8PTMFlhbR4yIADVDm5p0PHzL1z7Y08O4XRlIzpFwrA==","X-Received":"by 2002:aa7:9813:0:b0:52d:395d:c98d with SMTP id\n\te19-20020aa79813000000b0052d395dc98dmr30797618pfl.55.1660198594014; \n\tWed, 10 Aug 2022 23:16:34 -0700 (PDT)","Date":"Thu, 11 Aug 2022 11:46:28 +0530","To":"libcamera-devel@lists.libcamera.org","Message-ID":"<20220811061628.ck7zwizwpucnxhgw@gmail.com>","Mail-Followup-To":"libcamera-devel@lists.libcamera.org","References":"<20220810150349.414043-1-utkarsh02t@gmail.com>\n\t<20220810150349.414043-7-utkarsh02t@gmail.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=us-ascii","Content-Disposition":"inline","In-Reply-To":"<20220810150349.414043-7-utkarsh02t@gmail.com>","Subject":"Re: [libcamera-devel] [PATCH v8 6/8] qcam: CamSelectDialog: Add\n\tcapture script 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>"}}]