[libcamera-devel] [PATCH v2 03/11] qcam: Add GUI way to change control values
Utkarsh Tiwari
utkarsh02t at gmail.com
Fri Aug 12 14:46:43 CEST 2022
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 <utkarsh02t at gmail.com>
---
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<const libcamera::ControlList> 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<const libcamera::ControlList> controlList);
+
private:
int createToolbars();
@@ -145,4 +147,5 @@ private:
std::vector<std::unique_ptr<libcamera::Request>> requests_;
std::unique_ptr<CaptureScript> 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 <libcamera/controls.h>
+#include <QCheckBox>
#include <QFrame>
+#include <QHBoxLayout>
#include <QLabel>
#include <QString>
#include <QVBoxLayout>
@@ -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<bool>(true);
+ else
+ controlValue.set<bool>(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 <libcamera/controls.h>
+#include <QCheckBox>
#include <QFrame>
#include <QLabel>
#include <QWidget>
@@ -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<libcamera::Camera> camera_,
QWidget *parent)
- : QWidget(parent)
+ : QWidget(parent), controlList_(std::make_shared<ControlList>())
{
/* Main Layout for the tab */
QGridLayout *controlGLayout = new QGridLayout(this);
@@ -27,6 +29,8 @@ ControlsTab::ControlsTab(std::shared_ptr<libcamera::Camera> 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<libcamera::Camera> 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<libcamera::Camera> camera_, QWidget *parent);
~ControlsTab() = default;
+
+Q_SIGNALS:
+ void controlListChanged(const std::shared_ptr<const libcamera::ControlList>);
+
+public Q_SLOTS:
+ void controlChanged(const libcamera::ControlId *controlId,
+ const libcamera::ControlValue controlValue);
+
+private:
+ std::shared_ptr<libcamera::ControlList> 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<const libcamera::ControlList>);
};
--
2.25.1
More information about the libcamera-devel
mailing list