From patchwork Fri Jul 15 19:13:52 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Utkarsh Tiwari X-Patchwork-Id: 16658 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 9CA16BE173 for ; Fri, 15 Jul 2022 19:14:14 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 6565A6331C; Fri, 15 Jul 2022 21:14:14 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1657912454; bh=D4tPv2pcGoac+7pJ3/rPN0x+tV79NXmrCX7NjS6peAs=; 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=u3lgv6uUInx9vAvqm6LQ9OA619ED0H4kUe1/gRQZpBlMohDADMsvyf1Hce6yS2Rh0 qp/64ovoLlmw2lmhNlcLyg+QejrdeIXp12zjAjzyDrWvd+YHD5UupAziHZ7TcraPL3 +HWjhP5Ur2AH+LE+wHfEMWKuVz/qFyYMJXIQskfP8YA77PLhnqGirgTiLDpj7u/Uzb hqOlZIEGW4RXShi6QXeytJlnL3wg5GNGf6srvX8O2xL82qnYCAI6KqP+Y1g5l8v0XW E826DGk4kAZD1WxsnqAOu0uhgSbUN05pnSRiyW1P64C1U4hOSgb+Lc2kFHZiLMUunA wfs5sEi8ryBJA== Received: from mail-pj1-x1029.google.com (mail-pj1-x1029.google.com [IPv6:2607:f8b0:4864:20::1029]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E25B663312 for ; Fri, 15 Jul 2022 21:14:12 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="CI9b4HGt"; dkim-atps=neutral Received: by mail-pj1-x1029.google.com with SMTP id o15so6466186pjh.1 for ; Fri, 15 Jul 2022 12:14:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=0MiMzge+VGNgCec63JdCht73U4viSdAiz/rpsk0XHS0=; b=CI9b4HGt6ltItBk+B0dmlTgmhkn2c79IB31QPwOMd2RX2reTudkOt/T8HlhnAOEnPq NaOvVWgqHzzz5nEGmFgFprT4lY30E7nIgQHK6Q5T0L+1uHR5B2OrvGSfG37s+Ys8XfKh GwIa+JovYfHqFBfTn5AdPQMpaFv3qiWkh0QXGl1LH3h7DkidnvrkLLMLafm5Xr+i0BMY z8c4A98AvVaSYmB9A6FMAMu+Rdg3or1+OfEEAj722wzEsf3R+BqfNdhjGlPe0BvLPa9t wU+bIaNew5mR5OFaX9NvWtjg7rV3wk8lpkSxonFXxE8GybjVtHoYwtjbVeWesVXv2IrN VoWg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=0MiMzge+VGNgCec63JdCht73U4viSdAiz/rpsk0XHS0=; b=xzNm1FZ45aLcrPqKQIhMQY5kfhV8pB/BSAlRbl/bLPRrWIOi1sXWDS2jYDgx3X/VaH D0QMUNefS1vajDlCz6kFNUpl+H5oECj4lLeWobzmCYhRIG2Iy+UPJrQ35RaqfhsO5QIz GDiKu/kMnJg3NeZb7YpRIr5HWU3SHFE3W7z9UF9LuSr272Xj0cVHOQXpNY14lvbM4wIe +Cf+t8EnatRvSATy8hSz55wL568DRuPbXBNr1APU70RrN1RJKLIW8bVzGAzAt1UBCGbI ZM41Lq4Cu06R765FICK14ymgAMQ8ADtKmL0p1tUtviur+HTy3u01uje+pDhr/unEgnRX 2Rcg== X-Gm-Message-State: AJIora+Dcgq/gvOwcEc4pD+MJXWb9FkGqAQNAkNhmMqHbfMoywjs9lNi NfKjGsR4Qsn+xluX1DkLPFRuCzB99CtDUg== X-Google-Smtp-Source: AGRyM1twThedweFcFymnbjvdQBXXJynqBaK1yRYSTRtvopNSni5T8JkmWNfpBdNVMwVmnX/lg/TrGg== X-Received: by 2002:a17:90b:4f85:b0:1f0:95d:c02b with SMTP id qe5-20020a17090b4f8500b001f0095dc02bmr23410718pjb.89.1657912450844; Fri, 15 Jul 2022 12:14:10 -0700 (PDT) Received: from localhost.localdomain ([2406:3003:2005:1c27:d8e8:f9ac:919c:9ac8]) by smtp.gmail.com with ESMTPSA id s10-20020a170903214a00b0016be834d544sm3834315ple.237.2022.07.15.12.14.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 15 Jul 2022 12:14:10 -0700 (PDT) To: libcamera-devel@lists.libcamera.org Date: Sat, 16 Jul 2022 03:13:52 +0800 Message-Id: <20220715191400.890976-4-utkarsh02t@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220715191400.890976-1-utkarsh02t@gmail.com> References: <20220715191400.890976-1-utkarsh02t@gmail.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 03/11] qcam: Add GUI way to change control values 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" Introduce GUI ability to change control values currently only for boolean control types. Each of the ControlFrame first identifies the type of control it is and then accordingly constructs the widget that would help the user to interact with it. The current model of connecting the controls change info to the MaindWindow is as follows. +-----------+ |Main Window| +-----^-----+ | | +-------+-------+ |Settings Dialog| +--^----^------^+ | | | +------+ | +-----+ | | | +-----+---+ +----+----+ +----+----+ |Various | | Tabs | | ... | ++---+----+ +---+---+-+ ++--+--+--+ | | | | | | | | | | | | | | | | | | | | | Implement a controlLatch mechanism to update thec ontrolList. As the Settings QDialog and ControlFrame processing the change in control lie in the same thread there would be no race to access the ControlList. This allows us to use to shared_ptr to the ControlList to help us the same function from MaindWindow::queueRequest() and the controlListChanged() signal. We clear the controlList_ in MainWindow::queueRequest() and not in the MainWindow::controlListLatch() because when in controlListLatch() we need to clear it when we don't have script nor the alerted by the signal. But what Can happen is that we have controlListLatch() is triggered by the signal and then immediately called by the queueRequest() which would clear the controlList and it would not end up getting set. Signed-off-by: Utkarsh Tiwari --- src/qcam/main_window.cpp | 38 +++++++++++++-- src/qcam/main_window.h | 4 ++ src/qcam/settings/control_frame.cpp | 75 +++++++++++++++++++++++++++-- src/qcam/settings/control_frame.h | 13 ++++- src/qcam/settings/controls_tab.cpp | 19 +++++++- src/qcam/settings/controls_tab.h | 10 ++++ src/qcam/settings/settings_dialog.h | 5 ++ 7 files changed, 156 insertions(+), 8 deletions(-) diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp index f0916b8e..5d11ef98 100644 --- a/src/qcam/main_window.cpp +++ b/src/qcam/main_window.cpp @@ -98,7 +98,7 @@ private: MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options) : saveRaw_(nullptr), options_(options), cm_(cm), allocator_(nullptr), - isCapturing_(false), captureRaw_(false) + isCapturing_(false), captureRaw_(false), controlList_(ControlList()) { int ret; @@ -353,6 +353,8 @@ void MainWindow::openSettingsDialog() } settingsDialog_ = std::make_unique(camera_, this); + connect(settingsDialog_.get(), &SettingsDialog::controlListChanged, + this, &MainWindow::controlListLatch); settingsDialog_->show(); } @@ -891,10 +893,40 @@ void MainWindow::renderComplete(FrameBuffer *buffer) int MainWindow::queueRequest(Request *request) { - if (script_) - request->controls() = script_->frameControls(queueCount_); + /* + * Call the controlListLatch with a nullptr to indicate that it + * has been called by us and not by the signal. + */ + + controlListLatch(nullptr); + request->controls() = controlList_; + + /* Clear controlList_ to remove old controls that have been set.*/ + if (controlList_.size()) + controlList_.clear(); queueCount_++; return camera_->queueRequest(request); } + +void MainWindow::controlListLatch(std::shared_ptr controlList) +{ + /* + * If we have been given a non-null shared_ptr then it means we + * have been alerted by the SettingsWindow::controlListChanged signal. + */ + + if (controlList) { + controlList_ = *(controlList); + + /* Shut down the capture script */ + if (script_) + toggleScriptAction(true); + + return; + } + + if (script_) + controlList_ = script_->frameControls(queueCount_); +} diff --git a/src/qcam/main_window.h b/src/qcam/main_window.h index e35f9029..05207023 100644 --- a/src/qcam/main_window.h +++ b/src/qcam/main_window.h @@ -71,6 +71,8 @@ private Q_SLOTS: void renderComplete(libcamera::FrameBuffer *buffer); + void controlListLatch(std::shared_ptr controlList); + private: int createToolbars(); @@ -139,4 +141,6 @@ private: std::vector> requests_; std::unique_ptr script_; + + libcamera::ControlList controlList_; }; diff --git a/src/qcam/settings/control_frame.cpp b/src/qcam/settings/control_frame.cpp index 785e4040..e335d373 100644 --- a/src/qcam/settings/control_frame.cpp +++ b/src/qcam/settings/control_frame.cpp @@ -9,7 +9,9 @@ #include +#include #include +#include #include #include #include @@ -27,11 +29,11 @@ ControlFrame::ControlFrame(const libcamera::ControlId *control, frameVLayout->addWidget(new QLabel(QString::fromStdString(control_->name()))); /* - * No need to pass parents to widgets, - * as QVBoxLayout transfers ownership to - * its parent widget. + * No need to pass parents to widgets, as QVBoxLayout transfers + * ownership to its parent widget. */ frameVLayout->addWidget(defaultValueLabel()); + frameVLayout->addWidget(controlInteraction()); setFrameStyle(QFrame::StyledPanel); } @@ -49,6 +51,73 @@ QLabel *ControlFrame::defaultValueLabel(QWidget *parent) return defaultValLabel; } +QWidget *ControlFrame::controlInteraction(QWidget *parent) +{ + QWidget *containerWidget = new QWidget(parent); + + switch (control_->type()) { + case ControlTypeBool: { + QHBoxLayout *HCheckBoxLayout = new QHBoxLayout(containerWidget); + + HCheckBoxLayout->addWidget(new QLabel("Enabled :")); + + controlCheckBox_ = new QCheckBox; + /* + * In the start we are not sure what is exactly the state of + * the control. Do not assume. Set in partially checked state. + */ + controlCheckBox_->setCheckState(Qt::PartiallyChecked); + + connect(controlCheckBox_, &QCheckBox::stateChanged, + this, &ControlFrame::notifyControlChange); + + HCheckBoxLayout->addWidget(controlCheckBox_); + + /* Align it with the name of the control. */ + HCheckBoxLayout->setAlignment(Qt::AlignLeft); + HCheckBoxLayout->setMargin(0); + return containerWidget; + } + default: + return (new QLabel("Currently Unavailable")); + } +} + +/* ----------------------------------------------------------------------------- + * Qt Slots + */ + +void ControlFrame::notifyControlChange() +{ + ControlValue controlValue = ControlValue(); + + switch (control_->type()) { + case ControlTypeBool: + + /* + * When clicked for the first time, the switch comes from a + * partially checked state. Turn the triset off so we can have + * only enabled and disabled states. + */ + controlCheckBox_->setTristate(false); + /* + * When this function is invoked, the checkbox can only be in + * the Checked or Unchecked State (after the first time). + */ + if (controlCheckBox_->checkState() == Qt::CheckState::Checked) + controlValue.set(true); + else + controlValue.set(false); + + break; + default: + /* Nothing to emit so return */ + return; + } + + Q_EMIT controlChanged(control_, controlValue); +} + /* ----------------------------------------------------------------------------- * Helpers */ diff --git a/src/qcam/settings/control_frame.h b/src/qcam/settings/control_frame.h index 884feb6b..61005ea5 100644 --- a/src/qcam/settings/control_frame.h +++ b/src/qcam/settings/control_frame.h @@ -9,8 +9,9 @@ #include -#include +#include #include +#include #include class ControlFrame : public QFrame @@ -23,13 +24,23 @@ public: QWidget *parent); ~ControlFrame() = default; +Q_SIGNALS: + void controlChanged(const libcamera::ControlId *controlId, + const libcamera::ControlValue); + +private Q_SLOTS: + void notifyControlChange(); + private: const libcamera::ControlId *control_; const libcamera::ControlInfo &controlInfo_; /* Widgets */ + QWidget *controlInteraction(QWidget *parent = nullptr); QLabel *defaultValueLabel(QWidget *parent = nullptr); + QCheckBox *controlCheckBox_; + /* Helper Hunctions */ QString getDefaultValueQStr(); }; diff --git a/src/qcam/settings/controls_tab.cpp b/src/qcam/settings/controls_tab.cpp index adc24326..0777d708 100644 --- a/src/qcam/settings/controls_tab.cpp +++ b/src/qcam/settings/controls_tab.cpp @@ -17,9 +17,11 @@ #include "control_frame.h" +using namespace libcamera; + ControlsTab::ControlsTab(std::shared_ptr camera_, QWidget *parent) - : QWidget(parent) + : QWidget(parent), controlList_(std::make_shared()) { /* Main Layout for the tab */ QGridLayout *controlGLayout = new QGridLayout(this); @@ -27,6 +29,8 @@ ControlsTab::ControlsTab(std::shared_ptr camera_, int controlCount = 0; for (auto &[control, info] : camera_->controls()) { ControlFrame *controlFrame = new ControlFrame(control, info, this); + connect(controlFrame, &ControlFrame::controlChanged, + this, &ControlsTab::controlChanged); controlGLayout->addWidget(controlFrame, controlCount / 2, controlCount % 2); @@ -36,3 +40,16 @@ ControlsTab::ControlsTab(std::shared_ptr camera_, if (controlCount == 0) controlGLayout->addWidget(new QLabel("No controls available")); } + +/* ----------------------------------------------------------------------------- + * Qt Slots + */ + +void ControlsTab::controlChanged(const libcamera::ControlId *controlId, + const libcamera::ControlValue controlValue) +{ + controlList_->clear(); + + controlList_->set(controlId->id(), controlValue); + Q_EMIT controlListChanged(controlList_); +} diff --git a/src/qcam/settings/controls_tab.h b/src/qcam/settings/controls_tab.h index 6a63f334..5bac7d48 100644 --- a/src/qcam/settings/controls_tab.h +++ b/src/qcam/settings/controls_tab.h @@ -21,4 +21,14 @@ class ControlsTab : public QWidget public: ControlsTab(std::shared_ptr camera_, QWidget *parent); ~ControlsTab() = default; + +Q_SIGNALS: + void controlListChanged(const std::shared_ptr); + +public Q_SLOTS: + void controlChanged(const libcamera::ControlId *controlId, + const libcamera::ControlValue controlValue); + +private: + std::shared_ptr controlList_; }; diff --git a/src/qcam/settings/settings_dialog.h b/src/qcam/settings/settings_dialog.h index c2fa61ea..e6efd876 100644 --- a/src/qcam/settings/settings_dialog.h +++ b/src/qcam/settings/settings_dialog.h @@ -32,8 +32,13 @@ public: ControlsTab *controlsTab = new ControlsTab(camera, this); settingTabWidget->addTab(controlsTab, "Controls"); + connect(controlsTab, &ControlsTab::controlListChanged, + this, &SettingsDialog::controlListChanged); setWindowTitle("Settings"); } ~SettingsDialog() = default; + +Q_SIGNALS: + void controlListChanged(std::shared_ptr); };