Hi
Few days ago I was a little bit irritated to read a comment concerning the difficulties -or impossibility- to use external libraries (particularly from pip) in Krita’s scripting.
And especially, the impression given by these words that the basic problem was a shameful choice on the part of the developers to privilege the current architecture (ie: publish Krita through appimage for Linux instead of usual distro package).
Maybe my english level doesn’t help and I misinterpreted the comment…
But ok, can’t stay with this without doing anything.
So, as I prefer use appimage because I think that’s the simplest way to ensure that:
- All dependencies are up to date AND fully compatible with Krita
- Krita is always up-to-date
- Krita is available on all Linux without being dependent of distro specific package manager (rpm, apt, yum, … impossible to enumerate all and impossible for Krita’s team to build package for all possible package manager)
I took a look about how to execute pip within Krita’s appimage environment.
For curious, here results of my investigation.
Krita AppImage
In scripter, you can execute this
from PyQt5.Qt import *
import sys
import os
import runpy
import re
def pipInstallPath():
"""Return pip lib path
Eventually:
- create directory if not exist
- add it to sys.path
"""
returned=os.path.join(QStandardPaths.writableLocation(QStandardPaths.AppDataLocation), 'pykrita', 'piplib')
if not os.path.isdir(returned):
os.makedirs(returned)
if not returned in sys.path:
sys.path.append(returned)
return returned
def pip(param):
"""Execute pip
Given `param` is a list of pip command line parameters
Example:
To execute:
"python -m pip install numpy"
Call function:
pip(["install", "numpy"])
"""
def exitFct(exitCode):
return exitCode
pipLibPath=pipInstallPath()
# keep pointer to original values
sysArgv=sys.argv
sysExit=sys.exit
# replace exit function to be sure that pip won't stop script
sys.exit=exitFct
# prepare arguments for pip module
sys.argv=["pip"] + param
sys.argv.append(f'--target={pipLibPath}')
#print("pip command:", sys.argv)
runpy.run_module("pip", run_name='__main__')
sys.exit=sysExit
sys.argv=sysArgv
def checkPipLib(libNames):
"""Import a library installed from pip (example: numpy)
If library doesn't exists, do pip installation
"""
pipLibPath=pipInstallPath()
if isinstance(libNames, list) or isinstance(libNames, tuple):
installList=[]
for libName in libNames:
if isinstance(libName, dict):
libNameCheck=list(libName.keys())[0]
libInstall=libName[libNameCheck]
elif isinstance(libName, str):
libNameCheck=libName
libInstall=libName
try:
# try to import module
print("Try to load", libNameCheck)
__import__(libNameCheck)
print("Ok!")
except Exception as e:
print("Failed", str(e))
installList.append(libInstall)
if len(libInstall)>0:
pip(["install"]+installList)
elif isinstance(libNames, str):
checkPipLib([libNames])
print('-------------------------------------------------------------------------')
checkPipLib([
{"PIL": "Pillow"}, # module name (PIL) != pip installation name (Pillow)
"numpy" # module name = pip installation name
])
try:
import numpy
print("Numpy version", numpy.version.version)
except Exception as e:
print("Can't import numpy", str(e))
try:
import PIL
print("PIL version", PIL.__version__)
except Exception as e:
print("Can't import PIL", str(e))
print('-------------------------------------------------------------------------')
Results:
-
print("Numpy version", numpy.version.version)
Numpy version 1.20.2 -
print("PIL version", PIL.__version__)
PIL version 8.2.0
Modules are installed and ready to be used
(note: I didn’t tested them yet, but I consider that if module is installed and loaded, that should be ok)
Scripter is interesting but it’s better to proceed installation of modules from a Plugin (finally it’s the target)
Provided code can easily be copied/pasted in a plugin:
- If a required module is not installed, it will be installed at Krita’s startup (can be long according to your internet connection speed)
- If a required module is already installed, installation will be just ignored
- Modules installed from pip are located in
~/.local/share/krita/pykrita/piplib
directory (so, it’s local to user’s Krita installation)
Krita for Windows
Ok for windows things are more tricky… I don’t why, but in appimage the pip module is available by default, but not with windows… (problem of license??)
Note: that my installed Python3 environment on windows (not related with Krita) don’t have pip module too!
So it need a little bit more of work…
First, I had to install pip on my Python3 environment
Once installed, I made a copy/paste of site-package
directory (from my Python installation) to krita’s python
directory.
Now, in scripter it doesn’t work: the only way I found to use pip is through a Plugin executed at Krita’s startup:
(yes, on Windows I still have a 4.4.2

And then in scripter:
Conclusion
It’s possible to install properly pip modules for Krita
- For appimage it’s easy, as everything is already available
- For Windows, I don’t know: technically, it might be possible to zip binaries and provide a plugin ready to use that will install everything without complex action for user (clearly, what I did can’t be asked for a basic user) but I’m wondering about licenses (even under an open source license, providing pip windows binaries might be more complex than just providing source code…
)
Maybe some additional work have to be made to let the provided code being more generic and easier to use for plugins, but curently I don’t have time.
I keep this for the day I’ll need to use a pip library for a plugin…
Concerning pip module, maybe developpers can tell us more about why this library is available for appimage but has been excluded from windows binaries.
I’m curious
Grum999