[{"id":24495,"web_url":"https://patchwork.libcamera.org/comment/24495/","msgid":"<166008239451.2423137.17051102183892163023@Monstersaurus>","date":"2022-08-09T21:59:54","subject":"Re: [libcamera-devel] [PATCH v7 6/8] qcam: CamSelectDialog: Add\n\tcapture script button","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Utkarsh Tiwari via libcamera-devel (2022-08-09 21:50:40)\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 being clicked in open state, present them with a QFileDialog to\n\n\"When clicked in an open state, present the user with\" ...\n\n> allow user to select a single file. When the script is loaded the button\n\n\"to allow selecting a single file.\" ...\n\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> reseted and the capture is continues as it is (i.e it is not stopped or\n\ns/reseted/reset/\n\ns/capture is continues/capture continues/\n\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:\n>         1. override accept and reject to handle capture script paths.\n>  src/qcam/cam_select_dialog.cpp | 68 ++++++++++++++++++++++++++++++++--\n>  src/qcam/cam_select_dialog.h   | 21 ++++++++++-\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..f3df9970 100644\n> --- a/src/qcam/cam_select_dialog.cpp\n> +++ b/src/qcam/cam_select_dialog.cpp\n> @@ -20,8 +20,8 @@\n>  #include <QString>\n>  \n>  CameraSelectorDialog::CameraSelectorDialog(libcamera::CameraManager *cameraManager,\n> -                                          QWidget *parent)\n> -       : QDialog(parent), cm_(cameraManager)\n> +                                          bool isScriptRunning, QWidget *parent)\n> +       : QDialog(parent), cm_(cameraManager), isScriptRunning_(isScriptRunning)\n>  {\n>         /* Use a QFormLayout for the dialog. */\n>         QFormLayout *layout = new QFormLayout(this);\n> @@ -39,6 +39,16 @@ CameraSelectorDialog::CameraSelectorDialog(libcamera::CameraManager *cameraManag\n>         connect(cameraIdComboBox_, &QComboBox::currentTextChanged,\n>                 this, &CameraSelectorDialog::handleCameraChange);\n>  \n> +       captureScriptButton_ = new QPushButton;\n> +       connect(captureScriptButton_, &QPushButton::clicked,\n> +               this, &CameraSelectorDialog::handleCaptureScriptButton);\n> +\n> +       /* Display the action that would be performed when button is clicked. */\n> +       if (isScriptRunning_)\n> +               captureScriptButton_->setText(\"Stop\");\n> +       else\n> +               captureScriptButton_->setText(\"Open\");\n> +\n>         /* Setup the QDialogButton Box */\n>         QDialogButtonBox *buttonBox =\n>                 new QDialogButtonBox(QDialogButtonBox::Ok |\n> @@ -50,10 +60,10 @@ CameraSelectorDialog::CameraSelectorDialog(libcamera::CameraManager *cameraManag\n>                 this, &QDialog::reject);\n>  \n>         /* Set the layout. */\n> -\n>         layout->addRow(\"Camera:\", cameraIdComboBox_);\n>         layout->addRow(\"Location:\", cameraLocation_);\n>         layout->addRow(\"Model:\", cameraModel_);\n> +       layout->addRow(\"Capture Script:\", captureScriptButton_);\n>         layout->addWidget(buttonBox);\n>  }\n>  \n> @@ -62,6 +72,11 @@ std::string CameraSelectorDialog::getCameraId()\n>         return cameraIdComboBox_->currentText().toStdString();\n>  }\n>  \n> +std::string CameraSelectorDialog::getCaptureScript()\n> +{\n> +       return scriptPath_;\n> +}\n> +\n>  /* Hotplug / Unplug Support. */\n>  void CameraSelectorDialog::cameraAdded(libcamera::Camera *camera)\n>  {\n> @@ -115,3 +130,50 @@ void CameraSelectorDialog::updateCamInfo(const std::shared_ptr<libcamera::Camera\n>  \n>         cameraModel_->setText(QString::fromStdString(model));\n>  }\n> +\n> +/* Capture script support. */\n> +void CameraSelectorDialog::handleCaptureScriptButton()\n> +{\n> +       if (isScriptRunning_) {\n> +               Q_EMIT stopCaptureScript();\n> +               isScriptRunning_ = false;\n> +               captureScriptButton_->setText(\"Open\");\n> +       } else {\n> +               selectedScriptPath_ = QFileDialog::getOpenFileName(this,\n> +                                                                  \"Run Capture Script\", QDir::currentPath(),\n> +                                                                  \"Capture Script (*.yaml)\")\n> +                                             .toStdString();\n> +\n> +               if (!selectedScriptPath_.empty())\n> +                       captureScriptButton_->setText(\"Loaded\");\n> +               else\n> +                       captureScriptButton_->setText(\"Open\");\n> +       }\n> +}\n> +\n> +void CameraSelectorDialog::accept()\n> +{\n> +       scriptPath_ = selectedScriptPath_;\n> +       QDialog::accept();\n> +}\n> +\n> +void CameraSelectorDialog::reject()\n> +{\n> +       if (isScriptRunning_)\n> +               selectedScriptPath_ = scriptPath_;\n\nWhat is this for ?\n\n\n> +       QDialog::reject();\n> +}\n> +\n> +void CameraSelectorDialog::informScriptReset()\n> +{\n> +       isScriptRunning_ = false;\n> +       scriptPath_.clear();\n> +       captureScriptButton_->setText(\"Open\");\n> +}\n> +\n> +void CameraSelectorDialog::informScriptRunning(std::string scriptPath)\n> +{\n> +       isScriptRunning_ = true;\n> +       scriptPath_ = scriptPath;\n> +       captureScriptButton_->setText(\"Stop\");\n> +}\n> diff --git a/src/qcam/cam_select_dialog.h b/src/qcam/cam_select_dialog.h\n> index 359df811..56d90596 100644\n> --- a/src/qcam/cam_select_dialog.h\n> +++ b/src/qcam/cam_select_dialog.h\n> @@ -17,8 +17,10 @@\n>  #include <QComboBox>\n>  #include <QDialog>\n>  #include <QDialogButtonBox>\n> +#include <QFileDialog>\n>  #include <QFormLayout>\n>  #include <QLabel>\n> +#include <QPushButton>\n>  #include <QString>\n>  \n>  class CameraSelectorDialog : public QDialog\n> @@ -26,12 +28,14 @@ class CameraSelectorDialog : public QDialog\n>         Q_OBJECT\n>  public:\n>         CameraSelectorDialog(libcamera::CameraManager *cameraManager,\n> -                            QWidget *parent);\n> +                            bool isScriptRunning, QWidget *parent);\n>  \n>         ~CameraSelectorDialog() = default;\n>  \n>         std::string getCameraId();\n>  \n> +       std::string getCaptureScript();\n> +\n>         /* Hotplug / Unplug Support. */\n>         void cameraAdded(libcamera::Camera *camera);\n>  \n> @@ -41,11 +45,26 @@ public:\n>         void updateCamInfo(const std::shared_ptr<libcamera::Camera> &camera);\n>         void handleCameraChange();\n>  \n> +       /* Capture script support. */\n> +       void handleCaptureScriptButton();\n> +       void informScriptReset();\n> +       void informScriptRunning(std::string scriptPath);\n> +       void accept() override;\n> +       void reject() override;\n> +\n> +Q_SIGNALS:\n> +       void stopCaptureScript();\n> +\n>  private:\n>         libcamera::CameraManager *cm_;\n>  \n> +       bool isScriptRunning_;\n> +       std::string scriptPath_;\n> +       std::string selectedScriptPath_;\n> +\n>         /* UI elements. */\n>         QComboBox *cameraIdComboBox_;\n>         QLabel *cameraLocation_;\n>         QLabel *cameraModel_;\n> +       QPushButton *captureScriptButton_;\n>  };\n> diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp\n> index 3feabcff..d73fb42a 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> @@ -151,6 +153,9 @@ MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options)\n>                 return;\n>         }\n>  \n> +       /* Start capture script. */\n> +       loadCaptureScript();\n> +\n>         startStopAction_->setChecked(true);\n>  }\n>  \n> @@ -289,10 +294,53 @@ void MainWindow::switchCamera()\n>         startStopAction_->setChecked(true);\n>  }\n>  \n> +void MainWindow::stopCaptureScript()\n> +{\n> +       if (script_) {\n> +               script_.reset();\n> +               cameraSelectorDialog_->informScriptReset();\n> +       }\n> +}\n> +\n> +void MainWindow::loadCaptureScript()\n> +{\n> +       if (scriptPath_.empty() || camera_ == nullptr)\n> +               return;\n> +\n> +       script_ = std::make_unique<CaptureScript>(camera_, scriptPath_);\n> +\n> +       /*\n> +        * If we are already capturing, stop so we don't have stuck image\n> +        * in viewfinder.\n> +        */\n> +       bool wasCapturing = isCapturing_;\n> +       if (isCapturing_)\n> +               toggleCapture(false);\n> +\n> +       if (!script_->valid()) {\n> +               script_.reset();\n> +               cameraSelectorDialog_->informScriptReset();\n> +\n> +               QMessageBox::critical(this, \"Invalid Script\",\n> +                                     \"Couldn't load the capture script\");\n> +\n> +       } else\n> +               cameraSelectorDialog_->informScriptRunning(scriptPath_);\n> +\n> +       /* Start capture again if we were capturing before. */\n> +       if (wasCapturing)\n> +               toggleCapture(true);\n> +}\n> +\n>  std::string MainWindow::chooseCamera()\n>  {\n> +       bool scriptRunning = script_ != nullptr;\n> +\n>         /* Construct the selection dialog, unconditionally. */\n> -       cameraSelectorDialog_ = new CameraSelectorDialog(cm_, this);\n> +       cameraSelectorDialog_ = new CameraSelectorDialog(cm_, scriptRunning, this);\n> +\n> +       connect(cameraSelectorDialog_, &CameraSelectorDialog::stopCaptureScript,\n> +               this, &MainWindow::stopCaptureScript);\n\nI fear that creating a new dialog box every time we choose a camera\nmeans that every call here leaks at least a CameraSelectorDialog\nallocation, *and* because we're connecting more signals - we're going to\nend up with duplicted signals?\n\n>  \n>         /*\n>          * Use the camera specified on the command line, if any, or display the\n> @@ -305,6 +353,9 @@ std::string MainWindow::chooseCamera()\n>                 std::string cameraId = cameraSelectorDialog_->getCameraId();\n>                 cameraSelectButton_->setText(QString::fromStdString(cameraId));\n>  \n> +               scriptPath_ = cameraSelectorDialog_->getCaptureScript();\n> +               loadCaptureScript();\n> +\n>                 return cameraId;\n>         } else\n>                 return std::string();\n> @@ -502,6 +553,7 @@ int MainWindow::startCapture()\n>         previousFrames_ = 0;\n>         framesCaptured_ = 0;\n>         lastBufferTime_ = 0;\n> +       queueCount_ = 0;\n>  \n>         ret = camera_->start();\n>         if (ret) {\n> @@ -779,5 +831,10 @@ void MainWindow::renderComplete(FrameBuffer *buffer)\n>  \n>  int MainWindow::queueRequest(Request *request)\n>  {\n> +       if (script_)\n> +               request->controls() = script_->frameControls(queueCount_);\n> +\n> +       queueCount_++;\n> +\n>         return camera_->queueRequest(request);\n>  }\n> diff --git a/src/qcam/main_window.h b/src/qcam/main_window.h\n> index bd6f0172..887f1db1 100644\n> --- a/src/qcam/main_window.h\n> +++ b/src/qcam/main_window.h\n> @@ -28,6 +28,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> @@ -90,6 +91,9 @@ private:\n>         void processHotplug(HotplugEvent *e);\n>         void processViewfinder(libcamera::FrameBuffer *buffer);\n>  \n> +       void loadCaptureScript();\n> +       void stopCaptureScript();\n> +\n>         /* UI elements */\n>         QToolBar *toolbar_;\n>         QAction *startStopAction_;\n> @@ -130,6 +134,9 @@ private:\n>         QElapsedTimer frameRateInterval_;\n>         uint32_t previousFrames_;\n>         uint32_t framesCaptured_;\n> +       uint32_t queueCount_;\n>  \n>         std::vector<std::unique_ptr<libcamera::Request>> requests_;\n> +       std::unique_ptr<CaptureScript> script_;\n> +       std::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 55875BE173\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue,  9 Aug 2022 21:59:59 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id D48946332B;\n\tTue,  9 Aug 2022 23:59:58 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 24B8661FAA\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  9 Aug 2022 23:59:58 +0200 (CEST)","from pendragon.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust3082.18-1.cable.virginm.net [86.31.172.11])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 9B0C1481;\n\tTue,  9 Aug 2022 23:59:57 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1660082398;\n\tbh=Skx0TuR+kixJlXemviTbJ0q1/oxr4hPehva1e7iCE4c=;\n\th=In-Reply-To:References:To:Date:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:\n\tFrom;\n\tb=tkhXkM2fSqnqjyHMjNZIbQbAIWF76EjsIcqbAEOkw+cNlyF07mui/WJnhMLLAF9TV\n\trdzbC2JlLysupt+34fpjYWcbGxDX13PufXrd8IEr+BCEQsKnu0HpDAGZxd16bj9QEh\n\tbGotUmJ6ifyduYLE96BYCELAGzlV4DFgWqSxFgtHmDHmE6nAMFJCVA40ZWmjp3H6ox\n\tytiKPkDyTZLFFBpj/7PgkYf7MX1jdXt/5FsVbACB3OHrrPZxvuO8YeuM8JUMY0Wpfz\n\tTor8lCa5L+Q94DDejrJ56Rwz7TJMqkLrp11OhLPbJZNtvBzAvwgG5FcQifeWEVKCz3\n\tSjncpeqbl+/HA==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1660082397;\n\tbh=Skx0TuR+kixJlXemviTbJ0q1/oxr4hPehva1e7iCE4c=;\n\th=In-Reply-To:References:Subject:From:To:Date:From;\n\tb=aWhIc0yaXVOqYcWhNm0TouoNBkL+O/7gWcNR15dspT9fioQ4puOgn1IzX6R7YTetq\n\tdSyaxiIMo+lN3ERHsy7OhrHa8wmnmoD3qYSTaiBtAAbVBRsUaci3EaUDD3xYl9z67j\n\t1kvnQKxDnDJ0OZPAXgK8MZd+gGNKCddMpRn+H7mo="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"aWhIc0ya\"; dkim-atps=neutral","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20220809205042.344923-7-utkarsh02t@gmail.com>","References":"<20220809205042.344923-1-utkarsh02t@gmail.com>\n\t<20220809205042.344923-7-utkarsh02t@gmail.com>","To":"Utkarsh Tiwari <utkarsh02t@gmail.com>,\n\tlibcamera-devel@lists.libcamera.org","Date":"Tue, 09 Aug 2022 22:59:54 +0100","Message-ID":"<166008239451.2423137.17051102183892163023@Monstersaurus>","User-Agent":"alot/0.10","Subject":"Re: [libcamera-devel] [PATCH v7 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":"Kieran Bingham via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":24501,"web_url":"https://patchwork.libcamera.org/comment/24501/","msgid":"<CAHbe+E2fOZcb=TAuxN9uKvX0vcKV3TSESgvrzNjyocMjE5bntw@mail.gmail.com>","date":"2022-08-10T04:30:56","subject":"Re: [libcamera-devel] [PATCH v7 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":"Hi Kieran thanks for the review.\n\nI accept all the grammetical mistakes.\n\nOn Wed, Aug 10, 2022 at 3:29 AM Kieran Bingham <\nkieran.bingham@ideasonboard.com> wrote:\n\n> Quoting Utkarsh Tiwari via libcamera-devel (2022-08-09 21:50:40)\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 being clicked in open state, present them with a QFileDialog to\n>\n> \"When clicked in an open state, present the user with\" ...\n>\n> > allow user to select a single file. When the script is loaded the button\n>\n> \"to allow selecting a single file.\" ...\n>\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> > reseted and the capture is continues as it is (i.e it is not stopped or\n>\n> s/reseted/reset/\n>\n> s/capture is continues/capture continues/\n>\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:\n> >         1. override accept and reject to handle capture script paths.\n> >  src/qcam/cam_select_dialog.cpp | 68 ++++++++++++++++++++++++++++++++--\n> >  src/qcam/cam_select_dialog.h   | 21 ++++++++++-\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\n> b/src/qcam/cam_select_dialog.cpp\n> > index f97ad6eb..f3df9970 100644\n> > --- a/src/qcam/cam_select_dialog.cpp\n> > +++ b/src/qcam/cam_select_dialog.cpp\n> > @@ -20,8 +20,8 @@\n> >  #include <QString>\n> >\n> >  CameraSelectorDialog::CameraSelectorDialog(libcamera::CameraManager\n> *cameraManager,\n> > -                                          QWidget *parent)\n> > -       : QDialog(parent), cm_(cameraManager)\n> > +                                          bool isScriptRunning, QWidget\n> *parent)\n> > +       : QDialog(parent), cm_(cameraManager),\n> isScriptRunning_(isScriptRunning)\n> >  {\n> >         /* Use a QFormLayout for the dialog. */\n> >         QFormLayout *layout = new QFormLayout(this);\n> > @@ -39,6 +39,16 @@\n> CameraSelectorDialog::CameraSelectorDialog(libcamera::CameraManager\n> *cameraManag\n> >         connect(cameraIdComboBox_, &QComboBox::currentTextChanged,\n> >                 this, &CameraSelectorDialog::handleCameraChange);\n> >\n> > +       captureScriptButton_ = new QPushButton;\n> > +       connect(captureScriptButton_, &QPushButton::clicked,\n> > +               this, &CameraSelectorDialog::handleCaptureScriptButton);\n> > +\n> > +       /* Display the action that would be performed when button is\n> clicked. */\n> > +       if (isScriptRunning_)\n> > +               captureScriptButton_->setText(\"Stop\");\n> > +       else\n> > +               captureScriptButton_->setText(\"Open\");\n> > +\n> >         /* Setup the QDialogButton Box */\n> >         QDialogButtonBox *buttonBox =\n> >                 new QDialogButtonBox(QDialogButtonBox::Ok |\n> > @@ -50,10 +60,10 @@\n> CameraSelectorDialog::CameraSelectorDialog(libcamera::CameraManager\n> *cameraManag\n> >                 this, &QDialog::reject);\n> >\n> >         /* Set the layout. */\n> > -\n> >         layout->addRow(\"Camera:\", cameraIdComboBox_);\n> >         layout->addRow(\"Location:\", cameraLocation_);\n> >         layout->addRow(\"Model:\", cameraModel_);\n> > +       layout->addRow(\"Capture Script:\", captureScriptButton_);\n> >         layout->addWidget(buttonBox);\n> >  }\n> >\n> > @@ -62,6 +72,11 @@ std::string CameraSelectorDialog::getCameraId()\n> >         return cameraIdComboBox_->currentText().toStdString();\n> >  }\n> >\n> > +std::string CameraSelectorDialog::getCaptureScript()\n> > +{\n> > +       return scriptPath_;\n> > +}\n> > +\n> >  /* Hotplug / Unplug Support. */\n> >  void CameraSelectorDialog::cameraAdded(libcamera::Camera *camera)\n> >  {\n> > @@ -115,3 +130,50 @@ void CameraSelectorDialog::updateCamInfo(const\n> std::shared_ptr<libcamera::Camera\n> >\n> >         cameraModel_->setText(QString::fromStdString(model));\n> >  }\n> > +\n> > +/* Capture script support. */\n> > +void CameraSelectorDialog::handleCaptureScriptButton()\n> > +{\n> > +       if (isScriptRunning_) {\n> > +               Q_EMIT stopCaptureScript();\n> > +               isScriptRunning_ = false;\n> > +               captureScriptButton_->setText(\"Open\");\n> > +       } else {\n> > +               selectedScriptPath_ = QFileDialog::getOpenFileName(this,\n> > +                                                                  \"Run\n> Capture Script\", QDir::currentPath(),\n> > +\n> \"Capture Script (*.yaml)\")\n> > +                                             .toStdString();\n> > +\n> > +               if (!selectedScriptPath_.empty())\n> > +                       captureScriptButton_->setText(\"Loaded\");\n> > +               else\n> > +                       captureScriptButton_->setText(\"Open\");\n> > +       }\n> > +}\n> > +\n> > +void CameraSelectorDialog::accept()\n> > +{\n> > +       scriptPath_ = selectedScriptPath_;\n> > +       QDialog::accept();\n> > +}\n> > +\n> > +void CameraSelectorDialog::reject()\n> > +{\n> > +       if (isScriptRunning_)\n> > +               selectedScriptPath_ = scriptPath_;\n>\n> What is this for ?\n>\n>\n> > +       QDialog::reject();\n> > +}\n> > +\n> > +void CameraSelectorDialog::informScriptReset()\n> > +{\n> > +       isScriptRunning_ = false;\n> > +       scriptPath_.clear();\n> > +       captureScriptButton_->setText(\"Open\");\n> > +}\n> > +\n> > +void CameraSelectorDialog::informScriptRunning(std::string scriptPath)\n> > +{\n> > +       isScriptRunning_ = true;\n> > +       scriptPath_ = scriptPath;\n> > +       captureScriptButton_->setText(\"Stop\");\n> > +}\n> > diff --git a/src/qcam/cam_select_dialog.h b/src/qcam/cam_select_dialog.h\n> > index 359df811..56d90596 100644\n> > --- a/src/qcam/cam_select_dialog.h\n> > +++ b/src/qcam/cam_select_dialog.h\n> > @@ -17,8 +17,10 @@\n> >  #include <QComboBox>\n> >  #include <QDialog>\n> >  #include <QDialogButtonBox>\n> > +#include <QFileDialog>\n> >  #include <QFormLayout>\n> >  #include <QLabel>\n> > +#include <QPushButton>\n> >  #include <QString>\n> >\n> >  class CameraSelectorDialog : public QDialog\n> > @@ -26,12 +28,14 @@ class CameraSelectorDialog : public QDialog\n> >         Q_OBJECT\n> >  public:\n> >         CameraSelectorDialog(libcamera::CameraManager *cameraManager,\n> > -                            QWidget *parent);\n> > +                            bool isScriptRunning, QWidget *parent);\n> >\n> >         ~CameraSelectorDialog() = default;\n> >\n> >         std::string getCameraId();\n> >\n> > +       std::string getCaptureScript();\n> > +\n> >         /* Hotplug / Unplug Support. */\n> >         void cameraAdded(libcamera::Camera *camera);\n> >\n> > @@ -41,11 +45,26 @@ public:\n> >         void updateCamInfo(const std::shared_ptr<libcamera::Camera>\n> &camera);\n> >         void handleCameraChange();\n> >\n> > +       /* Capture script support. */\n> > +       void handleCaptureScriptButton();\n> > +       void informScriptReset();\n> > +       void informScriptRunning(std::string scriptPath);\n> > +       void accept() override;\n> > +       void reject() override;\n> > +\n> > +Q_SIGNALS:\n> > +       void stopCaptureScript();\n> > +\n> >  private:\n> >         libcamera::CameraManager *cm_;\n> >\n> > +       bool isScriptRunning_;\n> > +       std::string scriptPath_;\n> > +       std::string selectedScriptPath_;\n> > +\n> >         /* UI elements. */\n> >         QComboBox *cameraIdComboBox_;\n> >         QLabel *cameraLocation_;\n> >         QLabel *cameraModel_;\n> > +       QPushButton *captureScriptButton_;\n> >  };\n> > diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp\n> > index 3feabcff..d73fb42a 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> > @@ -151,6 +153,9 @@ MainWindow::MainWindow(CameraManager *cm, const\n> OptionsParser::Options &options)\n> >                 return;\n> >         }\n> >\n> > +       /* Start capture script. */\n> > +       loadCaptureScript();\n> > +\n> >         startStopAction_->setChecked(true);\n> >  }\n> >\n> > @@ -289,10 +294,53 @@ void MainWindow::switchCamera()\n> >         startStopAction_->setChecked(true);\n> >  }\n> >\n> > +void MainWindow::stopCaptureScript()\n> > +{\n> > +       if (script_) {\n> > +               script_.reset();\n> > +               cameraSelectorDialog_->informScriptReset();\n> > +       }\n> > +}\n> > +\n> > +void MainWindow::loadCaptureScript()\n> > +{\n> > +       if (scriptPath_.empty() || camera_ == nullptr)\n> > +               return;\n> > +\n> > +       script_ = std::make_unique<CaptureScript>(camera_, scriptPath_);\n> > +\n> > +       /*\n> > +        * If we are already capturing, stop so we don't have stuck image\n> > +        * in viewfinder.\n> > +        */\n> > +       bool wasCapturing = isCapturing_;\n> > +       if (isCapturing_)\n> > +               toggleCapture(false);\n> > +\n> > +       if (!script_->valid()) {\n> > +               script_.reset();\n> > +               cameraSelectorDialog_->informScriptReset();\n> > +\n> > +               QMessageBox::critical(this, \"Invalid Script\",\n> > +                                     \"Couldn't load the capture\n> script\");\n> > +\n> > +       } else\n> > +               cameraSelectorDialog_->informScriptRunning(scriptPath_);\n> > +\n> > +       /* Start capture again if we were capturing before. */\n> > +       if (wasCapturing)\n> > +               toggleCapture(true);\n> > +}\n> > +\n> >  std::string MainWindow::chooseCamera()\n> >  {\n> > +       bool scriptRunning = script_ != nullptr;\n> > +\n> >         /* Construct the selection dialog, unconditionally. */\n> > -       cameraSelectorDialog_ = new CameraSelectorDialog(cm_, this);\n> > +       cameraSelectorDialog_ = new CameraSelectorDialog(cm_,\n> scriptRunning, this);\n> > +\n> > +       connect(cameraSelectorDialog_,\n> &CameraSelectorDialog::stopCaptureScript,\n> > +               this, &MainWindow::stopCaptureScript);\n>\n> I fear that creating a new dialog box every time we choose a camera\n> means that every call here leaks at least a CameraSelectorDialog\n> allocation, *and* because we're connecting more signals - we're going to\n> end up with duplicted signals?\n>\n> Ah shoots this should be done in the MainWindow constructor.\n\n> >\n> >         /*\n> >          * Use the camera specified on the command line, if any, or\n> display the\n> > @@ -305,6 +353,9 @@ std::string MainWindow::chooseCamera()\n> >                 std::string cameraId =\n> cameraSelectorDialog_->getCameraId();\n> >\n>  cameraSelectButton_->setText(QString::fromStdString(cameraId));\n> >\n> > +               scriptPath_ = cameraSelectorDialog_->getCaptureScript();\n> > +               loadCaptureScript();\n> > +\n> >                 return cameraId;\n> >         } else\n> >                 return std::string();\n> > @@ -502,6 +553,7 @@ int MainWindow::startCapture()\n> >         previousFrames_ = 0;\n> >         framesCaptured_ = 0;\n> >         lastBufferTime_ = 0;\n> > +       queueCount_ = 0;\n> >\n> >         ret = camera_->start();\n> >         if (ret) {\n> > @@ -779,5 +831,10 @@ void MainWindow::renderComplete(FrameBuffer *buffer)\n> >\n> >  int MainWindow::queueRequest(Request *request)\n> >  {\n> > +       if (script_)\n> > +               request->controls() =\n> script_->frameControls(queueCount_);\n> > +\n> > +       queueCount_++;\n> > +\n> >         return camera_->queueRequest(request);\n> >  }\n> > diff --git a/src/qcam/main_window.h b/src/qcam/main_window.h\n> > index bd6f0172..887f1db1 100644\n> > --- a/src/qcam/main_window.h\n> > +++ b/src/qcam/main_window.h\n> > @@ -28,6 +28,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> > @@ -90,6 +91,9 @@ private:\n> >         void processHotplug(HotplugEvent *e);\n> >         void processViewfinder(libcamera::FrameBuffer *buffer);\n> >\n> > +       void loadCaptureScript();\n> > +       void stopCaptureScript();\n> > +\n> >         /* UI elements */\n> >         QToolBar *toolbar_;\n> >         QAction *startStopAction_;\n> > @@ -130,6 +134,9 @@ private:\n> >         QElapsedTimer frameRateInterval_;\n> >         uint32_t previousFrames_;\n> >         uint32_t framesCaptured_;\n> > +       uint32_t queueCount_;\n> >\n> >         std::vector<std::unique_ptr<libcamera::Request>> requests_;\n> > +       std::unique_ptr<CaptureScript> script_;\n> > +       std::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> >\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 6A225C3272\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 10 Aug 2022 04:31:13 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 1C1616332B;\n\tWed, 10 Aug 2022 06:31:13 +0200 (CEST)","from mail-pj1-x1030.google.com (mail-pj1-x1030.google.com\n\t[IPv6:2607:f8b0:4864:20::1030])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 519EA61FA9\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 10 Aug 2022 06:31:11 +0200 (CEST)","by mail-pj1-x1030.google.com with SMTP id t22so13643520pjy.1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 09 Aug 2022 21:31:11 -0700 (PDT)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1660105873;\n\tbh=WvNY+qLhJNOOYjt85mAh4KdYkHgvN99/ebDpSAi0fRE=;\n\th=References:In-Reply-To:Date:To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=1ErbW0d0wxL+aZgrMrzSzApvHCSeTx7P9OYH8gNBSg2WZKeiyDNwPig933L+T/iAN\n\tkKr/XwrH1ydDgoJBNrAcdYYko6tBQ+yPIwLCcMUgcYSN662L0DT7r1MFOR14+eoMUI\n\tS4JpEkS24nsqOoYItRMZ01hGzZC91kvrK1EFqbtQKf0ULK1qvWM7B6dVI3ASKvet+k\n\tBkNgdWHQ7WgUVdvOBdYU3LxxpeLH6JRnmaG0m4BHElppVpjbQCu9QFJFq7nNzAoyRa\n\t2/Z+x51ch4U9lr8AffucvcGU/9z9ru2Er8ST0j0JzVn4tIF6MvTpViPXhwXzUuQd9a\n\tACjYFFr4aUMow==","v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112;\n\th=cc:to:subject:message-id:date:from:in-reply-to:references\n\t:mime-version:from:to:cc;\n\tbh=9jpfBEmwcAuQT9KHQJpt+6pGAhu8QoOPogqblypN9xk=;\n\tb=OdopjOnMaYO5KhTSMWflYO6oXWOkm3YhRQ5iljCZfthGEv4sUCKrQyvIw4yr81V09U\n\tiimLlkj/oXHGf2D6Jvf3AXuUpLLBctmAem04GgeL/Ag4odF0s53vjqj7lq7gTm3XG8G6\n\tJnVrrd5tRm5OPtePszQGvhz4Q1dp1XBgfZKIWkt5oZrlN2sf0dvqoIdx3YmTFEF28N0+\n\tPd46U7h7xFnBlcq7+VYqTo+4LZtgUcQgsfWV4xgLgqbXsmf9sY6BHxuZNg6xK7hVRMjT\n\trfFFunS8VSxsb3UVQPieXbGbWPmjsr/S1Gnpv5ZBBaCeiS/h4ab8VkyzpJQH7SudXlkY\n\teO+g=="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key; \n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"OdopjOnM\"; dkim-atps=neutral","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20210112;\n\th=cc:to:subject:message-id:date:from:in-reply-to:references\n\t:mime-version:x-gm-message-state:from:to:cc;\n\tbh=9jpfBEmwcAuQT9KHQJpt+6pGAhu8QoOPogqblypN9xk=;\n\tb=gI+Xzzpx3FBr1WOUZTwxZGvf3j4uy2uhsdMlVtqo9Y5yGjVc/0qAHjALJ4R9xDXjml\n\ts0cIAj3amPTUfmfA1oBi4JW897UveMVRg7DA19lN8/7FDHO6xFPcT3250zQRB7GEPT7S\n\tWxlAPM4ZH15+4CR/BMr2wL4KrF2y19bfvzXAFjkOF9GLDnqKjjuRR/M1G63Z+rTD47AQ\n\tHMbPF14E5npaiHrzAKOY6YjrO4TQpCZE4gDQUMlxSlZiFJPRK0rOT00VOAsBtKZiWXkP\n\tRAZgWZvnkfzmqNTpWgy++QP3vCFk8S+R76wVoQJPc+ewIPpCMHcJ2o+SsRTstgYMHi2r\n\tIYUA==","X-Gm-Message-State":"ACgBeo283v15xF3Zrsu8k5I9DPjV80ur/FoLiTajysRlhen/bIQi6cx5\n\tYySrhZa4/V8aITl4Akpt4CQVY20kaoRYkrFPKlJmqmrJ","X-Google-Smtp-Source":"AA6agR7hxtPlFnERTWaUTqul+gv4fnwzN+Dfi2lqWecIcmA4tFTWMZtT/yJGrJ6RAJ8i6xNQrkzThlgmQBARbPLK8R0=","X-Received":"by 2002:a17:90b:1241:b0:1f3:1d9f:a933 with SMTP id\n\tgx1-20020a17090b124100b001f31d9fa933mr1694812pjb.221.1660105869864;\n\tTue, 09 Aug 2022 21:31:09 -0700 (PDT)","MIME-Version":"1.0","References":"<20220809205042.344923-1-utkarsh02t@gmail.com>\n\t<20220809205042.344923-7-utkarsh02t@gmail.com>\n\t<166008239451.2423137.17051102183892163023@Monstersaurus>","In-Reply-To":"<166008239451.2423137.17051102183892163023@Monstersaurus>","Date":"Wed, 10 Aug 2022 10:00:56 +0530","Message-ID":"<CAHbe+E2fOZcb=TAuxN9uKvX0vcKV3TSESgvrzNjyocMjE5bntw@mail.gmail.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Content-Type":"multipart/alternative; boundary=\"000000000000a9b0bc05e5db84aa\"","Subject":"Re: [libcamera-devel] [PATCH v7 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>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]