17#include <KDesktopFile>
18#include <KLocalizedString>
19#include <KSharedConfig>
20#include <KConfigGroup>
29 const QStringList languages = KLocalizedString::languages();
30 if (languages.isEmpty()) {
31 return QLocale().name();
33 return languages.first();
44 return filePath.replace(
".",
"/");
51 if (f.open(QFile::ReadOnly)) {
52 QByteArray ba = f.readAll();
67 dbgScript <<
"Ignore desktop file w/o a name";
71 dbgScript <<
"Ignore desktop file w/o a module to import";
81 return i18nc(
"@item:intable plugin status",
"Broken");
83 return i18nc(
"@item:intable plugin status",
"Unstable");
85 return i18nc(
"@item:intable plugin status",
"Loaded");
87 return i18nc(
"@item:intable plugin status",
"Not loaded");
104 if (index >= 0 && index <
m_plugins.count()) {
130 rel_path = rel_path +
"/" +
"__init__.py";
131 dbgScript <<
"Finding Python module with rel_path:" << rel_path;
135 dbgScript <<
"module_path:" << module_path;
137 if (module_path.isEmpty()) {
140 dbgScript <<
"Finding Python module with rel_path:" << rel_path;
142 dbgScript <<
"module_path:" << module_path;
146 if (module_path.isEmpty()) {
150 ,
"Unable to find the module specified <application>%1</application>"
156 dbgScript <<
"Found module path:" << module_path;
163 const int pnfo = d.indexOf(
'(');
165 QString dependency = d.mid(0, pnfo);
166 QString version_str = d.mid(pnfo + 1, d.size() - pnfo - 2).trimmed();
167 dbgScript <<
"Desired version spec [" << dependency <<
"]:" << version_str;
169 if (!(checker.
isValid() && d.endsWith(
')'))) {
170 dbgScript <<
"Invalid version spec " << d;
171 QString reason = i18nc(
173 ,
"<p>Specified version has invalid format for dependency <application>%1</application>: "
174 "<icode>%2</icode>. Skipped</p>"
180 return qMakePair(dependency, checker);
200 QString reason = i18nc(
"@info:tooltip",
"<title>Dependency check</title>");
201 Q_FOREACH(
const QString & d, dependencies) {
202 QPair<QString, PyKrita::version_checker> info_pair =
parseDependency(d);
206 reason += info_pair.first;
210 dbgScript <<
"Try to import dependency module/package:" << d;
213 const QString& dependency = info_pair.first;
214 PyObject*
module = py.moduleImport(PQ(dependency));
217 dbgScript <<
"No version to check, just make sure it's loaded:" << dependency;
223 PyObject* version_obj = py.
itemString(
"__version__",
PQ(dependency));
225 dbgScript <<
"No __version__ for " << dependency
231 ,
"<p>Failed to check version of dependency <application>%1</application>: "
232 "Module do not have PEP396 <code>__version__</code> attribute. "
233 "It is not disabled, but behaviour is unpredictable...</p>"
241 dbgScript <<
"***: Can't parse module version for" << dependency;
245 ,
"<p><application>%1</application>: Unexpected module's version format"
248 }
else if (!checker(dep_version)) {
249 dbgScript <<
"Version requirement check failed ["
253 <<
", but found" << QString(dep_version)
258 ,
"<p><application>%1</application>: No suitable version found. "
259 "Required version %2 %3, but found %4</p>"
263 , QString(dep_version)
273 ,
"<p>Failure on module load <application>%1</application>:</p><pre>%2</pre>"
289 KConfigGroup pluginSettings(KSharedConfig::openConfig(),
"python");
293 Q_FOREACH(
const QString &desktopFile, desktopFiles) {
295 KDesktopFile df(desktopFile);
297 const KConfigGroup dg = df.desktopGroup();
298 if (dg.readEntry(
"ServiceTypes") ==
"Krita/PythonPlugin") {
305 QString manual = dg.readEntry(
"X-Krita-Manual");
306 if (!manual.isEmpty()) {
355 dbgScript <<
"Loading module: " << module_name;
364 PyObject*
module = py.moduleImport(PQ(module_name));
367 const int ins_result = PyDict_SetItemString(
plugins,
PQ(module_name), module);
371 if (ins_result == 0) {
373 PyObject*
const args = Py_BuildValue(
"(s)",
PQ(module_name));
386 ,
"Module not loaded:<br/><code>%1</code>"
391 warnScript <<
"Error loading plugin" << module_name;
416 PyObject* sys_modules = py.
itemString(
"modules",
"sys");
432 KConfigGroup pluginSettings(KSharedConfig::openConfig(),
"python");
433 pluginSettings.writeEntry(QString(
"enable_") +
plugin.
moduleName(), enabled);
435 if (!wasEnabled && enabled) {
static QString currentLocale()
static QString currentLocale()
PythonPluginManager * instance
static void writeSysInfo(const QString &message)
Writes to the system information file and Krita log.
static QStringList findAllAssets(const QString &type, const QString &filter=QString(), SearchOptions options=NoSearchOptions)
static QString findAsset(const QString &type, const QString &fileName)
bool functionCall(const char *functionName, const char *moduleName=PYKRITA_ENGINE)
static const char * PYKRITA_ENGINE
PyObject * itemString(const char *item, const char *moduleName=PYKRITA_ENGINE)
QString lastTraceback(void) const
QString operationToString() const
static version_checker fromString(const QString &version_info)
static version fromPythonObject(PyObject *version_obj)
QList< PythonPlugin > m_plugins
PythonPluginsModel * model()
void setPluginEnabled(PythonPlugin &plugin, bool enabled)
PythonPlugin * plugin(int index)
PythonPluginsModel m_model
void tryLoadEnabledPlugins()
static bool verifyModuleExists(PythonPlugin &)
const QList< PythonPlugin > & plugins() const
void loadModule(PythonPlugin &plugin)
static void verifyDependenciesSetStatus(PythonPlugin &)
static QPair< QString, PyKrita::version_checker > parseDependency(const QString &)
void unloadModule(PythonPlugin &plugin)
QString moduleName() const
QString m_desktopFilePath
const QString & errorReason() const
QString moduleFilePathPart() const
QVariant property(const QString &name) const
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
#define PQ(x)
Save us some ruddy time when printing out QStrings with UTF-8.