388 lines
13 KiB
C++
388 lines
13 KiB
C++
/*
|
|
* GStreamer Camera Viewer
|
|
* Copyright (c) 2025 Maik Jurischka
|
|
* Licensed under CC BY-NC-SA 4.0
|
|
* https://creativecommons.org/licenses/by-nc-sa/4.0/
|
|
*/
|
|
|
|
#include "cameracontrolwidget.h"
|
|
#include <QVBoxLayout>
|
|
#include <QHBoxLayout>
|
|
#include <QFormLayout>
|
|
#include <QScrollArea>
|
|
#include <QButtonGroup>
|
|
#include <QJsonArray>
|
|
|
|
CameraControlWidget::CameraControlWidget(SocketClient* socketClient, QWidget *parent)
|
|
: QWidget(parent), m_socketClient(socketClient)
|
|
{
|
|
setupUI();
|
|
}
|
|
|
|
void CameraControlWidget::setupUI()
|
|
{
|
|
auto* mainLayout = new QVBoxLayout(this);
|
|
|
|
auto* scrollArea = new QScrollArea(this);
|
|
scrollArea->setWidgetResizable(true);
|
|
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
|
|
auto* scrollWidget = new QWidget();
|
|
auto* scrollLayout = new QVBoxLayout(scrollWidget);
|
|
|
|
scrollLayout->addWidget(createFormatGroup());
|
|
scrollLayout->addWidget(createExposureGroup());
|
|
scrollLayout->addWidget(createWhiteBalanceGroup());
|
|
scrollLayout->addWidget(createImageAdjustmentGroup());
|
|
scrollLayout->addStretch();
|
|
|
|
scrollWidget->setLayout(scrollLayout);
|
|
scrollArea->setWidget(scrollWidget);
|
|
scrollWidget->setMaximumWidth(500);
|
|
|
|
mainLayout->addWidget(scrollArea);
|
|
|
|
m_statusLabel = new QLabel("Status: Ready", this);
|
|
m_statusLabel->setStyleSheet("QLabel { background-color: #f0f0f0; padding: 5px; border-radius: 3px; }");
|
|
mainLayout->addWidget(m_statusLabel);
|
|
|
|
setLayout(mainLayout);
|
|
}
|
|
|
|
QGroupBox* CameraControlWidget::createFormatGroup()
|
|
{
|
|
auto* groupBox = new QGroupBox("Video Format", this);
|
|
auto* layout = new QVBoxLayout();
|
|
|
|
m_formatCombo = new QComboBox(this);
|
|
m_formatCombo->addItem("1280x720@30fps UYVY (Supported)", "1280,720,30,UYVY");
|
|
m_formatCombo->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
|
|
m_formatCombo->setMinimumContentsLength(20);
|
|
|
|
m_getFormatsBtn = new QPushButton("Get Available Formats", this);
|
|
m_setFormatBtn = new QPushButton("Set Format", this);
|
|
|
|
connect(m_getFormatsBtn, &QPushButton::clicked, this, &CameraControlWidget::onGetFormats);
|
|
connect(m_setFormatBtn, &QPushButton::clicked, this, &CameraControlWidget::onSetFormat);
|
|
|
|
layout->addWidget(new QLabel("Select Format:", this));
|
|
layout->addWidget(m_formatCombo);
|
|
layout->addWidget(m_getFormatsBtn);
|
|
layout->addWidget(m_setFormatBtn);
|
|
|
|
groupBox->setLayout(layout);
|
|
return groupBox;
|
|
}
|
|
|
|
QGroupBox* CameraControlWidget::createExposureGroup()
|
|
{
|
|
auto* groupBox = new QGroupBox("Exposure", this);
|
|
auto* layout = new QVBoxLayout();
|
|
|
|
auto* exposureGroup = new QButtonGroup(this);
|
|
m_exposureAuto = new QRadioButton("Auto", this);
|
|
m_exposureManual = new QRadioButton("Manual", this);
|
|
m_exposureAuto->setChecked(true);
|
|
|
|
exposureGroup->addButton(m_exposureAuto);
|
|
exposureGroup->addButton(m_exposureManual);
|
|
|
|
connect(m_exposureAuto, &QRadioButton::toggled, this, &CameraControlWidget::onExposureModeChanged);
|
|
|
|
auto* modeLayout = new QHBoxLayout();
|
|
modeLayout->addWidget(m_exposureAuto);
|
|
modeLayout->addWidget(m_exposureManual);
|
|
|
|
m_exposureValue = new QSpinBox(this);
|
|
m_exposureValue->setRange(1, 10000);
|
|
m_exposureValue->setValue(100);
|
|
m_exposureValue->setEnabled(false);
|
|
|
|
m_setExposureBtn = new QPushButton("Set Exposure", this);
|
|
connect(m_setExposureBtn, &QPushButton::clicked, this, &CameraControlWidget::onSetExposure);
|
|
|
|
auto* formLayout = new QFormLayout();
|
|
formLayout->addRow("Mode:", modeLayout);
|
|
formLayout->addRow("Value:", m_exposureValue);
|
|
|
|
layout->addLayout(formLayout);
|
|
layout->addWidget(m_setExposureBtn);
|
|
|
|
groupBox->setLayout(layout);
|
|
return groupBox;
|
|
}
|
|
|
|
QGroupBox* CameraControlWidget::createWhiteBalanceGroup()
|
|
{
|
|
auto* groupBox = new QGroupBox("White Balance", this);
|
|
auto* layout = new QVBoxLayout();
|
|
|
|
auto* wbGroup = new QButtonGroup(this);
|
|
m_whiteBalanceAuto = new QRadioButton("Auto", this);
|
|
m_whiteBalanceManual = new QRadioButton("Manual", this);
|
|
m_whiteBalanceAuto->setChecked(true);
|
|
|
|
wbGroup->addButton(m_whiteBalanceAuto);
|
|
wbGroup->addButton(m_whiteBalanceManual);
|
|
|
|
connect(m_whiteBalanceAuto, &QRadioButton::toggled, this, &CameraControlWidget::onWhiteBalanceModeChanged);
|
|
|
|
auto* modeLayout = new QHBoxLayout();
|
|
modeLayout->addWidget(m_whiteBalanceAuto);
|
|
modeLayout->addWidget(m_whiteBalanceManual);
|
|
|
|
m_whiteBalanceTemp = new QSpinBox(this);
|
|
m_whiteBalanceTemp->setRange(2800, 6500);
|
|
m_whiteBalanceTemp->setValue(4500);
|
|
m_whiteBalanceTemp->setSuffix(" K");
|
|
m_whiteBalanceTemp->setEnabled(false);
|
|
|
|
m_setWhiteBalanceBtn = new QPushButton("Set White Balance", this);
|
|
connect(m_setWhiteBalanceBtn, &QPushButton::clicked, this, &CameraControlWidget::onSetWhiteBalance);
|
|
|
|
auto* formLayout = new QFormLayout();
|
|
formLayout->addRow("Mode:", modeLayout);
|
|
formLayout->addRow("Temperature:", m_whiteBalanceTemp);
|
|
|
|
layout->addLayout(formLayout);
|
|
layout->addWidget(m_setWhiteBalanceBtn);
|
|
|
|
groupBox->setLayout(layout);
|
|
return groupBox;
|
|
}
|
|
|
|
QGroupBox* CameraControlWidget::createImageAdjustmentGroup()
|
|
{
|
|
auto* groupBox = new QGroupBox("Image Adjustments", this);
|
|
auto* layout = new QVBoxLayout();
|
|
|
|
layout->addWidget(createSliderControl("Brightness (0-255):", 0, 255, 128,
|
|
&m_brightnessSlider, &m_brightnessSpinBox));
|
|
connect(m_brightnessSlider, &QSlider::valueChanged, this, &CameraControlWidget::onBrightnessChanged);
|
|
|
|
layout->addWidget(createSliderControl("Contrast (0-255):", 0, 255, 32,
|
|
&m_contrastSlider, &m_contrastSpinBox));
|
|
connect(m_contrastSlider, &QSlider::valueChanged, this, &CameraControlWidget::onContrastChanged);
|
|
|
|
layout->addWidget(createSliderControl("Saturation (0-255):", 0, 255, 64,
|
|
&m_saturationSlider, &m_saturationSpinBox));
|
|
connect(m_saturationSlider, &QSlider::valueChanged, this, &CameraControlWidget::onSaturationChanged);
|
|
|
|
layout->addWidget(createSliderControl("Sharpness (0-255):", 0, 255, 3,
|
|
&m_sharpnessSlider, &m_sharpnessSpinBox));
|
|
connect(m_sharpnessSlider, &QSlider::valueChanged, this, &CameraControlWidget::onSharpnessChanged);
|
|
|
|
layout->addWidget(createSliderControl("Gamma (72-500):", 72, 500, 100,
|
|
&m_gammaSlider, &m_gammaSpinBox));
|
|
connect(m_gammaSlider, &QSlider::valueChanged, this, &CameraControlWidget::onGammaChanged);
|
|
|
|
layout->addWidget(createSliderControl("Gain (0-100):", 0, 100, 0,
|
|
&m_gainSlider, &m_gainSpinBox));
|
|
connect(m_gainSlider, &QSlider::valueChanged, this, &CameraControlWidget::onGainChanged);
|
|
|
|
groupBox->setLayout(layout);
|
|
return groupBox;
|
|
}
|
|
|
|
QWidget* CameraControlWidget::createSliderControl(const QString& label, int min, int max, int defaultValue,
|
|
QSlider** slider, QSpinBox** spinBox)
|
|
{
|
|
auto* widget = new QWidget(this);
|
|
auto* layout = new QVBoxLayout(widget);
|
|
layout->setContentsMargins(0, 5, 0, 5);
|
|
|
|
auto* titleLabel = new QLabel(label, this);
|
|
|
|
auto* controlLayout = new QHBoxLayout();
|
|
|
|
*slider = new QSlider(Qt::Horizontal, this);
|
|
(*slider)->setRange(min, max);
|
|
(*slider)->setValue(defaultValue);
|
|
|
|
*spinBox = new QSpinBox(this);
|
|
(*spinBox)->setRange(min, max);
|
|
(*spinBox)->setValue(defaultValue);
|
|
|
|
connect(*slider, &QSlider::valueChanged, *spinBox, &QSpinBox::setValue);
|
|
connect(*spinBox, QOverload<int>::of(&QSpinBox::valueChanged), *slider, &QSlider::setValue);
|
|
|
|
controlLayout->addWidget(*slider, 1);
|
|
controlLayout->addWidget(*spinBox);
|
|
|
|
layout->addWidget(titleLabel);
|
|
layout->addLayout(controlLayout);
|
|
|
|
return widget;
|
|
}
|
|
|
|
void CameraControlWidget::onGetFormats()
|
|
{
|
|
m_socketClient->sendCommand("get_formats", QJsonObject(),
|
|
[this](const QJsonObject& response) {
|
|
if (response.contains("formats")) {
|
|
QJsonArray formats = response["formats"].toArray();
|
|
m_formatCombo->clear();
|
|
|
|
for (const QJsonValue& val : formats) {
|
|
QJsonObject fmt = val.toObject();
|
|
int width = fmt["width"].toInt();
|
|
int height = fmt["height"].toInt();
|
|
int fps = fmt["framerate"].toInt();
|
|
QString format = fmt["format"].toString();
|
|
|
|
QString displayText = QString("%1x%2@%3fps %4").arg(width, height, fps).arg(format);
|
|
QString data = QString("%1,%2,%3,%4").arg(width).arg(height).arg(fps).arg(format);
|
|
|
|
m_formatCombo->addItem(displayText, data);
|
|
}
|
|
|
|
updateStatus(QString("Found %1 available formats").arg(formats.size()), true);
|
|
}
|
|
},
|
|
[this](const QString& error) {
|
|
updateStatus("Error: Failed to get formats: " + error, false);
|
|
});
|
|
}
|
|
|
|
void CameraControlWidget::onSetFormat()
|
|
{
|
|
QString data = m_formatCombo->currentData().toString();
|
|
QStringList parts = data.split(',');
|
|
|
|
if (parts.size() != 4) {
|
|
updateStatus("Error: Invalid format selection", false);
|
|
return;
|
|
}
|
|
|
|
QJsonObject params;
|
|
params["width"] = parts[0];
|
|
params["height"] = parts[1];
|
|
params["framerate"] = parts[2];
|
|
params["format"] = parts[3];
|
|
|
|
m_socketClient->sendCommand("set_format", params,
|
|
[this](const QJsonObject& response) {
|
|
updateStatus("Format set successfully", true);
|
|
},
|
|
[this](const QString& error) {
|
|
updateStatus("Error: Failed to set format: " + error, false);
|
|
});
|
|
}
|
|
|
|
void CameraControlWidget::onSetExposure()
|
|
{
|
|
QJsonObject params;
|
|
params["mode"] = m_exposureAuto->isChecked() ? "auto" : "manual";
|
|
if (m_exposureManual->isChecked()) {
|
|
params["value"] = QString::number(m_exposureValue->value());
|
|
}
|
|
|
|
m_socketClient->sendCommand("set_exposure", params,
|
|
[this](const QJsonObject& response) {
|
|
updateStatus("Exposure set successfully", true);
|
|
},
|
|
[this](const QString& error) {
|
|
updateStatus("Error: Failed to set exposure: " + error, false);
|
|
});
|
|
}
|
|
|
|
void CameraControlWidget::onSetWhiteBalance()
|
|
{
|
|
QJsonObject params;
|
|
params["mode"] = m_whiteBalanceAuto->isChecked() ? "auto" : "manual";
|
|
if (m_whiteBalanceManual->isChecked()) {
|
|
params["temperature"] = QString::number(m_whiteBalanceTemp->value());
|
|
}
|
|
|
|
m_socketClient->sendCommand("set_whitebalance", params,
|
|
[this](const QJsonObject& response) {
|
|
updateStatus("White balance set successfully", true);
|
|
},
|
|
[this](const QString& error) {
|
|
updateStatus("Error: Failed to set white balance: " + error, false);
|
|
});
|
|
}
|
|
|
|
void CameraControlWidget::onBrightnessChanged(int value)
|
|
{
|
|
QJsonObject params;
|
|
params["value"] = QString::number(value);
|
|
|
|
m_socketClient->sendCommand("set_brightness", params,
|
|
[](const QJsonObject&) {},
|
|
[](const QString&) {});
|
|
}
|
|
|
|
void CameraControlWidget::onContrastChanged(int value)
|
|
{
|
|
QJsonObject params;
|
|
params["value"] = QString::number(value);
|
|
|
|
m_socketClient->sendCommand("set_contrast", params,
|
|
[](const QJsonObject&) {},
|
|
[](const QString&) {});
|
|
}
|
|
|
|
void CameraControlWidget::onSaturationChanged(int value)
|
|
{
|
|
QJsonObject params;
|
|
params["value"] = QString::number(value);
|
|
|
|
m_socketClient->sendCommand("set_saturation", params,
|
|
[](const QJsonObject&) {},
|
|
[](const QString&) {});
|
|
}
|
|
|
|
void CameraControlWidget::onSharpnessChanged(int value)
|
|
{
|
|
QJsonObject params;
|
|
params["value"] = QString::number(value);
|
|
|
|
m_socketClient->sendCommand("set_sharpness", params,
|
|
[](const QJsonObject&) {},
|
|
[](const QString&) {});
|
|
}
|
|
|
|
void CameraControlWidget::onGammaChanged(int value)
|
|
{
|
|
QJsonObject params;
|
|
params["value"] = QString::number(value);
|
|
|
|
m_socketClient->sendCommand("set_gamma", params,
|
|
[](const QJsonObject&) {},
|
|
[](const QString&) {});
|
|
}
|
|
|
|
void CameraControlWidget::onGainChanged(int value)
|
|
{
|
|
QJsonObject params;
|
|
params["value"] = QString::number(value);
|
|
|
|
m_socketClient->sendCommand("set_gain", params,
|
|
[](const QJsonObject&) {},
|
|
[](const QString&) {});
|
|
}
|
|
|
|
void CameraControlWidget::onExposureModeChanged()
|
|
{
|
|
m_exposureValue->setEnabled(m_exposureManual->isChecked());
|
|
}
|
|
|
|
void CameraControlWidget::onWhiteBalanceModeChanged()
|
|
{
|
|
m_whiteBalanceTemp->setEnabled(m_whiteBalanceManual->isChecked());
|
|
}
|
|
|
|
void CameraControlWidget::updateStatus(const QString& status, bool isSuccess)
|
|
{
|
|
m_statusLabel->setText("Status: " + status);
|
|
|
|
if (isSuccess) {
|
|
m_statusLabel->setStyleSheet("QLabel { background-color: #90EE90; padding: 5px; border-radius: 3px; }");
|
|
} else if (status.startsWith("Error")) {
|
|
m_statusLabel->setStyleSheet("QLabel { background-color: #FFB6C1; padding: 5px; border-radius: 3px; }");
|
|
} else {
|
|
m_statusLabel->setStyleSheet("QLabel { background-color: #f0f0f0; padding: 5px; border-radius: 3px; }");
|
|
}
|
|
}
|