Hoe een Python plug-in te maken in Krita¶
Wellicht heeft u enkele leuke scripts die u heeft geschreven in de Scripter Python runner, en misschien wilt u er meer mee doen, bijvoorbeeld het automatisch laten uitvoeren. Uw script wrappen in een plugin zal u veel meer flexibiliteit en kracht geven dan als u het vanuit de Scripter editor opstart.
Okay, zelfs als u redelijk bekent bent met python, dan zijn er toch enkele kleine details waar u voor moet zorgen voordat Krita een python plugin zal herkennen. Daarom geeft deze pagina een overzicht voor hoe u de verschillende typen python script voor Krita creëert.
Deze mini-tutorials zijn geschreven voor mensen met een basis kennis van python, en zijn op een dergelijke manier geschreven dat ze aanmoedigen om te experimenteren en niet om gewoon de code alleen maar te kopiëren en te plakken, lees daarom de tekst nauwkeurig.
Zorgen dat Krita uw plug-in herkent¶
Een script in Krita heeft twee componenten – de script map (waarin uw script’s Python bestanden staan) en een “.desktop” bestand wat Krita gebruikt om uw script te laden en te registreren. Om Krita uw script te kunnen laden moeten ze allebei in de submap file:pykrita in de hulpbronmap van Krita geplaatst zijn (Lees Hulpdatabeheer voor de locatie bij elk besturingssysteem). Om uw hulpbronmap te vinden, start u Krita en opent u het menuitem . Dit opent een dialoogvenster. Klik op de knop Map met hulpbronnen openen. Er zou nu een dialoogvenster voor bestandsbeheer moeten openen met daarin de hulpbronmap van Krita. Zie de API docs onder “Auto starting scripts”. Als er nog geen pykrita
submap in de hulpbronmap van Krita aanwezig is, gebruik dan uw bestandsbeheerder om er een te creëren.
Scripts worden geïdentificeerd door een bestand dat eindigt met een ´´.desktop´´- extensie die informatie bevat over het script zelf.
Voor elke juiste plug-in is het nodig dat u een map aanmaakt en een .desktop-bestand.
Het desktop-bestand zou er als volgt uit moeten zien:
[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=meinplugin
X-Python-2-Compatible=false
X-Krita-Manual=meinPluginHandleiding.html
Name=Mijn eigen Plugin
Comment=Onze heel eigen plugin.
- Type
Dit zou altijd service moeten zijn.
- ServiceTypes
Dit zou altijd
Krita/PythonPlugin
moeten zijn voor python plugins.- X-KDE-Library
Dit zou de naam moeten zijn van de plugin-map die u net heeft gecreëerd.
- X-Python-2-Compatible
Of het compatibel is met python 2. Als Krita is gebouwd met python 2 in plaats van met 3 (
-DENABLE_PYTHON_2=ON
in de cmake configuratie), dan zou deze plugin in de lijst niet zichtbaar moeten zijn.- X-Krita-Manual
Een optionele waarde die wijst naar de handleiding. Deze is zichtbaar in de Python Plugin manager. Als het een HTML-bestand is, dan is het zichtbaar als rich text <https://doc.qt.io/qt-5/richtext-html-subset.html>`_, als dat niet het geval is, dan is het zichtbaar als gewone tekst.
- Naam
De naam die zichtbaar zal zijn in de Python Plugin Manager.
- Comment
De omschrijving die zichtbaar is in de Python Plugin Manager.
Krita python plugins moeten python modules zijn, zorg er daarom voor dat er een __init__.py
script aanwezig is, waarin iets voorkomt als…
from .meinplugin import *
Waar mijnplugin de naam is van het hoofd-bestand van uw plugin. Als u Krita opnieuw start, dan zou het nu in de Python Plugin Manager in de instellingen zichtbaar moeten zijn, maar het zal grijs zijn, omdat er geen mijnplugin.py aanwezig is. Als u met de muis boven de gedeactiveerde plugin zweeft, dan kunt de foutmelding zien.
Notitie
U moet expliciet uw plugin inschakelen. Ga daarvoor naar de Instellingen-menu, open het dialoogvenster Krita instellen en ga naar de pagina voor de Python Plugin Manager en schakel uw plugin in.
Samenvatting¶
Samenvattend, als u een script genaamd meinplugin
wilt creëren:
- creëert u in de hulpbronmap van Krita
resources/pykrita
map een map genaamd
meinplugin
een bestand genaamd
meinplugin.desktop
- creëert u in de hulpbronmap van Krita
- in de map
meinplugin
creëert u een bestand genaamd
__init__.py
een bestand genaamd
meinplugin.py
- in de map
in het bestand:__init__.py plaatst u deze code:
from .meinplugin import *
in het desktop-bestand plaatst u deze code:
[Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=meinplugin X-Python-2-Compatible=false X-Krita-Manual=meinPluginHandleiding.html Name=Mijn eigen Plugin Comment=Onze heel eigen plugin.
schrijf uw script in het bestand file:myplugin/meinplugin.py.
Een extension creëren¶
Extensions zijn relatief eenvoudige python scripts die worden uitgevoerd bij de start van Krita. Ze worden gemaakt door de Extension class uit te breiden, en de meest eenvoudige extension ziet er als volgt uit:
from krita import *
class MyExtension(Extension):
def __init__(self, parent):
# Dit initialiseert de parent, altijd belangrijk bij het subclassing.
super().__init__(parent)
def setup(self):
pass
def createActions(self, window):
pass
# En voegt de extensie toe aan de lijst in Krita met extensies:
Krita.instance().addExtension(MyExtension(Krita.instance()))
Deze code doet natuurlijk niets. In createActions voegen we standaard actions toe aan Krita, zodat we vanuit Hulpmiddelen menu toegang hebben tot ons script..
Laten we eerst een action creëren. We kunnen dat heel makkelijk doen met Window.createAction(). Krita zal createActions aanroepen voor elke Window die is gecreëerd en het juiste window object dat we moeten gebruiken doorgeven.
Dus…
def createActions(self, window):
action = window.createAction("meinActie", "Mein Script", "tools/scripts")
- “meinActie”
Dit zal worden vervangen door een uniek ID dat Krita zal gebruiken om de actie te vinden.
- “Mein Script”
Dit is wat u ziet in het Menu Hulpmiddelen.
Als u nu Krita opnieuw start, dan zal u een action hebben genaamd “Mein Script”. Dat doet nog steeds niets, omdat we het nog niet verbonden hebben met een script.
Laten we daarom een eenvoudig document-export script maken. Voeg het volgende toe aan de extension class, zorg er voor dat het boven de plek waar u de extension aan Krita toevoegde:
def exportDocument(self):
# Haal het document op:
doc = Krita.instance().activeDocument()
# Het opslaan van een niet bestaand document veroorzaakt crashes, daarom controleren dat eerst.
if doc is not None:
# Dit roept aan het save dialog. Het save dialog geeft een tuple.
fileName = QFileDialog.getSaveFileName()[0]
# En exporteer het document naar de fileName locatie.
# InfoObject is een woordenboek met specifieke export opties, maar als we een lege creëren dan zal Krita de export defaults gebruiken.
doc.exportImage(fileName, InfoObject())
En voeg de import toe voor de QFileDialog van hierboven met de imports:
from krita import *
from PyQt5.QtWidgets import QFileDialog
Om dan de action met het nieuwe export document te verbinden:
def createActions(self, window):
action = window.createAction("meinActie", "Mein Script")
action.triggered.connect(self.exportDocument)
Dit is een voorbeeld van een signal/slot connection, wat Qt-programma’s zoals Krita veel gebruiken. We zullen later meer vertellen over hoe we onze eigen signals en slots creëren.
Start Krita opnieuw op en uw nieuwe action zou nu het document moeten exporteren.
Instelbare sneltoetsen creëren¶
Nu is uw nieuwe action nog niet te zien in
.Om verschillende redenen voegt Krita alleen actions toe aan de Instellingen van sneltoetsen als ze aanwezig zijn in een .action
-bestand. Het action-bestand waar we onze action vandaan krijgen om het toe te voegen aan de sneltoetsen, zou er als volgt uit moeten zien:
<?xml version="1.0" encoding="UTF-8"?>
<ActionCollection version="2" name="Scripts">
<Actions category="Scripts">
<text>Mein Scripts</text>
<Action name="myAction">
<icon></icon>
<text>Mein Script</text>
<whatsThis></whatsThis>
<toolTip></toolTip>
<iconText></iconText>
<activationFlags>10000</activationFlags>
<activationConditions>0</activationConditions>
<shortcut>ctrl+alt+shift+p</shortcut>
<isCheckable>false</isCheckable>
<statusTip></statusTip>
</Action>
</Actions>
</ActionCollection>
- <text>Mein Scripts</text>
Dit creëert onder scripts een een subcategorie genaamd “Mein Scripts” waaraan uw sneltoetsen worden toegevoegd.
- name
Dit zou het unieke ID moeten zijn dat u maakte voor uw action bij het creëren daarvan in de setup van de extension.
- icon
De naam van een mogelijk icoon. Deze is wordt alleen gebruikt bij KDE plasma, omdat Gnome en Windows-gebruikers klaagden dat ze er lelijk uit zagen.
- text
De tekst die zichtbaar is in de sneltoets editor.
- whatsThis
Deze tekst wordt zichtbaar als een Qt-programma een specifieke oproep doet voor ‘what is this’, wat een help actie is.
- toolTip
Dee tool tip, dit wordt zichtbaar als u met uw muis er boven zweeft.
- iconText
Deze tekst wordt getoond bij gebruik in een werkbalk. Als voorbeeld dus, “Resize Image to New Size” zou om ruimte besparen kunnen worden afgekort tot “Resize Image”, dus dat is wat we hier plaatsen.
- activationFlags
Dit bepaalt of een action is uitgeschakeld.
- activationConditions
Dit bepaalt onder welke condities het geactiveerd kan worden (b.v. activeer het alleen als de selectie bewerkbaar is). Zie de code voor voorbeelden.
- shortcut
Standaard sneltoets.
- isCheckable
Of het een keuzevakje is.
- statusTip
De status tip wordt getoond op de statusbalk.
Sla dit bestand op als meinplugin.action
waar meinplugin de naam is van uw plugin. Het action-bestand zou moeten worden opgeslagen, niet in de map pykrita, maar in de map genaamd “actions”. (Dus, share/pykrita
is waar de python plugins en desktop bestanden gaan, en share/actions
is waar de action bestanden gaan). Start Krita opnieuw op. De sneltoets zou nu zichtbaar moeten zijn in de lijst met sneltoetsen.
Een dialoogvenster creëren¶
De creatie van een dialoogvenster lijkt er op de creatie van een extension. Dialoogvensters zijn op een bepaalde manier een beetje makkelijker, maar ze hebben ook meer het gebruik van widgets nodig. Dit is de code voor een barebone dialoogvenster:
from PyQt5.QtWidgets import *
from krita import *
class MeinDocker(DockWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Mein Docker")
def canvasChanged(self, canvas):
pass
Krita.instance().addDockWidgetFactory(DockWidgetFactory("meinDocker", DockWidgetFactoryBase.DockRight, MeinDocker))
De window title bepaalt hoe het verschijnt in de docker lijst van Krita. canvasChanged moet altijd aanwezig zijn, maar als u er niets mee te doen heeft, dan vult u gewoon in ‘pass’.
Voor de addDockWidgetFactory…
- “meinDocker”
Vervang dit door een unieke ID voor uw docker zodat Krita het altijd kan aanroepen.
- DockWidgetFactoryBase.DockRight
De locatie. Dit kan zijn
DockTornOff
,DockTop
,DockBottom
,DockRight
,DockLeft
, ofDockMinimized
- MeinDocker
Vervang dit door de class name van de docker die u wilt toevoegen.
Als we onze export document functie die we in de extension sectie hebben gecreëerd toevoegen aan deze docker code, hoe kunnen de gebruiker het laaten activeren? Eerst moeten we wat Qt GUI programmeren: We gaan een knop toevoegen!
Krita gebruikt standaard PyQt, maar de documentatie daarvan is vrij slecht, voornamelijk omdat de normale Qt documentatie zo goed is, en u zal vaak merken dat de PyQt documentatie van een een class, bijvoorbeeld , QWidget is een rare kopie lijkt van de normale Qt documentatie voor die class.
Hoe dan ook, het eerste wat we moeten doen is dat we een QWidget
creëren, het is niet erg ingewikkeld, voeg onder setWindowTitle
toe:
mainWidget = QWidget(self)
self.setWidget(mainWidget)
Dan gaan we een knop creëren:
buttonExportDocument = QPushButton("Export Document", mainWidget)
Om nu de knop met onze functie te verbinden, moeten we een kijkje nemen in de documentatie voor de signals. QPushButton heeft geen unieke signals van zichzelf, maar er staat dat het 4 signals erft van QAbstractButton, wat betekent dat we die ook kunnen gebruiken. In ons geval willen we clicked.
buttonExportDocument.clicked.connect(self.exportDocument)
Als we nu Krita opnieuw starten, dan hebben we een nieuwe docker en in die docker is er een knop. Als we op die knop klikken dan roepen we de export export functie aan.
Maar, de knop lijkt een beetje raar uitgelijnd. Dat is omdat onze mainWidget
geen layout heeft. Laten we er snel eentje maken:
mainWidget.setLayout(QVBoxLayout())
mainWidget.layout().addWidget(buttonExportDocument)
Qt heeft meerdere layouts, maar de QHBoxLayout en de QVBoxLayout zijn het makkelijkst om te gebruiken, ze lijnen de widgets alleen maar horizontaal of verticaal uit.
Start Krita opnieuw op en de knop zou nu netjes uitgelijnd moeten zijn.
PyQt Signals en Slots¶
We hebben al gebruik gemaakt van PyQt signals en slots, maar er zijn momenten dat we onze eigen signals en slots willen creëren. Omdat de documentatie van PyQt vrij moeilijk te begrijpen is, en de manier waarop signals en slots worden gecreëerd heel erg afwijkt van bij C++ Qt, gaan we het hier uitleggen:
Alle python functies die u in PyQt maakt, kunnen worden begrepen als slots, wat inhoud dat ze kunnen worden verbonden aan signals zoals Action.triggered
of QPushButton.clicked
. Echter, QCheckBox
heeft een signal voor omgeschakeld, wat een boolean zendt. Hoe zorgen we ervoor dat onze functie die boolean accepteert?
Zorg er eerst voor dat u de juiste import heeft voor aangepaste slots:
from PyQt5.QtCore import pyqtSlot
(Als er al een from PyQt5.QtCore import *
aanwezig is in de lijst met imports, dan hoeft u dit natuurlijk niet te doen.)
U moet dan een PyQt slot definitie (eerder dan uw) voor uw functie toevoegen:
@pyqtSlot(bool)
def myFunction(self, enabled):
enabledString = "disabled"
if (enabled == True):
enabledString = "enabled"
print("The checkbox is"+enabledString)
Als u dan uw checkbox heeft gecreëerd, dan kunt u zoiets doen als myCheckbox.toggled.connect(self.myFunction).
Om op dezelfde manier uw eigen PyQt signals te maken, doet u het volgende:
# signal name is added to the member variables of the class
signal_name = pyqtSignal(bool, name='signalName')
def emitMySignal(self):
# And this is how you trigger the signal to be emitted.
self.signal_name.emit(True)
En gebruik de juiste import:
from PyQt5.QtCore import pyqtSignal
Om voor objecten die geen standaard python objecten zijn, slots te creëren of te verzenden, hoeft u hun namen alleen maar tussen aanhalingstekens (’) te plaatsen.
Een opmerking over unit tests¶
Als u unit tests voor uw plugin wilt schrijven, kijk dan eens naar de mock krita module.
Conclusie¶
Okay, zo dat dekt alle specifieke details wat betreft Krita voor de creatie van python plugins. Het beschrijft niet hoe u pixel data moet hanteren, of de best practices met documenten, maar als u een beetje ervaring heeft met python dan zou u in staat moeten zijn om te beginnen met de creatie van uw eigen plugins.
En zoals altijd, bestudeer de code nauwkeurig en lees de API docs voor python, Krita en Qt nauwkeurig om te ontdekken wat mogelijk is, en u zou een heel eind moeten komen.