mirror of
				https://github.com/hyperion-project/hyperion.ng.git
				synced 2025-03-01 10:33:28 +00:00 
			
		
		
		
	Refactor Python
This commit is contained in:
		@@ -24,13 +24,13 @@ public:
 | 
			
		||||
 | 
			
		||||
	friend class EffectModule;
 | 
			
		||||
 | 
			
		||||
	Effect(Hyperion *hyperion
 | 
			
		||||
	Effect(Hyperion* hyperion
 | 
			
		||||
		, int priority
 | 
			
		||||
		, int timeout
 | 
			
		||||
				, const QString &script
 | 
			
		||||
				, const QString &name
 | 
			
		||||
				, const QJsonObject &args = QJsonObject()
 | 
			
		||||
				, const QString &imageData = ""
 | 
			
		||||
		, const QString& script
 | 
			
		||||
		, const QString& name
 | 
			
		||||
		, const QJsonObject& args = QJsonObject()
 | 
			
		||||
		, const QString& imageData = ""
 | 
			
		||||
	);
 | 
			
		||||
	~Effect() override;
 | 
			
		||||
 | 
			
		||||
@@ -64,20 +64,20 @@ public:
 | 
			
		||||
	QString getScript() const { return _script; }
 | 
			
		||||
	QString getName() const { return _name; }
 | 
			
		||||
 | 
			
		||||
	int getTimeout() const {return _timeout; }
 | 
			
		||||
	int getTimeout() const { return _timeout; }
 | 
			
		||||
	bool isEndless() const { return _isEndless; }
 | 
			
		||||
 | 
			
		||||
	QJsonObject getArgs() const { return _args; }
 | 
			
		||||
 | 
			
		||||
signals:
 | 
			
		||||
	void setInput(int priority, const std::vector<ColorRgb> &ledColors, int timeout_ms, bool clearEffect);
 | 
			
		||||
	void setInputImage(int priority, const Image<ColorRgb> &image, int timeout_ms, bool clearEffect);
 | 
			
		||||
	void setInput(int priority, const std::vector<ColorRgb>& ledColors, int timeout_ms, bool clearEffect);
 | 
			
		||||
	void setInputImage(int priority, const Image<ColorRgb>& image, int timeout_ms, bool clearEffect);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	void setModuleParameters();
 | 
			
		||||
	bool setModuleParameters();
 | 
			
		||||
	void addImage();
 | 
			
		||||
 | 
			
		||||
	Hyperion *_hyperion;
 | 
			
		||||
	Hyperion* _hyperion;
 | 
			
		||||
 | 
			
		||||
	const int _priority;
 | 
			
		||||
 | 
			
		||||
@@ -95,12 +95,12 @@ private:
 | 
			
		||||
	/// Buffer for colorData
 | 
			
		||||
	QVector<ColorRgb> _colors;
 | 
			
		||||
 | 
			
		||||
	Logger *_log;
 | 
			
		||||
	Logger* _log;
 | 
			
		||||
	// Reflects whenever this effects should interrupt (timeout or external request)
 | 
			
		||||
	std::atomic<bool> _interupt {};
 | 
			
		||||
	std::atomic<bool> _interupt{};
 | 
			
		||||
 | 
			
		||||
	QSize           _imageSize;
 | 
			
		||||
	QImage          _image;
 | 
			
		||||
	QPainter       *_painter;
 | 
			
		||||
	QPainter*       _painter;
 | 
			
		||||
	QVector<QImage> _imageStack;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -8,47 +8,41 @@
 | 
			
		||||
 | 
			
		||||
class Effect;
 | 
			
		||||
 | 
			
		||||
class EffectModule: public QObject
 | 
			
		||||
class EffectModule : public QObject
 | 
			
		||||
{
 | 
			
		||||
	Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	// Python 3 module def
 | 
			
		||||
	static struct PyModuleDef moduleDef;
 | 
			
		||||
 | 
			
		||||
	// Init module
 | 
			
		||||
	static PyObject* PyInit_hyperion();
 | 
			
		||||
 | 
			
		||||
	// Register module once
 | 
			
		||||
	static void registerHyperionExtensionModule();
 | 
			
		||||
 | 
			
		||||
	// json 2 python
 | 
			
		||||
	static PyObject * json2python(const QJsonValue & jsonData);
 | 
			
		||||
	static PyObject* json2python(const QJsonValue& jsonData);
 | 
			
		||||
 | 
			
		||||
	// Wrapper methods for Python interpreter extra buildin methods
 | 
			
		||||
	static PyMethodDef effectMethods[];
 | 
			
		||||
	static PyObject* wrapSetColor              (PyObject *self, PyObject *args);
 | 
			
		||||
	static PyObject* wrapSetImage              (PyObject *self, PyObject *args);
 | 
			
		||||
	static PyObject* wrapGetImage              (PyObject *self, PyObject *args);
 | 
			
		||||
	static PyObject* wrapAbort                 (PyObject *self, PyObject *args);
 | 
			
		||||
	static PyObject* wrapImageShow             (PyObject *self, PyObject *args);
 | 
			
		||||
	static PyObject* wrapImageLinearGradient   (PyObject *self, PyObject *args);
 | 
			
		||||
	static PyObject* wrapImageConicalGradient  (PyObject *self, PyObject *args);
 | 
			
		||||
	static PyObject* wrapImageRadialGradient   (PyObject *self, PyObject *args);
 | 
			
		||||
	static PyObject* wrapImageSolidFill        (PyObject *self, PyObject *args);
 | 
			
		||||
	static PyObject* wrapImageDrawLine         (PyObject *self, PyObject *args);
 | 
			
		||||
	static PyObject* wrapImageDrawPoint        (PyObject *self, PyObject *args);
 | 
			
		||||
	static PyObject* wrapImageDrawRect         (PyObject *self, PyObject *args);
 | 
			
		||||
	static PyObject* wrapImageDrawPolygon      (PyObject *self, PyObject *args);
 | 
			
		||||
	static PyObject* wrapImageDrawPie          (PyObject *self, PyObject *args);
 | 
			
		||||
	static PyObject* wrapImageSetPixel         (PyObject *self, PyObject *args);
 | 
			
		||||
	static PyObject* wrapImageGetPixel         (PyObject *self, PyObject *args);
 | 
			
		||||
	static PyObject* wrapImageSave             (PyObject *self, PyObject *args);
 | 
			
		||||
	static PyObject* wrapImageMinSize          (PyObject *self, PyObject *args);
 | 
			
		||||
	static PyObject* wrapImageWidth            (PyObject *self, PyObject *args);
 | 
			
		||||
	static PyObject* wrapImageHeight           (PyObject *self, PyObject *args);
 | 
			
		||||
	static PyObject* wrapImageCRotate          (PyObject *self, PyObject *args);
 | 
			
		||||
	static PyObject* wrapImageCOffset          (PyObject *self, PyObject *args);
 | 
			
		||||
	static PyObject* wrapImageCShear           (PyObject *self, PyObject *args);
 | 
			
		||||
	static PyObject* wrapImageResetT           (PyObject *self, PyObject *args);
 | 
			
		||||
	static PyObject* wrapSetColor(PyObject* self, PyObject* args);
 | 
			
		||||
	static PyObject* wrapSetImage(PyObject* self, PyObject* args);
 | 
			
		||||
	static PyObject* wrapGetImage(PyObject* self, PyObject* args);
 | 
			
		||||
	static PyObject* wrapAbort(PyObject* self, PyObject* args);
 | 
			
		||||
	static PyObject* wrapImageShow(PyObject* self, PyObject* args);
 | 
			
		||||
	static PyObject* wrapImageLinearGradient(PyObject* self, PyObject* args);
 | 
			
		||||
	static PyObject* wrapImageConicalGradient(PyObject* self, PyObject* args);
 | 
			
		||||
	static PyObject* wrapImageRadialGradient(PyObject* self, PyObject* args);
 | 
			
		||||
	static PyObject* wrapImageSolidFill(PyObject* self, PyObject* args);
 | 
			
		||||
	static PyObject* wrapImageDrawLine(PyObject* self, PyObject* args);
 | 
			
		||||
	static PyObject* wrapImageDrawPoint(PyObject* self, PyObject* args);
 | 
			
		||||
	static PyObject* wrapImageDrawRect(PyObject* self, PyObject* args);
 | 
			
		||||
	static PyObject* wrapImageDrawPolygon(PyObject* self, PyObject* args);
 | 
			
		||||
	static PyObject* wrapImageDrawPie(PyObject* self, PyObject* args);
 | 
			
		||||
	static PyObject* wrapImageSetPixel(PyObject* self, PyObject* args);
 | 
			
		||||
	static PyObject* wrapImageGetPixel(PyObject* self, PyObject* args);
 | 
			
		||||
	static PyObject* wrapImageSave(PyObject* self, PyObject* args);
 | 
			
		||||
	static PyObject* wrapImageMinSize(PyObject* self, PyObject* args);
 | 
			
		||||
	static PyObject* wrapImageWidth(PyObject* self, PyObject* args);
 | 
			
		||||
	static PyObject* wrapImageHeight(PyObject* self, PyObject* args);
 | 
			
		||||
	static PyObject* wrapImageCRotate(PyObject* self, PyObject* args);
 | 
			
		||||
	static PyObject* wrapImageCOffset(PyObject* self, PyObject* args);
 | 
			
		||||
	static PyObject* wrapImageCShear(PyObject* self, PyObject* args);
 | 
			
		||||
	static PyObject* wrapImageResetT(PyObject* self, PyObject* args);
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,9 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#undef slots
 | 
			
		||||
#include <Python.h>
 | 
			
		||||
#define slots Q_SLOTS
 | 
			
		||||
 | 
			
		||||
///
 | 
			
		||||
/// @brief Handle the PythonInit, module registers and DeInit
 | 
			
		||||
///
 | 
			
		||||
@@ -10,4 +14,6 @@ private:
 | 
			
		||||
 | 
			
		||||
	PythonInit();
 | 
			
		||||
	~PythonInit();
 | 
			
		||||
 | 
			
		||||
	void handlePythonError(PyStatus status, PyConfig& config);
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,8 @@
 | 
			
		||||
#include "Python.h"
 | 
			
		||||
#define slots
 | 
			
		||||
 | 
			
		||||
#include <python/PythonUtils.h>
 | 
			
		||||
 | 
			
		||||
class Logger;
 | 
			
		||||
 | 
			
		||||
class PythonProgram
 | 
			
		||||
@@ -17,9 +19,15 @@ public:
 | 
			
		||||
	PythonProgram(const QString & name, Logger * log);
 | 
			
		||||
	~PythonProgram();
 | 
			
		||||
 | 
			
		||||
	operator PyThreadState* ()
 | 
			
		||||
	{
 | 
			
		||||
		return _tstate;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void execute(const QByteArray &python_code);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
 | 
			
		||||
	QString _name;
 | 
			
		||||
	Logger* _log;
 | 
			
		||||
	PyThreadState* _tstate;
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@
 | 
			
		||||
// python utils
 | 
			
		||||
#include <python/PythonProgram.h>
 | 
			
		||||
 | 
			
		||||
Effect::Effect(Hyperion *hyperion, int priority, int timeout, const QString &script, const QString &name, const QJsonObject &args, const QString &imageData)
 | 
			
		||||
Effect::Effect(Hyperion* hyperion, int priority, int timeout, const QString& script, const QString& name, const QJsonObject& args, const QString& imageData)
 | 
			
		||||
	: QThread()
 | 
			
		||||
	, _hyperion(hyperion)
 | 
			
		||||
	, _priority(priority)
 | 
			
		||||
@@ -26,7 +26,7 @@ Effect::Effect(Hyperion *hyperion, int priority, int timeout, const QString &scr
 | 
			
		||||
	, _endTime(-1)
 | 
			
		||||
	, _interupt(false)
 | 
			
		||||
	, _imageSize(hyperion->getLedGridSize())
 | 
			
		||||
	, _image(_imageSize,QImage::Format_ARGB32_Premultiplied)
 | 
			
		||||
	, _image(_imageSize, QImage::Format_ARGB32_Premultiplied)
 | 
			
		||||
{
 | 
			
		||||
	_colors.resize(_hyperion->getLedCount());
 | 
			
		||||
	_colors.fill(ColorRgb::BLACK);
 | 
			
		||||
@@ -61,41 +61,81 @@ int Effect::getRemaining() const
 | 
			
		||||
 | 
			
		||||
	if (timeout >= 0)
 | 
			
		||||
	{
 | 
			
		||||
		timeout = static_cast<int>( _endTime - QDateTime::currentMSecsSinceEpoch());
 | 
			
		||||
		timeout = static_cast<int>(_endTime - QDateTime::currentMSecsSinceEpoch());
 | 
			
		||||
	}
 | 
			
		||||
	return timeout;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Effect::setModuleParameters()
 | 
			
		||||
bool Effect::setModuleParameters()
 | 
			
		||||
{
 | 
			
		||||
	// import the buildtin Hyperion module
 | 
			
		||||
	PyObject * module = PyImport_ImportModule("hyperion");
 | 
			
		||||
	PyObject* module = PyImport_ImportModule("hyperion");
 | 
			
		||||
 | 
			
		||||
	// add a capsule containing 'this' to the module to be able to retrieve the effect from the callback function
 | 
			
		||||
	PyModule_AddObject(module, "__effectObj", PyCapsule_New((void*)this, "hyperion.__effectObj", nullptr));
 | 
			
		||||
	if (module == nullptr) {
 | 
			
		||||
		PyErr_Print();  // Print error if module import fails
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// add ledCount variable to the interpreter
 | 
			
		||||
	// Add a capsule containing 'this' to the module for callback access
 | 
			
		||||
	PyObject* capsule = PyCapsule_New((void*)this, "hyperion.__effectObj", nullptr);
 | 
			
		||||
	if (capsule == nullptr || PyModule_AddObject(module, "__effectObj", capsule) < 0) {
 | 
			
		||||
		PyErr_Print();  // Print error if adding capsule fails
 | 
			
		||||
		Py_XDECREF(module);  // Clean up if capsule addition fails
 | 
			
		||||
		Py_XDECREF(capsule);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Add ledCount variable to the interpreter
 | 
			
		||||
	int ledCount = 0;
 | 
			
		||||
	QMetaObject::invokeMethod(_hyperion, "getLedCount", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, ledCount));
 | 
			
		||||
	PyObject_SetAttrString(module, "ledCount", Py_BuildValue("i", ledCount));
 | 
			
		||||
	PyObject* ledCountObj = Py_BuildValue("i", ledCount);
 | 
			
		||||
	if (PyObject_SetAttrString(module, "ledCount", ledCountObj) < 0) {
 | 
			
		||||
		PyErr_Print();  // Print error if setting attribute fails
 | 
			
		||||
	}
 | 
			
		||||
	Py_XDECREF(ledCountObj);
 | 
			
		||||
 | 
			
		||||
	// add minimumWriteTime variable to the interpreter
 | 
			
		||||
	// Add minimumWriteTime variable to the interpreter
 | 
			
		||||
	int latchTime = 0;
 | 
			
		||||
	QMetaObject::invokeMethod(_hyperion, "getLatchTime", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, latchTime));
 | 
			
		||||
	PyObject_SetAttrString(module, "latchTime", Py_BuildValue("i", latchTime));
 | 
			
		||||
	PyObject* latchTimeObj = Py_BuildValue("i", latchTime);
 | 
			
		||||
	if (PyObject_SetAttrString(module, "latchTime", latchTimeObj) < 0) {
 | 
			
		||||
		PyErr_Print();  // Print error if setting attribute fails
 | 
			
		||||
	}
 | 
			
		||||
	Py_XDECREF(latchTimeObj);
 | 
			
		||||
 | 
			
		||||
	// add a args variable to the interpreter
 | 
			
		||||
	PyObject_SetAttrString(module, "args", EffectModule::json2python(_args));
 | 
			
		||||
	// Add args variable to the interpreter
 | 
			
		||||
	PyObject* argsObj = EffectModule::json2python(_args);
 | 
			
		||||
	if (PyObject_SetAttrString(module, "args", argsObj) < 0) {
 | 
			
		||||
		PyErr_Print();  // Print error if setting attribute fails
 | 
			
		||||
	}
 | 
			
		||||
	Py_XDECREF(argsObj);
 | 
			
		||||
 | 
			
		||||
	// decref the module
 | 
			
		||||
	// Decrement module reference
 | 
			
		||||
	Py_XDECREF(module);
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Effect::run()
 | 
			
		||||
{
 | 
			
		||||
	PythonProgram program(_name, _log);
 | 
			
		||||
 | 
			
		||||
	setModuleParameters();
 | 
			
		||||
#if (PY_VERSION_HEX < 0x030C0000)
 | 
			
		||||
	PyThreadState* prev_thread_state = PyThreadState_Swap(program);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	if (!setModuleParameters())
 | 
			
		||||
	{
 | 
			
		||||
		Error(_log, "Failed to set Module parameters. Effect will not be executed.");
 | 
			
		||||
#if (PY_VERSION_HEX < 0x030C0000)
 | 
			
		||||
		PyThreadState_Swap(prev_thread_state);
 | 
			
		||||
#endif
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#if (PY_VERSION_HEX < 0x030C0000)
 | 
			
		||||
	PyThreadState_Swap(prev_thread_state);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	// Set the end time if applicable
 | 
			
		||||
	if (_timeout > 0)
 | 
			
		||||
@@ -104,7 +144,7 @@ void Effect::run()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Run the effect script
 | 
			
		||||
	QFile file (_script);
 | 
			
		||||
	QFile file(_script);
 | 
			
		||||
	if (file.open(QIODevice::ReadOnly))
 | 
			
		||||
	{
 | 
			
		||||
		program.execute(file.readAll());
 | 
			
		||||
 
 | 
			
		||||
@@ -17,25 +17,81 @@
 | 
			
		||||
#include <QNetworkAccessManager>
 | 
			
		||||
#include <QEventLoop>
 | 
			
		||||
 | 
			
		||||
// Define a struct for per-interpreter state
 | 
			
		||||
typedef struct {
 | 
			
		||||
} hyperion_module_state;
 | 
			
		||||
 | 
			
		||||
// Macro to access the module state
 | 
			
		||||
#define GET_HYPERION_STATE(module) ((hyperion_module_state*)PyModule_GetState(module))
 | 
			
		||||
 | 
			
		||||
// Get the effect from the capsule
 | 
			
		||||
#define getEffect() static_cast<Effect*>((Effect*)PyCapsule_Import("hyperion.__effectObj", 0))
 | 
			
		||||
 | 
			
		||||
// create the hyperion module
 | 
			
		||||
struct PyModuleDef EffectModule::moduleDef = {
 | 
			
		||||
	PyModuleDef_HEAD_INIT,
 | 
			
		||||
	"hyperion",            /* m_name */
 | 
			
		||||
	"Hyperion module",     /* m_doc */
 | 
			
		||||
	-1,                    /* m_size */
 | 
			
		||||
	EffectModule::effectMethods, /* m_methods */
 | 
			
		||||
	NULL,                  /* m_reload */
 | 
			
		||||
	NULL,                  /* m_traverse */
 | 
			
		||||
	NULL,                  /* m_clear */
 | 
			
		||||
	NULL,                  /* m_free */
 | 
			
		||||
// Module execution function for multi-phase init
 | 
			
		||||
static int hyperion_exec(PyObject* module) {
 | 
			
		||||
	// Initialize per-interpreter state
 | 
			
		||||
	hyperion_module_state* state = GET_HYPERION_STATE(module);
 | 
			
		||||
	if (state == NULL)
 | 
			
		||||
	{
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Module creation function for multi-phase init, used in Py_mod_create slot
 | 
			
		||||
static PyObject* hyperion_create(PyModuleDef* def, PyObject* args) {
 | 
			
		||||
	PyObject* module = PyModule_Create(def);
 | 
			
		||||
	if (!module)
 | 
			
		||||
	{
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Execute any additional module initialization logic
 | 
			
		||||
	if (hyperion_exec(module) < 0)
 | 
			
		||||
	{
 | 
			
		||||
		Py_DECREF(module);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return module;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Module deallocation function to clean up per-interpreter state
 | 
			
		||||
static void hyperion_free(void* module)
 | 
			
		||||
{
 | 
			
		||||
	// No specific cleanup required in this example
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyModuleDef_Slot hyperion_slots[] = {
 | 
			
		||||
	{Py_mod_exec, hyperion_exec},
 | 
			
		||||
#if (PY_VERSION_HEX >= 0x030C0000)
 | 
			
		||||
	{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
 | 
			
		||||
#endif
 | 
			
		||||
	{0, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
PyObject* EffectModule::PyInit_hyperion()
 | 
			
		||||
// Module definition with multi-phase and per-interpreter state
 | 
			
		||||
static struct PyModuleDef hyperion_module = {
 | 
			
		||||
	PyModuleDef_HEAD_INIT,
 | 
			
		||||
	"hyperion",                    // Module name
 | 
			
		||||
	"Hyperion module",             // Module docstring
 | 
			
		||||
	sizeof(hyperion_module_state), // Size of per-interpreter state
 | 
			
		||||
	EffectModule::effectMethods,   // Methods array
 | 
			
		||||
	NULL,                          // Slots array (will be added in PyInit_hyperion)
 | 
			
		||||
	NULL,                          // Traverse function (optional)
 | 
			
		||||
	NULL,                          // Clear function (optional)
 | 
			
		||||
	hyperion_free                  // Free function
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// initialize function for the hyperion module
 | 
			
		||||
PyMODINIT_FUNC PyInit_hyperion(void)
 | 
			
		||||
{
 | 
			
		||||
	return PyModule_Create(&moduleDef);
 | 
			
		||||
 | 
			
		||||
	// assign slots to the module definition
 | 
			
		||||
	hyperion_module.m_slots = hyperion_slots;
 | 
			
		||||
 | 
			
		||||
	// return a new module definition instance
 | 
			
		||||
	return PyModuleDef_Init(&hyperion_module);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EffectModule::registerHyperionExtensionModule()
 | 
			
		||||
@@ -43,54 +99,77 @@ void EffectModule::registerHyperionExtensionModule()
 | 
			
		||||
	PyImport_AppendInittab("hyperion", &PyInit_hyperion);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject *EffectModule::json2python(const QJsonValue &jsonData)
 | 
			
		||||
PyObject* EffectModule::json2python(const QJsonValue& jsonData)
 | 
			
		||||
{
 | 
			
		||||
	switch (jsonData.type())
 | 
			
		||||
	{
 | 
			
		||||
	case QJsonValue::Null:
 | 
			
		||||
		Py_RETURN_NONE;
 | 
			
		||||
 | 
			
		||||
	case QJsonValue::Undefined:
 | 
			
		||||
		Py_RETURN_NOTIMPLEMENTED;
 | 
			
		||||
 | 
			
		||||
	case QJsonValue::Double:
 | 
			
		||||
	{
 | 
			
		||||
			double doubleIntegratlPart;
 | 
			
		||||
			double doubleFractionalPart = std::modf(jsonData.toDouble(), &doubleIntegratlPart);
 | 
			
		||||
			if (doubleFractionalPart > std::numeric_limits<double>::epsilon())
 | 
			
		||||
		double value = jsonData.toDouble();
 | 
			
		||||
		if (value == static_cast<int>(value))  // If no fractional part, value is equal to its integer representation
 | 
			
		||||
		{
 | 
			
		||||
				return Py_BuildValue("d", jsonData.toDouble());
 | 
			
		||||
			return Py_BuildValue("i", static_cast<int>(value));
 | 
			
		||||
		}
 | 
			
		||||
			return Py_BuildValue("i", jsonData.toInt());
 | 
			
		||||
		return Py_BuildValue("d", value);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	case QJsonValue::Bool:
 | 
			
		||||
			return Py_BuildValue("i", jsonData.toBool() ? 1 : 0);
 | 
			
		||||
		return PyBool_FromLong(jsonData.toBool() ? 1 : 0);
 | 
			
		||||
 | 
			
		||||
	case QJsonValue::String:
 | 
			
		||||
			return Py_BuildValue("s", jsonData.toString().toUtf8().constData());
 | 
			
		||||
		case QJsonValue::Object:
 | 
			
		||||
		{
 | 
			
		||||
			PyObject * dict= PyDict_New();
 | 
			
		||||
			QJsonObject objectData = jsonData.toObject();
 | 
			
		||||
			for (QJsonObject::iterator i = objectData.begin(); i != objectData.end(); ++i)
 | 
			
		||||
			{
 | 
			
		||||
				PyObject * obj = json2python(*i);
 | 
			
		||||
				PyDict_SetItemString(dict, i.key().toStdString().c_str(), obj);
 | 
			
		||||
				Py_XDECREF(obj);
 | 
			
		||||
			}
 | 
			
		||||
			return dict;
 | 
			
		||||
		}
 | 
			
		||||
		return PyUnicode_FromString(jsonData.toString().toUtf8().constData());
 | 
			
		||||
 | 
			
		||||
	case QJsonValue::Array:
 | 
			
		||||
	{
 | 
			
		||||
		QJsonArray arrayData = jsonData.toArray();
 | 
			
		||||
			PyObject * list = PyList_New(arrayData.size());
 | 
			
		||||
		PyObject* list = PyList_New(arrayData.size());
 | 
			
		||||
		int index = 0;
 | 
			
		||||
		for (QJsonArray::iterator i = arrayData.begin(); i != arrayData.end(); ++i, ++index)
 | 
			
		||||
		{
 | 
			
		||||
				PyObject * obj = json2python(*i);
 | 
			
		||||
			PyObject* obj = json2python(*i);
 | 
			
		||||
			Py_INCREF(obj);
 | 
			
		||||
			PyList_SetItem(list, index, obj);
 | 
			
		||||
			Py_XDECREF(obj);
 | 
			
		||||
		}
 | 
			
		||||
		return list;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	case QJsonValue::Object: {
 | 
			
		||||
		// Python's dict
 | 
			
		||||
		QJsonObject jsonObject = jsonData.toObject();
 | 
			
		||||
		PyObject* pyDict = PyDict_New();
 | 
			
		||||
		for (auto it = jsonObject.begin(); it != jsonObject.end(); ++it) {
 | 
			
		||||
			// Convert key
 | 
			
		||||
			PyObject* pyKey = PyUnicode_FromString(it.key().toUtf8().constData());
 | 
			
		||||
			if (!pyKey) {
 | 
			
		||||
				Py_XDECREF(pyDict);
 | 
			
		||||
				return nullptr;  // Error occurred, return null
 | 
			
		||||
			}
 | 
			
		||||
			// Convert value
 | 
			
		||||
			PyObject* pyValue = json2python(it.value());
 | 
			
		||||
			if (!pyValue) {
 | 
			
		||||
				Py_XDECREF(pyKey);
 | 
			
		||||
				Py_XDECREF(pyDict);
 | 
			
		||||
				return nullptr;  // Error occurred, return null
 | 
			
		||||
			}
 | 
			
		||||
			// Add to dictionary
 | 
			
		||||
			PyDict_SetItem(pyDict, pyKey, pyValue);
 | 
			
		||||
			Py_XDECREF(pyKey);
 | 
			
		||||
			Py_XDECREF(pyValue);
 | 
			
		||||
		}
 | 
			
		||||
		return pyDict;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		// Unsupported type
 | 
			
		||||
		PyErr_SetString(PyExc_TypeError, "Unsupported QJsonValue type.");
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	assert(false);
 | 
			
		||||
@@ -126,7 +205,7 @@ PyMethodDef EffectModule::effectMethods[] = {
 | 
			
		||||
	{NULL, NULL, 0, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
PyObject* EffectModule::wrapSetColor(PyObject *self, PyObject *args)
 | 
			
		||||
PyObject* EffectModule::wrapSetColor(PyObject* self, PyObject* args)
 | 
			
		||||
{
 | 
			
		||||
	// check the number of arguments
 | 
			
		||||
	int argCount = PyTuple_Size(args);
 | 
			
		||||
@@ -138,7 +217,7 @@ PyObject* EffectModule::wrapSetColor(PyObject *self, PyObject *args)
 | 
			
		||||
		{
 | 
			
		||||
			getEffect()->_colors.fill(color);
 | 
			
		||||
			QVector<ColorRgb> _cQV = getEffect()->_colors;
 | 
			
		||||
			emit getEffect()->setInput(getEffect()->_priority, std::vector<ColorRgb>( _cQV.begin(), _cQV.end() ), getEffect()->getRemaining(), false);
 | 
			
		||||
			emit getEffect()->setInput(getEffect()->_priority, std::vector<ColorRgb>(_cQV.begin(), _cQV.end()), getEffect()->getRemaining(), false);
 | 
			
		||||
			Py_RETURN_NONE;
 | 
			
		||||
		}
 | 
			
		||||
		return nullptr;
 | 
			
		||||
@@ -146,7 +225,7 @@ PyObject* EffectModule::wrapSetColor(PyObject *self, PyObject *args)
 | 
			
		||||
	else if (argCount == 1)
 | 
			
		||||
	{
 | 
			
		||||
		// bytearray of values
 | 
			
		||||
		PyObject * bytearray = nullptr;
 | 
			
		||||
		PyObject* bytearray = nullptr;
 | 
			
		||||
		if (PyArg_ParseTuple(args, "O", &bytearray))
 | 
			
		||||
		{
 | 
			
		||||
			if (PyByteArray_Check(bytearray))
 | 
			
		||||
@@ -154,10 +233,10 @@ PyObject* EffectModule::wrapSetColor(PyObject *self, PyObject *args)
 | 
			
		||||
				size_t length = PyByteArray_Size(bytearray);
 | 
			
		||||
				if (length == 3 * static_cast<size_t>(getEffect()->_hyperion->getLedCount()))
 | 
			
		||||
				{
 | 
			
		||||
					char * data = PyByteArray_AS_STRING(bytearray);
 | 
			
		||||
					char* data = PyByteArray_AS_STRING(bytearray);
 | 
			
		||||
					memcpy(getEffect()->_colors.data(), data, length);
 | 
			
		||||
					QVector<ColorRgb> _cQV = getEffect()->_colors;
 | 
			
		||||
					emit getEffect()->setInput(getEffect()->_priority, std::vector<ColorRgb>( _cQV.begin(), _cQV.end() ), getEffect()->getRemaining(), false);
 | 
			
		||||
					emit getEffect()->setInput(getEffect()->_priority, std::vector<ColorRgb>(_cQV.begin(), _cQV.end()), getEffect()->getRemaining(), false);
 | 
			
		||||
					Py_RETURN_NONE;
 | 
			
		||||
				}
 | 
			
		||||
				else
 | 
			
		||||
@@ -184,12 +263,12 @@ PyObject* EffectModule::wrapSetColor(PyObject *self, PyObject *args)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* EffectModule::wrapSetImage(PyObject *self, PyObject *args)
 | 
			
		||||
PyObject* EffectModule::wrapSetImage(PyObject* self, PyObject* args)
 | 
			
		||||
{
 | 
			
		||||
	// bytearray of values
 | 
			
		||||
	int width = 0;
 | 
			
		||||
	int height = 0;
 | 
			
		||||
	PyObject * bytearray = nullptr;
 | 
			
		||||
	PyObject* bytearray = nullptr;
 | 
			
		||||
	if (PyArg_ParseTuple(args, "iiO", &width, &height, &bytearray))
 | 
			
		||||
	{
 | 
			
		||||
		if (PyByteArray_Check(bytearray))
 | 
			
		||||
@@ -198,7 +277,7 @@ PyObject* EffectModule::wrapSetImage(PyObject *self, PyObject *args)
 | 
			
		||||
			if (length == 3 * width * height)
 | 
			
		||||
			{
 | 
			
		||||
				Image<ColorRgb> image(width, height);
 | 
			
		||||
				char * data = PyByteArray_AS_STRING(bytearray);
 | 
			
		||||
				char* data = PyByteArray_AS_STRING(bytearray);
 | 
			
		||||
				memcpy(image.memptr(), data, length);
 | 
			
		||||
				emit getEffect()->setInputImage(getEffect()->_priority, image, getEffect()->getRemaining(), false);
 | 
			
		||||
				Py_RETURN_NONE;
 | 
			
		||||
@@ -225,11 +304,11 @@ PyObject* EffectModule::wrapSetImage(PyObject *self, PyObject *args)
 | 
			
		||||
	return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* EffectModule::wrapGetImage(PyObject *self, PyObject *args)
 | 
			
		||||
PyObject* EffectModule::wrapGetImage(PyObject* self, PyObject* args)
 | 
			
		||||
{
 | 
			
		||||
	QBuffer buffer;
 | 
			
		||||
	QImageReader reader;
 | 
			
		||||
	char *source;
 | 
			
		||||
	char* source;
 | 
			
		||||
	int cropLeft = 0, cropTop = 0, cropRight = 0, cropBottom = 0;
 | 
			
		||||
	int grayscale = false;
 | 
			
		||||
 | 
			
		||||
@@ -237,7 +316,7 @@ PyObject* EffectModule::wrapGetImage(PyObject *self, PyObject *args)
 | 
			
		||||
	{
 | 
			
		||||
		Q_INIT_RESOURCE(EffectEngine);
 | 
			
		||||
 | 
			
		||||
		if(!PyArg_ParseTuple(args, "s|iiiip", &source, &cropLeft, &cropTop, &cropRight, &cropBottom, &grayscale))
 | 
			
		||||
		if (!PyArg_ParseTuple(args, "s|iiiip", &source, &cropLeft, &cropTop, &cropRight, &cropBottom, &grayscale))
 | 
			
		||||
		{
 | 
			
		||||
			PyErr_SetString(PyExc_TypeError, "String required");
 | 
			
		||||
			return nullptr;
 | 
			
		||||
@@ -246,8 +325,8 @@ PyObject* EffectModule::wrapGetImage(PyObject *self, PyObject *args)
 | 
			
		||||
		const QUrl url = QUrl(source);
 | 
			
		||||
		if (url.isValid())
 | 
			
		||||
		{
 | 
			
		||||
			QNetworkAccessManager *networkManager = new QNetworkAccessManager();
 | 
			
		||||
			QNetworkReply * networkReply = networkManager->get(QNetworkRequest(url));
 | 
			
		||||
			QNetworkAccessManager* networkManager = new QNetworkAccessManager();
 | 
			
		||||
			QNetworkReply* networkReply = networkManager->get(QNetworkRequest(url));
 | 
			
		||||
 | 
			
		||||
			QEventLoop eventLoop;
 | 
			
		||||
			connect(networkReply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);
 | 
			
		||||
@@ -269,7 +348,7 @@ PyObject* EffectModule::wrapGetImage(PyObject *self, PyObject *args)
 | 
			
		||||
			QString file = QString::fromUtf8(source);
 | 
			
		||||
 | 
			
		||||
			if (file.mid(0, 1) == ":")
 | 
			
		||||
				file = ":/effects/"+file.mid(1);
 | 
			
		||||
				file = ":/effects/" + file.mid(1);
 | 
			
		||||
 | 
			
		||||
			reader.setDecideFormatFromContent(true);
 | 
			
		||||
			reader.setFileName(file);
 | 
			
		||||
@@ -286,7 +365,7 @@ PyObject* EffectModule::wrapGetImage(PyObject *self, PyObject *args)
 | 
			
		||||
 | 
			
		||||
	if (reader.canRead())
 | 
			
		||||
	{
 | 
			
		||||
		PyObject *result = PyList_New(reader.imageCount());
 | 
			
		||||
		PyObject* result = PyList_New(reader.imageCount());
 | 
			
		||||
 | 
			
		||||
		for (int i = 0; i < reader.imageCount(); ++i)
 | 
			
		||||
		{
 | 
			
		||||
@@ -312,18 +391,18 @@ PyObject* EffectModule::wrapGetImage(PyObject *self, PyObject *args)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				QByteArray binaryImage;
 | 
			
		||||
				for (int i = 0; i<height; i++)
 | 
			
		||||
				for (int i = 0; i < height; i++)
 | 
			
		||||
				{
 | 
			
		||||
					const QRgb *scanline = reinterpret_cast<const QRgb *>(qimage.scanLine(i));
 | 
			
		||||
					const QRgb *end = scanline + qimage.width();
 | 
			
		||||
					const QRgb* scanline = reinterpret_cast<const QRgb*>(qimage.scanLine(i));
 | 
			
		||||
					const QRgb* end = scanline + qimage.width();
 | 
			
		||||
					for (; scanline != end; scanline++)
 | 
			
		||||
					{
 | 
			
		||||
						binaryImage.append(!grayscale ? (char) qRed(scanline[0]) : (char) qGray(scanline[0]));
 | 
			
		||||
						binaryImage.append(!grayscale ? (char) qGreen(scanline[1]) : (char) qGray(scanline[1]));
 | 
			
		||||
						binaryImage.append(!grayscale ? (char) qBlue(scanline[2]) : (char) qGray(scanline[2]));
 | 
			
		||||
						binaryImage.append(!grayscale ? (char)qRed(scanline[0]) : (char)qGray(scanline[0]));
 | 
			
		||||
						binaryImage.append(!grayscale ? (char)qGreen(scanline[1]) : (char)qGray(scanline[1]));
 | 
			
		||||
						binaryImage.append(!grayscale ? (char)qBlue(scanline[2]) : (char)qGray(scanline[2]));
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				PyList_SET_ITEM(result, i, Py_BuildValue("{s:i,s:i,s:O}", "imageWidth", width, "imageHeight", height, "imageData", PyByteArray_FromStringAndSize(binaryImage.constData(),binaryImage.size())));
 | 
			
		||||
				PyList_SET_ITEM(result, i, Py_BuildValue("{s:i,s:i,s:O}", "imageWidth", width, "imageHeight", height, "imageData", PyByteArray_FromStringAndSize(binaryImage.constData(), binaryImage.size())));
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
@@ -341,13 +420,13 @@ PyObject* EffectModule::wrapGetImage(PyObject *self, PyObject *args)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* EffectModule::wrapAbort(PyObject *self, PyObject *)
 | 
			
		||||
PyObject* EffectModule::wrapAbort(PyObject* self, PyObject*)
 | 
			
		||||
{
 | 
			
		||||
	return Py_BuildValue("i", getEffect()->isInterruptionRequested() ? 1 : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PyObject* EffectModule::wrapImageShow(PyObject *self, PyObject *args)
 | 
			
		||||
PyObject* EffectModule::wrapImageShow(PyObject* self, PyObject* args)
 | 
			
		||||
{
 | 
			
		||||
	int argCount = PyTuple_Size(args);
 | 
			
		||||
	int imgId = -1;
 | 
			
		||||
@@ -357,27 +436,27 @@ PyObject* EffectModule::wrapImageShow(PyObject *self, PyObject *args)
 | 
			
		||||
		argsOk = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ( ! argsOk || (imgId>-1 && imgId >= getEffect()->_imageStack.size()))
 | 
			
		||||
	if (!argsOk || (imgId > -1 && imgId >= getEffect()->_imageStack.size()))
 | 
			
		||||
	{
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	QImage * qimage = (imgId<0) ? &(getEffect()->_image) : &(getEffect()->_imageStack[imgId]);
 | 
			
		||||
	QImage* qimage = (imgId < 0) ? &(getEffect()->_image) : &(getEffect()->_imageStack[imgId]);
 | 
			
		||||
	int width = qimage->width();
 | 
			
		||||
	int height = qimage->height();
 | 
			
		||||
 | 
			
		||||
	Image<ColorRgb> image(width, height);
 | 
			
		||||
	QByteArray binaryImage;
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i<height; ++i)
 | 
			
		||||
	for (int i = 0; i < height; ++i)
 | 
			
		||||
	{
 | 
			
		||||
		const QRgb * scanline = reinterpret_cast<const QRgb *>(qimage->scanLine(i));
 | 
			
		||||
		for (int j = 0; j< width; ++j)
 | 
			
		||||
		const QRgb* scanline = reinterpret_cast<const QRgb*>(qimage->scanLine(i));
 | 
			
		||||
		for (int j = 0; j < width; ++j)
 | 
			
		||||
		{
 | 
			
		||||
			binaryImage.append((char) qRed(scanline[j]));
 | 
			
		||||
			binaryImage.append((char) qGreen(scanline[j]));
 | 
			
		||||
			binaryImage.append((char) qBlue(scanline[j]));
 | 
			
		||||
			binaryImage.append((char)qRed(scanline[j]));
 | 
			
		||||
			binaryImage.append((char)qGreen(scanline[j]));
 | 
			
		||||
			binaryImage.append((char)qBlue(scanline[j]));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -387,27 +466,27 @@ PyObject* EffectModule::wrapImageShow(PyObject *self, PyObject *args)
 | 
			
		||||
	return Py_BuildValue("");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* EffectModule::wrapImageLinearGradient(PyObject *self, PyObject *args)
 | 
			
		||||
PyObject* EffectModule::wrapImageLinearGradient(PyObject* self, PyObject* args)
 | 
			
		||||
{
 | 
			
		||||
	int argCount = PyTuple_Size(args);
 | 
			
		||||
	PyObject * bytearray = nullptr;
 | 
			
		||||
	PyObject* bytearray = nullptr;
 | 
			
		||||
	int startRX = 0;
 | 
			
		||||
	int startRY = 0;
 | 
			
		||||
	int startX = 0;
 | 
			
		||||
	int startY = 0;
 | 
			
		||||
	int width = getEffect()->_imageSize.width();
 | 
			
		||||
	int endX {width};
 | 
			
		||||
	int endX{ width };
 | 
			
		||||
	int height = getEffect()->_imageSize.height();
 | 
			
		||||
	int endY {height};
 | 
			
		||||
	int endY{ height };
 | 
			
		||||
	int spread = 0;
 | 
			
		||||
 | 
			
		||||
	bool argsOK = false;
 | 
			
		||||
 | 
			
		||||
	if ( argCount == 10 && PyArg_ParseTuple(args, "iiiiiiiiOi", &startRX, &startRY, &width, &height, &startX, &startY, &endX, &endY, &bytearray, &spread) )
 | 
			
		||||
	if (argCount == 10 && PyArg_ParseTuple(args, "iiiiiiiiOi", &startRX, &startRY, &width, &height, &startX, &startY, &endX, &endY, &bytearray, &spread))
 | 
			
		||||
	{
 | 
			
		||||
		argsOK = true;
 | 
			
		||||
	}
 | 
			
		||||
	if ( argCount == 6 && PyArg_ParseTuple(args, "iiiiOi", &startX, &startY, &endX, &endY, &bytearray, &spread) )
 | 
			
		||||
	if (argCount == 6 && PyArg_ParseTuple(args, "iiiiOi", &startX, &startY, &endX, &endY, &bytearray, &spread))
 | 
			
		||||
	{
 | 
			
		||||
		argsOK = true;
 | 
			
		||||
	}
 | 
			
		||||
@@ -420,19 +499,19 @@ PyObject* EffectModule::wrapImageLinearGradient(PyObject *self, PyObject *args)
 | 
			
		||||
			const unsigned arrayItemLength = 5;
 | 
			
		||||
			if (length % arrayItemLength == 0)
 | 
			
		||||
			{
 | 
			
		||||
				QRect myQRect(startRX,startRY,width,height);
 | 
			
		||||
				QLinearGradient gradient(QPoint(startX,startY), QPoint(endX,endY));
 | 
			
		||||
				char * data = PyByteArray_AS_STRING(bytearray);
 | 
			
		||||
				QRect myQRect(startRX, startRY, width, height);
 | 
			
		||||
				QLinearGradient gradient(QPoint(startX, startY), QPoint(endX, endY));
 | 
			
		||||
				char* data = PyByteArray_AS_STRING(bytearray);
 | 
			
		||||
 | 
			
		||||
				for (int idx=0; idx<length; idx+=arrayItemLength)
 | 
			
		||||
				for (int idx = 0; idx < length; idx += arrayItemLength)
 | 
			
		||||
				{
 | 
			
		||||
					gradient.setColorAt(
 | 
			
		||||
						((uint8_t)data[idx])/255.0,
 | 
			
		||||
						((uint8_t)data[idx]) / 255.0,
 | 
			
		||||
						QColor(
 | 
			
		||||
							(uint8_t)(data[idx+1]),
 | 
			
		||||
							(uint8_t)(data[idx+2]),
 | 
			
		||||
							(uint8_t)(data[idx+3]),
 | 
			
		||||
							(uint8_t)(data[idx+4])
 | 
			
		||||
							(uint8_t)(data[idx + 1]),
 | 
			
		||||
							(uint8_t)(data[idx + 2]),
 | 
			
		||||
							(uint8_t)(data[idx + 3]),
 | 
			
		||||
							(uint8_t)(data[idx + 4])
 | 
			
		||||
						));
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
@@ -456,10 +535,10 @@ PyObject* EffectModule::wrapImageLinearGradient(PyObject *self, PyObject *args)
 | 
			
		||||
	return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* EffectModule::wrapImageConicalGradient(PyObject *self, PyObject *args)
 | 
			
		||||
PyObject* EffectModule::wrapImageConicalGradient(PyObject* self, PyObject* args)
 | 
			
		||||
{
 | 
			
		||||
	int argCount = PyTuple_Size(args);
 | 
			
		||||
	PyObject * bytearray = nullptr;
 | 
			
		||||
	PyObject* bytearray = nullptr;
 | 
			
		||||
	int centerX = 0;
 | 
			
		||||
	int centerY = 0;
 | 
			
		||||
	int angle = 0;
 | 
			
		||||
@@ -470,15 +549,15 @@ PyObject* EffectModule::wrapImageConicalGradient(PyObject *self, PyObject *args)
 | 
			
		||||
 | 
			
		||||
	bool argsOK = false;
 | 
			
		||||
 | 
			
		||||
	if ( argCount == 8 && PyArg_ParseTuple(args, "iiiiiiiO", &startX, &startY, &width, &height, ¢erX, ¢erY, &angle, &bytearray) )
 | 
			
		||||
	if (argCount == 8 && PyArg_ParseTuple(args, "iiiiiiiO", &startX, &startY, &width, &height, ¢erX, ¢erY, &angle, &bytearray))
 | 
			
		||||
	{
 | 
			
		||||
		argsOK = true;
 | 
			
		||||
	}
 | 
			
		||||
	if ( argCount == 4 && PyArg_ParseTuple(args, "iiiO", ¢erX, ¢erY, &angle, &bytearray) )
 | 
			
		||||
	if (argCount == 4 && PyArg_ParseTuple(args, "iiiO", ¢erX, ¢erY, &angle, &bytearray))
 | 
			
		||||
	{
 | 
			
		||||
		argsOK = true;
 | 
			
		||||
	}
 | 
			
		||||
	angle = qMax(qMin(angle,360),0);
 | 
			
		||||
	angle = qMax(qMin(angle, 360), 0);
 | 
			
		||||
 | 
			
		||||
	if (argsOK)
 | 
			
		||||
	{
 | 
			
		||||
@@ -488,19 +567,19 @@ PyObject* EffectModule::wrapImageConicalGradient(PyObject *self, PyObject *args)
 | 
			
		||||
			const unsigned arrayItemLength = 5;
 | 
			
		||||
			if (length % arrayItemLength == 0)
 | 
			
		||||
			{
 | 
			
		||||
				QRect myQRect(startX,startY,width,height);
 | 
			
		||||
				QConicalGradient gradient(QPoint(centerX,centerY), angle );
 | 
			
		||||
				char * data = PyByteArray_AS_STRING(bytearray);
 | 
			
		||||
				QRect myQRect(startX, startY, width, height);
 | 
			
		||||
				QConicalGradient gradient(QPoint(centerX, centerY), angle);
 | 
			
		||||
				char* data = PyByteArray_AS_STRING(bytearray);
 | 
			
		||||
 | 
			
		||||
				for (int idx=0; idx<length; idx+=arrayItemLength)
 | 
			
		||||
				for (int idx = 0; idx < length; idx += arrayItemLength)
 | 
			
		||||
				{
 | 
			
		||||
					gradient.setColorAt(
 | 
			
		||||
						((uint8_t)data[idx])/255.0,
 | 
			
		||||
						((uint8_t)data[idx]) / 255.0,
 | 
			
		||||
						QColor(
 | 
			
		||||
							(uint8_t)(data[idx+1]),
 | 
			
		||||
							(uint8_t)(data[idx+2]),
 | 
			
		||||
							(uint8_t)(data[idx+3]),
 | 
			
		||||
							(uint8_t)(data[idx+4])
 | 
			
		||||
							(uint8_t)(data[idx + 1]),
 | 
			
		||||
							(uint8_t)(data[idx + 2]),
 | 
			
		||||
							(uint8_t)(data[idx + 3]),
 | 
			
		||||
							(uint8_t)(data[idx + 4])
 | 
			
		||||
						));
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
@@ -524,16 +603,16 @@ PyObject* EffectModule::wrapImageConicalGradient(PyObject *self, PyObject *args)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PyObject* EffectModule::wrapImageRadialGradient(PyObject *self, PyObject *args)
 | 
			
		||||
PyObject* EffectModule::wrapImageRadialGradient(PyObject* self, PyObject* args)
 | 
			
		||||
{
 | 
			
		||||
	int argCount = PyTuple_Size(args);
 | 
			
		||||
	PyObject * bytearray = nullptr;
 | 
			
		||||
	PyObject* bytearray = nullptr;
 | 
			
		||||
	int centerX = 0;
 | 
			
		||||
	int centerY = 0;
 | 
			
		||||
	int radius = 0;
 | 
			
		||||
	int focalX = 0;
 | 
			
		||||
	int focalY = 0;
 | 
			
		||||
	int focalRadius =0;
 | 
			
		||||
	int focalRadius = 0;
 | 
			
		||||
	int spread = 0;
 | 
			
		||||
	int startX = 0;
 | 
			
		||||
	int startY = 0;
 | 
			
		||||
@@ -542,22 +621,22 @@ PyObject* EffectModule::wrapImageRadialGradient(PyObject *self, PyObject *args)
 | 
			
		||||
 | 
			
		||||
	bool argsOK = false;
 | 
			
		||||
 | 
			
		||||
	if ( argCount == 12 && PyArg_ParseTuple(args, "iiiiiiiiiiOi", &startX, &startY, &width, &height, ¢erX, ¢erY, &radius, &focalX, &focalY, &focalRadius, &bytearray, &spread) )
 | 
			
		||||
	if (argCount == 12 && PyArg_ParseTuple(args, "iiiiiiiiiiOi", &startX, &startY, &width, &height, ¢erX, ¢erY, &radius, &focalX, &focalY, &focalRadius, &bytearray, &spread))
 | 
			
		||||
	{
 | 
			
		||||
		argsOK = true;
 | 
			
		||||
	}
 | 
			
		||||
	if ( argCount == 9 && PyArg_ParseTuple(args, "iiiiiiiOi", &startX, &startY, &width, &height, ¢erX, ¢erY, &radius, &bytearray, &spread) )
 | 
			
		||||
	if (argCount == 9 && PyArg_ParseTuple(args, "iiiiiiiOi", &startX, &startY, &width, &height, ¢erX, ¢erY, &radius, &bytearray, &spread))
 | 
			
		||||
	{
 | 
			
		||||
		argsOK = true;
 | 
			
		||||
		focalX = centerX;
 | 
			
		||||
		focalY = centerY;
 | 
			
		||||
		focalRadius = radius;
 | 
			
		||||
	}
 | 
			
		||||
	if ( argCount == 8 && PyArg_ParseTuple(args, "iiiiiiOi", ¢erX, ¢erY, &radius, &focalX, &focalY, &focalRadius, &bytearray, &spread) )
 | 
			
		||||
	if (argCount == 8 && PyArg_ParseTuple(args, "iiiiiiOi", ¢erX, ¢erY, &radius, &focalX, &focalY, &focalRadius, &bytearray, &spread))
 | 
			
		||||
	{
 | 
			
		||||
		argsOK = true;
 | 
			
		||||
	}
 | 
			
		||||
	if ( argCount == 5 && PyArg_ParseTuple(args, "iiiOi", ¢erX, ¢erY, &radius, &bytearray, &spread) )
 | 
			
		||||
	if (argCount == 5 && PyArg_ParseTuple(args, "iiiOi", ¢erX, ¢erY, &radius, &bytearray, &spread))
 | 
			
		||||
	{
 | 
			
		||||
		argsOK = true;
 | 
			
		||||
		focalX = centerX;
 | 
			
		||||
@@ -573,18 +652,18 @@ PyObject* EffectModule::wrapImageRadialGradient(PyObject *self, PyObject *args)
 | 
			
		||||
			if (length % 4 == 0)
 | 
			
		||||
			{
 | 
			
		||||
 | 
			
		||||
				QRect myQRect(startX,startY,width,height);
 | 
			
		||||
				QRadialGradient gradient(QPoint(centerX,centerY), qMax(radius,0) );
 | 
			
		||||
				char * data = PyByteArray_AS_STRING(bytearray);
 | 
			
		||||
				QRect myQRect(startX, startY, width, height);
 | 
			
		||||
				QRadialGradient gradient(QPoint(centerX, centerY), qMax(radius, 0));
 | 
			
		||||
				char* data = PyByteArray_AS_STRING(bytearray);
 | 
			
		||||
 | 
			
		||||
				for (int idx=0; idx<length; idx+=4)
 | 
			
		||||
				for (int idx = 0; idx < length; idx += 4)
 | 
			
		||||
				{
 | 
			
		||||
					gradient.setColorAt(
 | 
			
		||||
						((uint8_t)data[idx])/255.0,
 | 
			
		||||
						((uint8_t)data[idx]) / 255.0,
 | 
			
		||||
						QColor(
 | 
			
		||||
							(uint8_t)(data[idx+1]),
 | 
			
		||||
							(uint8_t)(data[idx+2]),
 | 
			
		||||
							(uint8_t)(data[idx+3])
 | 
			
		||||
							(uint8_t)(data[idx + 1]),
 | 
			
		||||
							(uint8_t)(data[idx + 2]),
 | 
			
		||||
							(uint8_t)(data[idx + 3])
 | 
			
		||||
						));
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
@@ -608,9 +687,9 @@ PyObject* EffectModule::wrapImageRadialGradient(PyObject *self, PyObject *args)
 | 
			
		||||
	return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* EffectModule::wrapImageDrawPolygon(PyObject *self, PyObject *args)
 | 
			
		||||
PyObject* EffectModule::wrapImageDrawPolygon(PyObject* self, PyObject* args)
 | 
			
		||||
{
 | 
			
		||||
	PyObject * bytearray = nullptr;
 | 
			
		||||
	PyObject* bytearray = nullptr;
 | 
			
		||||
 | 
			
		||||
	int argCount = PyTuple_Size(args);
 | 
			
		||||
	int r = 0;
 | 
			
		||||
@@ -620,11 +699,11 @@ PyObject* EffectModule::wrapImageDrawPolygon(PyObject *self, PyObject *args)
 | 
			
		||||
 | 
			
		||||
	bool argsOK = false;
 | 
			
		||||
 | 
			
		||||
	if ( argCount == 5 && PyArg_ParseTuple(args, "Oiiii", &bytearray, &r, &g, &b, &a) )
 | 
			
		||||
	if (argCount == 5 && PyArg_ParseTuple(args, "Oiiii", &bytearray, &r, &g, &b, &a))
 | 
			
		||||
	{
 | 
			
		||||
		argsOK = true;
 | 
			
		||||
	}
 | 
			
		||||
	if ( argCount == 4 && PyArg_ParseTuple(args, "Oiii", &bytearray, &r, &g, &b) )
 | 
			
		||||
	if (argCount == 4 && PyArg_ParseTuple(args, "Oiii", &bytearray, &r, &g, &b))
 | 
			
		||||
	{
 | 
			
		||||
		argsOK = true;
 | 
			
		||||
	}
 | 
			
		||||
@@ -637,18 +716,18 @@ PyObject* EffectModule::wrapImageDrawPolygon(PyObject *self, PyObject *args)
 | 
			
		||||
			if (length % 2 == 0)
 | 
			
		||||
			{
 | 
			
		||||
				QVector <QPoint> points;
 | 
			
		||||
				char * data = PyByteArray_AS_STRING(bytearray);
 | 
			
		||||
				char* data = PyByteArray_AS_STRING(bytearray);
 | 
			
		||||
 | 
			
		||||
				for (int idx=0; idx<length; idx+=2)
 | 
			
		||||
				for (int idx = 0; idx < length; idx += 2)
 | 
			
		||||
				{
 | 
			
		||||
					points.append(QPoint((int)(data[idx]),(int)(data[idx+1])));
 | 
			
		||||
					points.append(QPoint((int)(data[idx]), (int)(data[idx + 1])));
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				QPainter * painter = getEffect()->_painter;
 | 
			
		||||
				QPainter* painter = getEffect()->_painter;
 | 
			
		||||
				QPen oldPen = painter->pen();
 | 
			
		||||
				QPen newPen(QColor(r,g,b,a));
 | 
			
		||||
				QPen newPen(QColor(r, g, b, a));
 | 
			
		||||
				painter->setPen(newPen);
 | 
			
		||||
				painter->setBrush(QBrush(QColor(r,g,b,a), Qt::SolidPattern));
 | 
			
		||||
				painter->setBrush(QBrush(QColor(r, g, b, a), Qt::SolidPattern));
 | 
			
		||||
				painter->drawPolygon(points);
 | 
			
		||||
				painter->setPen(oldPen);
 | 
			
		||||
				Py_RETURN_NONE;
 | 
			
		||||
@@ -668,9 +747,9 @@ PyObject* EffectModule::wrapImageDrawPolygon(PyObject *self, PyObject *args)
 | 
			
		||||
	return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* EffectModule::wrapImageDrawPie(PyObject *self, PyObject *args)
 | 
			
		||||
PyObject* EffectModule::wrapImageDrawPie(PyObject* self, PyObject* args)
 | 
			
		||||
{
 | 
			
		||||
	PyObject * bytearray = nullptr;
 | 
			
		||||
	PyObject* bytearray = nullptr;
 | 
			
		||||
 | 
			
		||||
	QString brush;
 | 
			
		||||
	int argCount = PyTuple_Size(args);
 | 
			
		||||
@@ -686,30 +765,30 @@ PyObject* EffectModule::wrapImageDrawPie(PyObject *self, PyObject *args)
 | 
			
		||||
 | 
			
		||||
	bool argsOK = false;
 | 
			
		||||
 | 
			
		||||
	if ( argCount == 9 && PyArg_ParseTuple(args, "iiiiiiiii", ¢erX, ¢erY, &radius, &startAngle, &spanAngle, &r, &g, &b, &a) )
 | 
			
		||||
	if (argCount == 9 && PyArg_ParseTuple(args, "iiiiiiiii", ¢erX, ¢erY, &radius, &startAngle, &spanAngle, &r, &g, &b, &a))
 | 
			
		||||
	{
 | 
			
		||||
		argsOK = true;
 | 
			
		||||
	}
 | 
			
		||||
	if ( argCount == 8 && PyArg_ParseTuple(args, "iiiiiiii", ¢erX, ¢erY, &radius, &startAngle, &spanAngle, &r, &g, &b) )
 | 
			
		||||
	if (argCount == 8 && PyArg_ParseTuple(args, "iiiiiiii", ¢erX, ¢erY, &radius, &startAngle, &spanAngle, &r, &g, &b))
 | 
			
		||||
	{
 | 
			
		||||
		argsOK = true;
 | 
			
		||||
	}
 | 
			
		||||
	if ( argCount == 7 && PyArg_ParseTuple(args, "iiiiisO", ¢erX, ¢erY, &radius, &startAngle, &spanAngle, &brush, &bytearray) )
 | 
			
		||||
	if (argCount == 7 && PyArg_ParseTuple(args, "iiiiisO", ¢erX, ¢erY, &radius, &startAngle, &spanAngle, &brush, &bytearray))
 | 
			
		||||
	{
 | 
			
		||||
		argsOK = true;
 | 
			
		||||
	}
 | 
			
		||||
	if ( argCount == 5 && PyArg_ParseTuple(args, "iiisO", ¢erX, ¢erY, &radius, &brush, &bytearray) )
 | 
			
		||||
	if (argCount == 5 && PyArg_ParseTuple(args, "iiisO", ¢erX, ¢erY, &radius, &brush, &bytearray))
 | 
			
		||||
	{
 | 
			
		||||
		argsOK = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (argsOK)
 | 
			
		||||
	{
 | 
			
		||||
		QPainter * painter = getEffect()->_painter;
 | 
			
		||||
		startAngle = qMax(qMin(startAngle,360),0);
 | 
			
		||||
		spanAngle = qMax(qMin(spanAngle,360),-360);
 | 
			
		||||
		QPainter* painter = getEffect()->_painter;
 | 
			
		||||
		startAngle = qMax(qMin(startAngle, 360), 0);
 | 
			
		||||
		spanAngle = qMax(qMin(spanAngle, 360), -360);
 | 
			
		||||
 | 
			
		||||
		if( argCount == 7 || argCount == 5 )
 | 
			
		||||
		if (argCount == 7 || argCount == 5)
 | 
			
		||||
		{
 | 
			
		||||
			a = 0;
 | 
			
		||||
			if (PyByteArray_Check(bytearray))
 | 
			
		||||
@@ -718,20 +797,20 @@ PyObject* EffectModule::wrapImageDrawPie(PyObject *self, PyObject *args)
 | 
			
		||||
				if (length % 5 == 0)
 | 
			
		||||
				{
 | 
			
		||||
 | 
			
		||||
						QConicalGradient gradient(QPoint(centerX,centerY), startAngle);
 | 
			
		||||
					QConicalGradient gradient(QPoint(centerX, centerY), startAngle);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
					char * data = PyByteArray_AS_STRING(bytearray);
 | 
			
		||||
					char* data = PyByteArray_AS_STRING(bytearray);
 | 
			
		||||
 | 
			
		||||
					for (int idx=0; idx<length; idx+=5)
 | 
			
		||||
					for (int idx = 0; idx < length; idx += 5)
 | 
			
		||||
					{
 | 
			
		||||
						gradient.setColorAt(
 | 
			
		||||
							((uint8_t)data[idx])/255.0,
 | 
			
		||||
							((uint8_t)data[idx]) / 255.0,
 | 
			
		||||
							QColor(
 | 
			
		||||
								(uint8_t)(data[idx+1]),
 | 
			
		||||
								(uint8_t)(data[idx+2]),
 | 
			
		||||
								(uint8_t)(data[idx+3]),
 | 
			
		||||
								(uint8_t)(data[idx+4])
 | 
			
		||||
								(uint8_t)(data[idx + 1]),
 | 
			
		||||
								(uint8_t)(data[idx + 2]),
 | 
			
		||||
								(uint8_t)(data[idx + 3]),
 | 
			
		||||
								(uint8_t)(data[idx + 4])
 | 
			
		||||
							));
 | 
			
		||||
					}
 | 
			
		||||
					painter->setBrush(gradient);
 | 
			
		||||
@@ -752,10 +831,10 @@ PyObject* EffectModule::wrapImageDrawPie(PyObject *self, PyObject *args)
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			painter->setBrush(QBrush(QColor(r,g,b,a), Qt::SolidPattern));
 | 
			
		||||
			painter->setBrush(QBrush(QColor(r, g, b, a), Qt::SolidPattern));
 | 
			
		||||
		}
 | 
			
		||||
		QPen oldPen = painter->pen();
 | 
			
		||||
		QPen newPen(QColor(r,g,b,a));
 | 
			
		||||
		QPen newPen(QColor(r, g, b, a));
 | 
			
		||||
		painter->setPen(newPen);
 | 
			
		||||
		painter->drawPie(centerX - radius, centerY - radius, centerX + radius, centerY + radius, startAngle * 16, spanAngle * 16);
 | 
			
		||||
		painter->setPen(oldPen);
 | 
			
		||||
@@ -764,7 +843,7 @@ PyObject* EffectModule::wrapImageDrawPie(PyObject *self, PyObject *args)
 | 
			
		||||
	return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* EffectModule::wrapImageSolidFill(PyObject *self, PyObject *args)
 | 
			
		||||
PyObject* EffectModule::wrapImageSolidFill(PyObject* self, PyObject* args)
 | 
			
		||||
{
 | 
			
		||||
	int argCount = PyTuple_Size(args);
 | 
			
		||||
	int r = 0;
 | 
			
		||||
@@ -778,34 +857,34 @@ PyObject* EffectModule::wrapImageSolidFill(PyObject *self, PyObject *args)
 | 
			
		||||
 | 
			
		||||
	bool argsOK = false;
 | 
			
		||||
 | 
			
		||||
	if ( argCount == 8 && PyArg_ParseTuple(args, "iiiiiiii", &startX, &startY, &width, &height, &r, &g, &b, &a) )
 | 
			
		||||
	if (argCount == 8 && PyArg_ParseTuple(args, "iiiiiiii", &startX, &startY, &width, &height, &r, &g, &b, &a))
 | 
			
		||||
	{
 | 
			
		||||
		argsOK = true;
 | 
			
		||||
	}
 | 
			
		||||
	if ( argCount == 7 && PyArg_ParseTuple(args, "iiiiiii", &startX, &startY, &width, &height, &r, &g, &b) )
 | 
			
		||||
	if (argCount == 7 && PyArg_ParseTuple(args, "iiiiiii", &startX, &startY, &width, &height, &r, &g, &b))
 | 
			
		||||
	{
 | 
			
		||||
		argsOK = true;
 | 
			
		||||
	}
 | 
			
		||||
	if ( argCount == 4 && PyArg_ParseTuple(args, "iiii",&r, &g, &b, &a) )
 | 
			
		||||
	if (argCount == 4 && PyArg_ParseTuple(args, "iiii", &r, &g, &b, &a))
 | 
			
		||||
	{
 | 
			
		||||
		argsOK = true;
 | 
			
		||||
	}
 | 
			
		||||
	if ( argCount == 3 && PyArg_ParseTuple(args, "iii",&r, &g, &b) )
 | 
			
		||||
	if (argCount == 3 && PyArg_ParseTuple(args, "iii", &r, &g, &b))
 | 
			
		||||
	{
 | 
			
		||||
		argsOK = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (argsOK)
 | 
			
		||||
	{
 | 
			
		||||
		QRect myQRect(startX,startY,width,height);
 | 
			
		||||
		getEffect()->_painter->fillRect(myQRect, QColor(r,g,b,a));
 | 
			
		||||
		QRect myQRect(startX, startY, width, height);
 | 
			
		||||
		getEffect()->_painter->fillRect(myQRect, QColor(r, g, b, a));
 | 
			
		||||
		Py_RETURN_NONE;
 | 
			
		||||
	}
 | 
			
		||||
	return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PyObject* EffectModule::wrapImageDrawLine(PyObject *self, PyObject *args)
 | 
			
		||||
PyObject* EffectModule::wrapImageDrawLine(PyObject* self, PyObject* args)
 | 
			
		||||
{
 | 
			
		||||
	int argCount = PyTuple_Size(args);
 | 
			
		||||
	int r = 0;
 | 
			
		||||
@@ -820,21 +899,21 @@ PyObject* EffectModule::wrapImageDrawLine(PyObject *self, PyObject *args)
 | 
			
		||||
 | 
			
		||||
	bool argsOK = false;
 | 
			
		||||
 | 
			
		||||
	if ( argCount == 9 && PyArg_ParseTuple(args, "iiiiiiiii", &startX, &startY, &endX, &endY, &thick, &r, &g, &b, &a) )
 | 
			
		||||
	if (argCount == 9 && PyArg_ParseTuple(args, "iiiiiiiii", &startX, &startY, &endX, &endY, &thick, &r, &g, &b, &a))
 | 
			
		||||
	{
 | 
			
		||||
		argsOK = true;
 | 
			
		||||
	}
 | 
			
		||||
	if ( argCount == 8 && PyArg_ParseTuple(args, "iiiiiiii", &startX, &startY, &endX, &endY, &thick, &r, &g, &b) )
 | 
			
		||||
	if (argCount == 8 && PyArg_ParseTuple(args, "iiiiiiii", &startX, &startY, &endX, &endY, &thick, &r, &g, &b))
 | 
			
		||||
	{
 | 
			
		||||
		argsOK = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (argsOK)
 | 
			
		||||
	{
 | 
			
		||||
		QPainter * painter = getEffect()->_painter;
 | 
			
		||||
		QPainter* painter = getEffect()->_painter;
 | 
			
		||||
		QRect myQRect(startX, startY, endX, endY);
 | 
			
		||||
		QPen oldPen = painter->pen();
 | 
			
		||||
		QPen newPen(QColor(r,g,b,a));
 | 
			
		||||
		QPen newPen(QColor(r, g, b, a));
 | 
			
		||||
		newPen.setWidth(thick);
 | 
			
		||||
		painter->setPen(newPen);
 | 
			
		||||
		painter->drawLine(startX, startY, endX, endY);
 | 
			
		||||
@@ -845,7 +924,7 @@ PyObject* EffectModule::wrapImageDrawLine(PyObject *self, PyObject *args)
 | 
			
		||||
	return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* EffectModule::wrapImageDrawPoint(PyObject *self, PyObject *args)
 | 
			
		||||
PyObject* EffectModule::wrapImageDrawPoint(PyObject* self, PyObject* args)
 | 
			
		||||
{
 | 
			
		||||
	int argCount = PyTuple_Size(args);
 | 
			
		||||
	int r = 0;
 | 
			
		||||
@@ -858,20 +937,20 @@ PyObject* EffectModule::wrapImageDrawPoint(PyObject *self, PyObject *args)
 | 
			
		||||
 | 
			
		||||
	bool argsOK = false;
 | 
			
		||||
 | 
			
		||||
	if ( argCount == 7 && PyArg_ParseTuple(args, "iiiiiii", &x, &y, &thick, &r, &g, &b, &a) )
 | 
			
		||||
	if (argCount == 7 && PyArg_ParseTuple(args, "iiiiiii", &x, &y, &thick, &r, &g, &b, &a))
 | 
			
		||||
	{
 | 
			
		||||
		argsOK = true;
 | 
			
		||||
	}
 | 
			
		||||
	if ( argCount == 6 && PyArg_ParseTuple(args, "iiiiii", &x, &y, &thick, &r, &g, &b) )
 | 
			
		||||
	if (argCount == 6 && PyArg_ParseTuple(args, "iiiiii", &x, &y, &thick, &r, &g, &b))
 | 
			
		||||
	{
 | 
			
		||||
		argsOK = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (argsOK)
 | 
			
		||||
	{
 | 
			
		||||
		QPainter * painter = getEffect()->_painter;
 | 
			
		||||
		QPainter* painter = getEffect()->_painter;
 | 
			
		||||
		QPen oldPen = painter->pen();
 | 
			
		||||
		QPen newPen(QColor(r,g,b,a));
 | 
			
		||||
		QPen newPen(QColor(r, g, b, a));
 | 
			
		||||
		newPen.setWidth(thick);
 | 
			
		||||
		painter->setPen(newPen);
 | 
			
		||||
		painter->drawPoint(x, y);
 | 
			
		||||
@@ -882,7 +961,7 @@ PyObject* EffectModule::wrapImageDrawPoint(PyObject *self, PyObject *args)
 | 
			
		||||
	return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* EffectModule::wrapImageDrawRect(PyObject *self, PyObject *args)
 | 
			
		||||
PyObject* EffectModule::wrapImageDrawRect(PyObject* self, PyObject* args)
 | 
			
		||||
{
 | 
			
		||||
	int argCount = PyTuple_Size(args);
 | 
			
		||||
	int r = 0;
 | 
			
		||||
@@ -897,21 +976,21 @@ PyObject* EffectModule::wrapImageDrawRect(PyObject *self, PyObject *args)
 | 
			
		||||
 | 
			
		||||
	bool argsOK = false;
 | 
			
		||||
 | 
			
		||||
	if ( argCount == 9 && PyArg_ParseTuple(args, "iiiiiiiii", &startX, &startY, &width, &height, &thick, &r, &g, &b, &a) )
 | 
			
		||||
	if (argCount == 9 && PyArg_ParseTuple(args, "iiiiiiiii", &startX, &startY, &width, &height, &thick, &r, &g, &b, &a))
 | 
			
		||||
	{
 | 
			
		||||
		argsOK = true;
 | 
			
		||||
	}
 | 
			
		||||
	if ( argCount == 8 && PyArg_ParseTuple(args, "iiiiiiii", &startX, &startY, &width, &height, &thick, &r, &g, &b) )
 | 
			
		||||
	if (argCount == 8 && PyArg_ParseTuple(args, "iiiiiiii", &startX, &startY, &width, &height, &thick, &r, &g, &b))
 | 
			
		||||
	{
 | 
			
		||||
		argsOK = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (argsOK)
 | 
			
		||||
	{
 | 
			
		||||
		QPainter * painter = getEffect()->_painter;
 | 
			
		||||
		QRect myQRect(startX,startY,width,height);
 | 
			
		||||
		QPainter* painter = getEffect()->_painter;
 | 
			
		||||
		QRect myQRect(startX, startY, width, height);
 | 
			
		||||
		QPen oldPen = painter->pen();
 | 
			
		||||
		QPen newPen(QColor(r,g,b,a));
 | 
			
		||||
		QPen newPen(QColor(r, g, b, a));
 | 
			
		||||
		newPen.setWidth(thick);
 | 
			
		||||
		painter->setPen(newPen);
 | 
			
		||||
		painter->drawRect(startX, startY, width, height);
 | 
			
		||||
@@ -923,7 +1002,7 @@ PyObject* EffectModule::wrapImageDrawRect(PyObject *self, PyObject *args)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PyObject* EffectModule::wrapImageSetPixel(PyObject *self, PyObject *args)
 | 
			
		||||
PyObject* EffectModule::wrapImageSetPixel(PyObject* self, PyObject* args)
 | 
			
		||||
{
 | 
			
		||||
	int argCount = PyTuple_Size(args);
 | 
			
		||||
	int r = 0;
 | 
			
		||||
@@ -932,9 +1011,9 @@ PyObject* EffectModule::wrapImageSetPixel(PyObject *self, PyObject *args)
 | 
			
		||||
	int x = 0;
 | 
			
		||||
	int y = 0;
 | 
			
		||||
 | 
			
		||||
	if ( argCount == 5 && PyArg_ParseTuple(args, "iiiii", &x, &y, &r, &g, &b ) )
 | 
			
		||||
	if (argCount == 5 && PyArg_ParseTuple(args, "iiiii", &x, &y, &r, &g, &b))
 | 
			
		||||
	{
 | 
			
		||||
		getEffect()->_image.setPixel(x,y,qRgb(r,g,b));
 | 
			
		||||
		getEffect()->_image.setPixel(x, y, qRgb(r, g, b));
 | 
			
		||||
		Py_RETURN_NONE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -942,29 +1021,29 @@ PyObject* EffectModule::wrapImageSetPixel(PyObject *self, PyObject *args)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PyObject* EffectModule::wrapImageGetPixel(PyObject *self, PyObject *args)
 | 
			
		||||
PyObject* EffectModule::wrapImageGetPixel(PyObject* self, PyObject* args)
 | 
			
		||||
{
 | 
			
		||||
	int argCount = PyTuple_Size(args);
 | 
			
		||||
	int x = 0;
 | 
			
		||||
	int y = 0;
 | 
			
		||||
 | 
			
		||||
	if ( argCount == 2 && PyArg_ParseTuple(args, "ii", &x, &y) )
 | 
			
		||||
	if (argCount == 2 && PyArg_ParseTuple(args, "ii", &x, &y))
 | 
			
		||||
	{
 | 
			
		||||
		QRgb rgb = getEffect()->_image.pixel(x,y);
 | 
			
		||||
		return Py_BuildValue("iii",qRed(rgb),qGreen(rgb),qBlue(rgb));
 | 
			
		||||
		QRgb rgb = getEffect()->_image.pixel(x, y);
 | 
			
		||||
		return Py_BuildValue("iii", qRed(rgb), qGreen(rgb), qBlue(rgb));
 | 
			
		||||
	}
 | 
			
		||||
	return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* EffectModule::wrapImageSave(PyObject *self, PyObject *args)
 | 
			
		||||
PyObject* EffectModule::wrapImageSave(PyObject* self, PyObject* args)
 | 
			
		||||
{
 | 
			
		||||
	QImage img(getEffect()->_image.copy());
 | 
			
		||||
	getEffect()->_imageStack.append(img);
 | 
			
		||||
 | 
			
		||||
	return Py_BuildValue("i", getEffect()->_imageStack.size()-1);
 | 
			
		||||
	return Py_BuildValue("i", getEffect()->_imageStack.size() - 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* EffectModule::wrapImageMinSize(PyObject *self, PyObject *args)
 | 
			
		||||
PyObject* EffectModule::wrapImageMinSize(PyObject* self, PyObject* args)
 | 
			
		||||
{
 | 
			
		||||
	int argCount = PyTuple_Size(args);
 | 
			
		||||
	int w = 0;
 | 
			
		||||
@@ -972,13 +1051,13 @@ PyObject* EffectModule::wrapImageMinSize(PyObject *self, PyObject *args)
 | 
			
		||||
	int width = getEffect()->_imageSize.width();
 | 
			
		||||
	int height = getEffect()->_imageSize.height();
 | 
			
		||||
 | 
			
		||||
	if ( argCount == 2 && PyArg_ParseTuple(args, "ii", &w, &h) )
 | 
			
		||||
	if (argCount == 2 && PyArg_ParseTuple(args, "ii", &w, &h))
 | 
			
		||||
	{
 | 
			
		||||
		if (width<w || height<h)
 | 
			
		||||
		if (width < w || height < h)
 | 
			
		||||
		{
 | 
			
		||||
			delete getEffect()->_painter;
 | 
			
		||||
 | 
			
		||||
			getEffect()->_image = getEffect()->_image.scaled(qMax(width,w),qMax(height,h), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
 | 
			
		||||
			getEffect()->_image = getEffect()->_image.scaled(qMax(width, w), qMax(height, h), Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
 | 
			
		||||
			getEffect()->_imageSize = getEffect()->_image.size();
 | 
			
		||||
			getEffect()->_painter = new QPainter(&(getEffect()->_image));
 | 
			
		||||
		}
 | 
			
		||||
@@ -987,60 +1066,60 @@ PyObject* EffectModule::wrapImageMinSize(PyObject *self, PyObject *args)
 | 
			
		||||
	return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* EffectModule::wrapImageWidth(PyObject *self, PyObject *args)
 | 
			
		||||
PyObject* EffectModule::wrapImageWidth(PyObject* self, PyObject* args)
 | 
			
		||||
{
 | 
			
		||||
	return Py_BuildValue("i", getEffect()->_imageSize.width());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* EffectModule::wrapImageHeight(PyObject *self, PyObject *args)
 | 
			
		||||
PyObject* EffectModule::wrapImageHeight(PyObject* self, PyObject* args)
 | 
			
		||||
{
 | 
			
		||||
	return Py_BuildValue("i", getEffect()->_imageSize.height());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* EffectModule::wrapImageCRotate(PyObject *self, PyObject *args)
 | 
			
		||||
PyObject* EffectModule::wrapImageCRotate(PyObject* self, PyObject* args)
 | 
			
		||||
{
 | 
			
		||||
	int argCount = PyTuple_Size(args);
 | 
			
		||||
	int angle;
 | 
			
		||||
 | 
			
		||||
	if ( argCount == 1 && PyArg_ParseTuple(args, "i", &angle ) )
 | 
			
		||||
	if (argCount == 1 && PyArg_ParseTuple(args, "i", &angle))
 | 
			
		||||
	{
 | 
			
		||||
		angle = qMax(qMin(angle,360),0);
 | 
			
		||||
		angle = qMax(qMin(angle, 360), 0);
 | 
			
		||||
		getEffect()->_painter->rotate(angle);
 | 
			
		||||
		Py_RETURN_NONE;
 | 
			
		||||
	}
 | 
			
		||||
	return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* EffectModule::wrapImageCOffset(PyObject *self, PyObject *args)
 | 
			
		||||
PyObject* EffectModule::wrapImageCOffset(PyObject* self, PyObject* args)
 | 
			
		||||
{
 | 
			
		||||
	int offsetX = 0;
 | 
			
		||||
	int offsetY = 0;
 | 
			
		||||
	int argCount = PyTuple_Size(args);
 | 
			
		||||
 | 
			
		||||
	if ( argCount == 2 )
 | 
			
		||||
	if (argCount == 2)
 | 
			
		||||
	{
 | 
			
		||||
		PyArg_ParseTuple(args, "ii", &offsetX, &offsetY );
 | 
			
		||||
		PyArg_ParseTuple(args, "ii", &offsetX, &offsetY);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	getEffect()->_painter->translate(QPoint(offsetX,offsetY));
 | 
			
		||||
	getEffect()->_painter->translate(QPoint(offsetX, offsetY));
 | 
			
		||||
	Py_RETURN_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* EffectModule::wrapImageCShear(PyObject *self, PyObject *args)
 | 
			
		||||
PyObject* EffectModule::wrapImageCShear(PyObject* self, PyObject* args)
 | 
			
		||||
{
 | 
			
		||||
	int sh = 0;
 | 
			
		||||
	int sv = 0;
 | 
			
		||||
	int argCount = PyTuple_Size(args);
 | 
			
		||||
 | 
			
		||||
	if ( argCount == 2 && PyArg_ParseTuple(args, "ii", &sh, &sv ))
 | 
			
		||||
	if (argCount == 2 && PyArg_ParseTuple(args, "ii", &sh, &sv))
 | 
			
		||||
	{
 | 
			
		||||
		getEffect()->_painter->shear(sh,sv);
 | 
			
		||||
		getEffect()->_painter->shear(sh, sv);
 | 
			
		||||
		Py_RETURN_NONE;
 | 
			
		||||
	}
 | 
			
		||||
	return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PyObject* EffectModule::wrapImageResetT(PyObject *self, PyObject *args)
 | 
			
		||||
PyObject* EffectModule::wrapImageResetT(PyObject* self, PyObject* args)
 | 
			
		||||
{
 | 
			
		||||
	getEffect()->_painter->resetTransform();
 | 
			
		||||
	Py_RETURN_NONE;
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,7 @@
 | 
			
		||||
#include <HyperionConfig.h>
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
	#include <stdexcept>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define STRINGIFY2(x) #x
 | 
			
		||||
@@ -44,14 +44,14 @@ PythonInit::PythonInit()
 | 
			
		||||
#if (PY_VERSION_HEX >= 0x03080000)
 | 
			
		||||
	status = PyConfig_SetString(&config, &config.program_name, programName);
 | 
			
		||||
	if (PyStatus_Exception(status)) {
 | 
			
		||||
		goto exception;
 | 
			
		||||
		handlePythonError(status, config);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
#else
 | 
			
		||||
	Py_SetProgramName(programName);
 | 
			
		||||
#endif
 | 
			
		||||
	{
 | 
			
		||||
		// set Python module path when exists
 | 
			
		||||
		// set Python module path when it exists
 | 
			
		||||
		QString py_path = QDir::cleanPath(qApp->applicationDirPath() + "/../lib/python" + STRINGIFY(PYTHON_VERSION_MAJOR) + "." + STRINGIFY(PYTHON_VERSION_MINOR));
 | 
			
		||||
		QString py_file = QDir::cleanPath(qApp->applicationDirPath() + "/python" + STRINGIFY(PYTHON_VERSION_MAJOR) + STRINGIFY(PYTHON_VERSION_MINOR) + ".zip");
 | 
			
		||||
		QString py_framework = QDir::cleanPath(qApp->applicationDirPath() + "/../Frameworks/Python.framework/Versions/Current/lib/python" + STRINGIFY(PYTHON_VERSION_MAJOR) + "." + STRINGIFY(PYTHON_VERSION_MINOR));
 | 
			
		||||
@@ -68,12 +68,14 @@ PythonInit::PythonInit()
 | 
			
		||||
#if (PY_VERSION_HEX >= 0x03080000)
 | 
			
		||||
				status = PyConfig_SetBytesString(&config, &config.home, QSTRING_CSTR(py_file));
 | 
			
		||||
				if (PyStatus_Exception(status)) {
 | 
			
		||||
					goto exception;
 | 
			
		||||
					handlePythonError(status, config);
 | 
			
		||||
					return;
 | 
			
		||||
				}
 | 
			
		||||
				config.module_search_paths_set = 1;
 | 
			
		||||
				status = PyWideStringList_Append(&config.module_search_paths, const_cast<wchar_t*>(py_file.toStdWString().c_str()));
 | 
			
		||||
				if (PyStatus_Exception(status)) {
 | 
			
		||||
					goto exception;
 | 
			
		||||
					handlePythonError(status, config);
 | 
			
		||||
					return;
 | 
			
		||||
				}
 | 
			
		||||
#else
 | 
			
		||||
				Py_SetPythonHome(Py_DecodeLocale(py_file.toLatin1().data(), nullptr));
 | 
			
		||||
@@ -85,18 +87,21 @@ PythonInit::PythonInit()
 | 
			
		||||
#if (PY_VERSION_HEX >= 0x03080000)
 | 
			
		||||
				status = PyConfig_SetBytesString(&config, &config.home, QSTRING_CSTR(QDir::cleanPath(qApp->applicationDirPath() + "/../")));
 | 
			
		||||
				if (PyStatus_Exception(status)) {
 | 
			
		||||
					goto exception;
 | 
			
		||||
					handlePythonError(status, config);
 | 
			
		||||
					return;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				config.module_search_paths_set = 1;
 | 
			
		||||
				status = PyWideStringList_Append(&config.module_search_paths, const_cast<wchar_t*>(QDir(py_path).absolutePath().toStdWString().c_str()));
 | 
			
		||||
				if (PyStatus_Exception(status)) {
 | 
			
		||||
					goto exception;
 | 
			
		||||
					handlePythonError(status, config);
 | 
			
		||||
					return;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				status = PyWideStringList_Append(&config.module_search_paths, const_cast<wchar_t*>(QDir(py_path + "/lib-dynload").absolutePath().toStdWString().c_str()));
 | 
			
		||||
				if (PyStatus_Exception(status)) {
 | 
			
		||||
					goto exception;
 | 
			
		||||
					handlePythonError(status, config);
 | 
			
		||||
					return;
 | 
			
		||||
				}
 | 
			
		||||
#else
 | 
			
		||||
				QStringList python_paths;
 | 
			
		||||
@@ -114,18 +119,21 @@ PythonInit::PythonInit()
 | 
			
		||||
#if (PY_VERSION_HEX >= 0x03080000)
 | 
			
		||||
				status = PyConfig_SetBytesString(&config, &config.home, QSTRING_CSTR(QDir::cleanPath(qApp->applicationDirPath() + "/../Frameworks/Python.framework/Versions/Current")));
 | 
			
		||||
				if (PyStatus_Exception(status)) {
 | 
			
		||||
					goto exception;
 | 
			
		||||
					handlePythonError(status, config);
 | 
			
		||||
					return;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				config.module_search_paths_set = 1;
 | 
			
		||||
				status = PyWideStringList_Append(&config.module_search_paths, const_cast<wchar_t*>(QDir(py_framework).absolutePath().toStdWString().c_str()));
 | 
			
		||||
				if (PyStatus_Exception(status)) {
 | 
			
		||||
					goto exception;
 | 
			
		||||
					handlePythonError(status, config);
 | 
			
		||||
					return;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				status = PyWideStringList_Append(&config.module_search_paths, const_cast<wchar_t*>(QDir(py_framework + "/lib-dynload").absolutePath().toStdWString().c_str()));
 | 
			
		||||
				if (PyStatus_Exception(status)) {
 | 
			
		||||
					goto exception;
 | 
			
		||||
					handlePythonError(status, config);
 | 
			
		||||
					return;
 | 
			
		||||
				}
 | 
			
		||||
#else
 | 
			
		||||
				QStringList python_paths;
 | 
			
		||||
@@ -146,7 +154,8 @@ PythonInit::PythonInit()
 | 
			
		||||
#if (PY_VERSION_HEX >= 0x03080000)
 | 
			
		||||
	status = Py_InitializeFromConfig(&config);
 | 
			
		||||
	if (PyStatus_Exception(status)) {
 | 
			
		||||
		goto exception;
 | 
			
		||||
		handlePythonError(status, config);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	PyConfig_Clear(&config);
 | 
			
		||||
#endif
 | 
			
		||||
@@ -154,7 +163,8 @@ PythonInit::PythonInit()
 | 
			
		||||
	// init Python
 | 
			
		||||
	Debug(Logger::getInstance("DAEMON"), "Initializing Python interpreter");
 | 
			
		||||
	Py_InitializeEx(0);
 | 
			
		||||
	if ( !Py_IsInitialized() )
 | 
			
		||||
 | 
			
		||||
	if (!Py_IsInitialized())
 | 
			
		||||
	{
 | 
			
		||||
		throw std::runtime_error("Initializing Python failed!");
 | 
			
		||||
	}
 | 
			
		||||
@@ -165,20 +175,25 @@ PythonInit::PythonInit()
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	mainThreadState = PyEval_SaveThread();
 | 
			
		||||
	return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if (PY_VERSION_HEX >= 0x03080000)
 | 
			
		||||
exception:
 | 
			
		||||
// Error handling function to replace goto exception
 | 
			
		||||
void PythonInit::handlePythonError(PyStatus status, PyConfig& config)
 | 
			
		||||
{
 | 
			
		||||
	Error(Logger::getInstance("DAEMON"), "Initializing Python config failed with error [%s]", status.err_msg);
 | 
			
		||||
	PyConfig_Clear(&config);
 | 
			
		||||
 | 
			
		||||
	throw std::runtime_error("Initializing Python failed!");
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PythonInit::~PythonInit()
 | 
			
		||||
{
 | 
			
		||||
	Debug(Logger::getInstance("DAEMON"), "Cleaning up Python interpreter");
 | 
			
		||||
 | 
			
		||||
#if (PY_VERSION_HEX < 0x030C0000)
 | 
			
		||||
	PyEval_RestoreThread(mainThreadState);
 | 
			
		||||
	Py_Finalize();
 | 
			
		||||
#else
 | 
			
		||||
	PyThreadState_Swap(mainThreadState);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	int rc = Py_FinalizeEx();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,173 +1,184 @@
 | 
			
		||||
#include <python/PythonProgram.h>
 | 
			
		||||
#include <python/PythonUtils.h>
 | 
			
		||||
 | 
			
		||||
#include <utils/Logger.h>
 | 
			
		||||
 | 
			
		||||
#include <QThread>
 | 
			
		||||
 | 
			
		||||
PyThreadState* mainThreadState;
 | 
			
		||||
 | 
			
		||||
PythonProgram::PythonProgram(const QString & name, Logger * log) :
 | 
			
		||||
	_name(name), _log(log), _tstate(nullptr)
 | 
			
		||||
PythonProgram::PythonProgram(const QString& name, Logger* log) :
 | 
			
		||||
	_name(name)
 | 
			
		||||
	, _log(log)
 | 
			
		||||
	, _tstate(nullptr)
 | 
			
		||||
{
 | 
			
		||||
	// we probably need to wait until mainThreadState is available
 | 
			
		||||
	while(mainThreadState == nullptr){};
 | 
			
		||||
	QThread::msleep(10);
 | 
			
		||||
	while (mainThreadState == nullptr)
 | 
			
		||||
	{
 | 
			
		||||
		QThread::msleep(10);  // Wait with delay to avoid busy waiting
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Create a new subinterpreter for this thread
 | 
			
		||||
#if (PY_VERSION_HEX < 0x030C0000)
 | 
			
		||||
	// get global lock
 | 
			
		||||
	PyEval_RestoreThread(mainThreadState);
 | 
			
		||||
 | 
			
		||||
	// Initialize a new thread state
 | 
			
		||||
	_tstate = Py_NewInterpreter();
 | 
			
		||||
	if(_tstate == nullptr)
 | 
			
		||||
	{
 | 
			
		||||
#if (PY_VERSION_HEX >= 0x03020000)
 | 
			
		||||
		PyThreadState_Swap(mainThreadState);
 | 
			
		||||
		PyEval_SaveThread();
 | 
			
		||||
#else
 | 
			
		||||
		PyEval_ReleaseLock();
 | 
			
		||||
	PyThreadState* prev = PyThreadState_Swap(NULL);
 | 
			
		||||
 | 
			
		||||
	// Create a new interpreter configuration object
 | 
			
		||||
	PyInterpreterConfig config{};
 | 
			
		||||
 | 
			
		||||
	// Set configuration options
 | 
			
		||||
	config.use_main_obmalloc = 0;
 | 
			
		||||
	config.allow_fork = 0;
 | 
			
		||||
	config.allow_exec = 0;
 | 
			
		||||
	config.allow_threads = 1;
 | 
			
		||||
	config.allow_daemon_threads = 0;
 | 
			
		||||
	config.check_multi_interp_extensions = 1;
 | 
			
		||||
	config.gil = PyInterpreterConfig_OWN_GIL;
 | 
			
		||||
	Py_NewInterpreterFromConfig(&_tstate, &config);
 | 
			
		||||
#endif
 | 
			
		||||
		Error(_log, "Failed to get thread state for %s",QSTRING_CSTR(_name));
 | 
			
		||||
 | 
			
		||||
	if (_tstate == nullptr)
 | 
			
		||||
	{
 | 
			
		||||
		PyThreadState_Swap(mainThreadState);
 | 
			
		||||
#if (PY_VERSION_HEX < 0x030C0000)
 | 
			
		||||
		PyEval_SaveThread();
 | 
			
		||||
#endif
 | 
			
		||||
		Error(_log, "Failed to get thread state for %s", QSTRING_CSTR(_name));
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
#if (PY_VERSION_HEX < 0x030C0000)
 | 
			
		||||
	PyThreadState_Swap(_tstate);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PythonProgram::~PythonProgram()
 | 
			
		||||
{
 | 
			
		||||
	if (!_tstate)
 | 
			
		||||
	{
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	// stop sub threads if needed
 | 
			
		||||
	for (PyThreadState* s = PyInterpreterState_ThreadHead(_tstate->interp), *old = nullptr; s;)
 | 
			
		||||
	{
 | 
			
		||||
		if (s == _tstate)
 | 
			
		||||
		{
 | 
			
		||||
			s = s->next;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		if (old != s)
 | 
			
		||||
		{
 | 
			
		||||
			Debug(_log,"ID %s: Waiting on thread %u", QSTRING_CSTR(_name), s->thread_id);
 | 
			
		||||
			old = s;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		Py_BEGIN_ALLOW_THREADS;
 | 
			
		||||
		QThread::msleep(100);
 | 
			
		||||
		Py_END_ALLOW_THREADS;
 | 
			
		||||
 | 
			
		||||
		s = PyInterpreterState_ThreadHead(_tstate->interp);
 | 
			
		||||
	}
 | 
			
		||||
#if (PY_VERSION_HEX < 0x030C0000)
 | 
			
		||||
	PyThreadState* prev_thread_state = PyThreadState_Swap(_tstate);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	// Clean up the thread state
 | 
			
		||||
	Py_EndInterpreter(_tstate);
 | 
			
		||||
#if (PY_VERSION_HEX >= 0x03020000)
 | 
			
		||||
	PyThreadState_Swap(mainThreadState);
 | 
			
		||||
 | 
			
		||||
#if (PY_VERSION_HEX < 0x030C0000)
 | 
			
		||||
	PyThreadState_Swap(prev_thread_state);
 | 
			
		||||
	PyEval_SaveThread();
 | 
			
		||||
#else
 | 
			
		||||
	PyEval_ReleaseLock();
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PythonProgram::execute(const QByteArray & python_code)
 | 
			
		||||
void PythonProgram::execute(const QByteArray& python_code)
 | 
			
		||||
{
 | 
			
		||||
	if (!_tstate)
 | 
			
		||||
	{
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	PyObject *main_module = PyImport_ImportModule("__main__"); // New Reference
 | 
			
		||||
	PyObject *main_dict = PyModule_GetDict(main_module); // Borrowed reference
 | 
			
		||||
	Py_INCREF(main_dict); // Incref "main_dict" to use it in PyRun_String(), because PyModule_GetDict() has decref "main_dict"
 | 
			
		||||
	Py_DECREF(main_module); // // release "main_module" when done
 | 
			
		||||
	PyObject *result = PyRun_String(python_code.constData(), Py_file_input, main_dict, main_dict); // New Reference
 | 
			
		||||
	PyThreadState* prev_thread_state = PyThreadState_Swap(_tstate);
 | 
			
		||||
 | 
			
		||||
	PyObject* main_module = PyImport_ImportModule("__main__");
 | 
			
		||||
	if (!main_module)
 | 
			
		||||
	{
 | 
			
		||||
		// Restore the previous thread state
 | 
			
		||||
#if (PY_VERSION_HEX < 0x030C0000)
 | 
			
		||||
		PyThreadState_Swap(mainThreadState);
 | 
			
		||||
#else
 | 
			
		||||
		PyThreadState_Swap(prev_thread_state);
 | 
			
		||||
#endif
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	PyObject* main_dict = PyModule_GetDict(main_module);  // Borrowed reference to globals
 | 
			
		||||
	PyObject* result = PyRun_String(python_code.constData(), Py_file_input, main_dict, main_dict);
 | 
			
		||||
	if (!result)
 | 
			
		||||
	{
 | 
			
		||||
		if (PyErr_Occurred()) // Nothing needs to be done for a borrowed reference
 | 
			
		||||
		if (PyErr_Occurred())
 | 
			
		||||
		{
 | 
			
		||||
			Error(_log,"###### PYTHON EXCEPTION ######");
 | 
			
		||||
			Error(_log,"## In effect '%s'", QSTRING_CSTR(_name));
 | 
			
		||||
			/* Objects all initialized to NULL for Py_XDECREF */
 | 
			
		||||
			PyObject *errorType = NULL, *errorValue = NULL, *errorTraceback = NULL;
 | 
			
		||||
			Error(_log, "###### PYTHON EXCEPTION ######");
 | 
			
		||||
			Error(_log, "## In effect '%s'", QSTRING_CSTR(_name));
 | 
			
		||||
 | 
			
		||||
			PyErr_Fetch(&errorType, &errorValue, &errorTraceback); // New Reference or NULL
 | 
			
		||||
			PyObject* errorType = NULL, * errorValue = NULL, * errorTraceback = NULL;
 | 
			
		||||
 | 
			
		||||
			PyErr_Fetch(&errorType, &errorValue, &errorTraceback);
 | 
			
		||||
			PyErr_NormalizeException(&errorType, &errorValue, &errorTraceback);
 | 
			
		||||
 | 
			
		||||
			// Extract exception message from "errorValue"
 | 
			
		||||
			if(errorValue)
 | 
			
		||||
			if (errorValue)
 | 
			
		||||
			{
 | 
			
		||||
				QString message;
 | 
			
		||||
				if(PyObject_HasAttrString(errorValue, "__class__"))
 | 
			
		||||
				if (PyObject_HasAttrString(errorValue, "__class__"))
 | 
			
		||||
				{
 | 
			
		||||
					PyObject *classPtr = PyObject_GetAttrString(errorValue, "__class__"); // New Reference
 | 
			
		||||
					PyObject *class_name = NULL; /* Object "class_name" initialized to NULL for Py_XDECREF */
 | 
			
		||||
					class_name = PyObject_GetAttrString(classPtr, "__name__"); // New Reference or NULL
 | 
			
		||||
					PyObject* classPtr = PyObject_GetAttrString(errorValue, "__class__");
 | 
			
		||||
					PyObject* class_name = classPtr ? PyObject_GetAttrString(classPtr, "__name__") : NULL;
 | 
			
		||||
 | 
			
		||||
					if(class_name && PyUnicode_Check(class_name))
 | 
			
		||||
					if (class_name && PyUnicode_Check(class_name))
 | 
			
		||||
						message.append(PyUnicode_AsUTF8(class_name));
 | 
			
		||||
 | 
			
		||||
					Py_DECREF(classPtr); // release "classPtr" when done
 | 
			
		||||
					Py_XDECREF(class_name); // Use Py_XDECREF() to ignore NULL references
 | 
			
		||||
					Py_XDECREF(class_name);
 | 
			
		||||
					Py_DECREF(classPtr);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// Object "class_name" initialized to NULL for Py_XDECREF
 | 
			
		||||
				PyObject *valueString = NULL;
 | 
			
		||||
				valueString = PyObject_Str(errorValue); // New Reference or NULL
 | 
			
		||||
				PyObject* valueString = PyObject_Str(errorValue);
 | 
			
		||||
 | 
			
		||||
				if(valueString && PyUnicode_Check(valueString))
 | 
			
		||||
				if (valueString && PyUnicode_Check(valueString))
 | 
			
		||||
				{
 | 
			
		||||
					if(!message.isEmpty())
 | 
			
		||||
					if (!message.isEmpty())
 | 
			
		||||
						message.append(": ");
 | 
			
		||||
 | 
			
		||||
					message.append(PyUnicode_AsUTF8(valueString));
 | 
			
		||||
				}
 | 
			
		||||
				Py_XDECREF(valueString); // Use Py_XDECREF() to ignore NULL references
 | 
			
		||||
				Py_XDECREF(valueString);
 | 
			
		||||
 | 
			
		||||
				Error(_log, "## %s", QSTRING_CSTR(message));
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Extract exception message from "errorTraceback"
 | 
			
		||||
			if(errorTraceback)
 | 
			
		||||
			if (errorTraceback)
 | 
			
		||||
			{
 | 
			
		||||
				// Object "tracebackList" initialized to NULL for Py_XDECREF
 | 
			
		||||
				PyObject *tracebackModule = NULL, *methodName = NULL, *tracebackList = NULL;
 | 
			
		||||
				QString tracebackMsg;
 | 
			
		||||
				PyObject* tracebackModule = PyImport_ImportModule("traceback");
 | 
			
		||||
				PyObject* methodName = PyUnicode_FromString("format_exception");
 | 
			
		||||
				PyObject* tracebackList = tracebackModule && methodName
 | 
			
		||||
					? PyObject_CallMethodObjArgs(tracebackModule, methodName, errorType, errorValue, errorTraceback, NULL)
 | 
			
		||||
					: NULL;
 | 
			
		||||
 | 
			
		||||
				tracebackModule = PyImport_ImportModule("traceback"); // New Reference or NULL
 | 
			
		||||
				methodName = PyUnicode_FromString("format_exception"); // New Reference or NULL
 | 
			
		||||
				tracebackList = PyObject_CallMethodObjArgs(tracebackModule, methodName, errorType, errorValue, errorTraceback, NULL); // New Reference or NULL
 | 
			
		||||
 | 
			
		||||
				if(tracebackList)
 | 
			
		||||
				if (tracebackList)
 | 
			
		||||
				{
 | 
			
		||||
					PyObject* iterator = PyObject_GetIter(tracebackList); // New Reference
 | 
			
		||||
 | 
			
		||||
					PyObject* iterator = PyObject_GetIter(tracebackList);
 | 
			
		||||
					PyObject* item;
 | 
			
		||||
					while( (item = PyIter_Next(iterator)) ) // New Reference
 | 
			
		||||
					while ((item = PyIter_Next(iterator)))
 | 
			
		||||
					{
 | 
			
		||||
						Error(_log, "## %s",QSTRING_CSTR(QString(PyUnicode_AsUTF8(item)).trimmed()));
 | 
			
		||||
						Py_DECREF(item); // release "item" when done
 | 
			
		||||
						Error(_log, "## %s", QSTRING_CSTR(QString(PyUnicode_AsUTF8(item)).trimmed()));
 | 
			
		||||
						Py_DECREF(item);
 | 
			
		||||
					}
 | 
			
		||||
					Py_DECREF(iterator);  // release "iterator" when done
 | 
			
		||||
					Py_DECREF(iterator);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// Use Py_XDECREF() to ignore NULL references
 | 
			
		||||
				Py_XDECREF(tracebackModule);
 | 
			
		||||
				Py_XDECREF(methodName);
 | 
			
		||||
				Py_XDECREF(tracebackList);
 | 
			
		||||
 | 
			
		||||
				// Give the exception back to python and print it to stderr in case anyone else wants it.
 | 
			
		||||
				Py_XINCREF(errorType);
 | 
			
		||||
				Py_XINCREF(errorValue);
 | 
			
		||||
				Py_XINCREF(errorTraceback);
 | 
			
		||||
 | 
			
		||||
				PyErr_Restore(errorType, errorValue, errorTraceback);
 | 
			
		||||
				//PyErr_PrintEx(0); // Remove this line to switch off stderr output
 | 
			
		||||
			}
 | 
			
		||||
			Error(_log,"###### EXCEPTION END ######");
 | 
			
		||||
			Error(_log, "###### EXCEPTION END ######");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		Py_DECREF(result);  // release "result" when done
 | 
			
		||||
		Py_DECREF(result);  // Release result when done
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Py_DECREF(main_dict);  // release "main_dict" when done
 | 
			
		||||
	Py_DECREF(main_module);
 | 
			
		||||
 | 
			
		||||
	// Restore the previous thread state
 | 
			
		||||
#if (PY_VERSION_HEX < 0x030C0000)
 | 
			
		||||
	PyThreadState_Swap(mainThreadState);
 | 
			
		||||
#else
 | 
			
		||||
	PyThreadState_Swap(prev_thread_state);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user