From patchwork Fri Aug 12 12:46:43 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Utkarsh Tiwari X-Patchwork-Id: 17099 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 DDA1DBE173 for ; Fri, 12 Aug 2022 12:47:12 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 9B3A663334; Fri, 12 Aug 2022 14:47:12 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1660308432; bh=naxmq4pAh7h/eoNWdQ3P0xc3xcUhSSB+XP1uNsOQW+A=; 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=MXAQKDWF5rFOtxQycOfwClgyCZ2wGQToF7oA8eDk3mG+w4l/TLI1C2v4E9S+ba6Wz 2on4wivTZfy0FCObe8p90MNea5C+UKdlcRYtVLukhFV5TqOQUepYBnNdTutn5QAAnp 65/GwXSd9Ovgbpxw/dv/rzSv0mqju039IAyC92Mb3wQM5jZF8wRSDCnNf5yzkaFNA9 gGBfOISk9xBeOeT1Pvk+FPKk7f1LBC9qmrD1HySh7fe4tDBHaMPkoqtpO1od178J2+ FYsZgTyPv1uLDLyy7dDQqV8QmgY3ENk2B0ticR7+mUW+eWYTSp0nufM0dxIpwxrexH kE3xzPtc8+1bw== Received: from mail-pg1-x535.google.com (mail-pg1-x535.google.com [IPv6:2607:f8b0:4864:20::535]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 83CAB63328 for ; Fri, 12 Aug 2022 14:47:10 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="pOA0+LdR"; dkim-atps=neutral Received: by mail-pg1-x535.google.com with SMTP id 12so720158pga.1 for ; Fri, 12 Aug 2022 05:47:10 -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=x7UaddQYAA5WjaMHvFJtpiORxx5fo0lFeM0wW38ro28=; b=pOA0+LdROBGPYHLgAHXZUj6gk8/IuDmkwZg7UuH5NVtY0JBlooGLGV+lpGiV1aniRH w5fpAw299Y0UxLktrwSFzyKHVG07fgTgsDuH1gJZOP5oZ7ryES7wS/W5FcBXaIV7WyEa i1w8lP6ZODXQE0GosyrwtVRAPyfK1Fe7SbQ3FsZIWyZbBgGHsT/K+7ApJvZf/c38IH3b 6Ck7ioZbsUIyfaN+3gCYuX2yNcaXlUcuAa6ClZorC9JLyrLpl3Y9aXVmSJ4BF7gEQXsi SCd05KNx/Na3UC8YblQqHUprTztbTk3c4BuB9Wvm1GsBUtEqgTPR/5OO0v9UvDYIYzD/ RKDQ== 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=x7UaddQYAA5WjaMHvFJtpiORxx5fo0lFeM0wW38ro28=; b=k5me8ZxziIsRZgzfW5lM35KRhI7CdGgdjcauignnWWCoBcmBQgDtEZlKKsv/HjLKW9 3WtmhxltbT7b/x7C8Yg1E+9swo1kNbO1toDtI4ENbRO8b4K88t7V2hXjA1aFOl7Rvfaq BA5EjALtCBd+1N5P8ZcAl+zYRCpoXlLvK6xBtB6H0sg76T+UmccnGGW0mOwb+wxWTSQ5 zj1TaJMy1cYeOvv+R/IL7K/bRXIth9Lzeu2Nf/2401UljVxIZ3VdwqIpkTPWBlz3oiXl TJBHqT2Z52GlAFLUgstc0Tff8/pjyU18oiTQpbohjgn+KlfMwXaABmYFQpp+ynMD5jbK MzIw== X-Gm-Message-State: ACgBeo2aYPf4dcWywaimHKD+PvfzIb39Aw0T6SAMNRGsmr8f61M6+IKg Fdvhf+UFLbY0LdSlDjzkNzXkFTUENBI= X-Google-Smtp-Source: AA6agR77vjzNilKlD0qLIUc7/cUoNd0now+5LKy6aHVu1a5mWswxkRRQUpiRuolRdBc4AJekC26zsw== X-Received: by 2002:a05:6a00:1705:b0:52f:6028:5c33 with SMTP id h5-20020a056a00170500b0052f60285c33mr3789379pfc.29.1660308428646; Fri, 12 Aug 2022 05:47:08 -0700 (PDT) Received: from localhost.localdomain ([2404:bd00:3:dc0d:d8e:96a2:2dbe:5a83]) by smtp.gmail.com with ESMTPSA id e6-20020a17090a728600b001f069352d73sm1431785pjg.25.2022.08.12.05.47.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 12 Aug 2022 05:47:08 -0700 (PDT) To: libcamera-devel@lists.libcamera.org Date: Fri, 12 Aug 2022 18:16:43 +0530 Message-Id: <20220812124651.27496-4-utkarsh02t@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220812124651.27496-1-utkarsh02t@gmail.com> References: <20220812124651.27496-1-utkarsh02t@gmail.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 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 --- Difference from v1: 1. Now using infromScriptRest() to change the script button state. src/qcam/main_window.cpp | 40 +++++++++++++++-- src/qcam/main_window.h | 3 ++ src/qcam/settings/control_frame.cpp | 70 +++++++++++++++++++++++++++++ src/qcam/settings/control_frame.h | 11 +++++ src/qcam/settings/controls_tab.cpp | 19 +++++++- src/qcam/settings/controls_tab.h | 10 +++++ src/qcam/settings/settings_dialog.h | 5 +++ 7 files changed, 154 insertions(+), 4 deletions(-) diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp index 7861c34b..87cdad82 100644 --- a/src/qcam/main_window.cpp +++ b/src/qcam/main_window.cpp @@ -101,7 +101,7 @@ private: MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options) : saveRaw_(nullptr), cameraSelectorDialog_(nullptr), options_(options), cm_(cm), allocator_(nullptr), isCapturing_(false), captureRaw_(false), - firstCameraSelect_(true) + firstCameraSelect_(true), controlList_(ControlList()) { int ret; @@ -276,6 +276,8 @@ void MainWindow::openSettingsDialog() } settingsDialog_ = new SettingsDialog(camera_, this); + connect(settingsDialog_, &SettingsDialog::controlListChanged, + this, &MainWindow::controlListLatch); settingsDialog_->show(); } @@ -863,10 +865,42 @@ 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_) { + script_.reset(); + cameraSelectorDialog_->informScriptReset(); + } + + return; + } + + if (script_) + controlList_ = script_->frameControls(queueCount_); +} diff --git a/src/qcam/main_window.h b/src/qcam/main_window.h index 856a71b1..8137e736 100644 --- a/src/qcam/main_window.h +++ b/src/qcam/main_window.h @@ -75,6 +75,8 @@ private Q_SLOTS: void renderComplete(libcamera::FrameBuffer *buffer); + void controlListLatch(std::shared_ptr controlList); + private: int createToolbars(); @@ -145,4 +147,5 @@ private: std::vector> requests_; std::unique_ptr script_; std::string scriptPath_; + libcamera::ControlList controlList_; }; diff --git a/src/qcam/settings/control_frame.cpp b/src/qcam/settings/control_frame.cpp index 79cf67eb..273ce79b 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 @@ -30,6 +32,7 @@ ControlFrame::ControlFrame(const ControlId *control, * ownership to its parent widget. */ frameVLayout->addWidget(defaultValueLabel()); + frameVLayout->addWidget(controlInteraction()); setFrameStyle(QFrame::StyledPanel); } @@ -47,6 +50,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 280f07e0..61005ea5 100644 --- a/src/qcam/settings/control_frame.h +++ b/src/qcam/settings/control_frame.h @@ -9,6 +9,7 @@ #include +#include #include #include #include @@ -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); };