Split macro-condition-video.cpp UI into per-widget files
Some checks failed
debian-build / build (push) Has been cancelled
Push to master / Check Formatting 🔍 (push) Has been cancelled
Push to master / Build Project 🧱 (push) Has been cancelled
Push to master / Create Release 🛫 (push) Has been cancelled

This makes it easier to conditionally exclude video-edit-object.cpp
from the build when OpenCV >= 5 drops CascadeClassifier support.
This commit is contained in:
WarmUpTill
2026-07-18 22:13:54 +02:00
committed by WarmUpTill
parent 1030868a75
commit 107d77f1b8
23 changed files with 1098 additions and 861 deletions

View File

@@ -1,17 +1,15 @@
#include "macro-condition-video.hpp"
#include "cascade-classifier-detector.hpp"
#include "screenshot-dialog.hpp"
#include "layout-helpers.hpp"
#include "macro-condition-edit.hpp"
#include "plugin-state-helpers.hpp"
#include "selection-helpers.hpp"
#include "ui-helpers.hpp"
#include <layout-helpers.hpp>
#include <macro-condition-edit.hpp>
#include <plugin-state-helpers.hpp>
#include <QBuffer>
#include <QDesktopServices>
#include <QFileDialog>
#include <QMessageBox>
#include <QtGlobal>
#include <QToolTip>
#include <ui-helpers.hpp>
#include <selection-helpers.hpp>
namespace advss {
@@ -22,30 +20,6 @@ bool MacroConditionVideo::_registered = MacroConditionFactory::Register(
{MacroConditionVideo::Create, MacroConditionVideoEdit::Create,
"AdvSceneSwitcher.condition.video"});
const static std::map<VideoCondition, std::string> conditionTypes = {
{VideoCondition::MATCH,
"AdvSceneSwitcher.condition.video.condition.match"},
{VideoCondition::DIFFER,
"AdvSceneSwitcher.condition.video.condition.differ"},
{VideoCondition::HAS_NOT_CHANGED,
"AdvSceneSwitcher.condition.video.condition.hasNotChanged"},
{VideoCondition::HAS_CHANGED,
"AdvSceneSwitcher.condition.video.condition.hasChanged"},
{VideoCondition::NO_IMAGE,
"AdvSceneSwitcher.condition.video.condition.noImage"},
{VideoCondition::PATTERN,
"AdvSceneSwitcher.condition.video.condition.pattern"},
{VideoCondition::OBJECT,
"AdvSceneSwitcher.condition.video.condition.object"},
{VideoCondition::BRIGHTNESS,
"AdvSceneSwitcher.condition.video.condition.brightness"},
#ifdef OCR_SUPPORT
{VideoCondition::OCR, "AdvSceneSwitcher.condition.video.condition.ocr"},
#endif
{VideoCondition::COLOR,
"AdvSceneSwitcher.condition.video.condition.color"},
};
const static std::map<VideoInput::Type, std::string> videoInputTypes = {
{VideoInput::Type::OBS_MAIN_OUTPUT,
"AdvSceneSwitcher.condition.video.type.main"},
@@ -64,27 +38,6 @@ const static std::map<cv::TemplateMatchModes, std::string> patternMatchModes = {
"AdvSceneSwitcher.condition.video.patternMatchMode.squaredDifference"},
};
const static std::map<tesseract::PageSegMode, std::string> pageSegModes = {
{tesseract::PageSegMode::PSM_SINGLE_COLUMN,
"AdvSceneSwitcher.condition.video.ocrMode.singleColumn"},
{tesseract::PageSegMode::PSM_SINGLE_BLOCK_VERT_TEXT,
"AdvSceneSwitcher.condition.video.ocrMode.singleBlockVertText"},
{tesseract::PageSegMode::PSM_SINGLE_BLOCK,
"AdvSceneSwitcher.condition.video.ocrMode.singleBlock"},
{tesseract::PageSegMode::PSM_SINGLE_LINE,
"AdvSceneSwitcher.condition.video.ocrMode.singleLine"},
{tesseract::PageSegMode::PSM_SINGLE_WORD,
"AdvSceneSwitcher.condition.video.ocrMode.singleWord"},
{tesseract::PageSegMode::PSM_CIRCLE_WORD,
"AdvSceneSwitcher.condition.video.ocrMode.circleWord"},
{tesseract::PageSegMode::PSM_SINGLE_CHAR,
"AdvSceneSwitcher.condition.video.ocrMode.singleChar"},
{tesseract::PageSegMode::PSM_SPARSE_TEXT,
"AdvSceneSwitcher.condition.video.ocrMode.sparseText"},
{tesseract::PageSegMode::PSM_SPARSE_TEXT_OSD,
"AdvSceneSwitcher.condition.video.ocrMode.sparseTextOSD"},
};
static bool requiresFileInput(VideoCondition t)
{
return t == VideoCondition::MATCH || t == VideoCondition::DIFFER ||
@@ -94,7 +47,7 @@ static bool requiresFileInput(VideoCondition t)
bool MacroConditionVideo::CheckShouldBeSkipped()
{
if (_condition != VideoCondition::PATTERN &&
_condition != VideoCondition::OBJECT &&
_condition != VideoCondition::OBJECT_CASCADE &&
_condition != VideoCondition::HAS_CHANGED &&
_condition != VideoCondition::HAS_NOT_CHANGED) {
return false;
@@ -185,7 +138,7 @@ bool MacroConditionVideo::Save(obs_data_t *obj) const
_blockUntilScreenshotDone);
_brightnessThreshold.Save(obj, "brightnessThreshold");
_patternMatchParameters.Save(obj);
_objMatchParameters.Save(obj);
_cascadeMatchParameters.Save(obj);
_ocrParameters.Save(obj);
_colorParameters.Save(obj);
obs_data_set_bool(obj, "throttleEnabled", _throttleEnabled);
@@ -211,7 +164,7 @@ bool MacroConditionVideo::Load(obs_data_t *obj)
_brightnessThreshold.Load(obj, "brightnessThreshold");
}
_patternMatchParameters.Load(obj);
_objMatchParameters.Load(obj);
_cascadeMatchParameters.Load(obj);
_ocrParameters.Load(obj);
_colorParameters.Load(obj);
_throttleEnabled = obs_data_get_bool(obj, "throttleEnabled");
@@ -378,15 +331,11 @@ bool MacroConditionVideo::OutputChanged()
bool MacroConditionVideo::ScreenshotContainsObject()
{
auto model = _objMatchParameters.GetModel();
if (!model) {
auto *detector = _cascadeMatchParameters.GetDetector();
if (!detector) {
return false;
}
auto objects = MatchObject(_screenshotData.GetImage(), *model,
_objMatchParameters.scaleFactor,
_objMatchParameters.minNeighbors,
_objMatchParameters.minSize.CV(),
_objMatchParameters.maxSize.CV());
auto objects = detector->Detect(_screenshotData.GetImage());
const auto count = objects.size();
SetTempVarValue("objectCount", std::to_string(count));
return count > 0;
@@ -463,7 +412,7 @@ bool MacroConditionVideo::Compare()
return _screenshotData.GetImage().isNull();
case VideoCondition::PATTERN:
return ScreenshotContainsPattern();
case VideoCondition::OBJECT:
case VideoCondition::OBJECT_CASCADE:
return ScreenshotContainsObject();
case VideoCondition::BRIGHTNESS:
return CheckBrightnessThreshold();
@@ -531,7 +480,7 @@ void MacroConditionVideo::SetupTempVars()
obs_module_text(
"AdvSceneSwitcher.tempVar.video.matchHeight.description"));
break;
case VideoCondition::OBJECT:
case VideoCondition::OBJECT_CASCADE:
AddTempvar(
"objectCount",
obs_module_text(
@@ -584,18 +533,41 @@ static inline void populateVideoInputSelection(QComboBox *list)
static inline void populateConditionSelection(QComboBox *list)
{
const static std::vector<std::pair<VideoCondition, std::string>>
conditionTypes = {
{VideoCondition::MATCH,
"AdvSceneSwitcher.condition.video.condition.match"},
{VideoCondition::DIFFER,
"AdvSceneSwitcher.condition.video.condition.differ"},
{VideoCondition::HAS_NOT_CHANGED,
"AdvSceneSwitcher.condition.video.condition.hasNotChanged"},
{VideoCondition::HAS_CHANGED,
"AdvSceneSwitcher.condition.video.condition.hasChanged"},
{VideoCondition::NO_IMAGE,
"AdvSceneSwitcher.condition.video.condition.noImage"},
{VideoCondition::PATTERN,
"AdvSceneSwitcher.condition.video.condition.pattern"},
{VideoCondition::OBJECT_CASCADE,
"AdvSceneSwitcher.condition.video.condition.object"},
{VideoCondition::BRIGHTNESS,
"AdvSceneSwitcher.condition.video.condition.brightness"},
#ifdef OCR_SUPPORT
{VideoCondition::OCR,
"AdvSceneSwitcher.condition.video.condition.ocr"},
#endif
{VideoCondition::COLOR,
"AdvSceneSwitcher.condition.video.condition.color"},
};
for (auto &[value, name] : conditionTypes) {
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(value));
}
}
static inline void populatePageSegModeSelection(QComboBox *list)
{
for (const auto &[mode, name] : pageSegModes) {
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(mode));
}
SetRowVisibleByValue(
list,
obs_module_text(
"AdvSceneSwitcher.condition.video.condition.object"),
CascadeClassifierDetector::IsSupported());
}
static inline void populatePatternMatchModeSelection(QComboBox *list)
@@ -606,628 +578,6 @@ static inline void populatePatternMatchModeSelection(QComboBox *list)
}
}
BrightnessEdit::BrightnessEdit(QWidget *parent,
const std::shared_ptr<MacroConditionVideo> &data)
: QWidget(parent),
_threshold(new SliderSpinBox(
0., 1.,
obs_module_text(
"AdvSceneSwitcher.condition.video.brightnessThreshold"),
obs_module_text(
"AdvSceneSwitcher.condition.video.brightnessThresholdDescription"))),
_current(new QLabel),
_entryData(data)
{
auto layout = new QVBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(_threshold);
layout->addWidget(_current);
setLayout(layout);
QWidget::connect(
_threshold,
SIGNAL(DoubleValueChanged(const NumberVariable<double> &)),
this,
SLOT(BrightnessThresholdChanged(
const NumberVariable<double> &)));
QWidget::connect(&_timer, &QTimer::timeout, this,
&BrightnessEdit::UpdateCurrentBrightness);
_timer.start(1000);
_threshold->SetDoubleValue(_entryData->_brightnessThreshold);
_loading = false;
}
void BrightnessEdit::UpdateCurrentBrightness()
{
QString text = obs_module_text(
"AdvSceneSwitcher.condition.video.currentBrightness");
_current->setText(text.arg(_entryData->GetCurrentBrightness()));
}
void BrightnessEdit::BrightnessThresholdChanged(const DoubleVariable &value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_brightnessThreshold = value;
}
static void openFileInEditor(const std::string &filepath)
{
const auto path = QString::fromStdString(filepath);
const QFileInfo fileInfo(path);
if (!fileInfo.exists()) {
QFile file(path);
if (!file.open(QIODevice::WriteOnly)) {
DisplayMessage(obs_module_text(
"AdvSceneSwitcher.condition.video.ocrOpenConfig.createFailed"));
return;
}
file.close();
}
QUrl fileUrl = QUrl::fromLocalFile(path);
if (!QDesktopServices::openUrl(fileUrl)) {
DisplayMessage(obs_module_text(
"AdvSceneSwitcher.condition.video.ocrOpenConfig.openFailed"));
}
}
OCREdit::OCREdit(QWidget *parent, PreviewDialog *previewDialog,
const std::shared_ptr<MacroConditionVideo> &data)
: QWidget(parent),
_matchText(new VariableTextEdit(this)),
_regex(new RegexConfigWidget(this)),
_colorButton(new VariableColorButton(
this,
obs_module_text(
"AdvSceneSwitcher.condition.video.selectColor"))),
_colorThreshold(new SliderSpinBox(
0., 1.,
obs_module_text(
"AdvSceneSwitcher.condition.video.colorDeviationThreshold"),
obs_module_text(
"AdvSceneSwitcher.condition.video.colorDeviationThresholdDescription"),
true)),
_pageSegMode(new QComboBox()),
_tesseractBaseDir(new FileSelection(FileSelection::Type::FOLDER)),
_languageCode(new VariableLineEdit(this)),
_useConfig(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.condition.video.ocrUseConfigFile"))),
_configFile(new FileSelection(FileSelection::Type::WRITE, this)),
_openConfigFile(new QPushButton(obs_module_text(
"AdvSceneSwitcher.condition.video.ocrOpenConfigFile"))),
_reloadConfig(new QPushButton()),
_configLayout(new QHBoxLayout()),
_previewDialog(previewDialog),
_entryData(data)
{
populatePageSegModeSelection(_pageSegMode);
_reloadConfig->setMaximumWidth(22);
SetButtonIcon(_reloadConfig, GetThemeTypeName() == "Light"
? ":res/images/refresh.svg"
: "theme:Dark/refresh.svg");
_reloadConfig->setToolTip(obs_module_text(
"AdvSceneSwitcher.condition.video.ocrConfigReload"));
QWidget::connect(_colorButton,
SIGNAL(ColorVariableChanged(const ColorVariable &)),
this, SLOT(ColorChanged(const ColorVariable &)));
QWidget::connect(
_colorThreshold,
SIGNAL(DoubleValueChanged(const NumberVariable<double> &)),
this,
SLOT(ColorThresholdChanged(const NumberVariable<double> &)));
QWidget::connect(_matchText, SIGNAL(textChanged()), this,
SLOT(MatchTextChanged()));
QWidget::connect(_regex,
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
SLOT(RegexChanged(const RegexConfig &)));
QWidget::connect(_pageSegMode, SIGNAL(currentIndexChanged(int)), this,
SLOT(PageSegModeChanged(int)));
QWidget::connect(_tesseractBaseDir,
SIGNAL(PathChanged(const QString &)), this,
SLOT(TesseractBaseDirChanged(const QString &)));
QWidget::connect(_languageCode, SIGNAL(editingFinished()), this,
SLOT(LanguageChanged()));
QWidget::connect(_useConfig, SIGNAL(stateChanged(int)), this,
SLOT(UseConfigChanged(int)));
QWidget::connect(_configFile, SIGNAL(PathChanged(const QString &)),
this, SLOT(ConfigFileChanged(const QString &)));
QWidget::connect(_openConfigFile, &QPushButton::clicked, [this](bool) {
openFileInEditor(
_entryData->_ocrParameters.GetCustomConfigFile());
});
QWidget::connect(_reloadConfig, &QPushButton::clicked, [this](bool) {
GUARD_LOADING_AND_LOCK();
_entryData->_ocrParameters.EnableCustomConfig(true);
_previewDialog->OCRParametersChanged(
_entryData->_ocrParameters);
});
auto configFileHint = new QLabel();
const QString path = GetThemeTypeName() == "Light"
? ":/res/images/help.svg"
: ":/res/images/help_light.svg";
const QIcon icon(path);
const QPixmap pixmap = icon.pixmap(QSize(16, 16));
configFileHint->setPixmap(pixmap);
configFileHint->setToolTip(obs_module_text(
"AdvSceneSwitcher.condition.video.ocrConfigHint"));
const std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{color}}", _colorButton},
{"{{textType}}", _pageSegMode},
{"{{tesseractBaseDir}}", _tesseractBaseDir},
{"{{languageCode}}", _languageCode},
{"{{configFile}}", _configFile},
{"{{openConfigFile}}", _openConfigFile},
{"{{reloadConfig}}", _reloadConfig},
{"{{configFileHint}}", configFileHint},
};
auto layout = new QVBoxLayout();
layout->setContentsMargins(0, 0, 0, 0);
auto textLayout = new QHBoxLayout();
textLayout->setContentsMargins(0, 0, 0, 0);
textLayout->addWidget(_matchText);
textLayout->addWidget(_regex);
layout->addLayout(textLayout);
auto pageModeSegLayout = new QHBoxLayout();
PlaceWidgets(
obs_module_text(
"AdvSceneSwitcher.condition.video.layout.ocrTextType"),
pageModeSegLayout, widgetPlaceholders);
layout->addLayout(pageModeSegLayout);
auto baseDirLayout = new QHBoxLayout();
PlaceWidgets(
obs_module_text(
"AdvSceneSwitcher.condition.video.layout.ocrBaseDir"),
baseDirLayout, widgetPlaceholders, false);
layout->addLayout(baseDirLayout);
auto languageLayout = new QHBoxLayout();
PlaceWidgets(
obs_module_text(
"AdvSceneSwitcher.condition.video.layout.ocrLanguage"),
languageLayout, widgetPlaceholders);
layout->addLayout(languageLayout);
PlaceWidgets(
obs_module_text(
"AdvSceneSwitcher.condition.video.layout.ocrConfig"),
_configLayout, widgetPlaceholders, false);
layout->addWidget(_useConfig);
layout->addLayout(_configLayout);
auto colorPickLayout = new QHBoxLayout();
PlaceWidgets(
obs_module_text(
"AdvSceneSwitcher.condition.video.layout.ocrColorPick"),
colorPickLayout, widgetPlaceholders);
layout->addLayout(colorPickLayout);
layout->addWidget(_colorThreshold);
setLayout(layout);
_matchText->setPlainText(_entryData->_ocrParameters.text);
_regex->SetRegexConfig(_entryData->_ocrParameters.regex);
_colorButton->SetValue(_entryData->_ocrParameters.color);
_colorThreshold->SetDoubleValue(
_entryData->_ocrParameters.colorThreshold);
_pageSegMode->setCurrentIndex(_pageSegMode->findData(
static_cast<int>(_entryData->_ocrParameters.GetPageMode())));
_tesseractBaseDir->SetPath(
_entryData->_ocrParameters.GetTesseractBasePath());
_languageCode->setText(_entryData->_ocrParameters.GetLanguageCode());
_useConfig->setChecked(
_entryData->_ocrParameters.CustomConfigIsEnabled());
_configFile->SetPath(_entryData->_ocrParameters.GetCustomConfigFile());
SetLayoutVisible(_configLayout,
_entryData->_ocrParameters.CustomConfigIsEnabled());
_loading = false;
}
void OCREdit::ColorChanged(const ColorVariable &value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_ocrParameters.color = value;
_previewDialog->OCRParametersChanged(_entryData->_ocrParameters);
}
void OCREdit::ColorThresholdChanged(const DoubleVariable &value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_ocrParameters.colorThreshold = value;
_previewDialog->OCRParametersChanged(_entryData->_ocrParameters);
}
void OCREdit::MatchTextChanged()
{
GUARD_LOADING_AND_LOCK();
_entryData->_ocrParameters.text =
_matchText->toPlainText().toUtf8().constData();
adjustSize();
updateGeometry();
_previewDialog->OCRParametersChanged(_entryData->_ocrParameters);
}
void OCREdit::RegexChanged(const RegexConfig &conf)
{
GUARD_LOADING_AND_LOCK();
_entryData->_ocrParameters.regex = conf;
adjustSize();
updateGeometry();
_previewDialog->OCRParametersChanged(_entryData->_ocrParameters);
}
void OCREdit::PageSegModeChanged(int idx)
{
GUARD_LOADING_AND_LOCK();
_entryData->SetPageSegMode(static_cast<tesseract::PageSegMode>(
_pageSegMode->itemData(idx).toInt()));
_previewDialog->OCRParametersChanged(_entryData->_ocrParameters);
}
void OCREdit::TesseractBaseDirChanged(const QString &path)
{
GUARD_LOADING_AND_LOCK();
if (!_entryData->SetTesseractBaseDir(path.toStdString())) {
const QString message(obs_module_text(
"AdvSceneSwitcher.condition.video.ocrLanguageNotFound"));
const QDir dataDir(path);
const QString fileName(_languageCode->text() + ".traineddata");
DisplayMessage(message.arg(fileName, dataDir.absolutePath()));
// Reset to previous value
const QSignalBlocker b(this);
_tesseractBaseDir->SetPath(
_entryData->_ocrParameters.GetTesseractBasePath());
return;
}
_previewDialog->OCRParametersChanged(_entryData->_ocrParameters);
}
void OCREdit::LanguageChanged()
{
GUARD_LOADING_AND_LOCK();
if (!_entryData->SetLanguageCode(_languageCode->text().toStdString())) {
const QString message(obs_module_text(
"AdvSceneSwitcher.condition.video.ocrLanguageNotFound"));
const QDir dataDir(QString::fromStdString(
_entryData->_ocrParameters.GetTesseractBasePath()));
const QString fileName(_languageCode->text() + ".traineddata");
DisplayMessage(message.arg(fileName, dataDir.absolutePath()));
// Reset to previous value
const QSignalBlocker b(this);
_languageCode->setText(
_entryData->_ocrParameters.GetLanguageCode());
return;
}
_previewDialog->OCRParametersChanged(_entryData->_ocrParameters);
}
void OCREdit::UseConfigChanged(int value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_ocrParameters.EnableCustomConfig(value);
SetLayoutVisible(_configLayout, value);
adjustSize();
updateGeometry();
_previewDialog->OCRParametersChanged(_entryData->_ocrParameters);
}
void OCREdit::ConfigFileChanged(const QString &path)
{
GUARD_LOADING_AND_LOCK();
_entryData->_ocrParameters.SetCustomConfigFile(path.toStdString());
_previewDialog->OCRParametersChanged(_entryData->_ocrParameters);
}
ObjectDetectEdit::ObjectDetectEdit(
QWidget *parent, PreviewDialog *previewDialog,
const std::shared_ptr<MacroConditionVideo> &data)
: QWidget(parent),
_modelDataPath(new FileSelection()),
_objectScaleThreshold(new SliderSpinBox(
1.1, 5.,
obs_module_text(
"AdvSceneSwitcher.condition.video.objectScaleThreshold"),
obs_module_text(
"AdvSceneSwitcher.condition.video.objectScaleThresholdDescription"))),
_minNeighbors(new QSpinBox()),
_minNeighborsDescription(new QLabel(obs_module_text(
"AdvSceneSwitcher.condition.video.minNeighborDescription"))),
_minSize(new SizeSelection(0, 1024)),
_maxSize(new SizeSelection(0, 4096)),
_previewDialog(previewDialog),
_entryData(data)
{
_minNeighbors->setMinimum(minMinNeighbors);
_minNeighbors->setMaximum(maxMinNeighbors);
QWidget::connect(
_objectScaleThreshold,
SIGNAL(DoubleValueChanged(const NumberVariable<double> &)),
this,
SLOT(ObjectScaleThresholdChanged(
const NumberVariable<double> &)));
QWidget::connect(_minNeighbors, SIGNAL(valueChanged(int)), this,
SLOT(MinNeighborsChanged(int)));
QWidget::connect(_minSize, SIGNAL(SizeChanged(Size)), this,
SLOT(MinSizeChanged(Size)));
QWidget::connect(_maxSize, SIGNAL(SizeChanged(Size)), this,
SLOT(MaxSizeChanged(Size)));
QWidget::connect(_modelDataPath, SIGNAL(PathChanged(const QString &)),
this, SLOT(ModelPathChanged(const QString &)));
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{minNeighbors}}", _minNeighbors},
{"{{minSize}}", _minSize},
{"{{maxSize}}", _maxSize},
{"{{modelDataPath}}", _modelDataPath},
};
auto pathLayout = new QHBoxLayout;
pathLayout->setContentsMargins(0, 0, 0, 0);
PlaceWidgets(
obs_module_text(
"AdvSceneSwitcher.condition.video.layout.modelPath"),
pathLayout, widgetPlaceholders);
auto neighborsLayout = new QHBoxLayout;
neighborsLayout->setContentsMargins(0, 0, 0, 0);
PlaceWidgets(
obs_module_text(
"AdvSceneSwitcher.condition.video.layout.minNeighbor"),
neighborsLayout, widgetPlaceholders);
auto sizeGrid = new QGridLayout;
sizeGrid->addWidget(
new QLabel(obs_module_text(
"AdvSceneSwitcher.condition.video.minSize")),
0, 0);
sizeGrid->addWidget(_minSize, 0, 1);
sizeGrid->addWidget(
new QLabel(obs_module_text(
"AdvSceneSwitcher.condition.video.maxSize")),
1, 0);
sizeGrid->addWidget(_maxSize, 1, 1);
auto sizeLayout = new QHBoxLayout;
sizeLayout->setContentsMargins(0, 0, 0, 0);
sizeLayout->addLayout(sizeGrid);
sizeLayout->addStretch();
auto layout = new QVBoxLayout();
layout->setContentsMargins(0, 0, 0, 0);
layout->addLayout(pathLayout);
layout->addLayout(neighborsLayout);
layout->addLayout(sizeLayout);
setLayout(layout);
_modelDataPath->SetPath(_entryData->_objMatchParameters.GetModelPath());
_objectScaleThreshold->SetDoubleValue(
_entryData->_objMatchParameters.scaleFactor);
_minNeighbors->setValue(_entryData->_objMatchParameters.minNeighbors);
_minSize->SetSize(_entryData->_objMatchParameters.minSize);
_maxSize->SetSize(_entryData->_objMatchParameters.maxSize);
_loading = false;
}
void ObjectDetectEdit::ObjectScaleThresholdChanged(const DoubleVariable &value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_objMatchParameters.scaleFactor = value;
_previewDialog->ObjDetectParametersChanged(
_entryData->_objMatchParameters);
}
void ObjectDetectEdit::MinNeighborsChanged(int value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_objMatchParameters.minNeighbors = value;
_previewDialog->ObjDetectParametersChanged(
_entryData->_objMatchParameters);
}
void ObjectDetectEdit::MinSizeChanged(advss::Size value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_objMatchParameters.minSize = value;
_previewDialog->ObjDetectParametersChanged(
_entryData->_objMatchParameters);
}
void ObjectDetectEdit::MaxSizeChanged(advss::Size value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_objMatchParameters.maxSize = value;
_previewDialog->ObjDetectParametersChanged(
_entryData->_objMatchParameters);
}
void ObjectDetectEdit::ModelPathChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
bool dataLoaded = false;
{
auto lock = LockContext();
std::string path = text.toStdString();
dataLoaded = _entryData->_objMatchParameters.SetModelPath(path);
}
if (!dataLoaded) {
DisplayMessage(obs_module_text(
"AdvSceneSwitcher.condition.video.modelLoadFail"));
}
_previewDialog->ObjDetectParametersChanged(
_entryData->_objMatchParameters);
}
ColorEdit::ColorEdit(QWidget *parent,
const std::shared_ptr<MacroConditionVideo> &data)
: QWidget(parent),
_matchThreshold(new SliderSpinBox(
0., 1.,
obs_module_text(
"AdvSceneSwitcher.condition.video.colorMatchThreshold"),
obs_module_text(
"AdvSceneSwitcher.condition.video.colorMatchThresholdDescription"),
true)),
_colorThreshold(new SliderSpinBox(
0., 1.,
obs_module_text(
"AdvSceneSwitcher.condition.video.colorDeviationThreshold"),
obs_module_text(
"AdvSceneSwitcher.condition.video.colorDeviationThresholdDescription"),
true)),
_colorButton(new VariableColorButton(
this,
obs_module_text(
"AdvSceneSwitcher.condition.video.selectColor"))),
_entryData(data)
{
QWidget::connect(_colorButton,
SIGNAL(ColorVariableChanged(const ColorVariable &)),
this, SLOT(ColorChanged(const ColorVariable &)));
QWidget::connect(
_matchThreshold,
SIGNAL(DoubleValueChanged(const NumberVariable<double> &)),
this,
SLOT(MatchThresholdChanged(const NumberVariable<double> &)));
QWidget::connect(
_colorThreshold,
SIGNAL(DoubleValueChanged(const NumberVariable<double> &)),
this,
SLOT(ColorThresholdChanged(const NumberVariable<double> &)));
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{color}}", _colorButton},
};
auto colorLayout = new QHBoxLayout;
PlaceWidgets(obs_module_text(
"AdvSceneSwitcher.condition.video.layout.color"),
colorLayout, widgetPlaceholders);
auto layout = new QVBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->addLayout(colorLayout);
layout->addWidget(_colorThreshold);
layout->addWidget(_matchThreshold);
setLayout(layout);
_matchThreshold->SetDoubleValue(
_entryData->_colorParameters.matchThreshold);
_colorThreshold->SetDoubleValue(
_entryData->_colorParameters.colorThreshold);
_colorButton->SetValue(_entryData->_colorParameters.color);
_loading = false;
}
void ColorEdit::ColorChanged(const ColorVariable &value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_colorParameters.color = value;
}
void ColorEdit::MatchThresholdChanged(const DoubleVariable &value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_colorParameters.matchThreshold = value;
}
void ColorEdit::ColorThresholdChanged(const DoubleVariable &value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_colorParameters.colorThreshold = value;
}
AreaEdit::AreaEdit(QWidget *parent, PreviewDialog *previewDialog,
const std::shared_ptr<MacroConditionVideo> &data)
: QWidget(parent),
_checkAreaEnable(new QCheckBox(obs_module_text(
"AdvSceneSwitcher.condition.video.layout.checkAreaEnable"))),
_checkArea(new AreaSelection(0, 99999)),
_selectArea(new QPushButton(obs_module_text(
"AdvSceneSwitcher.condition.video.selectArea"))),
_previewDialog(previewDialog),
_entryData(data)
{
QWidget::connect(_checkAreaEnable, SIGNAL(stateChanged(int)), this,
SLOT(CheckAreaEnableChanged(int)));
QWidget::connect(_checkArea, SIGNAL(AreaChanged(Area)), this,
SLOT(CheckAreaChanged(Area)));
QWidget::connect(_selectArea, SIGNAL(clicked()), this,
SLOT(SelectAreaClicked()));
QWidget::connect(_previewDialog, SIGNAL(SelectionAreaChanged(QRect)),
this, SLOT(CheckAreaChanged(QRect)));
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{checkAreaEnable}}", _checkAreaEnable},
{"{{checkArea}}", _checkArea},
{"{{selectArea}}", _selectArea},
};
auto layout = new QHBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
PlaceWidgets(
obs_module_text(
"AdvSceneSwitcher.condition.video.layout.checkArea"),
layout, widgetPlaceholders);
setLayout(layout);
_checkAreaEnable->setChecked(_entryData->_areaParameters.enable);
_checkArea->SetArea(_entryData->_areaParameters.area);
SetWidgetVisibility();
_loading = false;
}
void AreaEdit::SetWidgetVisibility()
{
_checkArea->setVisible(_entryData->_areaParameters.enable);
_selectArea->setVisible(_entryData->_areaParameters.enable);
adjustSize();
updateGeometry();
}
void AreaEdit::SelectAreaClicked()
{
_previewDialog->show();
_previewDialog->raise();
_previewDialog->activateWindow();
_previewDialog->SelectArea();
}
void AreaEdit::CheckAreaEnableChanged(int value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_areaParameters.enable = value;
SetWidgetVisibility();
_previewDialog->AreaParametersChanged(_entryData->_areaParameters);
emit Resized();
}
void AreaEdit::CheckAreaChanged(Area value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_areaParameters.area = value;
_previewDialog->AreaParametersChanged(_entryData->_areaParameters);
}
void AreaEdit::CheckAreaChanged(QRect rect)
{
const QSignalBlocker b(_checkArea);
Area area{rect.topLeft().x(), rect.y(), rect.width(), rect.height()};
_checkArea->SetArea(area);
CheckAreaChanged(area);
}
static QStringList getVideoSourcesList()
{
auto sources = GetVideoSourceNames();
@@ -1263,7 +613,8 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
_previewDialog(this),
_brightness(new BrightnessEdit(this, entryData)),
_ocr(new OCREdit(this, &_previewDialog, entryData)),
_objectDetect(new ObjectDetectEdit(this, &_previewDialog, entryData)),
_cascadeClassifierEdit(
new CascadeClassifierEdit(this, &_previewDialog, entryData)),
_color(new ColorEdit(this, entryData)),
_area(new AreaEdit(this, &_previewDialog, entryData)),
_throttleControlLayout(new QHBoxLayout),
@@ -1292,8 +643,8 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
QSizePolicy::Preferred);
_ocr->setSizePolicy(QSizePolicy::MinimumExpanding,
QSizePolicy::Preferred);
_objectDetect->setSizePolicy(QSizePolicy::MinimumExpanding,
QSizePolicy::Preferred);
_cascadeClassifierEdit->setSizePolicy(QSizePolicy::MinimumExpanding,
QSizePolicy::Preferred);
_color->setSizePolicy(QSizePolicy::MinimumExpanding,
QSizePolicy::Preferred);
_area->setSizePolicy(QSizePolicy::MinimumExpanding,
@@ -1392,7 +743,7 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
mainLayout->addLayout(_patternMatchModeLayout);
mainLayout->addWidget(_brightness);
mainLayout->addWidget(_ocr);
mainLayout->addWidget(_objectDetect);
mainLayout->addWidget(_cascadeClassifierEdit);
mainLayout->addWidget(_color);
mainLayout->addLayout(_throttleControlLayout);
mainLayout->addWidget(_area);
@@ -1634,13 +985,14 @@ void MacroConditionVideoEdit::ShowMatchClicked()
static bool needsShowMatch(VideoCondition cond)
{
return cond == VideoCondition::PATTERN ||
cond == VideoCondition::OBJECT || cond == VideoCondition::OCR;
cond == VideoCondition::OBJECT_CASCADE ||
cond == VideoCondition::OCR;
}
static bool needsThrottleControls(VideoCondition cond)
{
return cond == VideoCondition::PATTERN ||
cond == VideoCondition::OBJECT ||
cond == VideoCondition::OBJECT_CASCADE ||
cond == VideoCondition::HAS_CHANGED ||
cond == VideoCondition::HAS_NOT_CHANGED;
}
@@ -1681,8 +1033,8 @@ void MacroConditionVideoEdit::SetWidgetVisibility()
VideoCondition::BRIGHTNESS);
_showMatch->setVisible(needsShowMatch(_entryData->GetCondition()));
_ocr->setVisible(_entryData->GetCondition() == VideoCondition::OCR);
_objectDetect->setVisible(_entryData->GetCondition() ==
VideoCondition::OBJECT);
_cascadeClassifierEdit->setVisible(_entryData->GetCondition() ==
VideoCondition::OBJECT_CASCADE);
_color->setVisible(_entryData->GetCondition() == VideoCondition::COLOR);
SetLayoutVisible(_throttleControlLayout,
needsThrottleControls(_entryData->GetCondition()));
@@ -1725,8 +1077,8 @@ void MacroConditionVideoEdit::SetupPreviewDialogParams()
{
_previewDialog.PatternMatchParametersChanged(
_entryData->_patternMatchParameters);
_previewDialog.ObjDetectParametersChanged(
_entryData->_objMatchParameters);
_previewDialog.CascadeClassifierParametersChanged(
_entryData->_cascadeMatchParameters);
_previewDialog.OCRParametersChanged(_entryData->_ocrParameters);
_previewDialog.VideoSelectionChanged(_entryData->_video);
_previewDialog.AreaParametersChanged(_entryData->_areaParameters);