From patchwork Wed Aug 31 05:49:37 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Utkarsh Tiwari X-Patchwork-Id: 17270 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 D9E2CC3272 for ; Wed, 31 Aug 2022 05:50:10 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 9284061FC0; Wed, 31 Aug 2022 07:50:10 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1661925010; bh=A9VDPAbsuWT4PSFI3gmdY8a8KPlIC/WDS7qw2qTMhWw=; 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=deeBSB8E3nEAoNWLE0Oy8DU6olr7uLiuyheOMYV0jcgDP75A6SeVEQeIaaqQWuxLt DSa83ApVlKtPsZYKrROy4WtdHeHk+tCbOBMEbY8esse6irUm3HWIL+PaqM0dgXus8V tmMmbmP2SfguEK/kpkvZE7kWAgEiX741WL9Uh/sqPvMUEZYpFlOCKWDQLIvJaBeagj ZTwBz1uvhTAJD42Nkpk3ezBPG5mRIzDnoJ+YVyZCYhzrUO/0raVylOhRZYa5INWOyR Gg6hzJPhHSkfUayFTdRwtiqsww0iAPsIEdrfqWj1Uei/Op8WbZWCIPoMtRWkM3ZCG0 dzRLFwtG+oucQ== Received: from mail-pl1-x62a.google.com (mail-pl1-x62a.google.com [IPv6:2607:f8b0:4864:20::62a]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 48CE561FBC for ; Wed, 31 Aug 2022 07:50:09 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="QrQlWXXE"; dkim-atps=neutral Received: by mail-pl1-x62a.google.com with SMTP id v5so6975835plo.9 for ; Tue, 30 Aug 2022 22:50:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc; bh=7oZU/s9Vw1tSQFJ3JtJmVKSNaVPLKq0zm5Ljm6xj+b0=; b=QrQlWXXEeUVW793k2fq9uuQ9DCrMOQXfup1KNfcUv+NSZijKjFo7i8qLLLJPlUi+lB xblbIzGTm1JbraYRO93R4jb3NMkb7zyMzPUatbcJ4wMbX7rGebBnDOw3m0YffqdzSsor pH4blkAZQzGghe8FabNtcRVA83vrPoapGSswyYDpLqCvvN/DAYh9mfp+lshGcItvCufk yYDatk5AyDHdQVwzla0Yxno8FA90ZouMZhWfUL9z4mB5jzuj0lkFymiG8cWO8XB9Oden 33fPXhXaZJ1SUEj7f8gsHIA/HMRK+0u6sbCZDpwgHAaQ/5uozFIzYx1M2Q6Ug+CbAy0e RO5Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc; bh=7oZU/s9Vw1tSQFJ3JtJmVKSNaVPLKq0zm5Ljm6xj+b0=; b=sOjSvqgR31ck/Ut7YSYdmMGVuEhybw335FwVCKaT6aPPw4mSUoAbU1OOgd4nJJHnAz SmUq6ELHUqmsRGz0CfpKwdnxUCQC2mEOwO+jvmZJLAvb47iP4Fu6Cc/0eZ+bEAcbkVvI 6MqGsgDS8Z9wUfzC+LwC7/tDlrleju7fN6Znzg5nTFkbq79msAK4Ra228qo9fEknL6IO TZQCwCrzTFi3y/dsE0WA0Xu6nazOhhfFWXwXfMRMAzWIP4DYvrLT+NCDw4uo9sMqVIC7 xF6n+dEaOHkrvc6Y/hL5Jbz78Wie59+aN2CUWYOwRbXBUBN8PCGbueiw2ynAS0SenH/H X+lw== X-Gm-Message-State: ACgBeo21TWoWvNveWVWf+S9lBPV7WLACgtnFxA/H3eLQx32gshhMOt5T 52U5In5GCXTFcPiHX5VHbcvoOFX756Y= X-Google-Smtp-Source: AA6agR6BRxXCjD9zDkfwqlkQx5qThUjIu0r73f7Xucc2bNvalQL5R5Yr5Yhz5Wxod4qhIUUBXUKCkw== X-Received: by 2002:a17:902:a60f:b0:172:e476:fde with SMTP id u15-20020a170902a60f00b00172e4760fdemr24122143plq.104.1661925007485; Tue, 30 Aug 2022 22:50:07 -0700 (PDT) Received: from devut-HP-Laptop-14q-cs0xxx.. ([103.36.82.122]) by smtp.gmail.com with ESMTPSA id q19-20020aa79833000000b00537eacc8fa6sm8307333pfl.40.2022.08.30.22.50.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 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 Subject: [libcamera-devel] [PATCH v9 6/7] qcam: CamSelectDialog: Add capture script button 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: Utkarsh Tiwari via libcamera-devel From: Utkarsh Tiwari Reply-To: Utkarsh Tiwari Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Display a QLabel which is readonly, and displays the currently selected capture script. The tooltip of the QLabel displays the file path of the script. Implement a capture script button which is a QToolButton which when clicked opens a QFileDialog this allows to select a capture script (*.yaml) file. Next to the capture scipt button, show a QToolButton which stops the capture script. If an invalid script has been selected show a QMesssageBox::critical and continue with the capture's previous state. Introduce a queueCount_ to keep track of the requests queued. When stopping the execution of the capture script the queueCount_ is not reset and the capture continues as it is (i.e it is not stopped or restarted). Requests are queued with any controls the script matching the current queueCount_. Reviewed-by: Kieran Bingham --- Differnce from v8: 1. Now display a QLabel with the fileName and filePath with button on the side. 2. infromScriptReset() informScriptRunning() are removed 3. Local script makes handling of invalid scripts easy. src/qcam/assets/feathericons/feathericons.qrc | 2 + src/qcam/cam_select_dialog.cpp | 88 ++++++++++++++++++- src/qcam/cam_select_dialog.h | 20 ++++- src/qcam/main_window.cpp | 67 +++++++++++++- src/qcam/main_window.h | 7 ++ src/qcam/meson.build | 2 + 6 files changed, 181 insertions(+), 5 deletions(-) diff --git a/src/qcam/assets/feathericons/feathericons.qrc b/src/qcam/assets/feathericons/feathericons.qrc index c5302040..0ea0c2d5 100644 --- a/src/qcam/assets/feathericons/feathericons.qrc +++ b/src/qcam/assets/feathericons/feathericons.qrc @@ -3,7 +3,9 @@ aperture.svg camera-off.svg + delete.svg play-circle.svg + upload.svg save.svg stop-circle.svg x-circle.svg diff --git a/src/qcam/cam_select_dialog.cpp b/src/qcam/cam_select_dialog.cpp index 6543228a..99405cc1 100644 --- a/src/qcam/cam_select_dialog.cpp +++ b/src/qcam/cam_select_dialog.cpp @@ -14,13 +14,18 @@ #include #include +#include #include +#include +#include #include #include +#include +#include CameraSelectorDialog::CameraSelectorDialog(libcamera::CameraManager *cameraManager, - QWidget *parent) - : QDialog(parent), cm_(cameraManager) + std::string scriptPath, QWidget *parent) + : QDialog(parent), cm_(cameraManager), scriptPath_(scriptPath) { /* Use a QFormLayout for the dialog. */ QFormLayout *layout = new QFormLayout(this); @@ -38,6 +43,41 @@ CameraSelectorDialog::CameraSelectorDialog(libcamera::CameraManager *cameraManag connect(cameraIdComboBox_, &QComboBox::currentTextChanged, this, &CameraSelectorDialog::updateCamInfo); + /* Set capture script selection / removal button. */ + QWidget *captureWidget = new QWidget(this); + QHBoxLayout *captureLayout = new QHBoxLayout(captureWidget); + + scriptFileLine_ = new QLabel; + scriptFileLine_->setFrameStyle(QFrame::Sunken | QFrame::StyledPanel); + + chooseCaptureScriptButton_ = new QToolButton; + chooseCaptureScriptButton_->setIcon(QIcon::fromTheme("document-open", + QIcon(":upload.svg"))); + chooseCaptureScriptButton_->setStyleSheet("border:none"); + connect(chooseCaptureScriptButton_, &QToolButton::clicked, + this, &CameraSelectorDialog::selectCaptureScript); + + QToolButton *stopCaptureScriptButton = new QToolButton; + stopCaptureScriptButton->setIcon(QIcon::fromTheme("edit-clear", + QIcon(":delete.svg"))); + stopCaptureScriptButton->setStyleSheet("border:node;"); + connect(stopCaptureScriptButton, &QToolButton::clicked, + this, &CameraSelectorDialog::resetCaptureScript); + + captureLayout->addWidget(scriptFileLine_); + captureLayout->addWidget(chooseCaptureScriptButton_); + captureLayout->addWidget(stopCaptureScriptButton); + captureLayout->setMargin(0); + + /* Set the file name of the capture script. */ + if (scriptPath_.empty()) { + scriptFileLine_->setText("No File Selected"); + } else { + scriptFileInfo_.setFile(QString::fromStdString(scriptPath_)); + scriptFileLine_->setText(scriptFileInfo_.fileName()); + scriptFileLine_->setToolTip(scriptFileInfo_.filePath()); + } + /* Setup the QDialogButton Box */ QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | @@ -52,6 +92,7 @@ CameraSelectorDialog::CameraSelectorDialog(libcamera::CameraManager *cameraManag layout->addRow("Camera:", cameraIdComboBox_); layout->addRow("Location:", cameraLocation_); layout->addRow("Model:", cameraModel_); + layout->addRow("Capture Script:", captureWidget); layout->addWidget(buttonBox); } @@ -110,3 +151,46 @@ void CameraSelectorDialog::updateCamInfo(QString cameraId) cameraModel_->setText(QString::fromStdString(model)); } + +/* Capture script support. */ +void CameraSelectorDialog::selectCaptureScript() +{ + selectedScriptPath_ = QFileDialog::getOpenFileName(this, + "Run Capture Script", QDir::currentPath(), + "Capture Script (*.yaml)") + .toStdString(); + + if (!selectedScriptPath_.empty()) { + scriptFileInfo_.setFile(QString::fromStdString(selectedScriptPath_)); + scriptFileLine_->setText(scriptFileInfo_.fileName()); + scriptFileLine_->setToolTip(scriptFileInfo_.filePath()); + } else { + selectedScriptPath_ = scriptPath_; + } +} + +void CameraSelectorDialog::resetCaptureScript() +{ + Q_EMIT stopCaptureScript(); + scriptPath_.clear(); + selectedScriptPath_.clear(); + scriptFileLine_->setText("No File Selected"); +} + +void CameraSelectorDialog::accept() +{ + scriptPath_ = selectedScriptPath_; + QDialog::accept(); +} + +void CameraSelectorDialog::reject() +{ + if (scriptPath_.empty()) { + scriptFileLine_->setText("No File Selected"); + } else { + scriptFileInfo_.setFile(QString::fromStdString(scriptPath_)); + scriptFileLine_->setText(scriptFileInfo_.fileName()); + scriptFileLine_->setToolTip(scriptFileInfo_.filePath()); + } + QDialog::reject(); +} diff --git a/src/qcam/cam_select_dialog.h b/src/qcam/cam_select_dialog.h index c91b7ebe..377faebc 100644 --- a/src/qcam/cam_select_dialog.h +++ b/src/qcam/cam_select_dialog.h @@ -15,21 +15,24 @@ #include #include +#include #include class QComboBox; class QLabel; +class QToolButton; class CameraSelectorDialog : public QDialog { Q_OBJECT public: CameraSelectorDialog(libcamera::CameraManager *cameraManager, - QWidget *parent); + std::string scriptPath, QWidget *parent); ~CameraSelectorDialog(); std::string getCameraId(); + std::string getCaptureScript() { return scriptPath_; }; /* Hotplug / Unplug Support. */ void addCamera(QString cameraId); @@ -38,11 +41,26 @@ public: /* Camera Information */ void updateCamInfo(QString cameraId); + /* Capture script support. */ + void selectCaptureScript(); + void resetCaptureScript(); + + void accept() override; + void reject() override; + +Q_SIGNALS: + void stopCaptureScript(); + private: libcamera::CameraManager *cm_; + std::string scriptPath_; + std::string selectedScriptPath_; + QFileInfo scriptFileInfo_; /* UI elements. */ QComboBox *cameraIdComboBox_; QLabel *cameraLocation_; QLabel *cameraModel_; + QLabel *scriptFileLine_; + QToolButton *chooseCaptureScriptButton_; }; diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp index 2a9ca830..af992b94 100644 --- a/src/qcam/main_window.cpp +++ b/src/qcam/main_window.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -18,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -143,7 +145,9 @@ MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options) cm_->cameraAdded.connect(this, &MainWindow::addCamera); cm_->cameraRemoved.connect(this, &MainWindow::removeCamera); - cameraSelectorDialog_ = new CameraSelectorDialog(cm_, this); + cameraSelectorDialog_ = new CameraSelectorDialog(cm_, scriptPath_, this); + connect(cameraSelectorDialog_, &CameraSelectorDialog::stopCaptureScript, + this, &MainWindow::stopCaptureScript); /* Open the camera and start capture. */ ret = openCamera(); @@ -152,6 +156,10 @@ MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options) return; } + /* Start capture script. */ + if (!scriptPath_.empty()) + ret = loadCaptureScript(); + startStopAction_->setChecked(true); } @@ -266,8 +274,11 @@ void MainWindow::switchCamera() if (newCameraId.empty()) return; - if (camera_ && newCameraId == camera_->id()) + if (camera_ && newCameraId == camera_->id()) { + // When user opens camera selection dialog for CaptureScript selection + loadCaptureScript(); return; + } const std::shared_ptr &cam = cm_->get(newCameraId); @@ -287,17 +298,63 @@ void MainWindow::switchCamera() camera_->release(); camera_ = cam; + loadCaptureScript(); + startStopAction_->setChecked(true); /* Display the current cameraId in the toolbar .*/ cameraSelectButton_->setText(QString::fromStdString(newCameraId)); } +void MainWindow::stopCaptureScript() +{ + if (script_) + script_.reset(); +} +/** + * \brief Loads and validates the current capture script + * + * returns -EINVAL on failure and 0 on success + */ +int MainWindow::loadCaptureScript() +{ + if (scriptPath_.empty() || camera_ == nullptr) + return -EINVAL; + + auto script = std::make_unique(camera_, scriptPath_); + + if (!script->valid()) { + script.reset(); + + QMessageBox::critical(this, "Invalid Script", + "Couldn't load the capture script"); + + return -EINVAL; + } + + /* + * If we are already capturing, stop so we don't have stuck image + * in viewfinder. + */ + bool wasCapturing = isCapturing_; + if (isCapturing_) + toggleCapture(false); + + script_ = std::move(script); + + /* Start capture again if we were capturing before. */ + if (wasCapturing) + toggleCapture(true); + return 0; +} + std::string MainWindow::chooseCamera() { if (cameraSelectorDialog_->exec() != QDialog::Accepted) return std::string(); + scriptPath_ = cameraSelectorDialog_->getCaptureScript(); + return cameraSelectorDialog_->getCameraId(); } @@ -499,6 +556,7 @@ int MainWindow::startCapture() previousFrames_ = 0; framesCaptured_ = 0; lastBufferTime_ = 0; + queueCount_ = 0; ret = camera_->start(); if (ret) { @@ -777,5 +835,10 @@ void MainWindow::renderComplete(FrameBuffer *buffer) int MainWindow::queueRequest(Request *request) { + if (script_) + request->controls() = script_->frameControls(queueCount_); + + queueCount_++; + return camera_->queueRequest(request); } diff --git a/src/qcam/main_window.h b/src/qcam/main_window.h index 22c85247..7c877ae1 100644 --- a/src/qcam/main_window.h +++ b/src/qcam/main_window.h @@ -27,6 +27,7 @@ #include #include +#include "../cam/capture_script.h" #include "../cam/stream_options.h" #include "viewfinder.h" @@ -89,6 +90,9 @@ private: void processHotplug(HotplugEvent *e); void processViewfinder(libcamera::FrameBuffer *buffer); + int loadCaptureScript(); + void stopCaptureScript(); + /* UI elements */ QToolBar *toolbar_; QAction *startStopAction_; @@ -129,6 +133,9 @@ private: QElapsedTimer frameRateInterval_; uint32_t previousFrames_; uint32_t framesCaptured_; + uint32_t queueCount_; std::vector> requests_; + std::unique_ptr script_; + std::string scriptPath_; }; diff --git a/src/qcam/meson.build b/src/qcam/meson.build index 61861ea6..70a18d7e 100644 --- a/src/qcam/meson.build +++ b/src/qcam/meson.build @@ -15,6 +15,7 @@ endif qcam_enabled = true qcam_sources = files([ + '../cam/capture_script.cpp', '../cam/image.cpp', '../cam/options.cpp', '../cam/stream_options.cpp', @@ -39,6 +40,7 @@ qcam_resources = files([ qcam_deps = [ libatomic, libcamera_public, + libyaml, qt5_dep, ]