Com crear un connector en Python per al Krita

És possible que tingueu alguns bons scripts escrits a l'executor Scripter de Python, però potser voleu fer més amb ell i executar-lo automàticament. Embolcallar el vostre script en un connector pot donar-vos molta més flexibilitat i executar scripts des de l'editor Scripter.

Bé, fins i tot si coneixeu el Python realment bé, hi ha alguns petits detalls perquè el Krita reconegui un connector en Python. De manera que aquesta pàgina us donarà una descripció general sobre com crear els diferents tipus de scripts en Python únics al Krita.

Aquestes mini guies d'aprenentatge estan escrites per persones amb un coneixement bàsic de Python, i de manera que fomenten l'experimentació en lloc de copiar i enganxar el codi, així que llegiu el text detingudament.

Aconseguir que el Krita reconegui el vostre connector

Un script al Krita té dos components: directori de l'script (conté els fitxers en Python per al vostre script) i un fitxer «.desktop» que el Krita fa servir per a carregar i enregistrar el vostre script. Perquè el Krita carregui el vostre script, tots dos hauran d'estar al subdirectori pykrita de la vostra carpeta de recursos del Krita (per als camins segons el sistema operatiu, vegeu Gestionar els recursos). Per a trobar la vostra carpeta de recursos, inicieu el Krita i feu clic a l'element de menú «Arranjament ‣ Gestiona els recursos...». Això obrirà un diàleg. Feu clic al botó Obre la carpeta de recursos. Aquest hauria d'obrir un gestor de fitxers en el vostre sistema amb la carpeta de recursos del Krita. Vegeu els documents de l'API sota «Auto starting scripts». Si no hi ha una subcarpeta pykrita al directori de recursos del Krita, utilitzeu el vostre gestor de fitxers per a crear-la.

Els scripts s'identifiquen per un fitxer que acaba amb una extensió .desktop, el qual conté informació sobre l'script en si.

Per tant, per a cada connector haureu de crear una carpeta i un fitxer d'escriptori.

El fitxer d'escriptori s'hauria d'assemblar al següent:

