@@ -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_);
+}
@@ -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_;
};
@@ -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
*/
@@ -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();
};
@@ -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_);
+}
@@ -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_;
};
@@ -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>);
};
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@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(-)