From d24fddaf217ca19daab2072c265fee1f93cdbe37 Mon Sep 17 00:00:00 2001 From: johan Date: Tue, 26 Nov 2013 21:38:24 +0100 Subject: [PATCH] Python interpreter added to EffectEngine Former-commit-id: f721f5952efe305d66347d9074ff760baabd2f18 --- include/effectengine/EffectEngine.h | 8 +++ libsrc/effectengine/Effect.cpp | 90 +++++++++++++++++++++++++++- libsrc/effectengine/Effect.h | 32 +++++++++- libsrc/effectengine/EffectEngine.cpp | 57 +++++++++++++++++- 4 files changed, 182 insertions(+), 5 deletions(-) diff --git a/include/effectengine/EffectEngine.h b/include/effectengine/EffectEngine.h index 6b81049a..b3b38a96 100644 --- a/include/effectengine/EffectEngine.h +++ b/include/effectengine/EffectEngine.h @@ -2,6 +2,9 @@ #include +// pre-declarioation +class Effect; + class EffectEngine : public QObject { Q_OBJECT @@ -22,8 +25,13 @@ public slots: /// Clear all effects void allChannelsCleared(); +private slots: + void effectFinished(Effect * effect); + private: Hyperion * _hyperion; std::map _availableEffects; + + std::list _activeEffects; }; diff --git a/libsrc/effectengine/Effect.cpp b/libsrc/effectengine/Effect.cpp index 4090a976..b772d074 100644 --- a/libsrc/effectengine/Effect.cpp +++ b/libsrc/effectengine/Effect.cpp @@ -1,10 +1,98 @@ +// stl includes +#include + +// effect engin eincludes #include "Effect.h" -Effect::Effect() +// Effect wrapper methods for Python interpreter extra build in methods +static PyObject* Effect_SetColor(PyObject *self, PyObject *args) { + return Py_BuildValue("i", 42); +} + +static PyObject* Effect_SetImage(PyObject *self, PyObject *args) +{ + return Py_BuildValue("i", 42); +} + +static PyObject* Effect_GetLedCount(PyObject *self, PyObject *args) +{ + return Py_BuildValue("i", 42); +} + +static PyObject* Effect_IsAbortRequested(PyObject *self, PyObject *args) +{ + return Py_BuildValue("i", 42); +} + +static PyMethodDef effectMethods[] = { + {"setColor", Effect_SetColor, METH_VARARGS, "Set a new color for the leds."}, + {"setImage", Effect_SetImage, METH_VARARGS, "Set a new image to process and determine new led colors."}, + {"getLedCount", Effect_GetLedCount, METH_VARARGS, "Get the number of avaliable led channels."}, + {"isAbortRequested", Effect_IsAbortRequested, METH_VARARGS, "Check if the effect should abort execution."}, + {NULL, NULL, 0, NULL} +}; + + +Effect::Effect(int priority, int timeout) : + QThread(), + _priority(priority), + _timeout(timeout), + _interpreterThreadState(nullptr), + _abortRequested(false) +{ + // connect the finished signal + connect(this, SIGNAL(finished()), this, SLOT(effectFinished())); } Effect::~Effect() { } +void Effect::run() +{ + // Initialize a new thread state + PyEval_AcquireLock(); // Get the GIL + _interpreterThreadState = Py_NewInterpreter(); + Py_InitModule("hyperiond", effectMethods); + + // Create hyperion instance in the new interpreter + std::ostringstream oss; + oss << "import hyperiond" << std::endl; + oss << "class Hyperion:" << std::endl; + oss << " def setColor(self):" << std::endl; + oss << " return hyperiond.setColor()" << std::endl; + oss << " def setImage(self):" << std::endl; + oss << " return hyperiond.setImage()" << std::endl; + oss << " def getLedCount(self):" << std::endl; + oss << " return hyperiond.getLedCount()" << std::endl; + oss << " def isAbortRequested(self):" << std::endl; + oss << " return hyperiond.isAbortRequested()" << std::endl; + oss << "hyperion = Hyperion()" << std::endl; + PyRun_SimpleString(oss.str().c_str()); + + // Run the effect script + std::string script = "test.py"; + FILE* file = fopen(script.c_str(), "r"); + PyRun_SimpleFile(file, script.c_str()); + + // Clean up the thread state + Py_EndInterpreter(_interpreterThreadState); + _interpreterThreadState = nullptr; + PyEval_ReleaseLock(); +} + +int Effect::getPriority() const +{ + return _priority; +} + +void Effect::abort() +{ + _abortRequested = true; +} + +void Effect::effectFinished() +{ + emit effectFinished(this); +} diff --git a/libsrc/effectengine/Effect.h b/libsrc/effectengine/Effect.h index 76257815..221a59d9 100644 --- a/libsrc/effectengine/Effect.h +++ b/libsrc/effectengine/Effect.h @@ -1,12 +1,38 @@ #pragma once -#include +// Qt includes +#include -class Effect : public QObject +// Python includes +#include + +class Effect : public QThread { Q_OBJECT public: - Effect(); + Effect(int priority, int timeout); virtual ~Effect(); + + virtual void run(); + + int getPriority() const; + +public slots: + void abort(); + +signals: + void effectFinished(Effect * effect); + +private slots: + void effectFinished(); + +private: + const int _priority; + + const int _timeout; + + PyThreadState * _interpreterThreadState; + + bool _abortRequested; }; diff --git a/libsrc/effectengine/EffectEngine.cpp b/libsrc/effectengine/EffectEngine.cpp index 5f0ddc3c..2771f3f8 100644 --- a/libsrc/effectengine/EffectEngine.cpp +++ b/libsrc/effectengine/EffectEngine.cpp @@ -1,8 +1,14 @@ +// Python includes +#include + +// effect engine includes #include +#include "Effect.h" EffectEngine::EffectEngine(Hyperion * hyperion) : _hyperion(hyperion), - _availableEffects() + _availableEffects(), + _activeEffects() { // connect the Hyperion channel clear feedback connect(_hyperion, SIGNAL(channelCleared(int)), this, SLOT(channelCleared(int))); @@ -10,10 +16,20 @@ EffectEngine::EffectEngine(Hyperion * hyperion) : // read all effects _availableEffects["test"] = "test.py"; + + // initialize the python interpreter + std::cout << "Initializing Python interpreter" << std::endl; + Py_InitializeEx(0); + PyEval_InitThreads(); // Create the GIL + PyEval_ReleaseLock(); // Release the GIL } EffectEngine::~EffectEngine() { + // clean up the Python interpreter + std::cout << "Cleaning up Python interpreter" << std::endl; + PyEval_AcquireLock(); // Get the Gil + Py_Finalize(); } std::list EffectEngine::getEffects() const @@ -28,15 +44,54 @@ std::list EffectEngine::getEffects() const int EffectEngine::runEffect(const std::string &effectName, int priority, int timeout) { std::cout << "run effect " << effectName << " on channel " << priority << std::endl; + + // clear current effect on the channel + channelCleared(priority); + + // create the effect + Effect * effect = new Effect(priority, timeout); + connect(effect, SIGNAL(effectFinished(Effect*)), this, SLOT(effectFinished(Effect*))); + _activeEffects.push_back(effect); + + // start the effect + effect->start(); + return 0; } void EffectEngine::channelCleared(int priority) { std::cout << "clear effect on channel " << priority << std::endl; + for (Effect * effect : _activeEffects) + { + if (effect->getPriority() == priority) + { + effect->abort(); + } + } } void EffectEngine::allChannelsCleared() { std::cout << "clear effect on every channel" << std::endl; + for (Effect * effect : _activeEffects) + { + effect->abort(); + } +} + +void EffectEngine::effectFinished(Effect *effect) +{ + std::cout << "effect finished" << std::endl; + for (auto effectIt = _activeEffects.begin(); effectIt != _activeEffects.end(); ++effectIt) + { + if (*effectIt == effect) + { + _activeEffects.erase(effectIt); + break; + } + } + + // cleanup the effect + effect->deleteLater(); }