Skip to content
This repository was archived by the owner on May 6, 2021. It is now read-only.

Commit b6aec95

Browse files
committed
Add support for Python 3
1 parent ba15d22 commit b6aec95

File tree

8 files changed

+101
-37
lines changed

8 files changed

+101
-37
lines changed

CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ include_directories(${CMAKE_SOURCE_DIR}/include)
4141
# Prefer static linking over dynamic
4242
#set(CMAKE_FIND_LIBRARY_SUFFIXES ".a;.so")
4343

44-
#set(CMAKE_BUILD_TYPE "Debug")
45-
set(CMAKE_BUILD_TYPE "Release")
44+
set(CMAKE_BUILD_TYPE "Debug")
45+
#set(CMAKE_BUILD_TYPE "Release")
4646

4747
# enable C++11
4848
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -Wall")

config/hyperion_x86.config.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
{
1515
"name" : "MyPi",
1616
"type" : "adalight",
17-
"output" : "/dev/ttyUSB0",
17+
"output" : "/dev/ttyUSB0",
1818
"rate" : 115200,
1919
"colorOrder" : "rgb"
2020
},
@@ -363,7 +363,7 @@
363363
{
364364
"paths" :
365365
[
366-
"/opt/hyperion/effects"
366+
"/home/dincs/projects/hyperion/effects"
367367
]
368368
},
369369

dependencies/build/getoptPlusPlus/getoptpp.cc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,6 @@ void PresettableUniquelySwitchable::preset() {
280280
template<>
281281
PODParameter<string>::PODParameter(char shortOption, const char *longOption,
282282
const char* description) : CommonParameter<PresettableUniquelySwitchable>(shortOption, longOption, description) {
283-
preset();
284283
}
285284

286285

include/effectengine/EffectEngine.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,5 @@ private slots:
5555

5656
std::list<Effect *> _activeEffects;
5757

58-
PyThreadState * _mainThreadState;
58+
PyThreadState * _mainThreadState;
5959
};

libsrc/effectengine/Effect.cpp

Lines changed: 69 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,39 @@ PyMethodDef Effect::effectMethods[] = {
1919
{NULL, NULL, 0, NULL}
2020
};
2121

22+
#if PY_MAJOR_VERSION >= 3
23+
// create the hyperion module
24+
struct PyModuleDef Effect::moduleDef = {
25+
PyModuleDef_HEAD_INIT,
26+
"hyperion", /* m_name */
27+
"Hyperion module", /* m_doc */
28+
-1, /* m_size */
29+
Effect::effectMethods, /* m_methods */
30+
NULL, /* m_reload */
31+
NULL, /* m_traverse */
32+
NULL, /* m_clear */
33+
NULL, /* m_free */
34+
};
35+
36+
PyObject* Effect::PyInit_hyperion()
37+
{
38+
return PyModule_Create(&moduleDef);
39+
}
40+
#else
41+
void Effect::PyInit_hyperion()
42+
{
43+
Py_InitModule("hyperion", effectMethods);
44+
}
45+
#endif
2246

23-
Effect::Effect(int priority, int timeout, const std::string & script, const Json::Value & args) :
47+
void Effect::registerHyperionExtensionModule()
48+
{
49+
PyImport_AppendInittab("hyperion", &PyInit_hyperion);
50+
}
51+
52+
Effect::Effect(PyThreadState * mainThreadState, int priority, int timeout, const std::string & script, const Json::Value & args) :
2453
QThread(),
54+
_mainThreadState(mainThreadState),
2555
_priority(priority),
2656
_timeout(timeout),
2757
_script(script),
@@ -44,20 +74,26 @@ Effect::~Effect()
4474

