#include "macro-condition-video.hpp" #include "screenshot-dialog.hpp" #include #include #include #include #include #include #include #include #include #include #include #include namespace advss { const std::string MacroConditionVideo::id = "video"; bool MacroConditionVideo::_registered = MacroConditionFactory::Register( MacroConditionVideo::id, {MacroConditionVideo::Create, MacroConditionVideoEdit::Create, "AdvSceneSwitcher.condition.video"}); const static std::map 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 videoInputTypes = { {VideoInput::Type::OBS_MAIN_OUTPUT, "AdvSceneSwitcher.condition.video.type.main"}, {VideoInput::Type::SOURCE, "AdvSceneSwitcher.condition.video.type.source"}, {VideoInput::Type::SCENE, "AdvSceneSwitcher.condition.video.type.scene"}, }; const static std::map patternMatchModes = { {cv::TemplateMatchModes::TM_CCOEFF_NORMED, "AdvSceneSwitcher.condition.video.patternMatchMode.correlationCoefficient"}, {cv::TemplateMatchModes::TM_CCORR_NORMED, "AdvSceneSwitcher.condition.video.patternMatchMode.crossCorrelation"}, {cv::TemplateMatchModes::TM_SQDIFF_NORMED, "AdvSceneSwitcher.condition.video.patternMatchMode.squaredDifference"}, }; const static std::map 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 || t == VideoCondition::PATTERN; } bool MacroConditionVideo::CheckShouldBeSkipped() { if (_condition != VideoCondition::PATTERN && _condition != VideoCondition::OBJECT && _condition != VideoCondition::HAS_CHANGED && _condition != VideoCondition::HAS_NOT_CHANGED) { return false; } if (_throttleEnabled) { if (_runCount <= _throttleCount) { _runCount++; return true; } else { _runCount = 0; } } return false; } MacroConditionVideo::MacroConditionVideo(Macro *m) : QObject(), MacroCondition(m, true) { } bool MacroConditionVideo::CheckCondition() { if (!_video.ValidSelection()) { return false; } bool match = false; if (CheckShouldBeSkipped()) { return _lastMatchResult; } if (!FileInputIsUpToDate()) { LoadImageFromFile(); } if (_blockUntilScreenshotDone) { GetScreenshot(true); } if (_screenshotData.IsDone()) { match = Compare(); _lastMatchResult = match; if (!requiresFileInput(_condition)) { _matchImage = std::move(_screenshotData.GetImage()); } _getNextScreenshot = true; } else { match = _lastMatchResult; } if (!_blockUntilScreenshotDone && _getNextScreenshot) { GetScreenshot(); } return match; } bool MacroConditionVideo::Save(obs_data_t *obj) const { MacroCondition::Save(obj); _video.Save(obj); obs_data_set_int(obj, "condition", static_cast(_condition)); obs_data_set_string(obj, "filePath", _file.c_str()); obs_data_set_bool(obj, "blockUntilScreenshotDone", _blockUntilScreenshotDone); _brightnessThreshold.Save(obj, "brightnessThreshold"); _patternMatchParameters.Save(obj); _objMatchParameters.Save(obj); _ocrParameters.Save(obj); _colorParameters.Save(obj); obs_data_set_bool(obj, "throttleEnabled", _throttleEnabled); obs_data_set_int(obj, "throttleCount", _throttleCount); _areaParameters.Save(obj); return true; } bool MacroConditionVideo::Load(obs_data_t *obj) { MacroCondition::Load(obj); _video.Load(obj); SetCondition(static_cast( obs_data_get_int(obj, "condition"))); _file = obs_data_get_string(obj, "filePath"); _blockUntilScreenshotDone = obs_data_get_bool(obj, "blockUntilScreenshotDone"); // TODO: Remove this fallback in a future version if (obs_data_has_user_value(obj, "brightness")) { _brightnessThreshold = obs_data_get_double(obj, "brightness"); } else { _brightnessThreshold.Load(obj, "brightnessThreshold"); } _patternMatchParameters.Load(obj); _objMatchParameters.Load(obj); _ocrParameters.Load(obj); _colorParameters.Load(obj); _throttleEnabled = obs_data_get_bool(obj, "throttleEnabled"); _throttleCount = obs_data_get_int(obj, "throttleCount"); _areaParameters.Load(obj); if (requiresFileInput(_condition)) { (void)LoadImageFromFile(); } return true; } std::string MacroConditionVideo::GetShortDesc() const { return _video.ToString(); } void MacroConditionVideo::GetScreenshot(bool blocking) { auto source = obs_weak_source_get_source(_video.GetVideo()); _screenshotData.~Screenshot(); QRect screenshotArea; if (_areaParameters.enable && _condition != VideoCondition::NO_IMAGE) { screenshotArea.setRect(_areaParameters.area.x, _areaParameters.area.y, _areaParameters.area.width, _areaParameters.area.height); } const int timeout = GetIntervalValue() < 300 ? 300 : GetIntervalValue(); new (&_screenshotData) Screenshot(source, screenshotArea, blocking, timeout); obs_source_release(source); _getNextScreenshot = false; } bool MacroConditionVideo::LoadImageFromFile() { const QFileInfo info(QString::fromStdString(_file)); _loadedFileLastModified = info.lastModified(); _loadedFile = _file; if (!_matchImage.load(info.absoluteFilePath())) { blog(LOG_WARNING, "Cannot load image data from file '%s'", _file.c_str()); (&_matchImage)->~QImage(); new (&_matchImage) QImage(); _patternImageData = {}; return false; } _matchImage = _matchImage.convertToFormat(QImage::Format::Format_RGBA8888); _patternMatchParameters.image = _matchImage; _patternImageData = CreatePatternData(_matchImage); emit InputFileChanged(); return true; } void MacroConditionVideo::SetPageSegMode(tesseract::PageSegMode mode) { _ocrParameters.SetPageMode(mode); } bool MacroConditionVideo::SetLanguageCode(const std::string &language) { return _ocrParameters.SetLanguageCode(language); } bool MacroConditionVideo::SetTesseractBaseDir(const std::string &dir) { return _ocrParameters.SetTesseractBasePath(dir); } void MacroConditionVideo::SetCondition(VideoCondition condition) { _condition = condition; SetupTempVars(); } bool MacroConditionVideo::ScreenshotContainsPattern() { cv::Mat result; MatchPattern(_screenshotData.GetImage(), _patternImageData, _patternMatchParameters.threshold, result, nullptr, _patternMatchParameters.useAlphaAsMask, _patternMatchParameters.matchMode); if (result.total() == 0) { SetTempVarValue("patternCount", "0"); return false; } const auto count = countNonZero(result); SetTempVarValue("patternCount", std::to_string(count)); return count > 0; } bool MacroConditionVideo::FileInputIsUpToDate() const { if (!requiresFileInput(_condition)) { return true; } const QFileInfo info(QString::fromStdString(_file)); return (_loadedFileLastModified == info.lastModified()) && (_file == _loadedFile); } bool MacroConditionVideo::OutputChanged() { if (!_patternMatchParameters.useForChangedCheck) { return _screenshotData.GetImage() != _matchImage; } cv::Mat result; _patternImageData = CreatePatternData(_matchImage); MatchPattern(_screenshotData.GetImage(), _patternImageData, _patternMatchParameters.threshold, result, nullptr, _patternMatchParameters.useAlphaAsMask, _patternMatchParameters.matchMode); if (result.total() == 0) { return false; } return countNonZero(result) == 0; } bool MacroConditionVideo::ScreenshotContainsObject() { auto model = _objMatchParameters.GetModel(); if (!model) { return false; } auto objects = MatchObject(_screenshotData.GetImage(), *model, _objMatchParameters.scaleFactor, _objMatchParameters.minNeighbors, _objMatchParameters.minSize.CV(), _objMatchParameters.maxSize.CV()); const auto count = objects.size(); SetTempVarValue("objectCount", std::to_string(count)); return count > 0; } bool MacroConditionVideo::CheckBrightnessThreshold() { _currentBrightness = GetAvgBrightness(_screenshotData.GetImage()) / 255.; SetTempVarValue("brightness", std::to_string(_currentBrightness)); return _currentBrightness > _brightnessThreshold; } bool MacroConditionVideo::CheckOCR() { if (!_ocrParameters.Initialized()) { return false; } auto text = RunOCR(_ocrParameters.GetOCR(), _screenshotData.GetImage(), _ocrParameters.color, _ocrParameters.colorThreshold); if (!text) { return false; } SetVariableValue(*text); SetTempVarValue("text", *text); if (!_ocrParameters.regex.Enabled()) { return text == std::string(_ocrParameters.text); } return _ocrParameters.regex.Matches(*text, _ocrParameters.text); } bool MacroConditionVideo::CheckColor() { const bool ret = ContainsPixelsInColorRange( _screenshotData.GetImage(), _colorParameters.color, _colorParameters.colorThreshold, _colorParameters.matchThreshold); // Way too slow for now //SetTempVarValue("dominantColor", GetDominantColor(_screenshotData.image, 3) // .name(QColor::HexArgb) // .toStdString()); SetTempVarValue("color", GetAverageColor(_screenshotData.GetImage()) .name(QColor::HexArgb) .toStdString()); return ret; } bool MacroConditionVideo::Compare() { if (_condition != VideoCondition::OCR) { SetVariableValue(""); } switch (_condition) { case VideoCondition::MATCH: return _screenshotData.GetImage() == _matchImage; case VideoCondition::DIFFER: return _screenshotData.GetImage() != _matchImage; case VideoCondition::HAS_CHANGED: return OutputChanged(); case VideoCondition::HAS_NOT_CHANGED: return !OutputChanged(); case VideoCondition::NO_IMAGE: return _screenshotData.GetImage().isNull(); case VideoCondition::PATTERN: return ScreenshotContainsPattern(); case VideoCondition::OBJECT: return ScreenshotContainsObject(); case VideoCondition::BRIGHTNESS: return CheckBrightnessThreshold(); case VideoCondition::OCR: return CheckOCR(); case VideoCondition::COLOR: return CheckColor(); default: break; } return false; } void MacroConditionVideo::SetupTempVars() { MacroCondition::SetupTempVars(); switch (_condition) { case VideoCondition::PATTERN: AddTempvar( "patternCount", obs_module_text( "AdvSceneSwitcher.tempVar.video.patternCount"), obs_module_text( "AdvSceneSwitcher.tempVar.video.patternCount.description")); break; case VideoCondition::OBJECT: AddTempvar( "objectCount", obs_module_text( "AdvSceneSwitcher.tempVar.video.objectCount"), obs_module_text( "AdvSceneSwitcher.tempVar.video.objectCount.description")); break; case VideoCondition::BRIGHTNESS: AddTempvar( "brightness", obs_module_text( "AdvSceneSwitcher.tempVar.video.brightness"), obs_module_text( "AdvSceneSwitcher.tempVar.video.brightness.description")); break; case VideoCondition::OCR: AddTempvar( "text", obs_module_text("AdvSceneSwitcher.tempVar.video.text"), obs_module_text( "AdvSceneSwitcher.tempVar.video.text.description")); break; case VideoCondition::COLOR: AddTempvar( "color", obs_module_text("AdvSceneSwitcher.tempVar.video.color"), obs_module_text( "AdvSceneSwitcher.tempVar.video.color.description")); break; case VideoCondition::MATCH: case VideoCondition::DIFFER: case VideoCondition::HAS_NOT_CHANGED: case VideoCondition::HAS_CHANGED: case VideoCondition::NO_IMAGE: default: break; } } static inline void populateVideoInputSelection(QComboBox *list) { for (const auto &[_, name] : videoInputTypes) { list->addItem(obs_module_text(name.c_str())); } } static inline void populateConditionSelection(QComboBox *list) { for (auto &[value, name] : conditionTypes) { list->addItem(obs_module_text(name.c_str()), static_cast(value)); } } static inline void populatePageSegModeSelection(QComboBox *list) { for (const auto &[mode, name] : pageSegModes) { list->addItem(obs_module_text(name.c_str()), static_cast(mode)); } } static inline void populatePatternMatchModeSelection(QComboBox *list) { for (const auto &[mode, name] : patternMatchModes) { list->addItem(obs_module_text(name.c_str()), static_cast(mode)); } } BrightnessEdit::BrightnessEdit(QWidget *parent, const std::shared_ptr &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 &)), this, SLOT(BrightnessThresholdChanged( const NumberVariable &))); 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 &data) : QWidget(parent), _matchText(new VariableTextEdit(this)), _regex(new RegexConfigWidget(this)), _textColor(new QLabel), _selectColor(new QPushButton(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(_selectColor, SIGNAL(clicked()), this, SLOT(SelectColorClicked())); QWidget::connect( _colorThreshold, SIGNAL(DoubleValueChanged(const NumberVariable &)), this, SLOT(ColorThresholdChanged(const NumberVariable &))); 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 widgetPlaceholders = { {"{{textColor}}", _textColor}, {"{{selectColor}}", _selectColor}, {"{{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); SetupColorLabel(_entryData->_ocrParameters.color); _colorThreshold->SetDoubleValue( _entryData->_ocrParameters.colorThreshold); _pageSegMode->setCurrentIndex(_pageSegMode->findData( static_cast(_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::SetupColorLabel(const QColor &color) { _textColor->setText(color.name()); _textColor->setPalette(QPalette(color)); _textColor->setAutoFillBackground(true); } void OCREdit::SelectColorClicked() { if (_loading || !_entryData) { return; } const QColor color = QColorDialog::getColor( _entryData->_ocrParameters.color, this, obs_module_text("AdvSceneSwitcher.condition.video.selectColor"), QColorDialog::ColorDialogOption()); if (!color.isValid()) { return; } SetupColorLabel(color); auto lock = LockContext(); _entryData->_ocrParameters.color = color; _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( _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 &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 &)), this, SLOT(ObjectScaleThresholdChanged( const NumberVariable &))); 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 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 &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)), _color(new QLabel), _selectColor(new QPushButton(obs_module_text( "AdvSceneSwitcher.condition.video.selectColor"))), _entryData(data) { QWidget::connect(_selectColor, SIGNAL(clicked()), this, SLOT(SelectColorClicked())); QWidget::connect( _matchThreshold, SIGNAL(DoubleValueChanged(const NumberVariable &)), this, SLOT(MatchThresholdChanged(const NumberVariable &))); QWidget::connect( _colorThreshold, SIGNAL(DoubleValueChanged(const NumberVariable &)), this, SLOT(ColorThresholdChanged(const NumberVariable &))); std::unordered_map widgetPlaceholders = { {"{{color}}", _color}, {"{{selectColor}}", _selectColor}, }; 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); SetupColorLabel(_entryData->_colorParameters.color); _loading = false; } void ColorEdit::SetupColorLabel(const QColor &color) { _color->setText(color.name()); _color->setPalette(QPalette(color)); _color->setAutoFillBackground(true); } 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; } void ColorEdit::SelectColorClicked() { if (_loading || !_entryData) { return; } const QColor color = QColorDialog::getColor( _entryData->_colorParameters.color, this, obs_module_text("AdvSceneSwitcher.condition.video.selectColor"), QColorDialog::ColorDialogOption()); if (!color.isValid()) { return; } SetupColorLabel(color); auto lock = LockContext(); _entryData->_colorParameters.color = color; } AreaEdit::AreaEdit(QWidget *parent, PreviewDialog *previewDialog, const std::shared_ptr &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 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(); sources.sort(); return sources; } MacroConditionVideoEdit::MacroConditionVideoEdit( QWidget *parent, std::shared_ptr entryData) : QWidget(parent), _videoInputTypes(new QComboBox()), _scenes(new SceneSelectionWidget(this, true, false, true, true, true)), _sources(new SourceSelectionWidget(this, getVideoSourcesList, true)), _condition(new QComboBox()), _reduceLatency(new QCheckBox(obs_module_text( "AdvSceneSwitcher.condition.video.reduceLatency"))), _usePatternForChangedCheck(new QCheckBox(obs_module_text( "AdvSceneSwitcher.condition.video.usePatternForChangedCheck"))), _imagePath(new FileSelection()), _patternThreshold(new SliderSpinBox( 0., 1., obs_module_text( "AdvSceneSwitcher.condition.video.patternThreshold"), obs_module_text( "AdvSceneSwitcher.condition.video.patternThresholdDescription"))), _useAlphaAsMask(new QCheckBox(obs_module_text( "AdvSceneSwitcher.condition.video.patternThresholdUseAlphaAsMask"))), _patternMatchModeLayout(new QHBoxLayout()), _patternMatchMode(new QComboBox()), _showMatch(new QPushButton(obs_module_text( "AdvSceneSwitcher.condition.video.showMatch"))), _previewDialog(this), _brightness(new BrightnessEdit(this, entryData)), _ocr(new OCREdit(this, &_previewDialog, entryData)), _objectDetect(new ObjectDetectEdit(this, &_previewDialog, entryData)), _color(new ColorEdit(this, entryData)), _area(new AreaEdit(this, &_previewDialog, entryData)), _throttleControlLayout(new QHBoxLayout), _throttleEnable(new QCheckBox()), _throttleCount(new QSpinBox()) { _reduceLatency->setToolTip(obs_module_text( "AdvSceneSwitcher.condition.video.reduceLatency.tooltip")); _imagePath->Button()->disconnect(); _usePatternForChangedCheck->setToolTip(obs_module_text( "AdvSceneSwitcher.condition.video.usePatternForChangedCheck.tooltip")); _patternMatchMode->setToolTip(obs_module_text( "AdvSceneSwitcher.condition.video.patternMatchMode.tip")); populatePatternMatchModeSelection(_patternMatchMode); _throttleCount->setMinimum(1 * GetIntervalValue()); _throttleCount->setMaximum(10 * GetIntervalValue()); _throttleCount->setSingleStep(GetIntervalValue()); _brightness->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); _ocr->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); _objectDetect->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); _color->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); _area->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); QWidget::connect(_videoInputTypes, SIGNAL(currentIndexChanged(int)), this, SLOT(VideoInputTypeChanged(int))); QWidget::connect(_sources, SIGNAL(SourceChanged(const SourceSelection &)), this, SLOT(SourceChanged(const SourceSelection &))); QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)), this, SLOT(SceneChanged(const SceneSelection &))); QWidget::connect(_condition, SIGNAL(currentIndexChanged(int)), this, SLOT(ConditionChanged(int))); QWidget::connect(_reduceLatency, SIGNAL(stateChanged(int)), this, SLOT(ReduceLatencyChanged(int))); QWidget::connect(_imagePath, SIGNAL(PathChanged(const QString &)), this, SLOT(ImagePathChanged(const QString &))); QWidget::connect(_imagePath->Button(), SIGNAL(clicked()), this, SLOT(ImageBrowseButtonClicked())); QWidget::connect(_usePatternForChangedCheck, SIGNAL(stateChanged(int)), this, SLOT(UsePatternForChangedCheckChanged(int))); QWidget::connect( _patternThreshold, SIGNAL(DoubleValueChanged(const NumberVariable &)), this, SLOT(PatternThresholdChanged(const NumberVariable &))); QWidget::connect(_useAlphaAsMask, SIGNAL(stateChanged(int)), this, SLOT(UseAlphaAsMaskChanged(int))); QWidget::connect(_patternMatchMode, SIGNAL(currentIndexChanged(int)), this, SLOT(PatternMatchModeChanged(int))); QWidget::connect(_throttleEnable, SIGNAL(stateChanged(int)), this, SLOT(ThrottleEnableChanged(int))); QWidget::connect(_throttleCount, SIGNAL(valueChanged(int)), this, SLOT(ThrottleCountChanged(int))); QWidget::connect(_showMatch, SIGNAL(clicked()), this, SLOT(ShowMatchClicked())); QWidget::connect(this, SIGNAL(VideoSelectionChanged(const VideoInput &)), &_previewDialog, SLOT(VideoSelectionChanged(const VideoInput &))); QWidget::connect(_area, SIGNAL(Resized()), this, SLOT(Resize())); QWidget::connect(entryData.get(), &MacroConditionVideo::InputFileChanged, this, [this]() { UpdatePreviewTooltip(); _previewDialog.PatternMatchParametersChanged( _entryData->_patternMatchParameters); }); populateVideoInputSelection(_videoInputTypes); populateConditionSelection(_condition); _patternMatchModeLayout->setContentsMargins(0, 0, 0, 0); _throttleControlLayout->setContentsMargins(0, 0, 0, 0); QHBoxLayout *entryLine1Layout = new QHBoxLayout; std::unordered_map widgetPlaceholders = { {"{{videoInputTypes}}", _videoInputTypes}, {"{{sources}}", _sources}, {"{{scenes}}", _scenes}, {"{{condition}}", _condition}, {"{{reduceLatency}}", _reduceLatency}, {"{{imagePath}}", _imagePath}, {"{{throttleEnable}}", _throttleEnable}, {"{{throttleCount}}", _throttleCount}, {"{{patternMatchingModes}}", _patternMatchMode}, }; PlaceWidgets(obs_module_text("AdvSceneSwitcher.condition.video.layout"), entryLine1Layout, widgetPlaceholders); PlaceWidgets( obs_module_text( "AdvSceneSwitcher.condition.video.patternMatchMode"), _patternMatchModeLayout, widgetPlaceholders); PlaceWidgets( obs_module_text( "AdvSceneSwitcher.condition.video.layout.throttle"), _throttleControlLayout, widgetPlaceholders); QHBoxLayout *showMatchLayout = new QHBoxLayout; showMatchLayout->addWidget(_showMatch); showMatchLayout->addStretch(); auto mainLayout = new QVBoxLayout; mainLayout->addLayout(entryLine1Layout); mainLayout->addWidget(_usePatternForChangedCheck); mainLayout->addWidget(_patternThreshold); mainLayout->addWidget(_useAlphaAsMask); mainLayout->addLayout(_patternMatchModeLayout); mainLayout->addWidget(_brightness); mainLayout->addWidget(_ocr); mainLayout->addWidget(_objectDetect); mainLayout->addWidget(_color); mainLayout->addLayout(_throttleControlLayout); mainLayout->addWidget(_area); mainLayout->addWidget(_reduceLatency); mainLayout->addLayout(showMatchLayout); setLayout(mainLayout); _entryData = entryData; UpdateEntryData(); _loading = false; } void MacroConditionVideoEdit::UpdatePreviewTooltip() { if (!_entryData) { return; } if (!requiresFileInput(_entryData->GetCondition())) { this->setToolTip(""); return; } QImage preview = _entryData->GetMatchImage().scaled( {300, 300}, Qt::KeepAspectRatio); QByteArray data; QBuffer buffer(&data); if (!preview.save(&buffer, "PNG")) { return; } QString html = QString("") .arg(QString(data.toBase64())); this->setToolTip(html); } void MacroConditionVideoEdit::SourceChanged(const SourceSelection &source) { GUARD_LOADING_AND_LOCK(); _entryData->_video.source = source; HandleVideoInputUpdate(); } void MacroConditionVideoEdit::SceneChanged(const SceneSelection &scene) { GUARD_LOADING_AND_LOCK(); _entryData->_video.scene = scene; HandleVideoInputUpdate(); } void MacroConditionVideoEdit::VideoInputTypeChanged(int type) { GUARD_LOADING_AND_LOCK(); _entryData->_video.type = static_cast(type); HandleVideoInputUpdate(); SetWidgetVisibility(); } void MacroConditionVideoEdit::HandleVideoInputUpdate() { _entryData->ResetLastMatch(); emit HeaderInfoChanged( QString::fromStdString(_entryData->GetShortDesc())); emit VideoSelectionChanged(_entryData->_video); } void MacroConditionVideoEdit::ConditionChanged(int idx) { GUARD_LOADING_AND_LOCK(); _entryData->SetCondition( static_cast(_condition->itemData(idx).toInt())); _entryData->ResetLastMatch(); SetWidgetVisibility(); // Reload image data to avoid incorrect matches. // // Condition type HAS_NOT_CHANGED will use matchImage to store previous // frame of video source, which will differ from the image stored at // specified file location. if (_entryData->LoadImageFromFile()) { UpdatePreviewTooltip(); } _previewDialog.PatternMatchParametersChanged( _entryData->_patternMatchParameters); SetupPreviewDialogParams(); } void MacroConditionVideoEdit::ImagePathChanged(const QString &text) { GUARD_LOADING_AND_LOCK(); _entryData->_file = text.toUtf8().constData(); _entryData->ResetLastMatch(); if (_entryData->LoadImageFromFile()) { UpdatePreviewTooltip(); } _previewDialog.PatternMatchParametersChanged( _entryData->_patternMatchParameters); } void MacroConditionVideoEdit::ImageBrowseButtonClicked() { if (_loading || !_entryData) { return; } QString path; bool useExistingFile = false; // Ask whether to create screenshot or to select existing file if (_entryData->_video.ValidSelection()) { QMessageBox msgBox( QMessageBox::Question, obs_module_text("AdvSceneSwitcher.windowTitle"), obs_module_text( "AdvSceneSwitcher.condition.video.askFileAction"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); auto yes = msgBox.button(QMessageBox::StandardButton::Yes); yes->setText(obs_module_text( "AdvSceneSwitcher.condition.video.askFileAction.file")); auto no = msgBox.button(QMessageBox::StandardButton::No); no->setText(obs_module_text( "AdvSceneSwitcher.condition.video.askFileAction.screenshot")); msgBox.setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::CustomizeWindowHint); const auto result = msgBox.exec(); if (result == QMessageBox::Cancel) { return; } useExistingFile = result == QMessageBox::Yes; } if (useExistingFile) { path = QFileDialog::getOpenFileName( this, "", FileSelection::ValidPathOrDesktop( QString::fromStdString(_entryData->_file))); if (path.isEmpty()) { return; } } else { path = QFileDialog::getSaveFileName( this, "", FileSelection::ValidPathOrDesktop( QString::fromStdString(_entryData->_file)), "*.png"); if (path.isEmpty()) { return; } QFile file(path); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { return; } auto screenshot = ScreenshotDialog::AskForScreenshot( _entryData->_video, _entryData->_areaParameters); if (!screenshot) { return; } screenshot->save(path); } _imagePath->SetPath(path); ImagePathChanged(path); } void MacroConditionVideoEdit::UsePatternForChangedCheckChanged(int value) { GUARD_LOADING_AND_LOCK(); _entryData->_patternMatchParameters.useForChangedCheck = value; SetWidgetVisibility(); } void MacroConditionVideoEdit::PatternThresholdChanged( const DoubleVariable &value) { GUARD_LOADING_AND_LOCK(); _entryData->_patternMatchParameters.threshold = value; _previewDialog.PatternMatchParametersChanged( _entryData->_patternMatchParameters); } void MacroConditionVideoEdit::ReduceLatencyChanged(int value) { GUARD_LOADING_AND_LOCK(); _entryData->_blockUntilScreenshotDone = value; } void MacroConditionVideoEdit::UseAlphaAsMaskChanged(int value) { GUARD_LOADING_AND_LOCK(); _entryData->_patternMatchParameters.useAlphaAsMask = value; _entryData->LoadImageFromFile(); _previewDialog.PatternMatchParametersChanged( _entryData->_patternMatchParameters); } void MacroConditionVideoEdit::PatternMatchModeChanged(int idx) { GUARD_LOADING_AND_LOCK(); _entryData->_patternMatchParameters.matchMode = static_cast( _patternMatchMode->itemData(idx).toInt()); _previewDialog.PatternMatchParametersChanged( _entryData->_patternMatchParameters); } void MacroConditionVideoEdit::ThrottleEnableChanged(int value) { GUARD_LOADING_AND_LOCK(); _entryData->_throttleEnabled = value; _throttleCount->setEnabled(value); } void MacroConditionVideoEdit::ThrottleCountChanged(int value) { GUARD_LOADING_AND_LOCK(); _entryData->_throttleCount = value / GetIntervalValue(); } void MacroConditionVideoEdit::ShowMatchClicked() { _previewDialog.show(); _previewDialog.raise(); _previewDialog.activateWindow(); _previewDialog.ShowMatch(); } static bool needsShowMatch(VideoCondition cond) { return cond == VideoCondition::PATTERN || cond == VideoCondition::OBJECT || cond == VideoCondition::OCR; } static bool needsThrottleControls(VideoCondition cond) { return cond == VideoCondition::PATTERN || cond == VideoCondition::OBJECT || cond == VideoCondition::HAS_CHANGED || cond == VideoCondition::HAS_NOT_CHANGED; } static bool needsThreshold(VideoCondition cond) { return cond == VideoCondition::PATTERN || cond == VideoCondition::HAS_CHANGED || cond == VideoCondition::HAS_NOT_CHANGED; } static bool patternControlIsOptional(VideoCondition cond) { return cond == VideoCondition::HAS_CHANGED || cond == VideoCondition::HAS_NOT_CHANGED; } static bool needsAreaControls(VideoCondition cond) { return cond != VideoCondition::NO_IMAGE; } void MacroConditionVideoEdit::SetWidgetVisibility() { _sources->setVisible(_entryData->_video.type == VideoInput::Type::SOURCE); _scenes->setVisible(_entryData->_video.type == VideoInput::Type::SCENE); _imagePath->setVisible(requiresFileInput(_entryData->GetCondition())); _usePatternForChangedCheck->setVisible( patternControlIsOptional(_entryData->GetCondition())); _patternThreshold->setVisible( needsThreshold(_entryData->GetCondition())); _useAlphaAsMask->setVisible(_entryData->GetCondition() == VideoCondition::PATTERN); SetLayoutVisible(_patternMatchModeLayout, _entryData->GetCondition() == VideoCondition::PATTERN); _brightness->setVisible(_entryData->GetCondition() == VideoCondition::BRIGHTNESS); _showMatch->setVisible(needsShowMatch(_entryData->GetCondition())); _ocr->setVisible(_entryData->GetCondition() == VideoCondition::OCR); _objectDetect->setVisible(_entryData->GetCondition() == VideoCondition::OBJECT); _color->setVisible(_entryData->GetCondition() == VideoCondition::COLOR); SetLayoutVisible(_throttleControlLayout, needsThrottleControls(_entryData->GetCondition())); _area->setVisible(needsAreaControls(_entryData->GetCondition())); if (_entryData->GetCondition() == VideoCondition::HAS_CHANGED || _entryData->GetCondition() == VideoCondition::HAS_NOT_CHANGED) { _patternThreshold->setVisible( _entryData->_patternMatchParameters.useForChangedCheck); SetLayoutVisible( _patternMatchModeLayout, _entryData->_patternMatchParameters.useForChangedCheck); } // TODO: // Remove "reduce matching latency" and "reduce cpu load" options as they // have become superfluous with short circuit evaluation if (!_entryData->_throttleEnabled) { SetLayoutVisible(_throttleControlLayout, false); } if (_entryData->_blockUntilScreenshotDone) { _reduceLatency->hide(); } Resize(); } void MacroConditionVideoEdit::Resize() { adjustSize(); updateGeometry(); } void MacroConditionVideoEdit::SetupPreviewDialogParams() { _previewDialog.PatternMatchParametersChanged( _entryData->_patternMatchParameters); _previewDialog.ObjDetectParametersChanged( _entryData->_objMatchParameters); _previewDialog.OCRParametersChanged(_entryData->_ocrParameters); _previewDialog.VideoSelectionChanged(_entryData->_video); _previewDialog.AreaParametersChanged(_entryData->_areaParameters); _previewDialog.ConditionChanged( static_cast(_entryData->GetCondition())); } void MacroConditionVideoEdit::UpdateEntryData() { if (!_entryData) { return; } _videoInputTypes->setCurrentIndex( static_cast(_entryData->_video.type)); _scenes->SetScene(_entryData->_video.scene); _sources->SetSource(_entryData->_video.source); _condition->setCurrentIndex(_condition->findData( static_cast(_entryData->GetCondition()))); _reduceLatency->setChecked(_entryData->_blockUntilScreenshotDone); _imagePath->SetPath(QString::fromStdString(_entryData->_file)); _usePatternForChangedCheck->setChecked( _entryData->_patternMatchParameters.useForChangedCheck); _patternThreshold->SetDoubleValue( _entryData->_patternMatchParameters.threshold); _useAlphaAsMask->setChecked( _entryData->_patternMatchParameters.useAlphaAsMask); _patternMatchMode->setCurrentIndex(_patternMatchMode->findData( _entryData->_patternMatchParameters.matchMode)); _throttleEnable->setChecked(_entryData->_throttleEnabled); _throttleCount->setValue(_entryData->_throttleCount * GetIntervalValue()); UpdatePreviewTooltip(); SetupPreviewDialogParams(); SetWidgetVisibility(); } } // namespace advss