Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Commit 6509644e authored by Jeroen Hoolmans's avatar Jeroen Hoolmans

Added support to compile with Python2.7 on Linux

Windows support will come later as discussed in
https://phabricator.kde.org/D12838. To compile use the
-DENABLE_PYTHON_2 flag. Python 3 is still the default.

For the plugins to work correctly we need the tollius and pathlib
modules through pip.

Differential Revision: https://phabricator.kde.org/D12838
parent 65e24ca5
......@@ -5,7 +5,7 @@ project (krita-and-all-its-deps)
# Parameters: EXTERNALS_DOWNLOAD_DIR place to download all packages
# INSTALL_ROOT place to install everything to
# MXE_TOOLCHAIN: the toolchain file to cross-compile using MXE
#
#
# Example usage: cmake ..\kritadeposx -DEXTERNALS_DOWNLOAD_DIR=/dev2/d -DINSTALL_ROOT=/dev2/i -DWIN64_BUILD=TRUE -DBOOST_LIBRARYDIR=/dev2/i/lib -G "Visual Studio 11 Win64"
......@@ -37,11 +37,11 @@ set(EXTPREFIX "${TOP_INST_DIR}")
set(CMAKE_PREFIX_PATH "${EXTPREFIX}")
if (${CMAKE_GENERATOR} STREQUAL "Visual Studio 14 2015 Win64")
SET(GLOBAL_PROFILE
-DCMAKE_MODULE_LINKER_FLAGS=/machine:x64
-DCMAKE_EXE_LINKER_FLAGS=/machine:x64
-DCMAKE_SHARED_LINKER_FLAGS=/machine:x64
-DCMAKE_STATIC_LINKER_FLAGS=/machine:x64
SET(GLOBAL_PROFILE
-DCMAKE_MODULE_LINKER_FLAGS=/machine:x64
-DCMAKE_EXE_LINKER_FLAGS=/machine:x64
-DCMAKE_SHARED_LINKER_FLAGS=/machine:x64
-DCMAKE_STATIC_LINKER_FLAGS=/machine:x64
)
endif ()
......@@ -105,16 +105,16 @@ endif()
if (MSYS)
set(PATCH_COMMAND patch)
set(GLOBAL_PROFILE ${GLOBAL_PROFILE}
set(GLOBAL_PROFILE ${GLOBAL_PROFILE}
-DCMAKE_TOOLCHAIN_FILE=${MXE_TOOLCHAIN}
-DCMAKE_FIND_PREFIX_PATH=${CMAKE_PREFIX_PATH}
-DCMAKE_SYSTEM_INCLUDE_PATH=${CMAKE_PREFIX_PATH}/include
-DCMAKE_INCLUDE_PATH=${CMAKE_PREFIX_PATH}/include
-DCMAKE_INCLUDE_PATH=${CMAKE_PREFIX_PATH}/include
-DCMAKE_LIBRARY_PATH=${CMAKE_PREFIX_PATH}/lib
-DZLIB_ROOT=${CMAKE_PREFIX_PATH}
)
set(GLOBAL_AUTOMAKE_PROFILE --host=i686-pc-mingw32 )
endif()
endif()
if (APPLE)
set(GLOBAL_PROFILE ${GLOBAL_PROFILE} -DCMAKE_MACOSX_RPATH=ON -DKDE_SKIP_RPATH_SETTINGS=ON -DBUILD_WITH_INSTALL_RPATH=ON -DAPPLE_SUPPRESS_X11_WARNING=ON)
......@@ -144,8 +144,12 @@ endfunction()
if (MINGW)
option(ENABLE_PYTHON_DEPS "Enable Python deps (sip, pyqt)" ON)
if (ENABLE_PYTHON_DEPS)
find_package(PythonInterp 3.6 EXACT)
find_package(PythonLibs 3.6 EXACT)
if (ENABLE_PYTHON_2)
message(FATAL_ERROR "Python 2.7 is not supported on Windows at the moment.")
else(ENABLE_PYTHON_2)
find_package(PythonInterp 3.6 EXACT)
find_package(PythonLibs 3.6 EXACT)
endif(ENABLE_PYTHON_2)
if (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND)
message(STATUS "Python requirements met.")
TestCompileLinkPythonLibs(CAN_USE_PYTHON_LIBS)
......
......@@ -194,6 +194,7 @@ add_feature_info("Foundation Build" FOUNDATION_BUILD "A Foundation build is a bi
option(KRITA_ENABLE_BROKEN_TESTS "Enable tests that are marked as broken" OFF)
add_feature_info("Enable Broken Tests" KRITA_ENABLE_BROKEN_TESTS "Runs broken test when \"make test\" is invoked (use -DKRITA_ENABLE_BROKEN_TESTS=ON to enable).")
option(ENABLE_PYTHON_2 "Enables the compiler to look for Python 2.7 instead of Python 3. Some packaged scripts are not compatible with Python 2 and this should only be used if you really have to use 2.7." OFF)
include(MacroJPEG)
......@@ -218,18 +219,31 @@ int main(int argc, char *argv[]) {
endfunction()
if(MINGW)
find_package(PythonInterp 3.6 EXACT)
find_package(PythonLibs 3.6 EXACT)
if(ENABLE_PYTHON_2)
message(FATAL_ERROR "Python 2.7 is not supported on Windows at the moment.")
else(ENABLE_PYTHON_2)
find_package(PythonInterp 3.6 EXACT)
find_package(PythonLibs 3.6 EXACT)
endif(ENABLE_PYTHON_2)
if (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND)
find_package(PythonLibrary 3.6)
if(ENABLE_PYTHON_2)
find_package(PythonLibrary 2.7)
else(ENABLE_PYTHON_2)
find_package(PythonLibrary 3.6)
endif(ENABLE_PYTHON_2)
TestCompileLinkPythonLibs(CAN_USE_PYTHON_LIBS)
if (NOT CAN_USE_PYTHON_LIBS)
message(FATAL_ERROR "Compiling with Python library failed, please check whether the architecture is correct. Python will be disabled.")
endif (NOT CAN_USE_PYTHON_LIBS)
endif (PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND)
else(MINGW)
find_package(PythonInterp 3.0)
find_package(PythonLibrary 3.0)
if(ENABLE_PYTHON_2)
find_package(PythonInterp 2.7)
find_package(PythonLibrary 2.7)
else(ENABLE_PYTHON_2)
find_package(PythonInterp 3.0)
find_package(PythonLibrary 3.0)
endif(ENABLE_PYTHON_2)
endif(MINGW)
########################
......@@ -459,7 +473,7 @@ if (APPLE)
endif()
add_definitions(-DBOOST_ALL_NO_LIB)
find_package(Boost 1.55 REQUIRED COMPONENTS system)
find_package(Boost 1.55 REQUIRED COMPONENTS system)
include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
##
## Test for GNU Scientific Library
......@@ -621,7 +635,7 @@ endif()
set(OLD_CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} )
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules )
set(HAVE_VC FALSE)
if (NOT ${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm")
if (NOT ${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm")
if(NOT MSVC)
find_package(Vc 1.1.0)
set_package_properties(Vc PROPERTIES
......
......@@ -25,7 +25,11 @@
include(FindPackageHandleStandardArgs)
find_package(PythonInterp)
if (ENABLE_PYTHON_2)
find_package(PythonInterp 2.7 REQUIRED)
else(ENABLE_PYTHON_2)
find_package(PythonInterp 3.0 REQUIRED)
endif(ENABLE_PYTHON_2)
if (PYTHONINTERP_FOUND)
......
......@@ -3,8 +3,8 @@
add_definitions(-DQT_NO_KEYWORDS)
configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h)
set(SOURCES
plugin.cpp
set(SOURCES
plugin.cpp
pyqtpluginsettings.cpp
utilities.cpp
PykritaModule.cpp
......@@ -12,8 +12,8 @@ set(SOURCES
PythonPluginsModel.cpp
)
ki18n_wrap_ui(SOURCES
info.ui
ki18n_wrap_ui(SOURCES
info.ui
manager.ui
)
......
......@@ -27,6 +27,17 @@
#define PYKRITA_INIT PyInit_pykrita
struct module_state {
PyObject *error;
};
#if defined(IS_PY3K)
#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
#else
#define GETSTATE(m) (&_state)
static struct module_state _state;
#endif
/// \note Namespace name written in uppercase intentionally!
/// It will appear in debug output from Python plugins...
namespace PYKRITA
......@@ -56,23 +67,48 @@ namespace
} // anonymous namespace
//BEGIN Python module registration
#if defined(IS_PY3K)
// Python 3 initializes modules differently from Python 2
//
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT
, "pykrita"
, "The pykrita module"
, -1
, pykritaMethods
, 0
, 0
, 0
, 0
};
#define INITERROR return NULL
PyMODINIT_FUNC PyInit_pykrita()
#else
#define INITERROR return
void
initpykrita(void)
#endif
{
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT
, "pykrita"
, "The pykrita module"
, -1
, pykritaMethods
, 0
, 0
, 0
, 0
};
#if defined(IS_PY3K)
PyObject *pykritaModule = PyModule_Create(&moduledef);
#else
PyObject *pykritaModule = Py_InitModule("pykrita", pykritaMethods);
#endif
if (pykritaModule == NULL)
INITERROR;
PyModule_AddStringConstant(pykritaModule, "__file__", __FILE__);
#if defined(IS_PY3K)
return pykritaModule;
#endif
}
//END Python module registration
// krita: space-indent on; indent-width 4;
......
......@@ -25,9 +25,19 @@
#include <Python.h>
#if PY_MAJOR_VERSION >= 3
#ifndef IS_PY3K
#define IS_PY3K
#endif
#endif
/**
* Initializer for the built-in Python module.
*/
#if defined(IS_PY3K)
PyMODINIT_FUNC PyInit_pykrita();
#else
void initpykrita();
#endif
#endif
......@@ -59,6 +59,13 @@ bool PythonPlugin::isValid() const
dbgScript << "Ignore desktop file w/o a module to import";
return false;
}
#if PY_MAJOR_VERSION == 2
// Check if the plug-in is compatible with Python 2 or not.
if (m_properties["X-Python-2-Compatible"].toBool() != true) {
dbgScript << "Ignoring plug-in. It is marked incompatible with Python 2.";
return false;
}
#endif
return true;
}
......
from __future__ import print_function
import pykrita
import os
import sys
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
from .api import *
from .decorators import *
from .dockwidgetfactory import *
from PyKrita import krita
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
krita_path = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, krita_path)
print("%s added to PYTHONPATH" % krita_path, file=sys.stderr)
......@@ -26,7 +28,10 @@ except ImportError:
import excepthook
excepthook.install()
import builtins
if sys.version_info[0] > 2:
import builtins
else:
import __builtin__ as builtins
builtins.i18n = lambda s: QCoreApplication.translate("PyKrita", s)
builtins.Scripter = Krita.instance()
builtins.Application = Krita.instance()
......
......@@ -7,7 +7,7 @@ from PyKrita.krita import *
class DockWidgetFactory(DockWidgetFactoryBase):
def __init__(self, _id, _dockPosition, _klass):
super().__init__(_id, _dockPosition)
super(DockWidgetFactory, self).__init__(_id, _dockPosition)
self.klass = _klass
def createDockWidget(self):
......
......@@ -76,7 +76,11 @@ namespace PyKrita
return initStatus;
}
#if defined(IS_PY3K)
if (0 != PyImport_AppendInittab(Python::PYKRITA_ENGINE, PyInit_pykrita)) {
#else
if (0 != PyImport_AppendInittab(Python::PYKRITA_ENGINE, initpykrita)) {
#endif
initStatus = INIT_CANNOT_LOAD_PYKRITA_MODULE;
return initStatus;
}
......@@ -101,6 +105,7 @@ namespace PyKrita
pluginManagerInstance.reset(new PythonPluginManager());
#if defined(IS_PY3K)
// Initialize our built-in module.
auto pykritaModule = PyInit_pykrita();
......@@ -109,6 +114,9 @@ namespace PyKrita
return initStatus;
//return i18nc("@info:tooltip ", "No <icode>pykrita</icode> built-in module");
}
#else
initpykrita();
#endif
initStatus = INIT_OK;
return initStatus;
......@@ -412,7 +420,9 @@ bool Python::setPath(const QStringList& scriptPaths)
if (KoResourcePaths::getApplicationRoot().contains(".mount_Krita")) {
QVector<wchar_t> joinedPathsWChars(joinedPaths.size() + 1, 0);
joinedPaths.toWCharArray(joinedPathsWChars.data());
Py_SetPath(joinedPathsWChars.data());
PyRun_SimpleString("import sys; import os");
QString pathCommand = QString("sys.path += '") + joinedPaths + QString("'.split(os.pathsep)");
PyRun_SimpleString(pathCommand.toUtf8().constData());
}
else {
qputenv("PYTHONPATH", joinedPaths.toLocal8Bit());
......
......@@ -6,7 +6,7 @@ message( ${SIP_EXECUTABLE} " - Path and filename of the SIP command line executa
message( ${SIP_INCLUDE_DIR} " - Directory holding the SIP C++ header file.")
message( ${SIP_DEFAULT_SIP_DIR} " - default SIP dir" )
set(SIP_INCLUDES
set(SIP_INCLUDES
${SIP_DEFAULT_SIP_DIR}
${PYQT5_SIP_DIR}
${PYQT_SIP_DIR_OVERRIDE}
......@@ -21,7 +21,10 @@ file(GLOB PYKRITA_KRITA_sip_files ./krita/*.sip)
set(SIP_EXTRA_FILES_DEPEND ${PYKRITA_KRITA_sip_files})
add_sip_python_module(PyKrita.krita ./krita/kritamod.sip kritalibkis kritaui kritaimage kritalibbrush)
#install(FILES
# ./__init__.py
# DESTINATION ${PYTHON_SITE_PACKAGES_INSTALL_DIR})
if (ENABLE_PYTHON_2)
# Add an init file to turn it into a valid py2 module.
# Otherwise PyKrita cannot be loaded.
install(FILES
./__init__.py
DESTINATION ${PYTHON_SITE_PACKAGES_INSTALL_DIR}/PyKrita)
endif (ENABLE_PYTHON_2)
......@@ -9,20 +9,20 @@ You can copy, modify, distribute and perform the work, even for commercial purpo
https://creativecommons.org/publicdomain/zero/1.0/legalcode
'''
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from krita import *
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QDialogButtonBox, QDialog,
QMessageBox, QComboBox, QVBoxLayout)
from krita import Extension
class AssignProfileDialog(Extension):
def __init__(self, parent):
super().__init__(parent)
super(AssignProfileDialog, self).__init__(parent)
def assignProfile(self):
doc = Application.activeDocument()
if doc == None:
if doc is None:
QMessageBox.information(Application.activeWindow().qwindow(), "Assign Profile", "There is no active document.")
return
......@@ -35,7 +35,7 @@ class AssignProfileDialog(Extension):
vbox = QVBoxLayout(self.dialog)
vbox.addWidget(self.cmbProfile)
self.buttonBox = QDialogButtonBox(self.dialog)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setOrientation(Qt.Horizontal)
self.buttonBox.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.buttonBox.accepted.connect(self.dialog.accept)
self.buttonBox.accepted.connect(self.accept)
......@@ -51,9 +51,10 @@ class AssignProfileDialog(Extension):
def setup(self):
pass
def createActions(self, window):
action = window.createAction("assing_profile_to_image", "Assign Profile to Image")
action.triggered.connect(self.assignProfile)
Scripter.addExtension(AssignProfileDialog(Application))
......@@ -2,7 +2,7 @@
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=assignprofiledialog
X-Python-2-Compatible=false
X-Python-2-Compatible=true
X-Krita-Manual=Manual.html
Name=Assign Profile to Image
Name[ar]=إسناد اللاحات إلى الصّور
......
......@@ -10,7 +10,7 @@ You can copy, modify, distribute and perform the work, even for commercial purpo
https://creativecommons.org/publicdomain/zero/1.0/legalcode
'''
import krita
from colorspace import uicolorspace
from . import uicolorspace
class ColorSpaceExtension(krita.Extension):
......@@ -20,7 +20,7 @@ class ColorSpaceExtension(krita.Extension):
def setup(self):
pass
def createActions(self, window):
action = window.createAction("color_space", "Color Space")
action.setToolTip("Plugin to change color space to selected documents")
......
......@@ -2,7 +2,7 @@
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=colorspace
X-Python-2-Compatible=false
X-Python-2-Compatible=true
X-Krita-Manual=Manual.html
Name=Color Space
Name[ar]=الفضاء اللونيّ
......
......@@ -9,8 +9,8 @@ You can copy, modify, distribute and perform the work, even for commercial purpo
https://creativecommons.org/publicdomain/zero/1.0/legalcode
'''
from colorspace import colorspacedialog
from colorspace.components import colormodelcombobox, colordepthcombobox, colorprofilecombobox
from . import colorspacedialog
from .components import colormodelcombobox, colordepthcombobox, colorprofilecombobox
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QFormLayout, QListWidget, QListWidgetItem,
QAbstractItemView, QComboBox, QDialogButtonBox,
......@@ -18,7 +18,7 @@ from PyQt5.QtWidgets import (QFormLayout, QListWidget, QListWidgetItem,
QHBoxLayout, QAbstractScrollArea)
from PyQt5.QtGui import QIcon
import krita
from colorspace import resources_rc
from . import resources_rc
class UIColorSpace(object):
......
......@@ -10,7 +10,7 @@ You can copy, modify, distribute and perform the work, even for commercial purpo
https://creativecommons.org/publicdomain/zero/1.0/legalcode
'''
import krita
from documenttools import uidocumenttools
from . import uidocumenttools
class DocumentToolsExtension(krita.Extension):
......@@ -20,7 +20,7 @@ class DocumentToolsExtension(krita.Extension):
def setup(self):
pass
def createActions(self, window):
action = window.createAction("document_tools", "Document Tools")
action.setToolTip("Plugin to manipulate properties of selected documents")
......
......@@ -2,7 +2,7 @@
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=documenttools
X-Python-2-Compatible=false
X-Python-2-Compatible=true
X-Krita-Manual=Manual.html
Name=Document Tools
Name[ar]=أدوات المستندات
......
......@@ -9,7 +9,7 @@ You can copy, modify, distribute and perform the work, even for commercial purpo
https://creativecommons.org/publicdomain/zero/1.0/legalcode
'''
from documenttools import documenttoolsdialog
from . import documenttoolsdialog
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QFormLayout, QListWidget, QAbstractItemView,
QDialogButtonBox, QVBoxLayout, QFrame, QTabWidget,
......@@ -71,7 +71,8 @@ class UIDocumentTools(object):
modules = []
for classPath in toolsModule.ToolClasses:
_module, _klass = classPath.rsplit('.', maxsplit=1)
_module = classPath[:classPath.rfind(".")]
_klass = classPath[classPath.rfind(".") + 1:]
modules.append(dict(module='{0}.{1}'.format(modulePath, _module),
klass=_klass))
......
......@@ -10,7 +10,7 @@ You can copy, modify, distribute and perform the work, even for commercial purpo
https://creativecommons.org/publicdomain/zero/1.0/legalcode
'''
import krita
from exportlayers import uiexportlayers
from . import uiexportlayers
class ExportLayersExtension(krita.Extension):
......@@ -20,7 +20,7 @@ class ExportLayersExtension(krita.Extension):
def setup(self):
pass
def createActions(self, window):
action = window.createAction("export_layers", "Export Layers")
action.setToolTip("Plugin to export layers from a document")
......
......@@ -3,7 +3,7 @@ Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=exportlayers
X-Krita-Manual=Manual.html
X-Python-2-Compatible=false
X-Python-2-Compatible=true
Name=Export Layers
Name[ar]=تصدير الطّبقات
Name[ca]=Exportació de capes
......
......@@ -9,7 +9,7 @@ You can copy, modify, distribute and perform the work, even for commercial purpo
https://creativecommons.org/publicdomain/zero/1.0/legalcode
'''
from exportlayers import exportlayersdialog
from . import exportlayersdialog
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QFormLayout, QListWidget, QHBoxLayout,
QDialogButtonBox, QVBoxLayout, QFrame,
......@@ -127,17 +127,20 @@ class UIExportLayers(object):
self.msgBox.exec_()
def mkdir(self, directory):
target_directory = self.directoryTextField.text() + directory
if os.path.exists(target_directory) and os.path.isdir(target_directory):
return
try:
os.makedirs(self.directoryTextField.text() + directory)
os.makedirs(target_directory)
except OSError as e:
if e.errno != errno.EEXIST:
raise
raise e
def export(self, document):
Application.setBatchmode(self.batchmodeCheckBox.isChecked())
documentName = document.fileName() if document.fileName() else 'Untitled'
fileName, extension = str(documentName).rsplit('/', maxsplit=1)[-1].split('.', maxsplit=1)
fileName, extension = os.path.splitext(os.path.basename(documentName))
self.mkdir('/' + fileName)
self._exportLayers(document.rootNode(), self.formatsComboBox.currentText(), '/' + fileName)
......@@ -150,7 +153,7 @@ class UIExportLayers(object):
for node in parentNode.childNodes():
newDir = ''
if node.type() == 'grouplayer':
newDir = parentDir + '/' + node.name()
newDir = os.path.join(parentDir, node.name())
self.mkdir(newDir)
elif not self.exportFilterLayersCheckBox.isChecked() and 'filter' in node.type():
continue
......@@ -164,8 +167,9 @@ class UIExportLayers(object):
elif '[png]' in nodeName:
_fileFormat = 'png'
layerFileName = '{0}{1}/{2}.{3}'.format(self.directoryTextField.text(), parentDir, node.name(), _fileFormat)
teste = node.save(layerFileName, self.xResSpinBox.value(), self.yResSpinBox.value())
layerFileName = '{0}{1}/{2}.{3}'.format(self.directoryTextField.text(),
parentDir, node.name(), _fileFormat)
node.save(layerFileName, self.xResSpinBox.value(), self.yResSpinBox.value())
if node.childNodes():
self._exportLayers(node, fileFormat, newDir)
......
......@@ -11,7 +11,7 @@ https://creativecommons.org/publicdomain/zero/1.0/legalcode
'''
from PyQt5.QtCore import QAbstractItemModel, QFile, QIODevice, QModelIndex, Qt
from PyQt5.QtWidgets import QApplication, QTreeView
from filtermanager.components import filtermanagertreeitem
from . import filtermanagertreeitem
from PyQt5.QtGui import QPixmap
......
......@@ -10,7 +10,7 @@ You can copy, modify, distribute and perform the work, even for commercial purpo
https://creativecommons.org/publicdomain/zero/1.0/legalcode
'''
import krita
from filtermanager import uifiltermanager
from . import uifiltermanager
class FilterManagerExtension(krita.Extension):
......@@ -20,7 +20,7 @@ class FilterManagerExtension(krita.Extension):
def setup(self):
pass
def createActions(self, window):
action = window.createAction("filter_manager", "Filter Manager")
action.setToolTip("Plugin to filters management")
......
......@@ -2,7 +2,7 @@
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=filtermanager
X-Python-2-Compatible=false
X-Python-2-Compatible=true
X-Krita-Manual=Manual.html
Name=Filter Manager
Name[ar]=مدير المرشّحات
......
......@@ -9,8 +9,8 @@ You can copy, modify, distribute and perform the work, even for commercial purpo
https://creativecommons.org/publicdomain/zero/1.0/legalcode
'''
from filtermanager import filtermanagerdialog
from filtermanager.components import (filtercombobox, filtermanagertreemodel)
from . import filtermanagerdialog
from .components import (filtercombobox, filtermanagertreemodel)
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QFormLayout, QAbstractItemView, QDialogButtonBox,