2019-02-09 13:05:06 +00:00
|
|
|
|
2019-02-18 13:33:44 +00:00
|
|
|
#include <cassert>
|
|
|
|
|
2019-02-13 22:24:22 +00:00
|
|
|
#ifdef CUTTER_ENABLE_PYTHON_BINDINGS
|
2019-02-09 13:05:06 +00:00
|
|
|
#include <Python.h>
|
2019-02-09 16:21:31 +00:00
|
|
|
#include <cutterbindings_python.h>
|
2019-02-11 20:26:11 +00:00
|
|
|
#include "PythonManager.h"
|
2019-02-09 13:05:06 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "PluginManager.h"
|
|
|
|
#include "CutterPlugin.h"
|
|
|
|
|
|
|
|
#include <QDir>
|
|
|
|
#include <QCoreApplication>
|
|
|
|
#include <QPluginLoader>
|
2019-02-09 13:16:33 +00:00
|
|
|
#include <QStandardPaths>
|
2019-02-09 13:05:06 +00:00
|
|
|
|
|
|
|
Q_GLOBAL_STATIC(PluginManager, uniqueInstance)
|
|
|
|
|
|
|
|
PluginManager *PluginManager::getInstance()
|
|
|
|
{
|
|
|
|
return uniqueInstance;
|
|
|
|
}
|
|
|
|
|
|
|
|
PluginManager::PluginManager()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
PluginManager::~PluginManager()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2019-03-11 10:54:19 +00:00
|
|
|
QString PluginManager::getPluginsDirectory() const
|
2019-02-09 13:05:06 +00:00
|
|
|
{
|
2019-02-09 13:16:33 +00:00
|
|
|
QStringList locations = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
|
|
|
|
if (locations.isEmpty()) {
|
2019-03-11 10:54:19 +00:00
|
|
|
return QString();
|
2019-02-09 13:05:06 +00:00
|
|
|
}
|
2019-02-09 13:16:33 +00:00
|
|
|
QDir pluginsDir(locations.first());
|
2019-03-11 10:54:19 +00:00
|
|
|
pluginsDir.mkpath("plugins");
|
|
|
|
if (!pluginsDir.cd("plugins")) {
|
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
return pluginsDir.absolutePath();
|
|
|
|
}
|
2019-02-09 13:16:33 +00:00
|
|
|
|
2019-03-11 10:54:19 +00:00
|
|
|
void PluginManager::loadPlugins()
|
|
|
|
{
|
|
|
|
assert(plugins.isEmpty());
|
2019-03-08 23:01:24 +00:00
|
|
|
|
2019-03-11 10:54:19 +00:00
|
|
|
QString pluginsDirStr = getPluginsDirectory();
|
|
|
|
if (pluginsDirStr.isEmpty()) {
|
|
|
|
qCritical() << "Failed to get a path to load plugins from.";
|
2019-02-09 13:05:06 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-03-11 10:54:19 +00:00
|
|
|
|
2019-04-04 08:58:36 +00:00
|
|
|
loadPluginsFromDir(QDir(pluginsDirStr));
|
|
|
|
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
QDir appDir;
|
|
|
|
appDir.mkdir("plugins");
|
|
|
|
if (appDir.cd("plugins")) {
|
|
|
|
loadPluginsFromDir(appDir);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void PluginManager::loadPluginsFromDir(const QDir &pluginsDir)
|
|
|
|
{
|
2019-03-11 10:54:19 +00:00
|
|
|
qInfo() << "Plugins are loaded from" << pluginsDir.absolutePath();
|
2019-04-04 08:58:36 +00:00
|
|
|
int loadedPlugins = plugins.length();
|
2019-02-09 13:05:06 +00:00
|
|
|
|
|
|
|
QDir nativePluginsDir = pluginsDir;
|
2019-02-09 13:16:33 +00:00
|
|
|
nativePluginsDir.mkdir("native");
|
2019-02-09 13:05:06 +00:00
|
|
|
if (nativePluginsDir.cd("native")) {
|
|
|
|
loadNativePlugins(nativePluginsDir);
|
|
|
|
}
|
|
|
|
|
2019-02-13 22:24:22 +00:00
|
|
|
#ifdef CUTTER_ENABLE_PYTHON_BINDINGS
|
2019-02-09 13:05:06 +00:00
|
|
|
QDir pythonPluginsDir = pluginsDir;
|
2019-02-09 13:16:33 +00:00
|
|
|
pythonPluginsDir.mkdir("python");
|
2019-02-09 13:05:06 +00:00
|
|
|
if (pythonPluginsDir.cd("python")) {
|
|
|
|
loadPythonPlugins(pythonPluginsDir.absolutePath());
|
|
|
|
}
|
2019-02-11 20:26:11 +00:00
|
|
|
#endif
|
2019-02-09 13:05:06 +00:00
|
|
|
|
2019-04-04 08:58:36 +00:00
|
|
|
loadedPlugins = plugins.length() - loadedPlugins;
|
|
|
|
qInfo() << "Loaded" << loadedPlugins << "plugin(s).";
|
2019-02-09 13:05:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PluginManager::destroyPlugins()
|
|
|
|
{
|
|
|
|
for (CutterPlugin *plugin : plugins) {
|
2019-03-23 11:23:31 +00:00
|
|
|
plugin->terminate();
|
2019-02-09 13:05:06 +00:00
|
|
|
delete plugin;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PluginManager::loadNativePlugins(const QDir &directory)
|
|
|
|
{
|
|
|
|
for (const QString &fileName : directory.entryList(QDir::Files)) {
|
|
|
|
QPluginLoader pluginLoader(directory.absoluteFilePath(fileName));
|
|
|
|
QObject *plugin = pluginLoader.instance();
|
|
|
|
if (!plugin) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
CutterPlugin *cutterPlugin = qobject_cast<CutterPlugin *>(plugin);
|
|
|
|
if (!cutterPlugin) {
|
|
|
|
continue;
|
|
|
|
}
|
2019-02-09 16:21:31 +00:00
|
|
|
cutterPlugin->setupPlugin();
|
2019-02-09 13:05:06 +00:00
|
|
|
plugins.append(cutterPlugin);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-13 22:24:22 +00:00
|
|
|
#ifdef CUTTER_ENABLE_PYTHON_BINDINGS
|
2019-02-09 13:05:06 +00:00
|
|
|
|
|
|
|
void PluginManager::loadPythonPlugins(const QDir &directory)
|
|
|
|
{
|
|
|
|
Python()->addPythonPath(directory.absolutePath().toLocal8Bit().data());
|
|
|
|
|
2019-02-09 16:21:31 +00:00
|
|
|
for (const QString &fileName : directory.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)) {
|
|
|
|
if (fileName == "__pycache__") {
|
|
|
|
continue;
|
|
|
|
}
|
2019-02-09 13:05:06 +00:00
|
|
|
QString moduleName;
|
|
|
|
if (fileName.endsWith(".py")) {
|
2019-03-23 10:54:34 +00:00
|
|
|
moduleName = fileName.chopped(3);
|
2019-02-09 16:21:31 +00:00
|
|
|
} else {
|
|
|
|
moduleName = fileName;
|
2019-02-09 13:05:06 +00:00
|
|
|
}
|
2019-02-09 16:21:31 +00:00
|
|
|
CutterPlugin *cutterPlugin = loadPythonPlugin(moduleName.toLocal8Bit().constData());
|
2019-02-09 13:05:06 +00:00
|
|
|
if (!cutterPlugin) {
|
|
|
|
continue;
|
|
|
|
}
|
2019-02-09 16:21:31 +00:00
|
|
|
cutterPlugin->setupPlugin();
|
2019-02-09 13:05:06 +00:00
|
|
|
plugins.append(cutterPlugin);
|
|
|
|
}
|
|
|
|
|
|
|
|
PythonManager::ThreadHolder threadHolder;
|
|
|
|
}
|
|
|
|
|
|
|
|
CutterPlugin *PluginManager::loadPythonPlugin(const char *moduleName)
|
|
|
|
{
|
|
|
|
PythonManager::ThreadHolder threadHolder;
|
|
|
|
|
|
|
|
PyObject *pluginModule = PyImport_ImportModule(moduleName);
|
|
|
|
if (!pluginModule) {
|
|
|
|
qWarning() << "Couldn't load module for plugin:" << QString(moduleName);
|
2019-02-09 16:21:31 +00:00
|
|
|
PyErr_Print();
|
|
|
|
return nullptr;
|
2019-02-09 13:05:06 +00:00
|
|
|
}
|
|
|
|
|
2019-02-09 16:21:31 +00:00
|
|
|
PyObject *createPluginFunc = PyObject_GetAttrString(pluginModule, "create_cutter_plugin");
|
|
|
|
if (!createPluginFunc || !PyCallable_Check(createPluginFunc)) {
|
2019-02-11 19:05:53 +00:00
|
|
|
qWarning() << "Plugin module does not contain create_cutter_plugin() function:" << QString(moduleName);
|
2019-02-09 16:21:31 +00:00
|
|
|
if (createPluginFunc) {
|
|
|
|
Py_DECREF(createPluginFunc);
|
|
|
|
}
|
|
|
|
Py_DECREF(pluginModule);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyObject *pluginObject = PyObject_CallFunction(createPluginFunc, nullptr);
|
|
|
|
Py_DECREF(createPluginFunc);
|
|
|
|
Py_DECREF(pluginModule);
|
2019-02-11 19:05:53 +00:00
|
|
|
if (!pluginObject) {
|
|
|
|
qWarning() << "Plugin's create_cutter_plugin() function failed.";
|
|
|
|
PyErr_Print();
|
|
|
|
return nullptr;
|
|
|
|
}
|
2019-02-09 16:21:31 +00:00
|
|
|
|
|
|
|
PythonToCppFunc pythonToCpp = Shiboken::Conversions::isPythonToCppPointerConvertible(reinterpret_cast<SbkObjectType *>(SbkCutterBindingsTypes[SBK_CUTTERPLUGIN_IDX]), pluginObject);
|
|
|
|
if (!pythonToCpp) {
|
2019-02-11 19:05:53 +00:00
|
|
|
qWarning() << "Plugin's create_cutter_plugin() function did not return an instance of CutterPlugin:" << QString(moduleName);
|
2019-02-09 16:21:31 +00:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
CutterPlugin *plugin;
|
|
|
|
pythonToCpp(pluginObject, &plugin);
|
2019-02-11 19:05:53 +00:00
|
|
|
if (!plugin) {
|
|
|
|
qWarning() << "Error during the setup of CutterPlugin:" << QString(moduleName);
|
|
|
|
return nullptr;
|
|
|
|
}
|
2019-02-09 13:05:06 +00:00
|
|
|
return plugin;
|
|
|
|
}
|
2019-02-11 19:05:53 +00:00
|
|
|
#endif
|