4575
void Effect::run()
4676
{
77+
// switch to the main thread state and acquire the GIL
78+
PyEval_RestoreThread(_mainThreadState);
79+
4780
// Initialize a new thread state
48-
PyEval_AcquireLock(); // Get the GIL
49-
_interpreterThreadState = Py_NewInterpreter();
81+
_interpreterThreadState = Py_NewInterpreter();
82+
83+
// import the buildtin Hyperion module
84+
PyObject * module = PyImport_ImportModule("hyperion");
85+
86+
// add a capsule containing 'this' to the module to be able to retrieve the effect from the callback function
87+
PyObject_SetAttrString(module, "__effectObj", PyCapsule_New(this, nullptr, nullptr));
5088

51-
// add methods extra builtin methods to the interpreter
52-
PyObject * thisCapsule = PyCapsule_New(this, nullptr, nullptr);
53-
PyObject * module = Py_InitModule4("hyperion", effectMethods, nullptr, thisCapsule, PYTHON_API_VERSION);
89+
// add ledCount variable to the interpreter
90+
PyObject_SetAttrString(module, "ledCount", Py_BuildValue("i", _imageProcessor->getLedCount()));
5491

55-
// add ledCount variable to the interpreter
56-
PyObject_SetAttrString(module, "ledCount", Py_BuildValue("i", _imageProcessor->getLedCount()));
92+
// add a args variable to the interpreter
93+
PyObject_SetAttrString(module, "args", json2python(_args));
5794

58-
// add a args variable to the interpreter
59-
PyObject_SetAttrString(module, "args", json2python(_args));
60-
//PyObject_SetAttrString(module, "args", Py_BuildValue("s", _args.c_str()));
95+
// decref the module
96+
Py_XDECREF(module);
6197

6298
// Set the end time if applicable
6399
if (_timeout > 0)
@@ -144,7 +180,7 @@ PyObject *Effect::json2python(const Json::Value &json) const
144180
PyObject* Effect::wrapSetColor(PyObject *self, PyObject *args)
145181
{
146182
// get the effect
147-
Effect * effect = getEffect(self);
183+
Effect * effect = getEffect();
148184

149185
// check if we have aborted already
150186
if (effect->_abortRequested)
@@ -229,7 +265,7 @@ PyObject* Effect::wrapSetColor(PyObject *self, PyObject *args)
229265
PyObject* Effect::wrapSetImage(PyObject *self, PyObject *args)
230266
{
231267
// get the effect
232-
Effect * effect = getEffect(self);
268+
Effect * effect = getEffect();
233269

234270
// check if we have aborted already
235271
if (effect->_abortRequested)
@@ -292,7 +328,7 @@ PyObject* Effect::wrapSetImage(PyObject *self, PyObject *args)
292328

293329
PyObject* Effect::wrapAbort(PyObject *self, PyObject *)
294330
{
295-
Effect * effect = getEffect(self);
331+
Effect * effect = getEffect();
296332

297333
// Test if the effect has reached it end time
298334
if (effect->_timeout > 0 && QDateTime::currentMSecsSinceEpoch() > effect->_endTime)
@@ -303,8 +339,24 @@ PyObject* Effect::wrapAbort(PyObject *self, PyObject *)
303339
return Py_BuildValue("i", effect->_abortRequested ? 1 : 0);
304340
}
305341

306-
Effect * Effect::getEffect(PyObject *self)
342+
Effect * Effect::getEffect()
307343
{
308-
// Get the effect from the capsule in the self pointer
309-
return reinterpret_cast<Effect *>(PyCapsule_GetPointer(self, nullptr));
344+
// extract the module from the runtime
345+
PyObject * module = PyObject_GetAttrString(PyImport_AddModule("__main__"), "hyperion");
346+
347+
if (PyModule_Check(module))
348+
{
349+
// retrieve the capsule with the effect
350+
PyObject * effectCapsule = PyObject_GetAttrString(module, "__effectObj");
351+
352+
if (PyCapsule_CheckExact(effectCapsule))
353+
{
354+
// Get the effect from the capsule
355+
return reinterpret_cast<Effect *>(PyCapsule_GetPointer(effectCapsule, nullptr));
356+
}
357+
}
358+
359+
// something is wrong
360+
std::cerr << "Unable to retrieve the effect object from the Python runtime" << std::endl;
361+
return nullptr;
310362
}

libsrc/effectengine/Effect.h

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class Effect : public QThread
1414
Q_OBJECT
1515

1616
public:
17-
Effect(int priority, int timeout, const std::string & script, const Json::Value & args = Json::Value());
17+
Effect(PyThreadState * mainThreadState, int priority, int timeout, const std::string & script, const Json::Value & args = Json::Value());
1818
virtual ~Effect();
1919

2020
virtual void run();
@@ -23,6 +23,9 @@ class Effect : public QThread
2323

2424
bool isAbortRequested() const;
2525

26+
/// This function registers the extension module in Python
27+
static void registerHyperionExtensionModule();
28+
2629
public slots:
2730
void abort();
2831

@@ -38,13 +41,22 @@ private slots:
3841
PyObject * json2python(const Json::Value & json) const;
3942

4043
// Wrapper methods for Python interpreter extra buildin methods
41-
static PyMethodDef effectMethods[];
42-
static PyObject* wrapSetColor(PyObject *self, PyObject *args);
44+
static PyMethodDef effectMethods[];
45+
static PyObject* wrapSetColor(PyObject *self, PyObject *args);
4346
static PyObject* wrapSetImage(PyObject *self, PyObject *args);
4447
static PyObject* wrapAbort(PyObject *self, PyObject *args);
45-
static Effect * getEffect(PyObject *self);
48+
static Effect * getEffect();
49+
50+
#if PY_MAJOR_VERSION >= 3
51+
static struct PyModuleDef moduleDef;
52+
static PyObject* PyInit_hyperion();
53+
#else
54+
static void PyInit_hyperion();
55+
#endif
4656

4757
private:
58+
PyThreadState * _mainThreadState;
59+
4860
const int _priority;
4961

5062
const int _timeout;

libsrc/effectengine/EffectEngine.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ EffectEngine::EffectEngine(Hyperion * hyperion, const Json::Value & jsonEffectCo
5454

5555
// initialize the python interpreter
5656
std::cout << "Initializing Python interpreter" << std::endl;
57+
Effect::registerHyperionExtensionModule();
5758
Py_InitializeEx(0);
5859
PyEval_InitThreads(); // Create the GIL
5960
_mainThreadState = PyEval_SaveThread();
@@ -151,7 +152,7 @@ int EffectEngine::runEffectScript(const std::string &script, const Json::Value &
151152
channelCleared(priority);
152153

153154
// create the effect
154-
Effect * effect = new Effect(priority, timeout, script, args);
155+
Effect * effect = new Effect(_mainThreadState, priority, timeout, script, args);
155156
connect(effect, SIGNAL(setColors(int,std::vector<ColorRgb>,int,bool)), _hyperion, SLOT(setColors(int,std::vector<ColorRgb>,int,bool)), Qt::QueuedConnection);
156157
connect(effect, SIGNAL(effectFinished(Effect*)), this, SLOT(effectFinished(Effect*)));
157158
_activeEffects.push_back(effect);

src/hyperion-remote/hyperion-remote.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ int main(int argc, char * argv[])
4848
IntParameter & argDuration = parameters.add<IntParameter> ('d', "duration" , "Specify how long the leds should be switched on in millseconds [default: infinity]");
4949
ColorParameter & argColor = parameters.add<ColorParameter> ('c', "color" , "Set all leds to a constant color (either RRGGBB hex value or a color name. The color may be repeated multiple time like: RRGGBBRRGGBB)");
5050
ImageParameter & argImage = parameters.add<ImageParameter> ('i', "image" , "Set the leds to the colors according to the given image file");
51-
StringParameter & argEffect = parameters.add<StringParameter> ('e', "effect" , "Enable the effect with the given name");
52-
StringParameter & argEffectArgs = parameters.add<StringParameter> (0x0, "effectArgs", "Arguments to use in combination with the specified effect. Should be a Json object string.");
51+
StringParameter & argEffect = parameters.add<StringParameter> ('e', "effect" , "Enable the effect with the given name");
52+
StringParameter & argEffectArgs = parameters.add<StringParameter> (0x0, "effectArgs", "Arguments to use in combination with the specified effect. Should be a Json object string.");
5353
SwitchParameter<> & argServerInfo = parameters.add<SwitchParameter<> >('l', "list" , "List server info");
5454
SwitchParameter<> & argClear = parameters.add<SwitchParameter<> >('x', "clear" , "Clear data for the priority channel provided by the -p option");
5555
SwitchParameter<> & argClearAll = parameters.add<SwitchParameter<> >(0x0, "clearall" , "Clear data for all active priority channels");
@@ -83,13 +83,13 @@ int main(int argc, char * argv[])
8383
bool colorTransform = argSaturation.isSet() || argValue.isSet() || argThreshold.isSet() || argGamma.isSet() || argBlacklevel.isSet() || argWhitelevel.isSet();
8484

8585
// check that exactly one command was given
86-
int commandCount = count({argColor.isSet(), argImage.isSet(), argEffect.isSet(), argServerInfo.isSet(), argClear.isSet(), argClearAll.isSet(), colorTransform});
86+
int commandCount = count({argColor.isSet(), argImage.isSet(), argEffect.isSet(), argServerInfo.isSet(), argClear.isSet(), argClearAll.isSet(), colorTransform});
8787
if (commandCount != 1)
8888
{
8989
std::cerr << (commandCount == 0 ? "No command found." : "Multiple commands found.") << " Provide exactly one of the following options:" << std::endl;
9090
std::cerr << " " << argColor.usageLine() << std::endl;
9191
std::cerr << " " << argImage.usageLine() << std::endl;
92-
std::cerr << " " << argEffect.usageLine() << std::endl;
92+
std::cerr << " " << argEffect.usageLine() << std::endl;
9393
std::cerr << " " << argServerInfo.usageLine() << std::endl;
9494
std::cerr << " " << argClear.usageLine() << std::endl;
9595
std::cerr << " " << argClearAll.usageLine() << std::endl;
@@ -116,11 +116,11 @@ int main(int argc, char * argv[])
116116
{
117117
connection.setImage(argImage.getValue(), argPriority.getValue(), argDuration.getValue());
118118
}
119-
else if (argEffect.isSet())
120-
{
121-
connection.setEffect(argEffect.getValue(), argEffectArgs.getValue(), argPriority.getValue(), argDuration.getValue());
122-
}
123-
else if (argServerInfo.isSet())
119+
else if (argEffect.isSet())
120+
{
121+
connection.setEffect(argEffect.getValue(), argEffectArgs.getValue(), argPriority.getValue(), argDuration.getValue());
122+
}
123+
else if (argServerInfo.isSet())
124124
{
125125
QString info = connection.getServerInfo();
126126
std::cout << "Server info:\n" << info.toStdString() << std::endl;

0 commit comments

Comments
 (0)