[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=myplugin
X-Python-2-Compatible=false
X-Krita-Manual=myPluginManual.html
Name=My Own Plugin
Comment=Our very own plugin.
Type

Aquest sempre haurà de ser «service» (servei).

ServiceTypes

Aquest sempre haurà de ser Krita/PythonPlugin per als connectors en Python.

X-KDE-Library

Aquest haurà de ser el nom de la carpeta del connector que acabeu de crear.

X-Python-2-Compatible

Quan és compatible amb el Python 2. Si el Krita es va construir amb el Python 2 en lloc del 3 (-DENABLE_PYTHON_2=ON a la configuració del «cmake»), llavors aquest connector no apareixerà a la llista.

X-Krita-Manual

Un valor opcional que apuntarà a l'element manual. Això es mostra al gestor de connectors en Python. Si és un fitxer HTML, es mostrarà com a text enriquit, en cas contrari, es mostrarà com a text sense format.

Name

El nom que es mostrarà al gestor de connectors en Python.

Comment

La descripció que es mostrarà al gestor de connectors en Python.

Els connectors en Python del Krita hauran de ser mòduls de Python, així que assegureu-vos que hi hagi un script __init__.py que contingui quelcom com...

from .myplugin import *

On «.myplugin» és el nom del fitxer principal del vostre connector. Si reinicieu el Krita, ara hauria de mostrar-lo a la configuració del Gestor de connectors en Python, però estarà en gris, perquè no hi ha el «myplugin.py». Si passeu per sobre dels connectors inhabilitats, veureu l'error.

Nota

Cal que habiliteu explícitament el vostre connector. Aneu al menú Arranjament, obriu el diàleg Configuració del Krita i aneu a la pàgina Gestor de connectors en Python i habiliteu el vostre connector.

Resum

En resum, si voleu crear un script anomenat myplugin:

  • en el directori resources/pykrita del Krita creareu
    • una carpeta anomenada myplugin

    • un fitxer anomenat myplugin.desktop

  • a la carpeta myplugin creareu
    • un fitxer anomenat __init__.py

    • un fitxer anomenat myplugin.py

  • al fitxer __init__.py introduïu aquest codi:

from .myplugin import *
  • al fitxer d'escriptori introduïu aquest codi:

    [Desktop Entry]
    Type=Service
    ServiceTypes=Krita/PythonPlugin
    X-KDE-Library=myplugin
    X-Python-2-Compatible=false
    Name=My Own Plugin
    Comment=Our very own plugin.
    
  • escriviu el vostre script al fitxer myplugin/myplugin.py.

Crear una extensió

Les extensions són scripts en Python relativament senzills que s'executen durant l'inici del Krita. Es fan estenent la classe «Extension», i la majoria de l'extensió en nu es veu així:

from krita import *

class MyExtension(Extension):

    def __init__(self, parent):
        # Això està inicialitzant el pare, sempre important quan és una subclasse.
        super().__init__(parent)

    def setup(self):
        pass

    def createActions(self, window):
        pass

# I s'afegeix l'extensió a la llista d'extensions del Krita:
Krita.instance().addExtension(MyExtension(Krita.instance()))

Aquest codi, per descomptat, no fa res. Normalment, a «createActions» afegim accions al Krita, de manera que podrem accedir al nostre script des del menú Eines.

Primer, crearem una acció. Podem fer-ho amb facilitat amb Window.createAction(). El Krita cridarà a «createActions» per a cada finestra que es creï i passarà l'objecte a la finestra correcte que hem d'utilitzar.

Així que...

def createActions(self, window):
    action = window.createAction("myAction", "El meu script", "tools/scripts")
«myAction»

Això haurà de ser substituït amb un ID únic que el Krita utilitzarà per a trobar l'acció.

«El meu script»

Això és el que serà visible en el El menú Eines.

Si ara reinicieu el Krita, tindreu una acció anomenada «El meu script». Encara no fa res, perquè no l'hem connectat amb un script.

Llavors, creem un senzill script d'exportació de documents. Afegim el següent a la classe «Extension», assegureu-vos que estigui a sobre d'on afegiu l'extensió al Krita:

def exportDocument(self):
    # Obtenir el document:
    doc =  Krita.instance().activeDocument()
    # El desament d'un document inexistent provoca errors, de manera que comproveu primer el document.
    if doc is not None:
        # Això crida el diàleg per a desar. El diàleg de desar retorna una tupla.
        fileName = QFileDialog.getSaveFileName()[0]
        # I exporta el document a la ubicació de «FileName».
        # «InfoObject» és un diccionari amb opcions específiques per a l'exportació, però quan es crea un buit, el Krita utilitzarà els valors predeterminats per a l'exportació.
        doc.exportImage(fileName, InfoObject())

I afegiu la importació per a «QFileDialog» a sobre amb les importacions:

from krita import *
from PyQt5.QtWidgets import QFileDialog

Després, per a connectar l'acció amb el nou document d'exportació:

def createActions(self, window):
    action = window.createAction("myAction", "El meu script")
    action.triggered.connect(self.exportDocument)

Aquest és un exemple d'una connexió de senyal/ranura, el qual utilitzen molt les aplicacions Qt com el Krita. Repassarem una mica més endavant com crear les nostres pròpies senyals i ranures.

Reinicieu el Krita i la vostra nova acció hauria d'exportar el document.

Crear dreceres de teclat configurables

Ara, la vostra nova acció no apareix a «Arranjament ‣ Configura el Krita... ‣ Dreceres de teclat».

Per diverses raons, el Krita només afegeix accions al Configuració de les dreceres quan estan presents en un fitxer .action. El fitxer d'acció perquè la nostra acció s'afegeixi a les dreceres haurà de tenir aquest aspecte:

<?xml version="1.0" encoding="UTF-8"?>
<ActionCollection version="2" name="Scripts">
    <Actions category="Scripts">
        <text>Els meus scripts</text>

        <Action name="myAction">
        <icon></icon>
        <text>El meu 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>Els meus scripts</text>

Això crearà una subcategoria sota els scripts anomenada «Els meus scripts» per a afegir-la a les vostres dreceres.

name

Aquest haurà de ser l'ID exclusiu que vàreu crear per a la vostra acció en crear-lo a l'ajust de l'extensió.

icon

El nom d'una possible icona. Aquesta només es mostrarà al Plasma del KDE, perquè els usuaris del Gnome i de Windows es van queixar que es veien lletges.

text

El text que es mostrarà a l'editor de les dreceres.

whatsThis

El text que es mostrarà quan una aplicació Qt cridi específicament a «Què és això», la qual és una acció d'ajuda.

toolTip

El consell de l'eina, apareixerà en passar per sobre.

iconText

El text que es mostrarà quan es mostri en una barra d'eines. Així, per exemple, «Resize Image to New Size» (Redimensiona la imatge a una mida nova) es podria escurçar a «Resize Image» (Redimensiona la imatge) per a estalviar espai, així que el posaríem aquí.

activationFlags

Això determina quan una acció està o no inhabilitada.

activationConditions

Això determina les condicions d'activació (p. ex., activar només quan la selecció sigui editable). Vegeu el codi per a exemples.

shortcut

Drecera predeterminada.

isCheckable

Quan es tracta o no d'una casella de selecció.

statusTip

El consell d'estat que es mostra en una barra d'estat.

Deseu aquest fitxer com a myplugin.action on myplugin serà el nom del vostre connector. Ara s'haurà de desar el fitxer d'acció, no a la carpeta de recursos «pykrita», sinó en una carpeta de recursos anomenada «actions». (De manera que a share/pykrita és on van els connectors en Python i els fitxers, i a share/actions és on van els fitxers d'acció). Torneu a iniciar el Krita. Ara hauria d'aparèixer la drecera a la llista d'accions amb drecera.

Crear un acoblador

Crear un acoblador personalitzat és molt semblant a crear una extensió. Els acobladors són, en certa manera, una mica més fàcils, però també requereixen un ús major dels ginys. Aquest és el codi nu de l'acoblador:

from PyQt5.QtWidgets import *
from krita import *

class MyDocker(DockWidget):

    def __init__(self):
        super().__init__()
        self.setWindowTitle("El meu acoblador")

    def canvasChanged(self, canvas):
        pass

Krita.instance().addDockWidgetFactory(DockWidgetFactory("myDocker", DockWidgetFactoryBase.DockRight, MyDocker))

El títol de la finestra és com apareixerà a la llista d'acobladors en el Krita. La «canvasChanged» sempre haurà d'estar present, però no heu de fer res amb això, de manera que només «pass».

Per a l'«addDockWidgetFactory»...

«myDocker»

Substituïu això amb un ID únic per al vostre acoblador que utilitza el Krita per a fer un seguiment.

DockWidgetFactoryBase.DockRight

La ubicació. Aquesta pot ser DockTornOff, DockTop, DockBottom, DockRight, DockLeft o DockMinimized

MyDocker

Substituïu això amb el nom de la classe de l'acoblador que voleu afegir.

De manera que, si afegim la nostra funció d'exportació de documents que hem creat a la secció extensió a aquest codi de l'acoblador. Com permetem que l'usuari l'activi? Primer, haurem de crear codi IGU per a les Qt: Afegim un botó!

De manera predeterminada, el Krita utilitza el PyQt, però la seva documentació és força dolenta, principalment perquè la documentació de les Qt és realment bona, i sovint trobareu que la documentació de PyQt d'una classe, diu, el QWidget és com una còpia estranya de la documentació de les Qt per a aquesta classe.

De totes maneres, el primer que hem de fer és crear un QWidget, no és molt complicat, a sota de setWindowTitle, afegiu:

mainWidget = QWidget(self)
self.setWidget(mainWidget)

A continuació, crearem un botó:

buttonExportDocument = QPushButton("Export Document", mainWidget)

Ara, per a connectar el botó amb la nostra funció, haurem de veure els senyals a la documentació. El QPushButton no té senyals úniques pròpies, però si diu que hereta 4 senyals des de QAbstractButton, això vol dir que podem utilitzar-les. En el nostre cas, volem fer clic.

buttonExportDocument.clicked.connect(self.exportDocument)

Si ara reiniciem el Krita, tindrem un acoblador nou i en aquest hi haurà un botó. En fer-hi clic s'activarà la funció d'exportació.

No obstant això, el botó es veu alineat de manera una mica estranya. Això és perquè el nostre mainWidget no té disposició. Fem-ho ràpidament:

mainWidget.setLayout(QVBoxLayout())
mainWidget.layout().addWidget(buttonExportDocument)

Les Qt tenen diverses disposicions, però «QHBoxLayout» i «QVBoxLayout» són les més fàcils d'utilitzar, simplement organitzen els ginys de forma horitzontal o vertical.

Reinicieu el Krita i el botó hauria d'estar present.

Senyals i ranures de PyQt

Ja hem estat emprant senyals i ranures de PyQt, però hi ha ocasions en les quals voldreu crear les vostres pròpies senyals i ranures. Com la documentació de PyQt és força difícil d'entendre, i la forma en què es creen els senyals i les ranures és molt diferent de C++ de les Qt, ho expliquem aquí:

Totes les funcions de Python que realitzeu al PyQt es poden entendre com ranures, el qual vol dir que poden connectar-se a senyals com Action.triggered o QPushButton.clicked. No obstant això, la QCheckBox té un senyal per a alternar, la qual envia un booleà. Com aconseguim que la nostra funció l'accepti?

Primer, assegureu-vos de tenir la importació correcta per a crear les ranures personalitzades:

from PyQt5.QtCore import pyqtSlot

(Si hi ha importació des de from PyQt5.QtCore import * ja a la llista d'importacions, llavors no haureu de fer això, és clar).

Després, haureu d'afegir una definició de ranura de PyQt abans de la vostra funció:

@pyqtSlot(bool)
def myFunction(self, enabled):
    enabledString = "disabled"
    if (enabled == True):
        enabledString = "enabled"
    print("La casella de selecció és"+enabledString)

Després, quan hàgiu creat la casella de selecció, podreu fer quelcom com «myCheckbox.toggled.connect(self.myFunction)».

De manera similar, per a crear les vostres pròpies senyals de PyQt, feu el següent:

# el nom del senyal s'afegeix a les variables membres de la classe
signal_name = pyqtSignal(bool, name='signalName')

def emitMySignal(self):
    # I així és com s'activa el senyal a emetre.
    self.signal_name.emit(True)

I utilitzeu la importació correcta:

from PyQt5.QtCore import pyqtSignal

Per a emetre o crear ranures per a objectes que no són objectes estàndard de Python, només haureu de posar els seus noms entre cometes.

Una nota sobre les proves d'una unitat

Si voleu escriure proves de la unitat per al vostre connector, mireu al mòdul «mock» del Krita.

Conclusió

Bé, això cobreix tots els detalls específics del Krita per a crear connectors en Python. No maneja com analitzar les dades dels píxels o les millors pràctiques amb els documents, però si teniu una mica d'experiència amb Python, hauríeu de poder començar a crear els vostres propis connectors.

Com sempre, llegiu el codi amb cura i els documents de l'API per a Python, el Krita i les Qt per a veure què és possible, i arribareu molt lluny.