Python Scripting
Open, WishlistPublic

Description

"Python is the standard scripting language in graphics world, both for free software as for proprietary applications. Except Photoshop, which has... Javascript. So, for Krita we really want Python scripting! And done right, because we used to have Javascript-based scripting and cross-language-based scripting. But we never had a properly define scripting interface!

In the first place, we want scripts to have access to windows and views, the open images, the layer organization and definition, loading and saving. We want to make it possible to add dialogs and dockers. It should be possible to write color selectors using Python, or manage resources and bundles.

At first, we do not yet intend to give access to layers and masks at the pixel level, but it should be possible to save parts of layers, blend layers together, and modify layer properties such as visibility. But if there's time... Then we'll make it possible to write filters and generators, too."

Implies:

  • No editor or debugger
  • No safety... we'll need to have a nice default answer for this in the FAQ.
  • Access to layer-nodes but not pixel data.
  • Access to views.
  • Access to GUI.
  • Access to resources.
  • Access to KoColor and palls.

Gotchas:

  • We want it possible to run ad-hoc scripts and create plugins in Python
  • The plugins need to be translatable
  • We need a way to automatically set the python path to our extensions

Notes:

  • This is a 10.000 euro stretcher.
  • Only there because people claim that we have an audience for it. Let them prove it.
  • Will take a long time to implement.

Details

Differential Revisions
D9582: Change Python libraries install location
D9238: Refactor PyKrita plugin
D7568: Improve Last Documents Plugin
D7470: BugFix - Fixed excessive memory consumption on the application startup (Last Documents Docker)
D7278: Implement Ten Scripts Plugin
D6909: Last Ten Documents Thumbnails Plugin
D6861: Fix and Improve Ten Brushes Plugin
D6755: Do not dynamically load PYTHON_LIBRARY on Windows
D6747: Implement tools to DocumentTools
D6622: Export All Layers (Plugin) - It needs refactoring
D6385: Improve Filter Manager Plugin
D6374: Implement Filter Applier plugin
D6232: Implement Canvas Size Plugin
D6210: Setup to new plugins - Canvas Size and Filter Manager
D6187: Color Space Plugin - Refactoring
D6158: Color Space Plugin
D6057: Implement ClearAction to Scripter
D5998: Improve run action inside Scripter plugin
D5987: add basic transform operators to the pyKrita objects Document and Node
D5982: Implement All non-GUI tests to Python Scripting API
D5967: Implement all tests from Action Class to Python API
D5954: Code refactoring of the python unit tests(make test)
D5675: Initial structure of the cmake tests integration with python unit tests
D5427: Add mimetype setting to libkis to allow export via python.
D4973: BugFix for setColorSpace
D4852: QSettings working now
D4715: Better look and feel for the GUI of the Scripter - Suggestions maded by scottyp
D4666: Icons in the python scripter
D4627: Now is not necessary save a file to execute
D4303: BugFix - Debugger Feature
D4270: BugFix - Error when import of the krita is make
D4231: Showing locals and globals variables in a table
D4136: Bugs Fixes from D4097 and D3659
D4097: Debugger Feature
D3659: New and Save actions added
D3631: Action - Open File
D3613: Python Scripter Refactoring - 2
D3592: Python Scripter Refactoring
D3492: Font Families for the python editor
D3445: It changes Syntax Styles in runtime in the settings Dialog
D3410: Python Scripting - Ctrl+Wheel to zoom in and zoom out the text.
D3389: Python Scripting - Line Number Area and Highlighter to current line
Commits
D11247 / R37:a994cb46acbe: - Core refactoring to support multiple parents for actions - All methods…
D4616 / R37:70b950a2e616: Implementation of a better feedback in the GUI

Related Objects

There are a very large number of changes, so older changes are hidden. Show Older Changes
rempt added a comment.EditedFeb 15 2017, 1:33 PM

Scripting in Krita

