mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-09-07 09:05:57 -05:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9bc59c1082 | ||
|
|
8b9f03a349 | ||
|
|
093b0f9ec0 | ||
|
|
254eabfa8d | ||
|
|
b2da8faf37 | ||
|
|
3eba20e2d8 | ||
|
|
08841aa88e | ||
|
|
2e96c226b5 | ||
|
|
c34308f743 | ||
|
|
4af07d9d55 | ||
|
|
d743260ca8 | ||
|
|
8ff50056f0 | ||
|
|
b786831f3b | ||
|
|
32e2a5f44c | ||
|
|
e3f277c32a | ||
|
|
5a7d76061c | ||
|
|
39a8c34fa6 | ||
|
|
ead51e2f13 |
@@ -42,6 +42,7 @@ endif()
|
|||||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")
|
||||||
include(GetGitRevisionDescription)
|
include(GetGitRevisionDescription)
|
||||||
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
|
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
|
||||||
|
git_describe(GIT_TAG)
|
||||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.cpp.in"
|
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.cpp.in"
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/src/version.cpp" @ONLY)
|
"${CMAKE_CURRENT_BINARY_DIR}/src/version.cpp" @ONLY)
|
||||||
|
|
||||||
@@ -218,6 +219,7 @@ set(advanced-scene-switcher_HEADERS
|
|||||||
src/headers/macro-action-scene-visibility.hpp
|
src/headers/macro-action-scene-visibility.hpp
|
||||||
src/headers/macro-action-source.hpp
|
src/headers/macro-action-source.hpp
|
||||||
src/headers/macro-action-streaming.hpp
|
src/headers/macro-action-streaming.hpp
|
||||||
|
src/headers/macro-action-timer.hpp
|
||||||
src/headers/macro-action-transition.hpp
|
src/headers/macro-action-transition.hpp
|
||||||
src/headers/macro-action-virtual-cam.hpp
|
src/headers/macro-action-virtual-cam.hpp
|
||||||
src/headers/macro-action-wait.hpp
|
src/headers/macro-action-wait.hpp
|
||||||
@@ -308,6 +310,7 @@ set(advanced-scene-switcher_SOURCES
|
|||||||
src/macro-action-scene-visibility.cpp
|
src/macro-action-scene-visibility.cpp
|
||||||
src/macro-action-source.cpp
|
src/macro-action-source.cpp
|
||||||
src/macro-action-streaming.cpp
|
src/macro-action-streaming.cpp
|
||||||
|
src/macro-action-timer.cpp
|
||||||
src/macro-action-transition.cpp
|
src/macro-action-transition.cpp
|
||||||
src/macro-action-virtual-cam.cpp
|
src/macro-action-virtual-cam.cpp
|
||||||
src/macro-action-wait.cpp
|
src/macro-action-wait.cpp
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
#include "src/headers/version.h"
|
#include "src/headers/version.h"
|
||||||
#define GIT_SHA1 "@GIT_SHA1@"
|
#define GIT_SHA1 "@GIT_SHA1@"
|
||||||
const char g_GIT_SHA1[] = GIT_SHA1;
|
#define GIT_TAG "@GIT_TAG@"
|
||||||
|
const char g_GIT_SHA1[] = GIT_SHA1;
|
||||||
|
const char g_GIT_TAG[] = GIT_TAG;
|
||||||
@@ -97,7 +97,8 @@ AdvSceneSwitcher.condition.scene.type.current="Current scene is"
|
|||||||
AdvSceneSwitcher.condition.scene.type.previous="Previous scene is"
|
AdvSceneSwitcher.condition.scene.type.previous="Previous scene is"
|
||||||
AdvSceneSwitcher.condition.scene.type.changed="Scene chagned"
|
AdvSceneSwitcher.condition.scene.type.changed="Scene chagned"
|
||||||
AdvSceneSwitcher.condition.scene.type.notChanged="Scene has not changed"
|
AdvSceneSwitcher.condition.scene.type.notChanged="Scene has not changed"
|
||||||
AdvSceneSwitcher.condition.scene.entry="{{sceneType}} {{scenes}}"
|
AdvSceneSwitcher.condition.scene.waitForTransition="Wait for transition to complete"
|
||||||
|
AdvSceneSwitcher.condition.scene.entry="{{sceneType}} {{scenes}} {{waitForTransition}}"
|
||||||
AdvSceneSwitcher.condition.window="Window"
|
AdvSceneSwitcher.condition.window="Window"
|
||||||
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} exist and ..."
|
AdvSceneSwitcher.condition.window.entry.line1="{{windows}} exist and ..."
|
||||||
AdvSceneSwitcher.condition.window.entry.line2="... is {{fullscreen}} fullscreen {{maximized}} maximized {{focused}} focused {{windowFocusChanged}} foreground window changed"
|
AdvSceneSwitcher.condition.window.entry.line2="... is {{fullscreen}} fullscreen {{maximized}} maximized {{focused}} focused {{windowFocusChanged}} foreground window changed"
|
||||||
@@ -120,8 +121,10 @@ AdvSceneSwitcher.condition.video.askFileAction.file="Use existing file"
|
|||||||
AdvSceneSwitcher.condition.video.askFileAction.screenshot="Create screenshot"
|
AdvSceneSwitcher.condition.video.askFileAction.screenshot="Create screenshot"
|
||||||
AdvSceneSwitcher.condition.video.patternThreshold="Threshold: "
|
AdvSceneSwitcher.condition.video.patternThreshold="Threshold: "
|
||||||
AdvSceneSwitcher.condition.video.patternThresholdDescription="A higher threshold value means that the pattern needs to match the video source more closely."
|
AdvSceneSwitcher.condition.video.patternThresholdDescription="A higher threshold value means that the pattern needs to match the video source more closely."
|
||||||
|
AdvSceneSwitcher.condition.video.patternThresholdUseAlphaAsMask="Use alpha channel as mask for pattern."
|
||||||
AdvSceneSwitcher.condition.video.objectScaleThreshold="Scale factor: "
|
AdvSceneSwitcher.condition.video.objectScaleThreshold="Scale factor: "
|
||||||
AdvSceneSwitcher.condition.video.objectScaleThresholdDescription="A lower scale factor will lead to more matches but higher CPU load."
|
AdvSceneSwitcher.condition.video.objectScaleThresholdDescription="A lower scale factor will lead to more matches but higher CPU load."
|
||||||
|
AdvSceneSwitcher.condition.video.minNeighborDescription="A higher minimum neighbors value will result in fewer but higher quality matches."
|
||||||
AdvSceneSwitcher.condition.video.showMatch="Show match"
|
AdvSceneSwitcher.condition.video.showMatch="Show match"
|
||||||
AdvSceneSwitcher.condition.video.screenshotFail="Failed to get screenshot of source!"
|
AdvSceneSwitcher.condition.video.screenshotFail="Failed to get screenshot of source!"
|
||||||
AdvSceneSwitcher.condition.video.patternMatchFail="Pattern was not found!"
|
AdvSceneSwitcher.condition.video.patternMatchFail="Pattern was not found!"
|
||||||
@@ -150,9 +153,11 @@ AdvSceneSwitcher.condition.pluginState="Plugin state"
|
|||||||
AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="Automated scene change was triggered in this interval"
|
AdvSceneSwitcher.condition.pluginState.state.sceneSwitched="Automated scene change was triggered in this interval"
|
||||||
AdvSceneSwitcher.condition.pluginState.entry="{{condition}}"
|
AdvSceneSwitcher.condition.pluginState.entry="{{condition}}"
|
||||||
AdvSceneSwitcher.condition.timer="Timer"
|
AdvSceneSwitcher.condition.timer="Timer"
|
||||||
|
AdvSceneSwitcher.condition.timer.pause="Pause"
|
||||||
|
AdvSceneSwitcher.condition.timer.continue="Continue"
|
||||||
AdvSceneSwitcher.condition.timer.entry.line1="{{duration}} have passed"
|
AdvSceneSwitcher.condition.timer.entry.line1="{{duration}} have passed"
|
||||||
AdvSceneSwitcher.condition.timer.entry.line2="Time remaining: {{remaining}} seconds"
|
AdvSceneSwitcher.condition.timer.entry.line2="Time remaining: {{remaining}} seconds"
|
||||||
AdvSceneSwitcher.condition.timer.entry.line3="{{reset}} {{autoReset}} Automatically reset timer after duration was reached"
|
AdvSceneSwitcher.condition.timer.entry.line3="{{pauseContinue}} {{reset}} {{saveRemaining}} Save time remaining {{autoReset}} Automatically reset timer after duration was reached"
|
||||||
AdvSceneSwitcher.condition.timer.reset="Reset"
|
AdvSceneSwitcher.condition.timer.reset="Reset"
|
||||||
AdvSceneSwitcher.condition.macro="Macro"
|
AdvSceneSwitcher.condition.macro="Macro"
|
||||||
AdvSceneSwitcher.condition.macro.type.count="Count"
|
AdvSceneSwitcher.condition.macro.type.count="Count"
|
||||||
@@ -208,7 +213,7 @@ AdvSceneSwitcher.condition.date.state.after="After"
|
|||||||
AdvSceneSwitcher.condition.date.state.before="Before"
|
AdvSceneSwitcher.condition.date.state.before="Before"
|
||||||
AdvSceneSwitcher.condition.date.state.between="Between"
|
AdvSceneSwitcher.condition.date.state.between="Between"
|
||||||
AdvSceneSwitcher.condition.date.entry.line1="{{condition}} {{dateTime}} {{dateTime2}} {{ignoreDate}} Ignore date component {{ignoreTime}} Ignore time component"
|
AdvSceneSwitcher.condition.date.entry.line1="{{condition}} {{dateTime}} {{dateTime2}} {{ignoreDate}} Ignore date component {{ignoreTime}} Ignore time component"
|
||||||
AdvSceneSwitcher.condition.date.entry.line2="{{repeat}} Reschedule {{duration}} later when matching"
|
AdvSceneSwitcher.condition.date.entry.line2="{{repeat}} Repeat every {{duration}} on date match"
|
||||||
AdvSceneSwitcher.condition.sceneTransform="Scene item transform"
|
AdvSceneSwitcher.condition.sceneTransform="Scene item transform"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.getTransform="Get transform"
|
AdvSceneSwitcher.condition.sceneTransform.getTransform="Get transform"
|
||||||
AdvSceneSwitcher.condition.sceneTransform.regex="Use regular expressions"
|
AdvSceneSwitcher.condition.sceneTransform.regex="Use regular expressions"
|
||||||
@@ -329,6 +334,12 @@ AdvSceneSwitcher.action.SceneSwap.entry="Swap preview and program scene in studi
|
|||||||
AdvSceneSwitcher.action.transition="Transition"
|
AdvSceneSwitcher.action.transition="Transition"
|
||||||
AdvSceneSwitcher.action.transition.entry.line1="{{setType}}Set transition type to {{transitions}}"
|
AdvSceneSwitcher.action.transition.entry.line1="{{setType}}Set transition type to {{transitions}}"
|
||||||
AdvSceneSwitcher.action.transition.entry.line2="{{setDuration}}Set transition duration to {{duration}}seconds"
|
AdvSceneSwitcher.action.transition.entry.line2="{{setDuration}}Set transition duration to {{duration}}seconds"
|
||||||
|
AdvSceneSwitcher.action.timer="Timer"
|
||||||
|
AdvSceneSwitcher.action.timer.type.pause="Pause"
|
||||||
|
AdvSceneSwitcher.action.timer.type.continue="Continue"
|
||||||
|
AdvSceneSwitcher.action.timer.type.reset="Reset"
|
||||||
|
AdvSceneSwitcher.action.timer.type.setTimeRemaining="Set time remaining of"
|
||||||
|
AdvSceneSwitcher.action.timer.entry="{{timerAction}} timers on {{macros}} {{duration}}"
|
||||||
|
|
||||||
; Transition Tab
|
; Transition Tab
|
||||||
AdvSceneSwitcher.transitionTab.title="Transition"
|
AdvSceneSwitcher.transitionTab.title="Transition"
|
||||||
|
|||||||
@@ -258,13 +258,6 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<widget class="Line" name="line_6">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_52">
|
<layout class="QHBoxLayout" name="horizontalLayout_52">
|
||||||
<item>
|
<item>
|
||||||
@@ -362,20 +355,8 @@
|
|||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_41">
|
<layout class="QHBoxLayout" name="horizontalLayout_41">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QListWidget" name="priorityList"/>
|
<widget class="QListWidget" name="priorityList">
|
||||||
</item>
|
</widget>
|
||||||
<item>
|
|
||||||
<spacer name="horizontalSpacer_30">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@@ -232,12 +232,17 @@ void SwitcherData::Thread()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// After this point we will call frontend functions
|
// After this point we will call frontend functions like
|
||||||
// obs_frontend_set_current_scene() and
|
// obs_frontend_set_current_scene() and
|
||||||
// obs_frontend_set_current_transition()
|
// obs_frontend_set_current_transition()
|
||||||
//
|
//
|
||||||
// During this time SaveSceneSwitcher() could be called
|
// During this time SaveSceneSwitcher() could be called
|
||||||
// leading to a deadlock, so we have to unlock()
|
// leading to a deadlock with the frontend function being stuck
|
||||||
|
// in QMetaObject::invokeMethod() holding the mutex and
|
||||||
|
// OBS being stuck in SaveSceneSwitcher().
|
||||||
|
//
|
||||||
|
// So we have to unlock() risking race conditions as these are
|
||||||
|
// less frequent than the above described deadlock
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
@@ -599,6 +604,7 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
|
|||||||
|
|
||||||
extern "C" void InitSceneSwitcher()
|
extern "C" void InitSceneSwitcher()
|
||||||
{
|
{
|
||||||
|
blog(LOG_INFO, "version: %s", g_GIT_TAG);
|
||||||
blog(LOG_INFO, "version: %s", g_GIT_SHA1);
|
blog(LOG_INFO, "version: %s", g_GIT_SHA1);
|
||||||
|
|
||||||
QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction(
|
QAction *action = (QAction *)obs_frontend_add_tools_menu_qaction(
|
||||||
|
|||||||
@@ -46,6 +46,13 @@ double Duration::TimeRemaining()
|
|||||||
return (seconds * 1000 - runTime.count()) / 1000.;
|
return (seconds * 1000 - runTime.count()) / 1000.;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Duration::SetTimeRemaining(double remaining)
|
||||||
|
{
|
||||||
|
long long msPassed = (seconds - remaining) * 1000;
|
||||||
|
_startTime = std::chrono::high_resolution_clock::now() -
|
||||||
|
std::chrono::milliseconds(msPassed);
|
||||||
|
}
|
||||||
|
|
||||||
void Duration::Reset()
|
void Duration::Reset()
|
||||||
{
|
{
|
||||||
_startTime = {};
|
_startTime = {};
|
||||||
|
|||||||
@@ -887,4 +887,6 @@ void AdvSceneSwitcher::setupGeneralTab()
|
|||||||
QTimer *statusTimer = new QTimer(this);
|
QTimer *statusTimer = new QTimer(this);
|
||||||
connect(statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
|
connect(statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
|
||||||
statusTimer->start(1000);
|
statusTimer->start(1000);
|
||||||
|
|
||||||
|
setWindowTitle(windowTitle() + " - " + g_GIT_TAG);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ public:
|
|||||||
void SetEditMacro(Macro &m);
|
void SetEditMacro(Macro &m);
|
||||||
void HighlightAction(int idx);
|
void HighlightAction(int idx);
|
||||||
void HighlightCondition(int idx);
|
void HighlightCondition(int idx);
|
||||||
|
void PopulateMacroActions(Macro &m, int afterIdx = 0);
|
||||||
|
void PopulateMacroConditions(Macro &m, int afterIdx = 0);
|
||||||
void ConnectControlSignals(MacroActionEdit *);
|
void ConnectControlSignals(MacroActionEdit *);
|
||||||
void ConnectControlSignals(MacroConditionEdit *);
|
void ConnectControlSignals(MacroConditionEdit *);
|
||||||
void SwapActions(Macro *m, int pos1, int pos2);
|
void SwapActions(Macro *m, int pos1, int pos2);
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ public:
|
|||||||
|
|
||||||
bool DurationReached();
|
bool DurationReached();
|
||||||
double TimeRemaining();
|
double TimeRemaining();
|
||||||
|
void SetTimeRemaining(double);
|
||||||
void Reset();
|
void Reset();
|
||||||
std::string ToString();
|
std::string ToString();
|
||||||
|
|
||||||
|
|||||||
72
src/headers/macro-action-timer.hpp
Normal file
72
src/headers/macro-action-timer.hpp
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "macro-action-edit.hpp"
|
||||||
|
#include "duration-control.hpp"
|
||||||
|
#include "macro-selection.hpp"
|
||||||
|
|
||||||
|
#include <QDoubleSpinBox>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
|
||||||
|
enum class TimerAction {
|
||||||
|
PAUSE,
|
||||||
|
CONTINUE,
|
||||||
|
RESET,
|
||||||
|
SET_TIME_REMAINING,
|
||||||
|
};
|
||||||
|
|
||||||
|
class MacroActionTimer : public MacroRefAction {
|
||||||
|
public:
|
||||||
|
bool PerformAction();
|
||||||
|
void LogAction();
|
||||||
|
bool Save(obs_data_t *obj);
|
||||||
|
bool Load(obs_data_t *obj);
|
||||||
|
std::string GetShortDesc();
|
||||||
|
std::string GetId() { return id; };
|
||||||
|
static std::shared_ptr<MacroAction> Create()
|
||||||
|
{
|
||||||
|
return std::make_shared<MacroActionTimer>();
|
||||||
|
}
|
||||||
|
Duration _duration;
|
||||||
|
TimerAction _actionType = TimerAction::PAUSE;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool _registered;
|
||||||
|
static const std::string id;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MacroActionTimerEdit : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MacroActionTimerEdit(
|
||||||
|
QWidget *parent,
|
||||||
|
std::shared_ptr<MacroActionTimer> entryData = nullptr);
|
||||||
|
void UpdateEntryData();
|
||||||
|
static QWidget *Create(QWidget *parent,
|
||||||
|
std::shared_ptr<MacroAction> action)
|
||||||
|
{
|
||||||
|
return new MacroActionTimerEdit(
|
||||||
|
parent,
|
||||||
|
std::dynamic_pointer_cast<MacroActionTimer>(action));
|
||||||
|
}
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void MacroChanged(const QString &text);
|
||||||
|
void MacroRemove(const QString &name);
|
||||||
|
void DurationChanged(double value);
|
||||||
|
void DurationUnitChanged(DurationUnit unit);
|
||||||
|
void ActionTypeChanged(int value);
|
||||||
|
signals:
|
||||||
|
void HeaderInfoChanged(const QString &);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void SetWidgetVisibility();
|
||||||
|
|
||||||
|
MacroSelection *_macros;
|
||||||
|
DurationSelection *_duration;
|
||||||
|
QComboBox *_timerAction;
|
||||||
|
std::shared_ptr<MacroActionTimer> _entryData;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QHBoxLayout *_mainLayout;
|
||||||
|
bool _loading = true;
|
||||||
|
};
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
|
#include <QCheckBox>
|
||||||
|
|
||||||
enum class SceneType {
|
enum class SceneType {
|
||||||
CURRENT,
|
CURRENT,
|
||||||
@@ -24,6 +25,7 @@ public:
|
|||||||
return std::make_shared<MacroConditionScene>();
|
return std::make_shared<MacroConditionScene>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _waitForTransition = false;
|
||||||
SceneSelection _scene;
|
SceneSelection _scene;
|
||||||
SceneType _type;
|
SceneType _type;
|
||||||
|
|
||||||
@@ -52,12 +54,14 @@ public:
|
|||||||
private slots:
|
private slots:
|
||||||
void SceneChanged(const SceneSelection &);
|
void SceneChanged(const SceneSelection &);
|
||||||
void TypeChanged(int value);
|
void TypeChanged(int value);
|
||||||
|
void WaitForTransitionChanged(int state);
|
||||||
signals:
|
signals:
|
||||||
void HeaderInfoChanged(const QString &);
|
void HeaderInfoChanged(const QString &);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SceneSelectionWidget *_scenes;
|
SceneSelectionWidget *_scenes;
|
||||||
QComboBox *_sceneType;
|
QComboBox *_sceneType;
|
||||||
|
QCheckBox *_waitForTransition;
|
||||||
std::shared_ptr<MacroConditionScene> _entryData;
|
std::shared_ptr<MacroConditionScene> _entryData;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -16,8 +16,14 @@ public:
|
|||||||
{
|
{
|
||||||
return std::make_shared<MacroConditionTimer>();
|
return std::make_shared<MacroConditionTimer>();
|
||||||
}
|
}
|
||||||
|
void Pause();
|
||||||
|
void Continue();
|
||||||
|
void Reset();
|
||||||
|
|
||||||
Duration _duration;
|
Duration _duration;
|
||||||
|
bool _paused = false;
|
||||||
|
bool _saveRemaining = false;
|
||||||
|
double _remaining = 0.;
|
||||||
bool _oneshot = false;
|
bool _oneshot = false;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -44,14 +50,20 @@ public:
|
|||||||
private slots:
|
private slots:
|
||||||
void DurationChanged(double seconds);
|
void DurationChanged(double seconds);
|
||||||
void DurationUnitChanged(DurationUnit unit);
|
void DurationUnitChanged(DurationUnit unit);
|
||||||
|
void SaveRemainingChanged(int state);
|
||||||
void AutoResetChanged(int state);
|
void AutoResetChanged(int state);
|
||||||
|
void PauseContinueClicked();
|
||||||
void ResetClicked();
|
void ResetClicked();
|
||||||
void UpdateTimeRemaining();
|
void UpdateTimeRemaining();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void SetPauseContinueButtonLabel();
|
||||||
|
|
||||||
DurationSelection *_duration;
|
DurationSelection *_duration;
|
||||||
QCheckBox *_autoReset;
|
QCheckBox *_autoReset;
|
||||||
|
QCheckBox *_saveRemaining;
|
||||||
QPushButton *_reset;
|
QPushButton *_reset;
|
||||||
|
QPushButton *_pauseConinue;
|
||||||
QLabel *_remaining;
|
QLabel *_remaining;
|
||||||
std::shared_ptr<MacroConditionTimer> _entryData;
|
std::shared_ptr<MacroConditionTimer> _entryData;
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,12 @@ enum class VideoCondition {
|
|||||||
OBJECT,
|
OBJECT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PatternMatchData {
|
||||||
|
cv::Mat4b rgbaPattern;
|
||||||
|
cv::Mat3b rgbPattern;
|
||||||
|
cv::Mat1b mask;
|
||||||
|
};
|
||||||
|
|
||||||
constexpr int minMinNeighbors = 3;
|
constexpr int minMinNeighbors = 3;
|
||||||
constexpr int maxMinNeighbors = 6;
|
constexpr int maxMinNeighbors = 6;
|
||||||
|
|
||||||
@@ -44,6 +50,8 @@ public:
|
|||||||
OBSWeakSource _videoSource;
|
OBSWeakSource _videoSource;
|
||||||
VideoCondition _condition = VideoCondition::MATCH;
|
VideoCondition _condition = VideoCondition::MATCH;
|
||||||
std::string _file = obs_module_text("AdvSceneSwitcher.enterPath");
|
std::string _file = obs_module_text("AdvSceneSwitcher.enterPath");
|
||||||
|
bool _useAlphaAsMask = false;
|
||||||
|
PatternMatchData _patternData;
|
||||||
double _patternThreshold = 0.8;
|
double _patternThreshold = 0.8;
|
||||||
cv::CascadeClassifier _objectCascade;
|
cv::CascadeClassifier _objectCascade;
|
||||||
double _scaleFactor = 1.1;
|
double _scaleFactor = 1.1;
|
||||||
@@ -120,6 +128,7 @@ private slots:
|
|||||||
void ImagePathChanged(const QString &text);
|
void ImagePathChanged(const QString &text);
|
||||||
void ImageBrowseButtonClicked();
|
void ImageBrowseButtonClicked();
|
||||||
void PatternThresholdChanged(double);
|
void PatternThresholdChanged(double);
|
||||||
|
void UseAlphaAsMaskChanged(int value);
|
||||||
|
|
||||||
void ModelPathChanged(const QString &text);
|
void ModelPathChanged(const QString &text);
|
||||||
void ObjectScaleThresholdChanged(double);
|
void ObjectScaleThresholdChanged(double);
|
||||||
@@ -141,12 +150,14 @@ protected:
|
|||||||
|
|
||||||
FileSelection *_imagePath;
|
FileSelection *_imagePath;
|
||||||
ThresholdSlider *_patternThreshold;
|
ThresholdSlider *_patternThreshold;
|
||||||
|
QCheckBox *_useAlphaAsMask;
|
||||||
|
|
||||||
FileSelection *_modelDataPath;
|
FileSelection *_modelDataPath;
|
||||||
QHBoxLayout *_modelPathLayout;
|
QHBoxLayout *_modelPathLayout;
|
||||||
ThresholdSlider *_objectScaleThreshold;
|
ThresholdSlider *_objectScaleThreshold;
|
||||||
QHBoxLayout *_neighborsControlLayout;
|
QHBoxLayout *_neighborsControlLayout;
|
||||||
QSpinBox *_minNeighbors;
|
QSpinBox *_minNeighbors;
|
||||||
|
QLabel *_minNeighborsDescription;
|
||||||
QHBoxLayout *_minSizeControlLayout;
|
QHBoxLayout *_minSizeControlLayout;
|
||||||
QSpinBox *_minSizeX;
|
QSpinBox *_minSizeX;
|
||||||
QSpinBox *_minSizeY;
|
QSpinBox *_minSizeY;
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ public:
|
|||||||
// Use this function to avoid accidental edits when scrolling through
|
// Use this function to avoid accidental edits when scrolling through
|
||||||
// list of actions and conditions
|
// list of actions and conditions
|
||||||
void SetFocusPolicyOfWidgets();
|
void SetFocusPolicyOfWidgets();
|
||||||
|
void SetCollapsed(bool collapsed);
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void HeaderInfoChanged(const QString &);
|
void HeaderInfoChanged(const QString &);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "switch-generic.hpp"
|
#include "switch-generic.hpp"
|
||||||
|
#include "duration-control.hpp"
|
||||||
|
|
||||||
enum class sceneTriggerType {
|
enum class sceneTriggerType {
|
||||||
NONE = 0,
|
NONE = 0,
|
||||||
@@ -43,7 +44,7 @@ struct SceneTrigger : SceneSwitcherEntry {
|
|||||||
static bool pause;
|
static bool pause;
|
||||||
sceneTriggerType triggerType = sceneTriggerType::NONE;
|
sceneTriggerType triggerType = sceneTriggerType::NONE;
|
||||||
sceneTriggerAction triggerAction = sceneTriggerAction::NONE;
|
sceneTriggerAction triggerAction = sceneTriggerAction::NONE;
|
||||||
double duration = 0;
|
Duration duration;
|
||||||
OBSWeakSource audioSource = nullptr;
|
OBSWeakSource audioSource = nullptr;
|
||||||
|
|
||||||
const char *getType() { return "trigger"; }
|
const char *getType() { return "trigger"; }
|
||||||
@@ -70,13 +71,14 @@ public:
|
|||||||
private slots:
|
private slots:
|
||||||
void TriggerTypeChanged(int index);
|
void TriggerTypeChanged(int index);
|
||||||
void TriggerActionChanged(int index);
|
void TriggerActionChanged(int index);
|
||||||
void DurationChanged(double dur);
|
void DurationChanged(double seconds);
|
||||||
|
void DurationUnitChanged(DurationUnit unit);
|
||||||
void AudioSourceChanged(const QString &text);
|
void AudioSourceChanged(const QString &text);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QComboBox *triggers;
|
QComboBox *triggers;
|
||||||
QComboBox *actions;
|
QComboBox *actions;
|
||||||
QDoubleSpinBox *duration;
|
DurationSelection *duration;
|
||||||
QComboBox *audioSources;
|
QComboBox *audioSources;
|
||||||
|
|
||||||
SceneTrigger *switchData;
|
SceneTrigger *switchData;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ public:
|
|||||||
|
|
||||||
void SetContent(QWidget *w, bool collapsed = true);
|
void SetContent(QWidget *w, bool collapsed = true);
|
||||||
void AddHeaderWidget(QWidget *);
|
void AddHeaderWidget(QWidget *);
|
||||||
|
void SetCollapsed(bool);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool eventFilter(QObject *obj, QEvent *event) override;
|
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ void placeWidgets(std::string text, QBoxLayout *layout,
|
|||||||
std::unordered_map<std::string, QWidget *> placeholders,
|
std::unordered_map<std::string, QWidget *> placeholders,
|
||||||
bool addStretch = true);
|
bool addStretch = true);
|
||||||
void deleteLayoutItem(QLayoutItem *item);
|
void deleteLayoutItem(QLayoutItem *item);
|
||||||
void clearLayout(QLayout *layout);
|
void clearLayout(QLayout *layout, int afterIdx = 0);
|
||||||
QMetaObject::Connection PulseWidget(QWidget *widget, QColor startColor,
|
QMetaObject::Connection PulseWidget(QWidget *widget, QColor startColor,
|
||||||
QColor = QColor(0, 0, 0, 0),
|
QColor = QColor(0, 0, 0, 0),
|
||||||
QString specifier = "QLabel ",
|
QString specifier = "QLabel ",
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
extern const char g_GIT_SHA1[];
|
extern const char g_GIT_SHA1[];
|
||||||
|
extern const char g_GIT_TAG[];
|
||||||
|
|||||||
@@ -152,12 +152,8 @@ void AdvSceneSwitcher::AddMacroAction(int idx)
|
|||||||
}
|
}
|
||||||
macro->UpdateActionIndices();
|
macro->UpdateActionIndices();
|
||||||
|
|
||||||
// All entry pointers in existing edit widgets after the new entry will
|
clearLayout(ui->macroEditActionLayout, idx);
|
||||||
// be invalidated by adding the new entry so we have to recreate them
|
PopulateMacroActions(*macro, idx);
|
||||||
// and because I am lazy I am recreating every widget.
|
|
||||||
//
|
|
||||||
// If performance should become a concern this has to be revisited.
|
|
||||||
SetEditMacro(*macro);
|
|
||||||
HighlightAction(idx);
|
HighlightAction(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,12 +185,8 @@ void AdvSceneSwitcher::RemoveMacroAction(int idx)
|
|||||||
switcher->macroWaitCv.notify_one();
|
switcher->macroWaitCv.notify_one();
|
||||||
macro->UpdateActionIndices();
|
macro->UpdateActionIndices();
|
||||||
|
|
||||||
// All entry pointers in existing edit widgets after the new entry will
|
clearLayout(ui->macroEditActionLayout, idx);
|
||||||
// be invalidated by adding the new entry so we have to recreate them
|
PopulateMacroActions(*macro, idx);
|
||||||
// and because I am lazy I am recreating every widget.
|
|
||||||
//
|
|
||||||
// If performance should become a concern this has to be revisited.
|
|
||||||
SetEditMacro(*macro);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_actionRemove_clicked()
|
void AdvSceneSwitcher::on_actionRemove_clicked()
|
||||||
|
|||||||
228
src/macro-action-timer.cpp
Normal file
228
src/macro-action-timer.cpp
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
#include "headers/macro-action-timer.hpp"
|
||||||
|
#include "headers/macro-condition-timer.hpp"
|
||||||
|
#include "headers/advanced-scene-switcher.hpp"
|
||||||
|
#include "headers/utility.hpp"
|
||||||
|
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
const std::string MacroActionTimer::id = "timer";
|
||||||
|
|
||||||
|
bool MacroActionTimer::_registered = MacroActionFactory::Register(
|
||||||
|
MacroActionTimer::id,
|
||||||
|
{MacroActionTimer::Create, MacroActionTimerEdit::Create,
|
||||||
|
"AdvSceneSwitcher.action.Timer"});
|
||||||
|
|
||||||
|
static std::map<TimerAction, std::string> timerActions = {
|
||||||
|
{TimerAction::PAUSE, "AdvSceneSwitcher.action.timer.type.pause"},
|
||||||
|
{TimerAction::CONTINUE, "AdvSceneSwitcher.action.timer.type.continue"},
|
||||||
|
{TimerAction::RESET, "AdvSceneSwitcher.action.timer.type.reset"},
|
||||||
|
{TimerAction::SET_TIME_REMAINING,
|
||||||
|
"AdvSceneSwitcher.action.timer.type.setTimeRemaining"},
|
||||||
|
};
|
||||||
|
|
||||||
|
bool MacroActionTimer::PerformAction()
|
||||||
|
{
|
||||||
|
if (!_macro.get()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (auto c : _macro->Conditions()) {
|
||||||
|
if (c->GetId() != "timer") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto timerCondition =
|
||||||
|
dynamic_cast<MacroConditionTimer *>(c.get());
|
||||||
|
if (!timerCondition) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (_actionType) {
|
||||||
|
case TimerAction::PAUSE:
|
||||||
|
timerCondition->Pause();
|
||||||
|
break;
|
||||||
|
case TimerAction::CONTINUE:
|
||||||
|
timerCondition->Continue();
|
||||||
|
break;
|
||||||
|
case TimerAction::RESET:
|
||||||
|
timerCondition->Reset();
|
||||||
|
break;
|
||||||
|
case TimerAction::SET_TIME_REMAINING:
|
||||||
|
timerCondition->_duration.SetTimeRemaining(
|
||||||
|
_duration.seconds);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionTimer::LogAction()
|
||||||
|
{
|
||||||
|
if (!_macro.get()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (_actionType) {
|
||||||
|
case TimerAction::PAUSE:
|
||||||
|
vblog(LOG_INFO, "paused timers on \"%s\"",
|
||||||
|
_macro->Name().c_str());
|
||||||
|
break;
|
||||||
|
case TimerAction::CONTINUE:
|
||||||
|
vblog(LOG_INFO, "continued timers on \"%s\"",
|
||||||
|
_macro->Name().c_str());
|
||||||
|
break;
|
||||||
|
case TimerAction::RESET:
|
||||||
|
vblog(LOG_INFO, "reset timers on \"%s\"",
|
||||||
|
_macro->Name().c_str());
|
||||||
|
break;
|
||||||
|
case TimerAction::SET_TIME_REMAINING:
|
||||||
|
vblog(LOG_INFO,
|
||||||
|
"set time remaining of timers on \"%s\" to \"%s\"",
|
||||||
|
_macro->Name().c_str(), _duration.ToString().c_str());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroActionTimer::Save(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
MacroAction::Save(obj);
|
||||||
|
_macro.Save(obj);
|
||||||
|
_duration.Save(obj);
|
||||||
|
obs_data_set_int(obj, "actionType", static_cast<int>(_actionType));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroActionTimer::Load(obs_data_t *obj)
|
||||||
|
{
|
||||||
|
MacroAction::Load(obj);
|
||||||
|
_macro.Load(obj);
|
||||||
|
_duration.Load(obj);
|
||||||
|
_actionType =
|
||||||
|
static_cast<TimerAction>(obs_data_get_int(obj, "actionType"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string MacroActionTimer::GetShortDesc()
|
||||||
|
{
|
||||||
|
if (_macro.get()) {
|
||||||
|
return _macro->Name();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void populateTypeSelection(QComboBox *list)
|
||||||
|
{
|
||||||
|
for (auto entry : timerActions) {
|
||||||
|
list->addItem(obs_module_text(entry.second.c_str()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MacroActionTimerEdit::MacroActionTimerEdit(
|
||||||
|
QWidget *parent, std::shared_ptr<MacroActionTimer> entryData)
|
||||||
|
: QWidget(parent)
|
||||||
|
{
|
||||||
|
_macros = new MacroSelection(parent);
|
||||||
|
_duration = new DurationSelection();
|
||||||
|
_timerAction = new QComboBox();
|
||||||
|
|
||||||
|
populateTypeSelection(_timerAction);
|
||||||
|
|
||||||
|
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
|
||||||
|
this, SLOT(MacroChanged(const QString &)));
|
||||||
|
QWidget::connect(parent, SIGNAL(MacroRemoved(const QString &)), this,
|
||||||
|
SLOT(MacroRemove(const QString &)));
|
||||||
|
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
|
||||||
|
SLOT(DurationChanged(double)));
|
||||||
|
QWidget::connect(_duration, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||||
|
SLOT(DurationUnitChanged(DurationUnit)));
|
||||||
|
QWidget::connect(_timerAction, SIGNAL(currentIndexChanged(int)), this,
|
||||||
|
SLOT(ActionTypeChanged(int)));
|
||||||
|
|
||||||
|
_mainLayout = new QHBoxLayout;
|
||||||
|
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
|
{"{{macros}}", _macros},
|
||||||
|
{"{{duration}}", _duration},
|
||||||
|
{"{{timerAction}}", _timerAction},
|
||||||
|
};
|
||||||
|
placeWidgets(obs_module_text("AdvSceneSwitcher.action.timer.entry"),
|
||||||
|
_mainLayout, widgetPlaceholders);
|
||||||
|
setLayout(_mainLayout);
|
||||||
|
|
||||||
|
_entryData = entryData;
|
||||||
|
UpdateEntryData();
|
||||||
|
_loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionTimerEdit::UpdateEntryData()
|
||||||
|
{
|
||||||
|
if (!_entryData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_macros->SetCurrentMacro(_entryData->_macro.get());
|
||||||
|
_duration->SetDuration(_entryData->_duration);
|
||||||
|
_timerAction->setCurrentIndex(
|
||||||
|
static_cast<int>(_entryData->_actionType));
|
||||||
|
SetWidgetVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionTimerEdit::ActionTypeChanged(int value)
|
||||||
|
{
|
||||||
|
if (_loading || !_entryData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
_entryData->_actionType = static_cast<TimerAction>(value);
|
||||||
|
SetWidgetVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionTimerEdit::SetWidgetVisibility()
|
||||||
|
{
|
||||||
|
if (!_entryData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_duration->setVisible(_entryData->_actionType ==
|
||||||
|
TimerAction::SET_TIME_REMAINING);
|
||||||
|
adjustSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionTimerEdit::DurationChanged(double seconds)
|
||||||
|
{
|
||||||
|
if (_loading || !_entryData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
_entryData->_duration.seconds = seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionTimerEdit::DurationUnitChanged(DurationUnit unit)
|
||||||
|
{
|
||||||
|
if (_loading || !_entryData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
_entryData->_duration.displayUnit = unit;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionTimerEdit::MacroChanged(const QString &text)
|
||||||
|
{
|
||||||
|
if (_loading || !_entryData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
_entryData->_macro.UpdateRef(text);
|
||||||
|
emit HeaderInfoChanged(
|
||||||
|
QString::fromStdString(_entryData->GetShortDesc()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroActionTimerEdit::MacroRemove(const QString &)
|
||||||
|
{
|
||||||
|
if (_entryData) {
|
||||||
|
_entryData->_macro.UpdateRef();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -276,12 +276,8 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
|
|||||||
(*cond)->SetLogicType(logic);
|
(*cond)->SetLogicType(logic);
|
||||||
macro->UpdateConditionIndices();
|
macro->UpdateConditionIndices();
|
||||||
|
|
||||||
// All entry pointers in existing edit widgets after the new entry will
|
clearLayout(ui->macroEditConditionLayout, idx);
|
||||||
// be invalidated by adding the new entry so we have to recreate them
|
PopulateMacroConditions(*macro, idx);
|
||||||
// and because I am lazy I am recreating every widget.
|
|
||||||
//
|
|
||||||
// If performance should become a concern this has to be revisited.
|
|
||||||
SetEditMacro(*macro);
|
|
||||||
HighlightCondition(idx);
|
HighlightCondition(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,21 +307,13 @@ void AdvSceneSwitcher::RemoveMacroCondition(int idx)
|
|||||||
macro->Conditions().erase(macro->Conditions().begin() + idx);
|
macro->Conditions().erase(macro->Conditions().begin() + idx);
|
||||||
macro->UpdateConditionIndices();
|
macro->UpdateConditionIndices();
|
||||||
|
|
||||||
if (macro->Conditions().size() == 0) {
|
|
||||||
ui->macroEditConditionHelp->setVisible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (idx == 0 && macro->Conditions().size() > 0) {
|
if (idx == 0 && macro->Conditions().size() > 0) {
|
||||||
auto newRoot = macro->Conditions().at(0);
|
auto newRoot = macro->Conditions().at(0);
|
||||||
newRoot->SetLogicType(LogicType::ROOT_NONE);
|
newRoot->SetLogicType(LogicType::ROOT_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// All entry pointers in existing edit widgets after the new entry will
|
clearLayout(ui->macroEditConditionLayout, idx);
|
||||||
// be invalidated by adding the new entry so we have to recreate them
|
PopulateMacroConditions(*macro, idx);
|
||||||
// and because I am lazy I am recreating every widget.
|
|
||||||
//
|
|
||||||
// If performance should become a concern this has to be revisited.
|
|
||||||
SetEditMacro(*macro);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::on_conditionRemove_clicked()
|
void AdvSceneSwitcher::on_conditionRemove_clicked()
|
||||||
|
|||||||
@@ -28,7 +28,18 @@ bool MacroConditionScene::CheckCondition()
|
|||||||
|
|
||||||
switch (_type) {
|
switch (_type) {
|
||||||
case SceneType::CURRENT:
|
case SceneType::CURRENT:
|
||||||
return switcher->currentScene == _scene.GetScene(false);
|
if (_waitForTransition) {
|
||||||
|
return switcher->currentScene == _scene.GetScene(false);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
bool match = false;
|
||||||
|
auto current = obs_frontend_get_current_scene();
|
||||||
|
auto weak = obs_source_get_weak_source(current);
|
||||||
|
match = weak == _scene.GetScene(false);
|
||||||
|
obs_weak_source_release(weak);
|
||||||
|
obs_source_release(current);
|
||||||
|
return match;
|
||||||
|
}
|
||||||
case SceneType::PREVIOUS:
|
case SceneType::PREVIOUS:
|
||||||
return switcher->previousScene == _scene.GetScene(false);
|
return switcher->previousScene == _scene.GetScene(false);
|
||||||
case SceneType::CHANGED:
|
case SceneType::CHANGED:
|
||||||
@@ -47,6 +58,7 @@ bool MacroConditionScene::Save(obs_data_t *obj)
|
|||||||
MacroCondition::Save(obj);
|
MacroCondition::Save(obj);
|
||||||
_scene.Save(obj);
|
_scene.Save(obj);
|
||||||
obs_data_set_int(obj, "type", static_cast<int>(_type));
|
obs_data_set_int(obj, "type", static_cast<int>(_type));
|
||||||
|
obs_data_set_bool(obj, "waitForTransition", _waitForTransition);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,6 +67,12 @@ bool MacroConditionScene::Load(obs_data_t *obj)
|
|||||||
MacroCondition::Load(obj);
|
MacroCondition::Load(obj);
|
||||||
_scene.Load(obj);
|
_scene.Load(obj);
|
||||||
_type = static_cast<SceneType>(obs_data_get_int(obj, "type"));
|
_type = static_cast<SceneType>(obs_data_get_int(obj, "type"));
|
||||||
|
if (!obs_data_has_user_value(obj, "waitForTransition")) {
|
||||||
|
_waitForTransition = true;
|
||||||
|
} else {
|
||||||
|
_waitForTransition =
|
||||||
|
obs_data_get_bool(obj, "waitForTransition");
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,11 +94,15 @@ MacroConditionSceneEdit::MacroConditionSceneEdit(
|
|||||||
{
|
{
|
||||||
_scenes = new SceneSelectionWidget(window(), false, false, false);
|
_scenes = new SceneSelectionWidget(window(), false, false, false);
|
||||||
_sceneType = new QComboBox();
|
_sceneType = new QComboBox();
|
||||||
|
_waitForTransition = new QCheckBox(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.condition.scene.waitForTransition"));
|
||||||
|
|
||||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||||
this, SLOT(SceneChanged(const SceneSelection &)));
|
this, SLOT(SceneChanged(const SceneSelection &)));
|
||||||
QWidget::connect(_sceneType, SIGNAL(currentIndexChanged(int)), this,
|
QWidget::connect(_sceneType, SIGNAL(currentIndexChanged(int)), this,
|
||||||
SLOT(TypeChanged(int)));
|
SLOT(TypeChanged(int)));
|
||||||
|
QWidget::connect(_waitForTransition, SIGNAL(stateChanged(int)), this,
|
||||||
|
SLOT(WaitForTransitionChanged(int)));
|
||||||
|
|
||||||
populateTypeSelection(_sceneType);
|
populateTypeSelection(_sceneType);
|
||||||
|
|
||||||
@@ -88,6 +110,7 @@ MacroConditionSceneEdit::MacroConditionSceneEdit(
|
|||||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
{"{{scenes}}", _scenes},
|
{"{{scenes}}", _scenes},
|
||||||
{"{{sceneType}}", _sceneType},
|
{"{{sceneType}}", _sceneType},
|
||||||
|
{"{{waitForTransition}}", _waitForTransition},
|
||||||
};
|
};
|
||||||
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.scene.entry"),
|
placeWidgets(obs_module_text("AdvSceneSwitcher.condition.scene.entry"),
|
||||||
mainLayout, widgetPlaceholders);
|
mainLayout, widgetPlaceholders);
|
||||||
@@ -121,10 +144,21 @@ void MacroConditionSceneEdit::TypeChanged(int value)
|
|||||||
SetWidgetVisibility();
|
SetWidgetVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroConditionSceneEdit::WaitForTransitionChanged(int state)
|
||||||
|
{
|
||||||
|
if (_loading || !_entryData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
_entryData->_waitForTransition = state;
|
||||||
|
}
|
||||||
|
|
||||||
void MacroConditionSceneEdit::SetWidgetVisibility()
|
void MacroConditionSceneEdit::SetWidgetVisibility()
|
||||||
{
|
{
|
||||||
_scenes->setVisible(_entryData->_type == SceneType::CURRENT ||
|
_scenes->setVisible(_entryData->_type == SceneType::CURRENT ||
|
||||||
_entryData->_type == SceneType::PREVIOUS);
|
_entryData->_type == SceneType::PREVIOUS);
|
||||||
|
_waitForTransition->setVisible(_entryData->_type == SceneType::CURRENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionSceneEdit::UpdateEntryData()
|
void MacroConditionSceneEdit::UpdateEntryData()
|
||||||
@@ -135,5 +169,6 @@ void MacroConditionSceneEdit::UpdateEntryData()
|
|||||||
|
|
||||||
_scenes->SetScene(_entryData->_scene);
|
_scenes->SetScene(_entryData->_scene);
|
||||||
_sceneType->setCurrentIndex(static_cast<int>(_entryData->_type));
|
_sceneType->setCurrentIndex(static_cast<int>(_entryData->_type));
|
||||||
|
_waitForTransition->setChecked(_entryData->_waitForTransition);
|
||||||
SetWidgetVisibility();
|
SetWidgetVisibility();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ bool MacroConditionTimer::_registered = MacroConditionFactory::Register(
|
|||||||
|
|
||||||
bool MacroConditionTimer::CheckCondition()
|
bool MacroConditionTimer::CheckCondition()
|
||||||
{
|
{
|
||||||
|
if (_paused) {
|
||||||
|
return _remaining == 0.;
|
||||||
|
}
|
||||||
if (_duration.DurationReached()) {
|
if (_duration.DurationReached()) {
|
||||||
if (!_oneshot) {
|
if (!_oneshot) {
|
||||||
_duration.Reset();
|
_duration.Reset();
|
||||||
@@ -25,6 +28,16 @@ bool MacroConditionTimer::Save(obs_data_t *obj)
|
|||||||
{
|
{
|
||||||
MacroCondition::Save(obj);
|
MacroCondition::Save(obj);
|
||||||
_duration.Save(obj);
|
_duration.Save(obj);
|
||||||
|
if (!_paused) {
|
||||||
|
_remaining = _duration.TimeRemaining();
|
||||||
|
}
|
||||||
|
if (_saveRemaining) {
|
||||||
|
obs_data_set_double(obj, "remaining", _remaining);
|
||||||
|
} else {
|
||||||
|
obs_data_set_double(obj, "remaining", _duration.seconds);
|
||||||
|
}
|
||||||
|
obs_data_set_bool(obj, "saveRemaining", _saveRemaining);
|
||||||
|
obs_data_set_bool(obj, "paused", _paused);
|
||||||
obs_data_set_bool(obj, "oneshot", _oneshot);
|
obs_data_set_bool(obj, "oneshot", _oneshot);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -33,20 +46,49 @@ bool MacroConditionTimer::Load(obs_data_t *obj)
|
|||||||
{
|
{
|
||||||
MacroCondition::Load(obj);
|
MacroCondition::Load(obj);
|
||||||
_duration.Load(obj);
|
_duration.Load(obj);
|
||||||
|
_remaining = obs_data_get_double(obj, "remaining");
|
||||||
|
_paused = obs_data_get_bool(obj, "paused");
|
||||||
|
_saveRemaining = obs_data_get_bool(obj, "saveRemaining");
|
||||||
if (!obs_data_has_user_value(obj, "oneshot")) {
|
if (!obs_data_has_user_value(obj, "oneshot")) {
|
||||||
_oneshot = false;
|
_oneshot = false;
|
||||||
} else {
|
} else {
|
||||||
_oneshot = obs_data_get_bool(obj, "oneshot");
|
_oneshot = obs_data_get_bool(obj, "oneshot");
|
||||||
}
|
}
|
||||||
|
_duration.SetTimeRemaining(_remaining);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroConditionTimer::Pause()
|
||||||
|
{
|
||||||
|
if (!_paused) {
|
||||||
|
_paused = true;
|
||||||
|
_remaining = _duration.TimeRemaining();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroConditionTimer::Continue()
|
||||||
|
{
|
||||||
|
if (_paused) {
|
||||||
|
_paused = false;
|
||||||
|
_duration.SetTimeRemaining(_remaining);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroConditionTimer::Reset()
|
||||||
|
{
|
||||||
|
_remaining = _duration.seconds;
|
||||||
|
_duration.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
MacroConditionTimerEdit::MacroConditionTimerEdit(
|
MacroConditionTimerEdit::MacroConditionTimerEdit(
|
||||||
QWidget *parent, std::shared_ptr<MacroConditionTimer> entryData)
|
QWidget *parent, std::shared_ptr<MacroConditionTimer> entryData)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
{
|
{
|
||||||
_duration = new DurationSelection();
|
_duration = new DurationSelection();
|
||||||
_autoReset = new QCheckBox();
|
_autoReset = new QCheckBox();
|
||||||
|
_saveRemaining = new QCheckBox();
|
||||||
|
_pauseConinue = new QPushButton(
|
||||||
|
obs_module_text("AdvSceneSwitcher.condition.timer.pause"));
|
||||||
_reset = new QPushButton(
|
_reset = new QPushButton(
|
||||||
obs_module_text("AdvSceneSwitcher.condition.timer.reset"));
|
obs_module_text("AdvSceneSwitcher.condition.timer.reset"));
|
||||||
_remaining = new QLabel();
|
_remaining = new QLabel();
|
||||||
@@ -55,16 +97,22 @@ MacroConditionTimerEdit::MacroConditionTimerEdit(
|
|||||||
SLOT(DurationChanged(double)));
|
SLOT(DurationChanged(double)));
|
||||||
QWidget::connect(_duration, SIGNAL(UnitChanged(DurationUnit)), this,
|
QWidget::connect(_duration, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||||
SLOT(DurationUnitChanged(DurationUnit)));
|
SLOT(DurationUnitChanged(DurationUnit)));
|
||||||
|
QWidget::connect(_pauseConinue, SIGNAL(clicked()), this,
|
||||||
|
SLOT(PauseContinueClicked()));
|
||||||
QWidget::connect(_reset, SIGNAL(clicked()), this, SLOT(ResetClicked()));
|
QWidget::connect(_reset, SIGNAL(clicked()), this, SLOT(ResetClicked()));
|
||||||
QWidget::connect(_autoReset, SIGNAL(stateChanged(int)), this,
|
QWidget::connect(_autoReset, SIGNAL(stateChanged(int)), this,
|
||||||
SLOT(AutoResetChanged(int)));
|
SLOT(AutoResetChanged(int)));
|
||||||
|
QWidget::connect(_saveRemaining, SIGNAL(stateChanged(int)), this,
|
||||||
|
SLOT(SaveRemainingChanged(int)));
|
||||||
|
|
||||||
auto line1Layout = new QHBoxLayout;
|
auto line1Layout = new QHBoxLayout;
|
||||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||||
{"{{duration}}", _duration},
|
{"{{duration}}", _duration},
|
||||||
{"{{autoReset}}", _autoReset},
|
{"{{autoReset}}", _autoReset},
|
||||||
{"{{remaining}}", _remaining},
|
{"{{remaining}}", _remaining},
|
||||||
|
{"{{pauseContinue}}", _pauseConinue},
|
||||||
{"{{reset}}", _reset},
|
{"{{reset}}", _reset},
|
||||||
|
{"{{saveRemaining}}", _saveRemaining},
|
||||||
};
|
};
|
||||||
placeWidgets(
|
placeWidgets(
|
||||||
obs_module_text("AdvSceneSwitcher.condition.timer.entry.line1"),
|
obs_module_text("AdvSceneSwitcher.condition.timer.entry.line1"),
|
||||||
@@ -112,6 +160,16 @@ void MacroConditionTimerEdit::DurationUnitChanged(DurationUnit unit)
|
|||||||
_entryData->_duration.displayUnit = unit;
|
_entryData->_duration.displayUnit = unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroConditionTimerEdit::SaveRemainingChanged(int state)
|
||||||
|
{
|
||||||
|
if (_loading || !_entryData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
_entryData->_saveRemaining = state;
|
||||||
|
}
|
||||||
|
|
||||||
void MacroConditionTimerEdit::AutoResetChanged(int state)
|
void MacroConditionTimerEdit::AutoResetChanged(int state)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
if (_loading || !_entryData) {
|
||||||
@@ -122,6 +180,23 @@ void MacroConditionTimerEdit::AutoResetChanged(int state)
|
|||||||
_entryData->_oneshot = !state;
|
_entryData->_oneshot = !state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroConditionTimerEdit::PauseContinueClicked()
|
||||||
|
{
|
||||||
|
if (_loading || !_entryData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
if (_entryData->_paused) {
|
||||||
|
timer.start(1000);
|
||||||
|
_entryData->Continue();
|
||||||
|
} else {
|
||||||
|
_entryData->Pause();
|
||||||
|
timer.stop();
|
||||||
|
}
|
||||||
|
SetPauseContinueButtonLabel();
|
||||||
|
}
|
||||||
|
|
||||||
void MacroConditionTimerEdit::ResetClicked()
|
void MacroConditionTimerEdit::ResetClicked()
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
if (_loading || !_entryData) {
|
||||||
@@ -129,7 +204,7 @@ void MacroConditionTimerEdit::ResetClicked()
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
_entryData->_duration.Reset();
|
_entryData->Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionTimerEdit::UpdateTimeRemaining()
|
void MacroConditionTimerEdit::UpdateTimeRemaining()
|
||||||
@@ -138,8 +213,28 @@ void MacroConditionTimerEdit::UpdateTimeRemaining()
|
|||||||
_remaining->setText("-");
|
_remaining->setText("-");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_remaining->setText(
|
|
||||||
QString::number(_entryData->_duration.TimeRemaining()));
|
if (_entryData->_paused) {
|
||||||
|
_remaining->setText(QString::number(_entryData->_remaining));
|
||||||
|
} else {
|
||||||
|
_remaining->setText(
|
||||||
|
QString::number(_entryData->_duration.TimeRemaining()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacroConditionTimerEdit::SetPauseContinueButtonLabel()
|
||||||
|
{
|
||||||
|
if (!_entryData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_entryData->_paused) {
|
||||||
|
_pauseConinue->setText(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.condition.timer.continue"));
|
||||||
|
} else {
|
||||||
|
_pauseConinue->setText(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.condition.timer.pause"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MacroConditionTimerEdit::UpdateEntryData()
|
void MacroConditionTimerEdit::UpdateEntryData()
|
||||||
@@ -150,4 +245,6 @@ void MacroConditionTimerEdit::UpdateEntryData()
|
|||||||
|
|
||||||
_duration->SetDuration(_entryData->_duration);
|
_duration->SetDuration(_entryData->_duration);
|
||||||
_autoReset->setChecked(!_entryData->_oneshot);
|
_autoReset->setChecked(!_entryData->_oneshot);
|
||||||
|
_saveRemaining->setChecked(_entryData->_saveRemaining);
|
||||||
|
SetPauseContinueButtonLabel();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ bool MacroConditionVideo::Save(obs_data_t *obj)
|
|||||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||||
obs_data_set_string(obj, "filePath", _file.c_str());
|
obs_data_set_string(obj, "filePath", _file.c_str());
|
||||||
obs_data_set_double(obj, "threshold", _patternThreshold);
|
obs_data_set_double(obj, "threshold", _patternThreshold);
|
||||||
|
obs_data_set_bool(obj, "useAlphaAsMask", _useAlphaAsMask);
|
||||||
obs_data_set_string(obj, "modelDataPath", _modelDataPath.c_str());
|
obs_data_set_string(obj, "modelDataPath", _modelDataPath.c_str());
|
||||||
obs_data_set_double(obj, "scaleFactor", _scaleFactor);
|
obs_data_set_double(obj, "scaleFactor", _scaleFactor);
|
||||||
obs_data_set_int(obj, "minNeighbors", _minNeighbors);
|
obs_data_set_int(obj, "minNeighbors", _minNeighbors);
|
||||||
@@ -132,13 +133,14 @@ bool MacroConditionVideo::Load(obs_data_t *obj)
|
|||||||
static_cast<VideoCondition>(obs_data_get_int(obj, "condition"));
|
static_cast<VideoCondition>(obs_data_get_int(obj, "condition"));
|
||||||
_file = obs_data_get_string(obj, "filePath");
|
_file = obs_data_get_string(obj, "filePath");
|
||||||
_patternThreshold = obs_data_get_double(obj, "threshold");
|
_patternThreshold = obs_data_get_double(obj, "threshold");
|
||||||
|
_useAlphaAsMask = obs_data_get_bool(obj, "useAlphaAsMask");
|
||||||
_modelDataPath = obs_data_get_string(obj, "modelDataPath");
|
_modelDataPath = obs_data_get_string(obj, "modelDataPath");
|
||||||
_scaleFactor = obs_data_get_double(obj, "scaleFactor");
|
_scaleFactor = obs_data_get_double(obj, "scaleFactor");
|
||||||
if (!isScaleFactorValid(_scaleFactor)) {
|
if (!isScaleFactorValid(_scaleFactor)) {
|
||||||
_scaleFactor = 1.1;
|
_scaleFactor = 1.1;
|
||||||
}
|
}
|
||||||
_minNeighbors = obs_data_get_int(obj, "minNeighbors");
|
_minNeighbors = obs_data_get_int(obj, "minNeighbors");
|
||||||
if (!isMinNeighborsValid(_scaleFactor)) {
|
if (!isMinNeighborsValid(_minNeighbors)) {
|
||||||
_minNeighbors = minMinNeighbors;
|
_minNeighbors = minMinNeighbors;
|
||||||
}
|
}
|
||||||
_minSizeX = obs_data_get_int(obj, "minSizeX");
|
_minSizeX = obs_data_get_int(obj, "minSizeX");
|
||||||
@@ -174,26 +176,7 @@ void MacroConditionVideo::GetScreenshot()
|
|||||||
obs_source_release(source);
|
obs_source_release(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacroConditionVideo::LoadImageFromFile()
|
// Assumption is that QImage uses Format_RGBA8888.
|
||||||
{
|
|
||||||
if (!_matchImage.load(QString::fromStdString(_file))) {
|
|
||||||
blog(LOG_WARNING, "Cannot load image data from file '%s'",
|
|
||||||
_file.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
_matchImage =
|
|
||||||
_matchImage.convertToFormat(QImage::Format::Format_RGBX8888);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MacroConditionVideo::LoadModelData(std::string &path)
|
|
||||||
{
|
|
||||||
_modelDataPath = path;
|
|
||||||
_objectCascade = initObjectCascade(path);
|
|
||||||
return !_objectCascade.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assumption is that QImage uses Format_RGBX8888.
|
|
||||||
// Conversion from: https://github.com/dbzhang800/QtOpenCV
|
// Conversion from: https://github.com/dbzhang800/QtOpenCV
|
||||||
cv::Mat QImageToMat(const QImage &img)
|
cv::Mat QImageToMat(const QImage &img)
|
||||||
{
|
{
|
||||||
@@ -210,31 +193,94 @@ QImage MatToQImage(const cv::Mat &mat)
|
|||||||
return QImage();
|
return QImage();
|
||||||
}
|
}
|
||||||
return QImage(mat.data, mat.cols, mat.rows, mat.step,
|
return QImage(mat.data, mat.cols, mat.rows, mat.step,
|
||||||
QImage::Format::Format_RGBX8888);
|
QImage::Format::Format_RGBA8888);
|
||||||
}
|
}
|
||||||
|
|
||||||
void matchPattern(QImage &img, QImage &pattern, double threshold,
|
PatternMatchData createPatternData(QImage &pattern)
|
||||||
cv::Mat &result)
|
|
||||||
{
|
{
|
||||||
if (img.isNull() || pattern.isNull()) {
|
PatternMatchData data;
|
||||||
|
if (pattern.isNull()) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.rgbaPattern = QImageToMat(pattern);
|
||||||
|
std::vector<cv::Mat1b> rgbaChannelsPattern;
|
||||||
|
cv::split(data.rgbaPattern, rgbaChannelsPattern);
|
||||||
|
std::vector<cv::Mat1b> rgbChanlesPattern(
|
||||||
|
rgbaChannelsPattern.begin(), rgbaChannelsPattern.begin() + 3);
|
||||||
|
cv::merge(rgbChanlesPattern, data.rgbPattern);
|
||||||
|
cv::threshold(rgbaChannelsPattern[3], data.mask, 0, 255,
|
||||||
|
cv::THRESH_BINARY);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroConditionVideo::LoadImageFromFile()
|
||||||
|
{
|
||||||
|
if (!_matchImage.load(QString::fromStdString(_file))) {
|
||||||
|
blog(LOG_WARNING, "Cannot load image data from file '%s'",
|
||||||
|
_file.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_matchImage =
|
||||||
|
_matchImage.convertToFormat(QImage::Format::Format_RGBA8888);
|
||||||
|
_patternData = createPatternData(_matchImage);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacroConditionVideo::LoadModelData(std::string &path)
|
||||||
|
{
|
||||||
|
_modelDataPath = path;
|
||||||
|
_objectCascade = initObjectCascade(path);
|
||||||
|
return !_objectCascade.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void matchPattern(QImage &img, PatternMatchData &patternData, double threshold,
|
||||||
|
cv::Mat &result, bool useAlphaAsMask = true)
|
||||||
|
{
|
||||||
|
if (img.isNull() || patternData.rgbaPattern.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (img.height() < pattern.height() || img.width() < pattern.width()) {
|
if (img.height() < patternData.rgbaPattern.rows ||
|
||||||
|
img.width() < patternData.rgbaPattern.cols) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto i = QImageToMat(img);
|
auto i = QImageToMat(img);
|
||||||
auto p = QImageToMat(pattern);
|
|
||||||
|
|
||||||
cv::matchTemplate(i, p, result, cv::TM_CCOEFF_NORMED);
|
if (useAlphaAsMask) {
|
||||||
cv::threshold(result, result, threshold, 0, cv::THRESH_TOZERO);
|
std::vector<cv::Mat1b> rgbaChannelsImage;
|
||||||
|
cv::split(i, rgbaChannelsImage);
|
||||||
|
std::vector<cv::Mat1b> rgbChanlesImage(
|
||||||
|
rgbaChannelsImage.begin(),
|
||||||
|
rgbaChannelsImage.begin() + 3);
|
||||||
|
|
||||||
|
cv::Mat3b rgbImage;
|
||||||
|
cv::merge(rgbChanlesImage, rgbImage);
|
||||||
|
|
||||||
|
cv::matchTemplate(rgbImage, patternData.rgbPattern, result,
|
||||||
|
cv::TM_CCORR_NORMED, patternData.mask);
|
||||||
|
cv::threshold(result, result, threshold, 0, cv::THRESH_TOZERO);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
cv::matchTemplate(i, patternData.rgbaPattern, result,
|
||||||
|
cv::TM_CCOEFF_NORMED);
|
||||||
|
cv::threshold(result, result, threshold, 0, cv::THRESH_TOZERO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void matchPattern(QImage &img, QImage &pattern, double threshold,
|
||||||
|
cv::Mat &result, bool useAlphaAsMask)
|
||||||
|
{
|
||||||
|
auto data = createPatternData(pattern);
|
||||||
|
matchPattern(img, data, threshold, result, useAlphaAsMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacroConditionVideo::ScreenshotContainsPattern()
|
bool MacroConditionVideo::ScreenshotContainsPattern()
|
||||||
{
|
{
|
||||||
cv::Mat result;
|
cv::Mat result;
|
||||||
matchPattern(_screenshotData->image, _matchImage, _patternThreshold,
|
matchPattern(_screenshotData->image, _patternData, _patternThreshold,
|
||||||
result);
|
result, _useAlphaAsMask);
|
||||||
return countNonZero(result) > 0;
|
return countNonZero(result) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,6 +404,8 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
|
|||||||
"AdvSceneSwitcher.condition.video.patternThreshold"),
|
"AdvSceneSwitcher.condition.video.patternThreshold"),
|
||||||
obs_module_text(
|
obs_module_text(
|
||||||
"AdvSceneSwitcher.condition.video.patternThresholdDescription"));
|
"AdvSceneSwitcher.condition.video.patternThresholdDescription"));
|
||||||
|
_useAlphaAsMask = new QCheckBox(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.condition.video.patternThresholdUseAlphaAsMask"));
|
||||||
|
|
||||||
_modelDataPath = new FileSelection();
|
_modelDataPath = new FileSelection();
|
||||||
_objectScaleThreshold = new ThresholdSlider(
|
_objectScaleThreshold = new ThresholdSlider(
|
||||||
@@ -369,6 +417,8 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
|
|||||||
_minNeighbors = new QSpinBox();
|
_minNeighbors = new QSpinBox();
|
||||||
_minNeighbors->setMinimum(minMinNeighbors);
|
_minNeighbors->setMinimum(minMinNeighbors);
|
||||||
_minNeighbors->setMaximum(maxMinNeighbors);
|
_minNeighbors->setMaximum(maxMinNeighbors);
|
||||||
|
_minNeighborsDescription = new QLabel(obs_module_text(
|
||||||
|
"AdvSceneSwitcher.condition.video.minNeighborDescription"));
|
||||||
_minSizeX = new QSpinBox();
|
_minSizeX = new QSpinBox();
|
||||||
_minSizeY = new QSpinBox();
|
_minSizeY = new QSpinBox();
|
||||||
_minSizeX->setMaximum(1024);
|
_minSizeX->setMaximum(1024);
|
||||||
@@ -397,6 +447,8 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
|
|||||||
SLOT(ImageBrowseButtonClicked()));
|
SLOT(ImageBrowseButtonClicked()));
|
||||||
QWidget::connect(_patternThreshold, SIGNAL(DoubleValueChanged(double)),
|
QWidget::connect(_patternThreshold, SIGNAL(DoubleValueChanged(double)),
|
||||||
this, SLOT(PatternThresholdChanged(double)));
|
this, SLOT(PatternThresholdChanged(double)));
|
||||||
|
QWidget::connect(_useAlphaAsMask, SIGNAL(stateChanged(int)), this,
|
||||||
|
SLOT(UseAlphaAsMaskChanged(int)));
|
||||||
QWidget::connect(_objectScaleThreshold,
|
QWidget::connect(_objectScaleThreshold,
|
||||||
SIGNAL(DoubleValueChanged(double)), this,
|
SIGNAL(DoubleValueChanged(double)), this,
|
||||||
SLOT(ObjectScaleThresholdChanged(double)));
|
SLOT(ObjectScaleThresholdChanged(double)));
|
||||||
@@ -472,9 +524,11 @@ MacroConditionVideoEdit::MacroConditionVideoEdit(
|
|||||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||||
mainLayout->addLayout(entryLine1Layout);
|
mainLayout->addLayout(entryLine1Layout);
|
||||||
mainLayout->addWidget(_patternThreshold);
|
mainLayout->addWidget(_patternThreshold);
|
||||||
|
mainLayout->addWidget(_useAlphaAsMask);
|
||||||
mainLayout->addLayout(_modelPathLayout);
|
mainLayout->addLayout(_modelPathLayout);
|
||||||
mainLayout->addWidget(_objectScaleThreshold);
|
mainLayout->addWidget(_objectScaleThreshold);
|
||||||
mainLayout->addLayout(_neighborsControlLayout);
|
mainLayout->addLayout(_neighborsControlLayout);
|
||||||
|
mainLayout->addWidget(_minNeighborsDescription);
|
||||||
mainLayout->addLayout(_minSizeControlLayout);
|
mainLayout->addLayout(_minSizeControlLayout);
|
||||||
mainLayout->addLayout(_maxSizeControlLayout);
|
mainLayout->addLayout(_maxSizeControlLayout);
|
||||||
mainLayout->addLayout(showMatchLayout);
|
mainLayout->addLayout(showMatchLayout);
|
||||||
@@ -525,11 +579,6 @@ void MacroConditionVideoEdit::SourceChanged(const QString &text)
|
|||||||
QString::fromStdString(_entryData->GetShortDesc()));
|
QString::fromStdString(_entryData->GetShortDesc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool needsPatternThreshold(VideoCondition cond)
|
|
||||||
{
|
|
||||||
return cond == VideoCondition::PATTERN;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MacroConditionVideoEdit::ConditionChanged(int cond)
|
void MacroConditionVideoEdit::ConditionChanged(int cond)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
if (_loading || !_entryData) {
|
||||||
@@ -644,6 +693,17 @@ void MacroConditionVideoEdit::PatternThresholdChanged(double value)
|
|||||||
_entryData->_patternThreshold = value;
|
_entryData->_patternThreshold = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroConditionVideoEdit::UseAlphaAsMaskChanged(int value)
|
||||||
|
{
|
||||||
|
if (_loading || !_entryData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
_entryData->_useAlphaAsMask = value;
|
||||||
|
_entryData->LoadImageFromFile();
|
||||||
|
}
|
||||||
|
|
||||||
void MacroConditionVideoEdit::ObjectScaleThresholdChanged(double value)
|
void MacroConditionVideoEdit::ObjectScaleThresholdChanged(double value)
|
||||||
{
|
{
|
||||||
if (_loading || !_entryData) {
|
if (_loading || !_entryData) {
|
||||||
@@ -734,7 +794,7 @@ QImage markPatterns(cv::Mat &matchResult, QImage &image, QImage &pattern)
|
|||||||
rectangle(matchImg, {col, row},
|
rectangle(matchImg, {col, row},
|
||||||
cv::Point(col + pattern.width(),
|
cv::Point(col + pattern.width(),
|
||||||
row + pattern.height()),
|
row + pattern.height()),
|
||||||
cv::Scalar(255, 0, 0, 0), 2, 8, 0);
|
cv::Scalar(255, 0, 0, 255), 2, 8, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -748,7 +808,7 @@ QImage markObjects(QImage &image, std::vector<cv::Rect> &objects)
|
|||||||
rectangle(frame, cv::Point(objects[i].x, objects[i].y),
|
rectangle(frame, cv::Point(objects[i].x, objects[i].y),
|
||||||
cv::Point(objects[i].x + objects[i].width,
|
cv::Point(objects[i].x + objects[i].width,
|
||||||
objects[i].y + objects[i].height),
|
objects[i].y + objects[i].height),
|
||||||
cv::Scalar(255, 0, 0, 0), 2, 8, 0);
|
cv::Scalar(255, 0, 0, 255), 2, 8, 0);
|
||||||
}
|
}
|
||||||
return MatToQImage(frame);
|
return MatToQImage(frame);
|
||||||
}
|
}
|
||||||
@@ -772,7 +832,8 @@ void MacroConditionVideoEdit::ShowMatchClicked()
|
|||||||
if (_entryData->_condition == VideoCondition::PATTERN) {
|
if (_entryData->_condition == VideoCondition::PATTERN) {
|
||||||
cv::Mat result;
|
cv::Mat result;
|
||||||
matchPattern(screenshot->image, pattern,
|
matchPattern(screenshot->image, pattern,
|
||||||
_entryData->_patternThreshold, result);
|
_entryData->_patternThreshold, result,
|
||||||
|
_entryData->_useAlphaAsMask);
|
||||||
if (countNonZero(result) == 0) {
|
if (countNonZero(result) == 0) {
|
||||||
DisplayMessage(obs_module_text(
|
DisplayMessage(obs_module_text(
|
||||||
"AdvSceneSwitcher.condition.video.patternMatchFail"));
|
"AdvSceneSwitcher.condition.video.patternMatchFail"));
|
||||||
@@ -851,13 +912,17 @@ void setLayoutVisible(QLayout *layout, bool visible)
|
|||||||
void MacroConditionVideoEdit::SetWidgetVisibility()
|
void MacroConditionVideoEdit::SetWidgetVisibility()
|
||||||
{
|
{
|
||||||
_imagePath->setVisible(requiresFileInput(_entryData->_condition));
|
_imagePath->setVisible(requiresFileInput(_entryData->_condition));
|
||||||
_patternThreshold->setVisible(
|
_patternThreshold->setVisible(_entryData->_condition ==
|
||||||
needsPatternThreshold(_entryData->_condition));
|
VideoCondition::PATTERN);
|
||||||
|
_useAlphaAsMask->setVisible(_entryData->_condition ==
|
||||||
|
VideoCondition::PATTERN);
|
||||||
_showMatch->setVisible(needsShowMatch(_entryData->_condition));
|
_showMatch->setVisible(needsShowMatch(_entryData->_condition));
|
||||||
_objectScaleThreshold->setVisible(
|
_objectScaleThreshold->setVisible(
|
||||||
needsObjectControls(_entryData->_condition));
|
needsObjectControls(_entryData->_condition));
|
||||||
setLayoutVisible(_neighborsControlLayout,
|
setLayoutVisible(_neighborsControlLayout,
|
||||||
needsObjectControls(_entryData->_condition));
|
needsObjectControls(_entryData->_condition));
|
||||||
|
_minNeighborsDescription->setVisible(
|
||||||
|
needsObjectControls(_entryData->_condition));
|
||||||
setLayoutVisible(_minSizeControlLayout,
|
setLayoutVisible(_minSizeControlLayout,
|
||||||
needsObjectControls(_entryData->_condition));
|
needsObjectControls(_entryData->_condition));
|
||||||
setLayoutVisible(_maxSizeControlLayout,
|
setLayoutVisible(_maxSizeControlLayout,
|
||||||
@@ -880,6 +945,7 @@ void MacroConditionVideoEdit::UpdateEntryData()
|
|||||||
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
_condition->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||||
_imagePath->SetPath(QString::fromStdString(_entryData->_file));
|
_imagePath->SetPath(QString::fromStdString(_entryData->_file));
|
||||||
_patternThreshold->SetDoubleValue(_entryData->_patternThreshold);
|
_patternThreshold->SetDoubleValue(_entryData->_patternThreshold);
|
||||||
|
_useAlphaAsMask->setChecked(_entryData->_useAlphaAsMask);
|
||||||
_modelDataPath->SetPath(_entryData->GetModelDataPath().c_str());
|
_modelDataPath->SetPath(_entryData->GetModelDataPath().c_str());
|
||||||
_objectScaleThreshold->SetDoubleValue(_entryData->_scaleFactor);
|
_objectScaleThreshold->SetDoubleValue(_entryData->_scaleFactor);
|
||||||
_minNeighbors->setValue(_entryData->_minNeighbors);
|
_minNeighbors->setValue(_entryData->_minNeighbors);
|
||||||
|
|||||||
@@ -145,3 +145,8 @@ void MacroSegmentEdit::SetFocusPolicyOfWidgets()
|
|||||||
w->installEventFilter(new MouseWheelWidgetAdjustmentGuard(w));
|
w->installEventFilter(new MouseWheelWidgetAdjustmentGuard(w));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MacroSegmentEdit::SetCollapsed(bool collapsed)
|
||||||
|
{
|
||||||
|
_section->SetCollapsed(collapsed);
|
||||||
|
}
|
||||||
|
|||||||
@@ -170,42 +170,46 @@ void AdvSceneSwitcher::on_macroName_editingFinished()
|
|||||||
emit MacroRenamed(oldName, newName);
|
emit MacroRenamed(oldName, newName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::PopulateMacroActions(Macro &m, int afterIdx)
|
||||||
|
{
|
||||||
|
bool root = afterIdx == 0;
|
||||||
|
auto &actions = m.Actions();
|
||||||
|
for (; afterIdx < actions.size(); afterIdx++) {
|
||||||
|
auto newEntry = new MacroActionEdit(this, &actions[afterIdx],
|
||||||
|
actions[afterIdx]->GetId());
|
||||||
|
ConnectControlSignals(newEntry);
|
||||||
|
ui->macroEditActionLayout->addWidget(newEntry);
|
||||||
|
ui->macroEditActionHelp->setVisible(false);
|
||||||
|
root = false;
|
||||||
|
}
|
||||||
|
ui->macroEditActionHelp->setVisible(actions.size() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvSceneSwitcher::PopulateMacroConditions(Macro &m, int afterIdx)
|
||||||
|
{
|
||||||
|
bool root = afterIdx == 0;
|
||||||
|
auto &conditions = m.Conditions();
|
||||||
|
for (; afterIdx < conditions.size(); afterIdx++) {
|
||||||
|
auto newEntry = new MacroConditionEdit(
|
||||||
|
this, &conditions[afterIdx],
|
||||||
|
conditions[afterIdx]->GetId(), root);
|
||||||
|
ConnectControlSignals(newEntry);
|
||||||
|
ui->macroEditConditionLayout->addWidget(newEntry);
|
||||||
|
ui->macroEditConditionHelp->setVisible(false);
|
||||||
|
root = false;
|
||||||
|
}
|
||||||
|
ui->macroEditConditionHelp->setVisible(conditions.size() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::SetEditMacro(Macro &m)
|
void AdvSceneSwitcher::SetEditMacro(Macro &m)
|
||||||
{
|
{
|
||||||
ui->macroName->setText(m.Name().c_str());
|
ui->macroName->setText(m.Name().c_str());
|
||||||
clearLayout(ui->macroEditConditionLayout);
|
clearLayout(ui->macroEditConditionLayout);
|
||||||
clearLayout(ui->macroEditActionLayout);
|
clearLayout(ui->macroEditActionLayout);
|
||||||
|
|
||||||
bool root = true;
|
PopulateMacroConditions(m);
|
||||||
for (auto &c : m.Conditions()) {
|
PopulateMacroActions(m);
|
||||||
auto newEntry =
|
|
||||||
new MacroConditionEdit(this, &c, c->GetId(), root);
|
|
||||||
ConnectControlSignals(newEntry);
|
|
||||||
ui->macroEditConditionLayout->addWidget(newEntry);
|
|
||||||
ui->macroEditConditionHelp->setVisible(false);
|
|
||||||
root = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &a : m.Actions()) {
|
|
||||||
auto newEntry = new MacroActionEdit(this, &a, a->GetId());
|
|
||||||
ConnectControlSignals(newEntry);
|
|
||||||
ui->macroEditActionLayout->addWidget(newEntry);
|
|
||||||
ui->macroEditActionHelp->setVisible(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
ui->macroEdit->setDisabled(false);
|
ui->macroEdit->setDisabled(false);
|
||||||
|
|
||||||
if (m.Conditions().size() == 0) {
|
|
||||||
ui->macroEditConditionHelp->setVisible(true);
|
|
||||||
} else {
|
|
||||||
ui->macroEditConditionHelp->setVisible(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m.Actions().size() == 0) {
|
|
||||||
ui->macroEditActionHelp->setVisible(true);
|
|
||||||
} else {
|
|
||||||
ui->macroEditActionHelp->setVisible(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::HighlightAction(int idx)
|
void AdvSceneSwitcher::HighlightAction(int idx)
|
||||||
@@ -400,17 +404,25 @@ void AdvSceneSwitcher::CopyMacro()
|
|||||||
ui->macros->setCurrentItem(item);
|
ui->macros->setCurrentItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setCollapsedStateOfSegmentsIn(QLayout *layout, bool collapse)
|
||||||
|
{
|
||||||
|
QLayoutItem *item = nullptr;
|
||||||
|
for (int i = 0; i < layout->count(); i++) {
|
||||||
|
item = layout->itemAt(i);
|
||||||
|
auto segment = dynamic_cast<MacroSegmentEdit *>(item->widget());
|
||||||
|
if (segment) {
|
||||||
|
segment->SetCollapsed(collapse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::ExpandAllActions()
|
void AdvSceneSwitcher::ExpandAllActions()
|
||||||
{
|
{
|
||||||
auto m = getSelectedMacro();
|
auto m = getSelectedMacro();
|
||||||
if (!m) {
|
if (!m) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
setCollapsedStateOfSegmentsIn(ui->macroEditActionLayout, false);
|
||||||
for (auto &a : m->Actions()) {
|
|
||||||
a->SetCollapsed(false);
|
|
||||||
}
|
|
||||||
SetEditMacro(*m);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::ExpandAllConditions()
|
void AdvSceneSwitcher::ExpandAllConditions()
|
||||||
@@ -419,11 +431,7 @@ void AdvSceneSwitcher::ExpandAllConditions()
|
|||||||
if (!m) {
|
if (!m) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
setCollapsedStateOfSegmentsIn(ui->macroEditConditionLayout, false);
|
||||||
for (auto &c : m->Conditions()) {
|
|
||||||
c->SetCollapsed(false);
|
|
||||||
}
|
|
||||||
SetEditMacro(*m);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::CollapseAllActions()
|
void AdvSceneSwitcher::CollapseAllActions()
|
||||||
@@ -432,11 +440,7 @@ void AdvSceneSwitcher::CollapseAllActions()
|
|||||||
if (!m) {
|
if (!m) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
setCollapsedStateOfSegmentsIn(ui->macroEditActionLayout, true);
|
||||||
for (auto &a : m->Actions()) {
|
|
||||||
a->SetCollapsed(true);
|
|
||||||
}
|
|
||||||
SetEditMacro(*m);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSceneSwitcher::CollapseAllConditions()
|
void AdvSceneSwitcher::CollapseAllConditions()
|
||||||
@@ -445,9 +449,5 @@ void AdvSceneSwitcher::CollapseAllConditions()
|
|||||||
if (!m) {
|
if (!m) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
setCollapsedStateOfSegmentsIn(ui->macroEditConditionLayout, true);
|
||||||
for (auto &c : m->Conditions()) {
|
|
||||||
c->SetCollapsed(true);
|
|
||||||
}
|
|
||||||
SetEditMacro(*m);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ bool Macro::PerformAction()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ret && _count != std::numeric_limits<int>::max()) {
|
if (_count != std::numeric_limits<int>::max()) {
|
||||||
_count++;
|
_count++;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
@@ -492,7 +492,7 @@ bool SwitcherData::checkMacros()
|
|||||||
|
|
||||||
bool SwitcherData::runMacros()
|
bool SwitcherData::runMacros()
|
||||||
{
|
{
|
||||||
for (auto &m : macros) {
|
for (auto m : macros) {
|
||||||
if (m.Matched()) {
|
if (m.Matched()) {
|
||||||
vblog(LOG_INFO, "running macro: %s", m.Name().c_str());
|
vblog(LOG_INFO, "running macro: %s", m.Name().c_str());
|
||||||
if (!m.PerformAction()) {
|
if (!m.PerformAction()) {
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ void SceneTrigger::logMatch()
|
|||||||
blog(LOG_INFO,
|
blog(LOG_INFO,
|
||||||
"scene '%s' in status '%s' triggering action '%s' after %f seconds",
|
"scene '%s' in status '%s' triggering action '%s' after %f seconds",
|
||||||
GetWeakSourceName(scene).c_str(), statusName.c_str(),
|
GetWeakSourceName(scene).c_str(), statusName.c_str(),
|
||||||
actionName.c_str(), duration);
|
actionName.c_str(), duration.seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
void frontEndActionThread(sceneTriggerAction action, double delay)
|
void frontEndActionThread(sceneTriggerAction action, double delay)
|
||||||
@@ -265,13 +265,15 @@ void SceneTrigger::performAction()
|
|||||||
std::thread t;
|
std::thread t;
|
||||||
|
|
||||||
if (isFrontendAction(triggerAction)) {
|
if (isFrontendAction(triggerAction)) {
|
||||||
t = std::thread(frontEndActionThread, triggerAction, duration);
|
t = std::thread(frontEndActionThread, triggerAction,
|
||||||
|
duration.seconds);
|
||||||
} else if (isAudioAction(triggerAction)) {
|
} else if (isAudioAction(triggerAction)) {
|
||||||
bool mute = triggerAction == sceneTriggerAction::MUTE_SOURCE;
|
bool mute = triggerAction == sceneTriggerAction::MUTE_SOURCE;
|
||||||
t = std::thread(muteThread, audioSource, duration, mute);
|
t = std::thread(muteThread, audioSource, duration.seconds,
|
||||||
|
mute);
|
||||||
} else if (isSwitcherStatusAction(triggerAction)) {
|
} else if (isSwitcherStatusAction(triggerAction)) {
|
||||||
bool stop = triggerAction == sceneTriggerAction::STOP_SWITCHER;
|
bool stop = triggerAction == sceneTriggerAction::STOP_SWITCHER;
|
||||||
t = std::thread(statusThread, duration, stop);
|
t = std::thread(statusThread, duration.seconds, stop);
|
||||||
} else {
|
} else {
|
||||||
blog(LOG_WARNING, "ignoring unknown action '%d'",
|
blog(LOG_WARNING, "ignoring unknown action '%d'",
|
||||||
static_cast<int>(triggerAction));
|
static_cast<int>(triggerAction));
|
||||||
@@ -374,7 +376,7 @@ void SceneTrigger::save(obs_data_t *obj)
|
|||||||
obs_data_set_string(obj, "scene", GetWeakSourceName(scene).c_str());
|
obs_data_set_string(obj, "scene", GetWeakSourceName(scene).c_str());
|
||||||
obs_data_set_int(obj, "triggerType", static_cast<int>(triggerType));
|
obs_data_set_int(obj, "triggerType", static_cast<int>(triggerType));
|
||||||
obs_data_set_int(obj, "triggerAction", static_cast<int>(triggerAction));
|
obs_data_set_int(obj, "triggerAction", static_cast<int>(triggerAction));
|
||||||
obs_data_set_double(obj, "duration", duration);
|
duration.Save(obj, "duration");
|
||||||
obs_data_set_string(obj, "audioSource",
|
obs_data_set_string(obj, "audioSource",
|
||||||
GetWeakSourceName(audioSource).c_str());
|
GetWeakSourceName(audioSource).c_str());
|
||||||
}
|
}
|
||||||
@@ -388,7 +390,7 @@ void SceneTrigger::load(obs_data_t *obj)
|
|||||||
obs_data_get_int(obj, "triggerType"));
|
obs_data_get_int(obj, "triggerType"));
|
||||||
triggerAction = static_cast<sceneTriggerAction>(
|
triggerAction = static_cast<sceneTriggerAction>(
|
||||||
obs_data_get_int(obj, "triggerAction"));
|
obs_data_get_int(obj, "triggerAction"));
|
||||||
duration = obs_data_get_double(obj, "duration");
|
duration.Load(obj, "duration");
|
||||||
|
|
||||||
const char *audioSourceName = obs_data_get_string(obj, "audioSource");
|
const char *audioSourceName = obs_data_get_string(obj, "audioSource");
|
||||||
audioSource = GetWeakSourceByName(audioSourceName);
|
audioSource = GetWeakSourceByName(audioSourceName);
|
||||||
@@ -451,19 +453,17 @@ SceneTriggerWidget::SceneTriggerWidget(QWidget *parent, SceneTrigger *s)
|
|||||||
{
|
{
|
||||||
triggers = new QComboBox();
|
triggers = new QComboBox();
|
||||||
actions = new QComboBox();
|
actions = new QComboBox();
|
||||||
duration = new QDoubleSpinBox();
|
duration = new DurationSelection();
|
||||||
audioSources = new QComboBox();
|
audioSources = new QComboBox();
|
||||||
|
|
||||||
duration->setMinimum(0.0);
|
|
||||||
duration->setMaximum(99.000000);
|
|
||||||
duration->setSuffix("s");
|
|
||||||
|
|
||||||
QWidget::connect(triggers, SIGNAL(currentIndexChanged(int)), this,
|
QWidget::connect(triggers, SIGNAL(currentIndexChanged(int)), this,
|
||||||
SLOT(TriggerTypeChanged(int)));
|
SLOT(TriggerTypeChanged(int)));
|
||||||
QWidget::connect(actions, SIGNAL(currentIndexChanged(int)), this,
|
QWidget::connect(actions, SIGNAL(currentIndexChanged(int)), this,
|
||||||
SLOT(TriggerActionChanged(int)));
|
SLOT(TriggerActionChanged(int)));
|
||||||
QWidget::connect(duration, SIGNAL(valueChanged(double)), this,
|
QWidget::connect(duration, SIGNAL(DurationChanged(double)), this,
|
||||||
SLOT(DurationChanged(double)));
|
SLOT(DurationChanged(double)));
|
||||||
|
QWidget::connect(duration, SIGNAL(UnitChanged(DurationUnit)), this,
|
||||||
|
SLOT(DurationUnitChanged(DurationUnit)));
|
||||||
QWidget::connect(audioSources,
|
QWidget::connect(audioSources,
|
||||||
SIGNAL(currentTextChanged(const QString &)), this,
|
SIGNAL(currentTextChanged(const QString &)), this,
|
||||||
SLOT(AudioSourceChanged(const QString &)));
|
SLOT(AudioSourceChanged(const QString &)));
|
||||||
@@ -475,7 +475,7 @@ SceneTriggerWidget::SceneTriggerWidget(QWidget *parent, SceneTrigger *s)
|
|||||||
if (s) {
|
if (s) {
|
||||||
triggers->setCurrentIndex(static_cast<int>(s->triggerType));
|
triggers->setCurrentIndex(static_cast<int>(s->triggerType));
|
||||||
actions->setCurrentIndex(static_cast<int>(s->triggerAction));
|
actions->setCurrentIndex(static_cast<int>(s->triggerAction));
|
||||||
duration->setValue(s->duration);
|
duration->SetDuration(s->duration);
|
||||||
|
|
||||||
audioSources->setCurrentText(
|
audioSources->setCurrentText(
|
||||||
GetWeakSourceName(s->audioSource).c_str());
|
GetWeakSourceName(s->audioSource).c_str());
|
||||||
@@ -552,14 +552,24 @@ void SceneTriggerWidget::TriggerActionChanged(int index)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneTriggerWidget::DurationChanged(double dur)
|
void SceneTriggerWidget::DurationChanged(double seconds)
|
||||||
{
|
{
|
||||||
if (loading || !switchData) {
|
if (loading || !switchData) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(switcher->m);
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
switchData->duration = dur;
|
switchData->duration.seconds = seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneTriggerWidget::DurationUnitChanged(DurationUnit unit)
|
||||||
|
{
|
||||||
|
if (loading || !switchData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(switcher->m);
|
||||||
|
switchData->duration.displayUnit = unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneTriggerWidget::AudioSourceChanged(const QString &text)
|
void SceneTriggerWidget::AudioSourceChanged(const QString &text)
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ void AdvSSScreenshotObj::Copy()
|
|||||||
uint8_t *videoData = nullptr;
|
uint8_t *videoData = nullptr;
|
||||||
uint32_t videoLinesize = 0;
|
uint32_t videoLinesize = 0;
|
||||||
|
|
||||||
image = QImage(cx, cy, QImage::Format::Format_RGBX8888);
|
image = QImage(cx, cy, QImage::Format::Format_RGBA8888);
|
||||||
|
|
||||||
if (gs_stagesurface_map(stagesurf, &videoData, &videoLinesize)) {
|
if (gs_stagesurface_map(stagesurf, &videoData, &videoLinesize)) {
|
||||||
int linesize = image.bytesPerLine();
|
int linesize = image.bytesPerLine();
|
||||||
|
|||||||
@@ -93,6 +93,11 @@ void Section::AddHeaderWidget(QWidget *w)
|
|||||||
_headerWidgetLayout->addWidget(w);
|
_headerWidgetLayout->addWidget(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Section::SetCollapsed(bool collapsed)
|
||||||
|
{
|
||||||
|
Collapse(collapsed);
|
||||||
|
}
|
||||||
|
|
||||||
bool Section::eventFilter(QObject *obj, QEvent *event)
|
bool Section::eventFilter(QObject *obj, QEvent *event)
|
||||||
{
|
{
|
||||||
if (event->type() == QEvent::Resize && !_transitioning && !_collapsed) {
|
if (event->type() == QEvent::Resize && !_transitioning && !_collapsed) {
|
||||||
|
|||||||
@@ -392,6 +392,11 @@ SceneSequenceSwitch *SceneSequenceSwitch::extend()
|
|||||||
}
|
}
|
||||||
extendedSequence = std::make_unique<SceneSequenceSwitch>();
|
extendedSequence = std::make_unique<SceneSequenceSwitch>();
|
||||||
extendedSequence->startScene = scene;
|
extendedSequence->startScene = scene;
|
||||||
|
if (targetType == SwitchTargetType::SceneGroup) {
|
||||||
|
extendedSequence->startScene = nullptr;
|
||||||
|
extendedSequence->startTargetType =
|
||||||
|
SwitchTargetType::SceneGroup;
|
||||||
|
}
|
||||||
return extendedSequence.get();
|
return extendedSequence.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ bool VideoSwitch::loadImageFromFile()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
matchImage =
|
matchImage =
|
||||||
matchImage.convertToFormat(QImage::Format::Format_RGBX8888);
|
matchImage.convertToFormat(QImage::Format::Format_RGBA8888);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -213,10 +213,10 @@ void deleteLayoutItem(QLayoutItem *item)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void clearLayout(QLayout *layout)
|
void clearLayout(QLayout *layout, int afterIdx)
|
||||||
{
|
{
|
||||||
QLayoutItem *item;
|
QLayoutItem *item;
|
||||||
while ((item = layout->takeAt(0))) {
|
while (item = layout->takeAt(afterIdx)) {
|
||||||
if (item->layout()) {
|
if (item->layout()) {
|
||||||
clearLayout(item->layout());
|
clearLayout(item->layout());
|
||||||
delete item->layout();
|
delete item->layout();
|
||||||
|
|||||||
Reference in New Issue
Block a user