mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
per effect smoothing (#456)
* add dynamic smoothing first step * extend prio muxer to hold smoothing preset id * add icons for systray * fix missing changes in prio muxer * implement specific smoothing params for effects * refactoring: std::min/max to qMin/Max * some code optimization * fix schema and translation * revoke change of python include order * fix eol in effect shemas * optimize random,candle and fadecandy json schemas
This commit is contained in:
@@ -78,13 +78,14 @@ void Effect::registerHyperionExtensionModule()
|
||||
PyImport_AppendInittab("hyperion", &PyInit_hyperion);
|
||||
}
|
||||
|
||||
Effect::Effect(PyThreadState * mainThreadState, int priority, int timeout, const QString & script, const QString & name, const QJsonObject & args, const QString & origin)
|
||||
Effect::Effect(PyThreadState * mainThreadState, int priority, int timeout, const QString & script, const QString & name, const QJsonObject & args, const QString & origin, unsigned smoothCfg)
|
||||
: QThread()
|
||||
, _mainThreadState(mainThreadState)
|
||||
, _priority(priority)
|
||||
, _timeout(timeout)
|
||||
, _script(script)
|
||||
, _name(name)
|
||||
, _smoothCfg(smoothCfg)
|
||||
, _args(args)
|
||||
, _endTime(-1)
|
||||
, _interpreterThreadState(nullptr)
|
||||
@@ -93,7 +94,7 @@ Effect::Effect(PyThreadState * mainThreadState, int priority, int timeout, const
|
||||
, _colors()
|
||||
, _origin(origin)
|
||||
, _imageSize(Hyperion::getInstance()->getLedGridSize())
|
||||
,_image(_imageSize,QImage::Format_ARGB32_Premultiplied)
|
||||
, _image(_imageSize,QImage::Format_ARGB32_Premultiplied)
|
||||
{
|
||||
_colors.resize(_imageProcessor->getLedCount());
|
||||
_colors.fill(ColorRgb::BLACK);
|
||||
@@ -277,7 +278,7 @@ PyObject* Effect::wrapSetColor(PyObject *self, PyObject *args)
|
||||
if (PyArg_ParseTuple(args, "bbb", &color.red, &color.green, &color.blue))
|
||||
{
|
||||
effect->_colors.fill(color);
|
||||
effect->setColors(effect->_priority, effect->_colors.toStdVector(), timeout, false, hyperion::COMP_EFFECT, effect->_origin);
|
||||
effect->setColors(effect->_priority, effect->_colors.toStdVector(), timeout, false, hyperion::COMP_EFFECT, effect->_origin, effect->_smoothCfg);
|
||||
return Py_BuildValue("");
|
||||
}
|
||||
return nullptr;
|
||||
@@ -295,7 +296,7 @@ PyObject* Effect::wrapSetColor(PyObject *self, PyObject *args)
|
||||
{
|
||||
char * data = PyByteArray_AS_STRING(bytearray);
|
||||
memcpy(effect->_colors.data(), data, length);
|
||||
effect->setColors(effect->_priority, effect->_colors.toStdVector(), timeout, false, hyperion::COMP_EFFECT, effect->_origin);
|
||||
effect->setColors(effect->_priority, effect->_colors.toStdVector(), timeout, false, hyperion::COMP_EFFECT, effect->_origin, effect->_smoothCfg);
|
||||
return Py_BuildValue("");
|
||||
}
|
||||
else
|
||||
@@ -365,7 +366,7 @@ PyObject* Effect::wrapSetImage(PyObject *self, PyObject *args)
|
||||
memcpy(image.memptr(), data, length);
|
||||
std::vector<ColorRgb> v = effect->_colors.toStdVector();
|
||||
effect->_imageProcessor->process(image, v);
|
||||
effect->setColors(effect->_priority, v, timeout, false, hyperion::COMP_EFFECT, effect->_origin);
|
||||
effect->setColors(effect->_priority, v, timeout, false, hyperion::COMP_EFFECT, effect->_origin, effect->_smoothCfg);
|
||||
return Py_BuildValue("");
|
||||
}
|
||||
else
|
||||
@@ -456,7 +457,7 @@ PyObject* Effect::wrapImageShow(PyObject *self, PyObject *args)
|
||||
memcpy(image.memptr(), binaryImage.data(), binaryImage.size());
|
||||
std::vector<ColorRgb> v = effect->_colors.toStdVector();
|
||||
effect->_imageProcessor->process(image, v);
|
||||
effect->setColors(effect->_priority, v, timeout, false, hyperion::COMP_EFFECT, effect->_origin);
|
||||
effect->setColors(effect->_priority, v, timeout, false, hyperion::COMP_EFFECT, effect->_origin, effect->_smoothCfg);
|
||||
|
||||
return Py_BuildValue("");
|
||||
}
|
||||
@@ -552,7 +553,7 @@ PyObject* Effect::wrapImageConicalGradient(PyObject *self, PyObject *args)
|
||||
{
|
||||
argsOK = true;
|
||||
}
|
||||
angle = std::max(std::min(angle,360),0);
|
||||
angle = qMax(qMin(angle,360),0);
|
||||
|
||||
if (argsOK)
|
||||
{
|
||||
@@ -644,7 +645,7 @@ PyObject* Effect::wrapImageRadialGradient(PyObject *self, PyObject *args)
|
||||
{
|
||||
|
||||
QRect myQRect(startX,startY,width,height);
|
||||
QRadialGradient gradient(QPoint(centerX,centerY), std::max(radius,0) );
|
||||
QRadialGradient gradient(QPoint(centerX,centerY), qMax(radius,0) );
|
||||
char * data = PyByteArray_AS_STRING(bytearray);
|
||||
|
||||
for (int idx=0; idx<length; idx+=4)
|
||||
@@ -774,8 +775,8 @@ PyObject* Effect::wrapImageDrawPie(PyObject *self, PyObject *args)
|
||||
if (argsOK)
|
||||
{
|
||||
QPainter * painter = effect->_painter;
|
||||
startAngle = std::max(std::min(startAngle,360),0);
|
||||
spanAngle = std::max(std::min(spanAngle,360),-360);
|
||||
startAngle = qMax(qMin(startAngle,360),0);
|
||||
spanAngle = qMax(qMin(spanAngle,360),-360);
|
||||
|
||||
if( argCount == 7 || argCount == 5 )
|
||||
{
|
||||
@@ -1045,7 +1046,7 @@ PyObject* Effect::wrapImageMinSize(PyObject *self, PyObject *args)
|
||||
{
|
||||
delete effect->_painter;
|
||||
|
||||
effect->_image = effect->_image.scaled(std::max(width,w),std::max(height,h), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
|
||||
effect->_image = effect->_image.scaled(qMax(width,w),qMax(height,h), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
|
||||
effect->_imageSize = effect->_image.size();
|
||||
effect->_painter = new QPainter(&(effect->_image));
|
||||
}
|
||||
@@ -1075,7 +1076,7 @@ PyObject* Effect::wrapImageCRotate(PyObject *self, PyObject *args)
|
||||
|
||||
if ( argCount == 1 && PyArg_ParseTuple(args, "i", &angle ) )
|
||||
{
|
||||
angle = std::max(std::min(angle,360),0);
|
||||
angle = qMax(qMin(angle,360),0);
|
||||
effect->_painter->rotate(angle);
|
||||
return Py_BuildValue("");
|
||||
}
|
||||
|
@@ -19,7 +19,7 @@ class Effect : public QThread
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Effect(PyThreadState * mainThreadState, int priority, int timeout, const QString & script, const QString & name, const QJsonObject & args = QJsonObject(), const QString & origin="System");
|
||||
Effect(PyThreadState * mainThreadState, int priority, int timeout, const QString & script, const QString & name, const QJsonObject & args = QJsonObject(), const QString & origin="System", unsigned smoothCfg=0);
|
||||
virtual ~Effect();
|
||||
|
||||
virtual void run();
|
||||
@@ -44,7 +44,7 @@ public slots:
|
||||
signals:
|
||||
void effectFinished(Effect * effect);
|
||||
|
||||
void setColors(int priority, const std::vector<ColorRgb> &ledColors, const int timeout_ms, bool clearEffects, hyperion::Components componentconst, QString origin);
|
||||
void setColors(int priority, const std::vector<ColorRgb> &ledColors, const int timeout_ms, bool clearEffects, hyperion::Components componentconst, QString origin, unsigned smoothCfg);
|
||||
|
||||
private slots:
|
||||
void effectFinished();
|
||||
@@ -96,6 +96,7 @@ private:
|
||||
|
||||
const QString _script;
|
||||
const QString _name;
|
||||
unsigned _smoothCfg;
|
||||
|
||||
const QJsonObject _args;
|
||||
|
||||
@@ -112,10 +113,10 @@ private:
|
||||
QVector<ColorRgb> _colors;
|
||||
|
||||
|
||||
QString _origin;
|
||||
QSize _imageSize;
|
||||
QImage _image;
|
||||
QPainter* _painter;
|
||||
QString _origin;
|
||||
QSize _imageSize;
|
||||
QImage _image;
|
||||
QPainter* _painter;
|
||||
QVector<QImage> _imageStack;
|
||||
};
|
||||
|
||||
|
@@ -2,14 +2,12 @@
|
||||
#include <Python.h>
|
||||
#undef B0
|
||||
|
||||
// Stl includes
|
||||
#include <fstream>
|
||||
|
||||
// Qt includes
|
||||
#include <QResource>
|
||||
#include <QMetaType>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QMap>
|
||||
|
||||
// hyperion util includes
|
||||
#include <utils/jsonschema/QJsonSchemaChecker.h>
|
||||
@@ -41,7 +39,7 @@ EffectEngine::EffectEngine(Hyperion * hyperion, const QJsonObject & jsonEffectCo
|
||||
readEffects();
|
||||
|
||||
// initialize the python interpreter
|
||||
Debug(_log,"Initializing Python interpreter");
|
||||
Debug(_log, "Initializing Python interpreter");
|
||||
Effect::registerHyperionExtensionModule();
|
||||
Py_InitializeEx(0);
|
||||
PyEval_InitThreads(); // Create the GIL
|
||||
@@ -63,11 +61,11 @@ const std::list<ActiveEffectDefinition> &EffectEngine::getActiveEffects()
|
||||
for (Effect * effect : _activeEffects)
|
||||
{
|
||||
ActiveEffectDefinition activeEffectDefinition;
|
||||
activeEffectDefinition.script = effect->getScript();
|
||||
activeEffectDefinition.name = effect->getName();
|
||||
activeEffectDefinition.script = effect->getScript();
|
||||
activeEffectDefinition.name = effect->getName();
|
||||
activeEffectDefinition.priority = effect->getPriority();
|
||||
activeEffectDefinition.timeout = effect->getTimeout();
|
||||
activeEffectDefinition.args = effect->getArgs();
|
||||
activeEffectDefinition.timeout = effect->getTimeout();
|
||||
activeEffectDefinition.args = effect->getArgs();
|
||||
_availableActiveEffects.push_back(activeEffectDefinition);
|
||||
}
|
||||
|
||||
@@ -86,7 +84,7 @@ bool EffectEngine::loadEffectDefinition(const QString &path, const QString &effe
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
Error( log, "Effect file '%s' could not be loaded", fileName.toUtf8().constData());
|
||||
Error( log, "Effect file '%s' could not be loaded", QSTRING_CSTR(fileName));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -108,7 +106,7 @@ bool EffectEngine::loadEffectDefinition(const QString &path, const QString &effe
|
||||
}
|
||||
}
|
||||
|
||||
Error( log, "Error while reading effect: '%s' at Line: '%i' , Column: %i", error.errorString().toUtf8().constData(), errorLine, errorColumn);
|
||||
Error( log, "Error while reading effect: '%s' at Line: '%i' , Column: %i",QSTRING_CSTR( error.errorString()), errorLine, errorColumn);
|
||||
}
|
||||
|
||||
file.close();
|
||||
@@ -120,7 +118,7 @@ bool EffectEngine::loadEffectDefinition(const QString &path, const QString &effe
|
||||
|
||||
if (!schema.open(QIODevice::ReadOnly))
|
||||
{
|
||||
Error( log, "Schema not found: %s", schema.errorString().toUtf8().constData());
|
||||
Error( log, "Schema not found: %s", QSTRING_CSTR(schema.errorString()));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -142,7 +140,7 @@ bool EffectEngine::loadEffectDefinition(const QString &path, const QString &effe
|
||||
}
|
||||
}
|
||||
|
||||
Error( log, "ERROR: Json schema wrong: '%s' at Line: '%i' , Column: %i", error.errorString().toUtf8().constData(), errorLine, errorColumn);
|
||||
Error( log, "ERROR: Json schema wrong: '%s' at Line: '%i' , Column: %i", QSTRING_CSTR(error.errorString()), errorLine, errorColumn);
|
||||
}
|
||||
|
||||
schema.close();
|
||||
@@ -154,7 +152,7 @@ bool EffectEngine::loadEffectDefinition(const QString &path, const QString &effe
|
||||
if (!schemaChecker.validate(configEffect.object()).first)
|
||||
{
|
||||
const QStringList & errors = schemaChecker.getMessages();
|
||||
foreach (auto & error, errors)
|
||||
for (auto & error : errors)
|
||||
{
|
||||
Error( log, "Error while checking '%s':%s", QSTRING_CSTR(fileName), QSTRING_CSTR(error));
|
||||
}
|
||||
@@ -180,12 +178,27 @@ bool EffectEngine::loadEffectDefinition(const QString &path, const QString &effe
|
||||
} else
|
||||
{
|
||||
(!fileInfo.exists())
|
||||
? effectDefinition.script = path + QDir::separator().toLatin1() + scriptName
|
||||
? effectDefinition.script = path + QDir::separator() + scriptName
|
||||
: effectDefinition.script = scriptName;
|
||||
}
|
||||
|
||||
effectDefinition.args = config["args"].toObject();
|
||||
|
||||
effectDefinition.smoothCfg = SMOOTHING_MODE_PAUSE;
|
||||
if (effectDefinition.args["smoothing-custom-settings"].toBool())
|
||||
{
|
||||
bool pause = effectDefinition.args["smoothing-pause"].toBool();
|
||||
if (pause)
|
||||
{
|
||||
effectDefinition.smoothCfg = _hyperion->addSmoothingConfig(pause);
|
||||
}
|
||||
else
|
||||
{
|
||||
effectDefinition.smoothCfg = _hyperion->addSmoothingConfig(
|
||||
effectDefinition.args["smoothing-time_ms"].toInt(),
|
||||
effectDefinition.args["smoothing-updateFrequency"].toDouble(),
|
||||
effectDefinition.args["smoothing-updateDelay"].toInt() );
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -201,7 +214,7 @@ bool EffectEngine::loadEffectSchema(const QString &path, const QString &effectSc
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
Error( log, "Effect schema '%s' could not be loaded", fileName.toUtf8().constData());
|
||||
Error( log, "Effect schema '%s' could not be loaded", QSTRING_CSTR(fileName));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -223,7 +236,7 @@ bool EffectEngine::loadEffectSchema(const QString &path, const QString &effectSc
|
||||
}
|
||||
}
|
||||
|
||||
Error( log, "Error while reading effect schema: '%s' at Line: '%i' , Column: %i", error.errorString().toUtf8().constData(), errorLine, errorColumn);
|
||||
Error( log, "Error while reading effect schema: '%s' at Line: '%i' , Column: %i", QSTRING_CSTR(error.errorString()), errorLine, errorColumn);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -240,17 +253,13 @@ bool EffectEngine::loadEffectSchema(const QString &path, const QString &effectSc
|
||||
if (scriptName.isEmpty() || !pyFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
fileName = path + "schema/" + QDir::separator() + effectSchemaFile;
|
||||
Error( log, "Python script '%s' in effect schema '%s' could not be loaded", scriptName.toUtf8().constData(), fileName.toUtf8().constData());
|
||||
Error( log, "Python script '%s' in effect schema '%s' could not be loaded", QSTRING_CSTR(scriptName), QSTRING_CSTR(fileName));
|
||||
return false;
|
||||
}
|
||||
|
||||
pyFile.close();
|
||||
|
||||
if (scriptName.mid(0, 1) == ":" )
|
||||
effectSchema.pyFile = ":/effects/"+scriptName.mid(1);
|
||||
else
|
||||
effectSchema.pyFile = path + QDir::separator().toLatin1() + scriptName;
|
||||
|
||||
effectSchema.pyFile = (scriptName.mid(0, 1) == ":" ) ? ":/effects/"+scriptName.mid(1) : path + QDir::separator() + scriptName;
|
||||
effectSchema.pySchema = tempSchemaEffect;
|
||||
|
||||
return true;
|
||||
@@ -279,38 +288,36 @@ void EffectEngine::readEffects()
|
||||
disableList << efx.toString();
|
||||
}
|
||||
|
||||
std::map<QString, EffectDefinition> availableEffects;
|
||||
foreach (const QString & path, efxPathList )
|
||||
QMap<QString, EffectDefinition> availableEffects;
|
||||
for (const QString & path : efxPathList )
|
||||
{
|
||||
QDir directory(path);
|
||||
if (!directory.exists())
|
||||
{
|
||||
if(directory.mkpath(path))
|
||||
{
|
||||
Warning(_log, "New Effect path \"%s\" created successfull",path.toUtf8().constData() );
|
||||
Warning(_log, "New Effect path \"%s\" created successfull", QSTRING_CSTR(path) );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning(_log, "Failed to create Effect path \"%s\", please check permissions",path.toUtf8().constData() );
|
||||
Warning(_log, "Failed to create Effect path \"%s\", please check permissions", QSTRING_CSTR(path) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int efxCount = 0;
|
||||
QStringList filenames = directory.entryList(QStringList() << "*.json", QDir::Files, QDir::Name | QDir::IgnoreCase);
|
||||
foreach (const QString & filename, filenames)
|
||||
for (const QString & filename : filenames)
|
||||
{
|
||||
EffectDefinition def;
|
||||
if (loadEffectDefinition(path, filename, def))
|
||||
{
|
||||
if (availableEffects.find(def.name) != availableEffects.end())
|
||||
{
|
||||
Info(_log, "effect overload effect '%s' is now taken from %s'", def.name.toUtf8().constData(), path.toUtf8().constData() );
|
||||
}
|
||||
InfoIf(availableEffects.find(def.name) != availableEffects.end(), _log,
|
||||
"effect overload effect '%s' is now taken from %s'", QSTRING_CSTR(def.name), QSTRING_CSTR(path) );
|
||||
|
||||
if ( disableList.contains(def.name) )
|
||||
{
|
||||
Info(_log, "effect '%s' not loaded, because it is disabled in hyperion config", def.name.toUtf8().constData());
|
||||
Info(_log, "effect '%s' not loaded, because it is disabled in hyperion config", QSTRING_CSTR(def.name));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -319,13 +326,13 @@ void EffectEngine::readEffects()
|
||||
}
|
||||
}
|
||||
}
|
||||
Info(_log, "%d effects loaded from directory %s", efxCount, path.toUtf8().constData());
|
||||
Info(_log, "%d effects loaded from directory %s", efxCount, QSTRING_CSTR(path));
|
||||
|
||||
// collect effect schemas
|
||||
efxCount = 0;
|
||||
directory = path + "schema/";
|
||||
QStringList pynames = directory.entryList(QStringList() << "*.json", QDir::Files, QDir::Name | QDir::IgnoreCase);
|
||||
foreach (const QString & pyname, pynames)
|
||||
for (const QString & pyname : pynames)
|
||||
{
|
||||
EffectSchema pyEffect;
|
||||
if (loadEffectSchema(path, pyname, pyEffect))
|
||||
@@ -334,20 +341,16 @@ void EffectEngine::readEffects()
|
||||
efxCount++;
|
||||
}
|
||||
}
|
||||
if (efxCount > 0)
|
||||
Info(_log, "%d effect schemas loaded from directory %s", efxCount, (path + "schema/").toUtf8().constData());
|
||||
InfoIf(efxCount > 0, _log, "%d effect schemas loaded from directory %s", efxCount, QSTRING_CSTR((path + "schema/")));
|
||||
}
|
||||
}
|
||||
|
||||
foreach(auto item, availableEffects)
|
||||
for(auto item : availableEffects)
|
||||
{
|
||||
_availableEffects.push_back(item.second);
|
||||
_availableEffects.push_back(item);
|
||||
}
|
||||
|
||||
if (_availableEffects.size() == 0)
|
||||
{
|
||||
Error(_log, "no effects found, check your effect directories");
|
||||
}
|
||||
ErrorIf(_availableEffects.size()==0, _log, "no effects found, check your effect directories");
|
||||
}
|
||||
|
||||
int EffectEngine::runEffect(const QString &effectName, int priority, int timeout, const QString &origin)
|
||||
@@ -355,9 +358,9 @@ int EffectEngine::runEffect(const QString &effectName, int priority, int timeout
|
||||
return runEffect(effectName, QJsonObject(), priority, timeout, "", origin);
|
||||
}
|
||||
|
||||
int EffectEngine::runEffect(const QString &effectName, const QJsonObject &args, int priority, int timeout, const QString &pythonScript, const QString &origin)
|
||||
int EffectEngine::runEffect(const QString &effectName, const QJsonObject &args, int priority, int timeout, const QString &pythonScript, const QString &origin, unsigned smoothCfg)
|
||||
{
|
||||
Info( _log, "run effect %s on channel %d", effectName.toUtf8().constData(), priority);
|
||||
Info( _log, "run effect %s on channel %d", QSTRING_CSTR(effectName), priority);
|
||||
|
||||
if (pythonScript.isEmpty())
|
||||
{
|
||||
@@ -373,23 +376,23 @@ int EffectEngine::runEffect(const QString &effectName, const QJsonObject &args,
|
||||
if (effectDefinition == nullptr)
|
||||
{
|
||||
// no such effect
|
||||
Error(_log, "effect %s not found", effectName.toUtf8().constData());
|
||||
Error(_log, "effect %s not found", QSTRING_CSTR(effectName));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return runEffectScript(effectDefinition->script, effectName, (args.isEmpty() ? effectDefinition->args : args), priority, timeout, origin);
|
||||
return runEffectScript(effectDefinition->script, effectName, (args.isEmpty() ? effectDefinition->args : args), priority, timeout, origin, effectDefinition->smoothCfg);
|
||||
}
|
||||
return runEffectScript(pythonScript, effectName, args, priority, timeout, origin);
|
||||
return runEffectScript(pythonScript, effectName, args, priority, timeout, origin, smoothCfg);
|
||||
}
|
||||
|
||||
int EffectEngine::runEffectScript(const QString &script, const QString &name, const QJsonObject &args, int priority, int timeout, const QString & origin)
|
||||
int EffectEngine::runEffectScript(const QString &script, const QString &name, const QJsonObject &args, int priority, int timeout, const QString & origin, unsigned smoothCfg)
|
||||
{
|
||||
// clear current effect on the channel
|
||||
channelCleared(priority);
|
||||
|
||||
// create the effect
|
||||
Effect * effect = new Effect(_mainThreadState, priority, timeout, script, name, args, origin);
|
||||
connect(effect, SIGNAL(setColors(int,std::vector<ColorRgb>,int,bool,hyperion::Components,const QString)), _hyperion, SLOT(setColors(int,std::vector<ColorRgb>,int,bool,hyperion::Components,const QString)), Qt::QueuedConnection);
|
||||
Effect * effect = new Effect(_mainThreadState, priority, timeout, script, name, args, origin, smoothCfg);
|
||||
connect(effect, SIGNAL(setColors(int,std::vector<ColorRgb>,int,bool,hyperion::Components,const QString,unsigned)), _hyperion, SLOT(setColors(int,std::vector<ColorRgb>,int,bool,hyperion::Components,const QString,unsigned)), Qt::QueuedConnection);
|
||||
connect(effect, SIGNAL(effectFinished(Effect*)), this, SLOT(effectFinished(Effect*)));
|
||||
_activeEffects.push_back(effect);
|
||||
|
||||
|
Reference in New Issue
Block a user