Since early 2016, I've been working on the scripting functionality for Krita. I have tested a number of approaches. This report will describe the various ways we have implemented scripting before in Krita, the approaches I have tried, the final approach chosen. A number of notes on future work are added.

Earlier Scripting Plugins in Krita

Krita 1.4: kiskjsembed

The Krita 1.4 scripting plugin can be found in calligra-history/krita/plugins/kiskjsembed. It was started in 2004. This plugin was based on the kjs javascript interpreter (https://api.kde.org/frameworks/kjs/html/index.html, https://en.wikipedia.org/wiki/KJS_(software) ). The kiskjsembed plugin offered a javascript api with access to both functions and objects. When the plugin was abandoned, only a KisPaintDevice wrapper without functionality and a KisMainWindow wrapper that could raise, lower and close a window were implemented.

The functions were implemented as a static objects:

  • MainWindow
    • MainWindow.raise() : this function raises the main window
    • MainWindow.lower() : this function lowers the main window
    • MainWindow.close() : this function closes the main window

The objects could be created from Javascript:

Dynamic objects are objects that can be created.

  • PaintDevice

That was all.

Krita 1.5: Kross

Starting with Krita 1.5, the kiskjsembed plugin was replaced with the 'scripting' plugin. The Krita 1.5 scripting plugin can be found in calligra-history/krita/plugins/viewplugins/scripting. The plugin was retired because of lack of maintenance with the Calligra 2.5 release.

Based on the kross framework (https://api.kde.org/frameworks/kross/html/index.html, https://techbase.kde.org/Development/Tutorials/Kross/Introduction) which itself is now on life support, the scripting module provided one generic api definition that could be used from any language that was bound to the kross library. Kross also provided a generic way to create e.g. dialogs. Kross makes heavily use of QObject introspection, dynamically building classes from QObject-based wrapper classes.

The scripting plugin was loaded for every view/window. Scripts were available from a "Scripts Manager" docker. It was not possible to write actual plugins in Krita that would get loaded on startup.

The scripting api was implemented in the kritacore wrapper library. The wrapper library implemented the following classes:

  • Brush : a brush object
  • Color : represents a color
  • ConstIterator : an iterator for walking over the pixels of an image or layer
  • ConstPaintDevice : a layer within the image where you are able to perform paint operations on
  • Filter : access to the filter plugins in Krita; not the base class for new filters.
  • Histogram : gives access to the histogram of a paintdevice object
  • Image : an image object with some methods to manipulate the image, get some information and create new layers.
  • Iterator: an iterator that allows changing pixels. Pixel values where accessed by calling setPixel on the iterator.
  • Module: based on KoScriptingModule, provides static access to things like the active layer.
  • Monitor : was meant to check whether iterators need to be invalidated, seems unused.
  • PaintDevice : represents a layer that can be painted on
  • Painter: abstracts KisPainter and makes it possible to perform high-level painting on a paintdevice
  • PaintLayer : represents a layer that can be painted on (there seems to be some confusion with PaintDevice, dox-wise)
  • Pattern : wraps a pattern object. When passed to Painter::setPattern, the Painter class retrieves the actual pattern object from the wrapper. The script itself cannot get at the pattern, or its image.
  • Progress: makes it possible to display a progressbar in Krita
  • Wavelet: a fast wavelet object

Apart from these classes there were two helper classes. For the first there is no usage example.

  • KisScriptDecoration::KisCanvasDecoration : to implement canvas decorations like assistants, grids, rulers, guides.
  • KisScriptDockfactory: called to create new dockwidgets

A sample script:

~~~~
require "Krita"

fetch the image.

image = Krita.image()

countStar = 60
maxStarSize = 4

we like to manipulate the active painting layer.

layer = image.createPaintLayer("Sky", 255).paintDevice()

get the height and the width the image has.

width = image.width()
height = image.height()

start drawing

layer.beginPainting("sky")

painter = layer.createPainter()

painter.setStrokeStyle(1)
painter.setFillStyle(1)
painter.setPaintOp("paintbrush")

painter.setPaintColor( Krita.createRGBColor(24,24,24) )
painter.paintRect(0.0,0.0, width, height, 0.5)

painter.setPaintColor( Krita.createRGBColor(255,255,255) )
for i in 1..countStar

size = rand() * maxStarSize
size = 1 if(size < 1)
painter.setBrush(Krita.createCircleBrush(size,size, size / 2 +1, size / 2 +1) )
painter.paintAt(rand() * width, rand() * height, 0.5)

end

painting is done now.

layer.endPainting()
~~~~

Which scripts were loaded by the scripting docker was determined by a single file, scripts.rc that contained a short title, comment, name, type of interpreter and the single file that contained the script. Scripts could not consist of more than one file, as far as I can tell.

PyKrita

In the summer of 2016, another scripting plugin was started. This was based on Kate's "pate" plugin. This plugin makes it possible to load plugins written in a scripting language on startup. These plugins integrate with the application's main instance. Based on Pate, the plugin uses sip, PyQt, Python2 or Python3, a wrapper library and hand-written SIP wrapper files. The plugin has never been released, but has been used as a basis for further research.

Other Applications

Photoshop

Photoshop offers one single API in three languages: javascript, vbscript, applescript. There is, of course, no way to figure out how the scripting facility is bound to the application's functionality.

http://www.adobe.com/devnet/photoshop/scripting.html

When designing various API's for Krita's scripting module, the Photoshop API, which offers a hierarchy of objects acessible starting with the Application object, was used as an example. Scripts are available in the File/Scripts menu and it is possible to create scripts that are run on startup.

The object model starts with the Application object, which gives access to the Document object, which gives access to the LayerSet object, which contains the layers -- and so on.

GIMP

GIMP's scripting facility is based on the libgimp library. Gimp can be scripted in scheme and python: https://docs.gimp.org/en/gimp-scripting.html . The scripting system makes all functions available through the procedural database. This is exposed through the Procedure Browser, where for every function the script writer can see documentation, information about parameters, return values and additional information.

The PDB is generated at build time and doesn't seem to be created dynamically as plugins are loaded, but plugins can register and offer functionality for use in scripts.

Approaches to Scripting in Krita

QtScript

QtScript is Qt's scripting module: http://doc.qt.io/qt-5/qtscript-index.html . In Qt5, this module has been deprecated and cannot be used for new development. This is a pity because it is very complete, includes bindings to Qt and has even a complete ide-like script editor and debugger. It is well suited to extending an application with scripting, if javascript is acceptable.

QtQuick/QML

QtScript has been replaced by QJSEngine and QML. This is another javascript based environment, using a different javascript engine than QtScript. As an experiment, a Krita plugin that uses QJSEngine and QQmlEngine was written:

http://valdyas.org/~boud/jsdocker.tgz

Since there are no bindings for Qt, the GUI for plugins written with this engine should be written in QtQuick2, which is a declarative user-interface language. Exposing Krita objects works through QObject introspection.

In the end, the facilities for writing user interface controls that fit with the rest of Krita, debugging and exposing Krita functionality to the script writer were insufficient.

Python

In the VFX industry, Python is the standard scripting language. Applications like Mari are written almost entirely in Python (and glsl), Maya and other applications offer Python as a scripting language. Since Qt is
also virtually the standard toolkit in this field, the combination of Python and Qt would make Krita fit right in with the other players in the field.

The most used binding to Qt is PySide, which was created by Nokia because the long-standing PyQt bindings are only available under a dual GPL and commercial license. PySide is LGPL. However, maintenance of PySide has halted since Nokia divested itself of Qt. There is a wrapper, Qt.py that makes it possible to use the exact same syntax, no matter the underlying wrapper: https://github.com/mottosso/Qt.py/blob/master/Qt.py.

There are two approaches to exposing application functionality to Python, intersecting with two technical options:

  • wrap the core libraries (kritaimage, kritaui, etc) directly
  • create a wrapper library. the wrapper library can either be generated from a definition file, or hand-written. Gimp's libgimp is an example of the former, as is the Scribus' scripter-ng wrapper library. The kross-based krita scripting plugin was hand-written.

And either way, one can:

  • use qobject introspection
  • define the interface using a interface definition language. For PyQt that's sip. The sip files can be either written manually or generated. Automatically generating sip files means parsing C++ header files and then creating correct sip files/
QObject Introspection
Mikro

Inspired by Kross, Mikro or Mini Kross (https://wiki.scribus.net/canvas/ScripterNG), is a qobject-introspecter that automatically builds Python classes from QObject wrapper classes. As found in Scribus, the class didn't work. It turned out to be possible to make it mostly work, with Python 3 and some hacks.

The fixed class in in krita/plugins/extensions/pykrita/plugins/krita/mikro.py.

The result however was very fragile and very slow. It also turned out to be very hard to create signal/slot connections. The kind of Python that mikro generates looks "weird". The wrapper library needs to provide Q_PROPERTY definitions for getters and setters, Q_SLOTS for all callable methods and Q_SIGNALS for signals. Signals do not work with this solution; we were not able to figure out why.

Mikro would be packaged with krita.

PythonQt

PythonQt (http://pythonqt.sourceforge.net/) is a dynamic Python binding for the Qt framework. It offers an easy way to embed the Python scripting language into your C++ Qt applications. The focus of PythonQt is on embedding Python into an existing C++ application, not on writing the whole application completely in Python. PythonQt is a stable library that was developed to make the Image Processing and Visualization platform MeVisLab scriptable from Python.

We investigated PythonQt:

https://phabricator.kde.org/T3588

Since PythonQt is another QObject-introspection based solution, we would either need to create a QObject-based wrapper library, or make all relevant classes in Krita itself QObject-based. PythonQt is not generally available in Linux distributions, which makes it an objectionable dependency.

Wrapper Library
Generating SIP files at build time

Shaheed Haque and Stephen Kelly were working in 2016 on a way to generate the sip files for a KDE frameworks library. This is done using the extra-cmake-modules framework, which uses clang to parse all relevant header files and then generates the sip files. In order to "help" the clang-based parser/generator, specification files
need to be created with details on, for instance, which methods need to be skipped. The format for these files is fairly opaque.

The work is in:

https://phabricator.kde.org/source/krita/browse/rempt%252FT1625-python-scripting-ecm/

The big disadvantage of this approach is that everyone needs to have clang and python-clang installed in order to generate the bindings; the bindings would be regenerated every time Krita is built. We felt that this would make it too easy for Krita developers to not install the required dependencies, leading to them working on Krita and breaking the bindings without noticing.

Hand-written SIP files

SIP (https://riverbankcomputing.com/software/sip/intro) makes it possible to write wrappers for existing C++ headers. These wrappers can be annotated and contain custom code to make the C++ and Python memory and execution model work well together. The technology is already quite old: the first book in the topic appeared in 2001 (http://valdyas.org/python/book.html).

In the end, hand-writing and maintain SIP files for a single wrapper library turns out to be easier than maintain the specification file that the extra-cmake-modules based automatic sip wrapper generator needs. Automatic generation would be better if we would wrapp all of Krita; but we decided not to do that.

Wrap Everything

An initial investigation into wrapping all Krita's libraries proved that this would be unworkable:

  • After about twenty years of development, the API is very inconsistent
  • There are many C++ constructs used that are very hard to wrap
  • Automatic wrapping at runtime would be the only feasible way to wrap the about 2600 header files in Krita's 20 or so libraries.

But most importantly, Krita's libraries provide an internal API. Krita's plugins are all in-tree. Wrapping this API and making it available to script authors fixes the API once and for all time, depriving ourselves of the flexibility we need when developing Krita.

Conclusion

We decided to go with a specialized hand-written wrapper library, libkis. We generated the initial C++ files from an API definition file, and then tweaked the result to be usable both with Mikro and SIP. The we used Shaheed and Stephens SIP generator to generate the initial set of SIP files, and tweaked those.

The libkis library consists of QObject-based classes that are all introspectable.

The final API has been closely modeled on Photoshop's Javascript API (http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/photoshop/pdfs/photoshop-cc-scripting-guide-2015.pdf).

From that point on, the libkis wrapper library and the sip files have been hand-developed in tandem. It turns out that some Krita classes needed direct wrapping: one current example is KisCubicCurve. We do expect that some more classes will be wrapped like this, with the resource classes for patterns, brushes and so on being good candidates. Those have had a stable API for a long time, and spending time to manually write a C++ wrapper class for each resource type could be seen as a waste of time.

Currently, it is possible to run ad-hoc scripts in the scripter mini-IDE, mostly written by Eliakin Almeida, add dock widgets to the user interface, add scripts to the tools/scripts menu and, with hackery, to other menus. Plugin scripts, that are loaded on startup, can be enabled and disabled in the Settings window.

In addition, there is a separate executable, kritarunner, that can run scripts from the command-line. This might
be integrated as just another commandline option in the main krita executable (because you cannot have two executables in a Linux appimage).

API

The current scripting module provides the following objects:

  • Action : encapsulates a QAction
  • Canvas : provides access to the canvas, for rotation, zoom etc.
  • Channel : represents a single channel in a Node
  • DockWidgetFactoryBase : baseclass for scripts that want to add a docker to every window
  • DockWidget : base class for dockers implemented as a script.
  • Document : represents the combination of a KisDocument and KisImage (those are split in the internal API for internal reasons, the image contains the image data, the document the filename).
  • Filter : encapsulates a Krita filter. Cannot be used to implement new filters, as in the kross-based scripting plugin
  • Generator : encapsulates a Krita generator. Same restrictions as for Filter hold
  • InfoObject : a key/value store
  • KisCubicCurve : represents the values for a cubic curve
  • Krita : the root class, accessible as Krita.instance() or under the Application and Scripter aliases
  • Node : a node, that is a layer or mask
  • Notifier : emits signals when the application state changes.
  • Resource : a generic wrapper for resources like patterns, gradients or brush tips.
  • Selection : represents a selection of pixels
  • ViewExtension : represents a plugin that can be added to the Krita window. Can be re-implemented by a plugin script.
  • View : a view on an image in a window
  • Window : a single main window

Additional useful classes would be 'Histogram', 'Transformation' and others.

A sample script looks like this:

~~~~

  1. Tests the PyKrita API #

import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from krita import *

def main(args):

print("Arguments:", args)
Application.setBatchmode(True)
print("Batchmode: ", Application.batchmode())
print("Profiles:", Application.profiles("GRAYA", "U16"));
document = Application.openDocument(args[0])
print("Opened", document.fileName(), "WxH", document.width(), document.height(), "resolution", document.xRes(), document.yRes(), "in ppi", document.resolution())
node = document.rootNode()
print("Root", node.name(), "opacity", node.opacity())
for child in node.childNodes():
    print("\tChild", child.name(), "opacity", node.opacity(), node.blendingMode())
    #r = child.save(child.name() + ".png", document.xRes(), document.yRes());
    #print("Saving result:", r)
    for channel in child.channels():
        print("Channel", channel.name(), "contents:", len(channel.pixelData(node.bounds())))

document.close()

document = Application.createDocument(100, 100, "test", "GRAYA", "U16", "")
document.setBatchmode(True)
document.saveAs("test.kra")

~~~~

Maintenance

The first scripting plugin for Krita was never really finished; the second one bit-rotted because nobody used it and because it was too easy to break it while developing if one hadn't installed the optional kross runtimes.

This time, we propose to make sip, python and pyqt mandatory dependencies, on Linux at least, where most developers are. The libkis wrapper library is already always built.

Future Work

libkis is a generic wrapper

Libkis is a generic, QObject-based wrapper library. That means that if anyone steps up and wants to provide another scripting language, libkis can be re-used easily. QMetaObject-introspection based wrappers, like for QJSEngine, will work. Libkis is always built, even if the dependencies for the pykrita scripting module are missing.

libkis is an API for C++ plugins

Libkis is also poised to become a proper C++ API for external plugins. It still isn't intended to use libkis to to implement tools, filters, generators and brush engines as plugins. For those types of plugins, use of Krita's internal API is still needed. But most of the plugins in krita's plugins/extensions group of plugins could be rewritten against a single well-document libkis API without loss of performance.

Exposing plugin functionality dynamically

Right now, a script writer has access to all the KisAction objects and can toggle them; that will show the dialogs associated with those actions and act on the image and layer that is currently active in the user interface. It would be good to have some generic solution where plugins can not only add actions to the gui, but also methods to the scripting interface.

Something like gimp's pdb, without the generation step. It should be possible for plugins, and maybe some internal functionality, to register a method or a runner object in a central registry that describes the method, the parameters and the return values, which can then dynamically be added to the Krita object and exposed to the scripting environment.

Exposing Krita widgets

Many script and script-based plugins will need to be able to show krita-specific widgets, like color selectors, input spinboxes, resource selectors, curve widgets. Those probably need to be exposed directly, instead of through wrapper classes. We will work on that when the sample scripts we want to write are being implemented.

Notes

  • sometimes the krita process keeps running, when Python doesn't get closed
  • building on Windows has not been attempted: one attempt on OSX has been reported to be succesful
  • we need a good and flexible system to add script actions to menus and toolbars
  • we need a good set of useful sample scripts
  • the .desktop file based system for registering plugins is cumbersome
  • we need to provide unittests for the entire libkis library
  • locking while running from a script needs to be investigated
  • the script wrapper's actions are not undoable at the moment, and there is no access to the undo system yet.

Proposed Sample Scripts

  • Scripter ide/debugger
  • Palette manager
  • Ten Presets plugin: a plugin that allows the user to select 10 presets bound to ctrl-1 to ctrl-0
  • Reference image docker: a plugin that replaces the current reference images docker
woltherav added a comment.EditedMar 2 2017, 9:16 PM

To get this working I...

  1. Instaled KDE-neon 16.04, because I couldn't be bothered to build pyqt and friends for 5.6+
  2. Installed packages python3, python3-sip-dev, python3-pyqt-dev and pyqt5-dev (which also installs more sip)
  3. Use the following cmake config(it's basiclly the krita-with-cats cmake config with extra python options)
cmake -DCMAKE_INSTALL_PREFIX=$HOME/krita/inst $HOME/krita/src -DWITH_GMIC=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo -DPRODUCTSET=ALL -DPACKAGERS_BUILD=ON -DBUILD_TESTING=OFF -DKDE4_BUILD_TESTS=OFF -DPYTHON_EXECUTABLE=/usr/bin/python3 -DPYQT_SIP_DIR_OVERRIDE=/usr/share/sip/PyQt5

Make sure to delete the krita.xmlgui in the resources folder if you can't find the scripting under tools->scripting.

rempt added a comment.Apr 4 2017, 1:09 PM

Next up:

  • More unittests
  • Fix Channel (make sure that the individual channels work)
  • Fix Selection (use the selection manager instead)

But first:

  • people must test...

I just got master building with the vector + python features on Ubuntu 16.10. @woltherav extra cmake flags helped me get the SIP location pointing to the right place. I was getting the "QtCore/QtCoremod.sip can't be found" error before that.

I need to get more familiar with Python, but just playing around with the "10 brush" plugin, the shortcuts didn't appear to be doing anything when I actually pressed "Ctrl+1", etc.

So... I was attempting to make a super simple 'save to png' script, but for some reason it didn't work...

Then I realized this was because there's no such thing as a 'setOutputMimeType' in LibKis, and thus pyKrita was happily saving a kra file with a png extension(because KisDocument has no clue it should be setting the OutputMimeType to PNG.

I am hesitant to add this myself, as I am not sure if it should be added to the export section, or as a seperate function.

woltherav added a comment.EditedApr 10 2017, 4:20 PM

Behold, my beautiful attempt at a palette docker. (It's a docker with a combobox, which is as far as it can go for now...)

Some issues:

  1. I am not sure how to set the title of the docker. How should one go about this?
  2. for the resources, palette.name apparantly doesn't exist? How does this work???
    • Okay, so looking at the code, this is because the code gives a QMAP, while the docs act like it is a QLIST >:(
  3. Why does dockerwidget require a canvas changed entry? Is this a limitation of the bindings?

And of course, if I were to create a LibKis API file for the KoColorSet, how should I go about it?

And I see there's no KoColor api either. Or a way to change the current foreground color. Let alone something that can chuck the color through the displayviewer...

Not to mention that I have no clue how to actually get the signal slots stuff to work. Ah well, here's a version with some grayscale generated custom widgets.

boud, ik vind je niet aardig.

woltherav added a comment.EditedApr 13 2017, 3:30 PM

Okay, so the above is a docker with a bunch of odds and ends scripts.

General:

  • I cannot set a docker title and it annoys me to death.

Export...

  • The Document.export() doesn't set the mimetype, D5427 has a solution for this.
  • Document.resizeImage() doesn't actually resize the image proper. It seems to instead crop it. D5987 adds scale and rotate things.
  • Document.close(), while libkis does mark it to be deleted later, it doesn't actually seem to do something like it, so it gives a memory leak and also crashes Krita when using twice. Is it because I am not using the lock/unlock here?

Attaching an action to a pushbutton:

  • Despite being connected in canvasChanged(), where the canvas exists, and thus the actions exist. Despite checking if the action exists before attempting to connect it, Krita tells me the action I am attempting to connect is a nultype??? Is this a bug, or am I just not understanding python?

Switching the active brush preset...

  • No bugs here at all!
rempt added a comment.Jun 14 2017, 8:39 AM

We probably should use this:

http://pyqt.sourceforge.net/Docs/sip4/directives.html#directive-%Docstring

To add docstrings to the sip files.

woltherav added a comment.EditedJun 16 2017, 2:45 PM
  1. If you have a groupLayer, and you wish to addChild() then the API asks two nodex, the first as the node to add, and the second as the node to add it above. If your group is empty...
  1. Filter layers cannot have filters assigned to them, this makes them not very useful :(
  1. what are clone layer made of? Not as in "metal" or "sugar", but rather which layer are they referencing???
rempt added a comment.Jun 20 2017, 8:44 AM
  1. If you have a groupLayer, and you wish to addChild() then the API asks two nodex, the first as the node to add, and the second as the node to add it above. If your group is empty...

It should work to use None for the second node.

  1. Filter layers cannot have filters assigned to them, this makes them not very useful :(

We should extend the Node api with stuff like that -- Filter for filter layers/masks, the svg for vector layers.

  1. what are clone layer made of? Not as in "metal" or "sugar", but rather which layer are they referencing???

No idea... Probably another thing that we need to extend the api for.

woltherav closed subtask T586: (layer) node API as Resolved.
woltherav closed subtask T3079: KoColor api as Resolved.
woltherav added a comment.EditedJan 5 2018, 10:26 PM

Bugs open:

  • BatchMode is broken for python scripting
  • When exporting something with the comics manager, it will safe assert near constantly due to document.close() somehow not deleting documents properly in some way or another.
  • Action.setMenu() doesn't do anything.
  • newly created documents cannot be saved due to missing mimetype.
  • There is STILL not proper documentation for the document locking, not even in kis_image.h. This might not seem like much but having your python operations fail because there's no documentation on how to deal with Krita's multithreading is a HUGE BUG.
  • When opening a document with Krita.instance().openDocument(fileName), it somehow sets the new document window resolution to an a weird number. In my case I had a 72 dpi A4 I opened, and the new image dialog is set to 652x899 at 7200 dpi. The pixel dimensions are correct for a 72dpi a4, but why is openDocument saving this information?
  • The action files don't seem to be loaded for pyKrita resource folder plugins:

Missing features

  • There's no clone for document and node, despite the architecture being able to handle it.
  • There's no resource changed feedback.
rempt added a comment.Jan 6 2018, 2:37 PM

Scripter:

  • Needs a Save As option
  • Needs to have the file options in the toolbar
  • Needs to be always on top/tool window

Menus:

  • Probably needs to be done with xmlgui/.action files and propertizeAction.

I am not sure how to implement tests for Python plugins. The plugins rely on the Krita environment. Does Krita have a headless mode?

rempt added a comment.Jan 24 2018, 8:26 AM
I am not sure how to implement tests for Python plugins. The plugins rely

on the Krita environment. Does Krita have a headless mode?

Yes... But it's a bit untested. And undocumented. The pykrita plugin also
creates a kritarunner executable that can run scripts in the Krita
environment.

As for testing libkis, we do that using ordinary C++ unittests, of which there
aren't enough either...

Is there a way to access the button bitmaps from Python? For example, if I want to put an undo button in a docker can I get a copy of the undo bitmap to apply it to the button?

Is there a way to access the button bitmaps from Python? For example, if I want to put an undo button in a docker can I get a copy of the undo bitmap to apply it to the button?

Hm, probably not at this moment, but it should be easy enough to add an icon(name) method to the Krita api.

In T1625#129462, @rempt wrote:

Is there a way to access the button bitmaps from Python? For example, if I want to put an undo button in a docker can I get a copy of the undo bitmap to apply it to the button?

Hm, probably not at this moment, but it should be easy enough to add an icon(name) method to the Krita api.

Oh, wait, that's already there...

brendanbd added a comment.EditedFeb 21 2018, 11:07 AM

I think I can harvest QIcons from existing widgets. I was more wondering whether there was some sort of icon resource where I could find them more easily.
Accoding to the console if I try Krita.icon("name") it can't find it but falls back on QIcon::FromTheme("name")

That depends on the name. "edit-undo" should work; you can find the icons in krita/pics, and need to remove the dark_ or light_ prefix.

brendanbd added a comment.EditedFeb 23 2018, 12:58 AM

So, what's the story with PyQt5 - if you get the Windows build or the linux app image it's bundled into the package?
But if you build from source (and presumably for package management users) you have to have it installed as a dependency to run the scripts?

Also, how do you get Krita to tell you what the user's resource directory is?

rempt added a comment.Feb 23 2018, 8:10 AM

The windows build and the appimage come with python and pyqt; on Linux, if you build from source you'll need to install the dev packages for python3, sip and pyqt first. If you install from a repository, the packager should have setup the dependency chain. If you're on OSX or macOS, you should give me hand packaging python and pyqt in the dmg file :-)

The user's resource directory is fixed on each different OS. Settings/Manage Resources/Open Resource folder will always take you there.

"The user's resource directory is fixed on each different OS. Settings/Manage Resources/Open Resource folder will always take you there."

Yes, but I'm trying to reach the directory from a script.

rempt added a comment.EditedFeb 24 2018, 3:03 PM

Ah -- you don't need libkis for that. Use QStandardPaths and get the QStandardPaths::AppDataLocation location.

from PyQt5.QtCore import *
print(QStandardPaths.writableLocation(QStandardPaths.AppDataLocation))