Come creare un’estensione python per Krita

Potresti avere alcuni script ben curati nel Creatore di script python, ma potresti voler fare di più con qualcuno di essi, come per esempio avviarlo automaticamente. Impacchettare il tuo script in un’estensione può fornirti molta più flessibilità e potenzialità che avviare script dall’editor del Creatore di script.

Anche se conosci molto bene Python, esistono alcuni piccoli dettagli per far sì che Krita riconosca un’estensione python. Questa pagina ti fornirà una panoramica su come creare i vari tipi di script python specifici di Krita.

Queste mini esercitazioni sono scritte per utenti con una comprensione elementare di Python e in modo tale da incoraggiare la sperimentazione anziché fornire semplici copia e incolla di codice, dunque leggi attentamente il testo.

Far riconoscere a Krita la tua estensione

Uno script in Krita ha due componenti: la cartella dello script (che contiene i file Python del tuo script) e un file «.desktop» che Krita usa per caricare e registrare il tuo script. Affinché Krita carichi il tuo script, entrambi gli elementi debbono trovarsi nella sottocartella pykrita della cartella delle risorse di Krita (per i percorsi del sistema operativo vedi Gestione delle risorse). Per trovare la cartella delle risorse, avvia Krita e fai clic sull’elemento di menu Impostazioni ‣ Gestisci le risorse…. Si aprirà un riquadro di dialogo. Fai clic sul pulsante Apri cartella delle risorse. Si aprirà il gestore di file del sistema alla posizione della cartella delle risorse di Krita. Consulta la documentazione API in «Auto starting scripts». Se non esiste una sottocartella pykrita nella cartella delle risorse di Krita, usa il tuo gestore di file per crearne una.

Gli script sono identificati da un file che termina con l’estensione .desktop e che contiene le informazioni sullo script stesso.

Per ciascuna estensione corretta, dunque, dovrai creare una cartella e un file desktop.

Il file desktop deve apparire come segue:

[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=myplugin
X-Python-2-Compatible=false
X-Krita-Manual=manualeDellaMiaEstensione.html
Name=La mia estensione
Comment=La nostra bella estensione.
Tipo

Deve essere sempre un servizio.

ServiceTypes

Per le estensioni python devono essere sempre Krita/PythonPlugin.

X-KDE-Library

Deve essere il nome della cartella dell’estensione che hai appena creato.

X-Python-2-Compatible

Se è compatibile con Python 2. Se Krita è stato generato con Python 2 anziché col 3 (-DENABLE_PYTHON_2=ON nella configurazione cmake), allora questa estensione non verrà mostrata nell’elenco.

X-Krita-Manual

Un valore opzionale che punta al manuale. Viene mostrato nel Gestore estensioni python. Se è un file HTML sarà mostrato come testo formattato, diversamente sarà mostrato come testo semplice.

Name

Il nome che verrà mostrato nel Gestore estensioni python.

Comment

La descrizione che verrà mostrata nel Gestore estensioni python.

Le estensioni python di Krita devono essere moduli python, accertati dunque che esista uno script __init__.py contenente qualcosa tipo…

from .myplugin import *

Dove .myplugin è il nome del file principale della tua estensione. Se riavvi Krita, esso viene ora mostrato Gestore delle estensioni python nelle impostazioni, ma sarà disattivato in grigio perché non è presente alcun file myplugin.py. Se passi il mouse sulle estensioni disabilitate potrai visualizzare l’errore restituito.

Nota

Devi abilitare esplicitamente la tua estensione. Vai al menu Impostazioni, apri la finestra di dialogo Configura Krita, vai alla pagina del Gestore estensioni e abilita l’estensione.

Riepilogo

In breve, se vuoi creare uno script di nome myplugin:

  • nella cartella resources/pykrita di Krita crea
    • una cartella di nome myplugin

    • un file di nome myplugin.desktop

  • nella cartella myplugin crea
    • un file di nome __init__.py

    • un file di nome myplugin.py

  • nel file __init__.py inserisci questo codice:

from .myplugin import *
  • nel file desktop inserisci questo codice:

    [Desktop Entry]
    Type=Service
    ServiceTypes=Krita/PythonPlugin
    X-KDE-Library=myplugin
    X-Python-2-Compatible=false
    Name=La mia estensione
    Comment=La nostra bella estensione.
    
  • scrivi il tuo script nel file myplugin/myplugin.py.

Creazione di un’estensione

Le estensioni sono script python relativamente semplici che vengono eseguiti all’avvio di Krita. Sono creati estendendo la classe Extension, e l’estensione più semplice possibile appare così:

from krita import *

class MyExtension(Extension):

    def __init__(self, parent):
        # Questo inizializza il genitore, sempre importante si crea una sottoclasse.
        super().__init__(parent)

    def setup(self):
        pass

    def createActions(self, window):
        pass

# E aggiunge l'estensione all'elenco delle estensioni di Krita:
Krita.instance().addExtension(MyExtension(Krita.instance()))

Questo codice ovviamente non fa nulla. Tipicamente, in createActions aggiungiamo azioni a Krita, dunque possiamo accedere al nostro script dal menu Strumenti.

Per prima cosa creiamo una azione. Possiamo farlo facilmente con Window.createAction(). Krita richiamerà createActions per ogni finestra che viene creata e passa l’oggetto finestra corretto che dobbiamo usare.

Dunque…

def createActions(self, window):
    action = window.createAction("miaAzione", "Mio script", "tools/scripts")
«miaAzione»

Deve essere sostituito con un identificatore univoco che Krita utilizzerà per trovare l’azione.

«Mio script»

Quello che verrà visualizzato nel Menu Strumenti.

Se ora riavvi Krita, avrai un’azione chiamata «Mio script». Non fa ancora nulla perché non l’abbiamo collegata a uno script.

Creiamo dunque un semplice script per l’esportazione dei documenti. Aggiungi il codice seguente alla classe dell’estensione, assicurandoti che si trovi sopra dove aggiungi l’estensione a Krita:

def exportDocument(self):
    # Ottieni il documento:
    doc =  Krita.instance().activeDocument()
    # Salvare un documento non esistente causa un crash, verifica questo prima.
    if doc is not None:
        # Richiama la finestra di salvataggio. La finestra di salvataggio restituisce una tupla.
        fileName = QFileDialog.getSaveFileName()[0]
        # Ed esporta il documento nella posizione fileName.
        # InfoObject è un dizionario con opzioni di esportazione specifiche, ma quando ne creiamo uno vuoto Krita userà le impostazioni predefinite di esportazione.
        doc.exportImage(fileName, InfoObject())

E aggiungi l’importazione per QFileDialog sopra con le opzioni di importazione:

from krita import *
from PyQt5.QtWidgets import QFileDialog

Quindi colleghi l’azione al nuovo documento di esportazione:

def createActions(self, window):
    action = window.createAction("miaAzione", "Mio script")
    action.triggered.connect(self.exportDocument)

Questo è un esempio di collegamento segnale/slot, che le applicazioni Qt tipo Krita usano molto. Andiamo a creare i nostri segnali e slot un po” più tardi.

Riavvia Krita e la nuova azione dovrebbe ora esportare il documento.

Creazione di scorciatoie di tastiera configurabili

Ora, la tua nuova azione non viene mostrata in Impostazioni ‣ Configura Krita ‣ Tasti scorciatoia.

Krita, per varie ragioni, aggiunge alle Impostazioni delle scorciatoie solo le azioni presenti in un file .action. Per ottenere l’azione da aggiungere alle scorciatoie, il file action dovrebbe assomigliare a questo:

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

        <Action name="myAction">
        <icon></icon>
        <text>Mio 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>Miei script</text>

Creerà una sottocategoria negli script chiamata «Miei script» in cui aggiungere le scorciatoie.

name

Deve essere l’identificatore univoco creato per la tua azione quando è stata creata nella configurazione dell’estensione.

icon

Il nome di una possibile icona. Vengono mostrate solo in KDE Plasma, perché gli utenti Gnome e Windows si sono lamentati che sono brutte.

text

Il testo che sarà mostrato nell’editor delle scorciatoie.

whatsThis

Il testo che sarà mostrato quando un’applicazione Qt richiama in modo specifico “what is this”, che è un’azione di aiuto.

toolTip

Il suggerimento che verrà mostrato al passaggio del mouse.

iconText

Il testo che verrà mostrato quando visualizzata in una barra degli strumenti. Per esempio, «Ridimensiona immagina a una nuova dimensione» deve essere accorciata in «Ridimensiona immagine» per risparmiare spazio, dunque qui inseriremo questo testo.

activationFlags

Determina quando un’azione è disabilitata o no.

activationConditions

Determina le condizioni di attivazione (per es., attiva solo quando la selezione è modificabile). Consulta il codice per esempi.

shortcut

La scorciatoia predefinita.

isCheckable

Se è o no una casella di attivazione.

statusTip

Il suggerimento dello stato visualizzato nella barra di stato.

Salva questo file come myplugin.action, dove myplugin è il nome della tua estensione. Il file action deve essere salvato non nella cartella delle risorse pykrita, ma piuttosto in una cartella di risorse chiamata «actions» (quindi share/pykrita è dove vanno le estensioni python e i file desktop, e share/actions è dove vanno i file action). Riavvia Krita. La scorciatoia dovrebbe ora essere visualizzata nell’elenco delle azioni scorciatoia.

Creazione di un’area di aggancio

La creazione di un’area di aggancio (docker) personalizzata è molto simile alla creazione di un’estensione. Le aree di aggancio sono in qualche misura più semplici, ma richiedono un uso maggiore di oggetti grafici. Questo è il codice più semplice possibile per le aree di aggancio:

from PyQt5.QtWidgets import *
from krita import *

class MyDocker(DockWidget):

    def __init__(self):
        super().__init__()
        self.setWindowTitle("Mio docker")

    def canvasChanged(self, canvas):
        pass

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

Il titolo della finestra è come apparirà nell’elenco dell’area di aggancio in Krita. canvasChanged deve essere sempre presente, ma con essa non devi fare altro, da cui l’istruzione “pass” e nient’altro.

Per addDockWidgetFactory…

«mioDocker»

Sostituiscilo con un identificatore univoco per la tua area di aggancio che Krita utilizzerà per tenerne traccia.

DockWidgetFactoryBase.DockRight

La posizione. I valori possono essere DockTornOff, DockTop, DockBottom, DockRight, DockLeft o DockMinimized

MyDocker

Sostituiscilo col nome della classe dell’area di aggancio da aggiungere.

Dunque, se aggiungiamo la funzione di esportazione del documento che abbiamo creato nella sezione delle estensioni a questo codice di area di aggancio, come facciamo sì che l’utente lo attivi? Per prima cosa ci serve un po” di codice Qt per l’interfaccia grafica: aggiungere un pulsante!

Per impostazione predefinita, Krita usa PyQt, ma la sua documentazione è piuttosto scarsa, dato che la documentazione di Qt è ben curata, e spesso noterai che la documentazione PyQt di una classe, diciamo QWidget, appare come una strana copia della normale documentazione di Qt per quella classe.

Ad ogni modo, quello di cui abbiamo bisogno è innanzitutto creare un QWidget. Non è molto complicato, sotto setWindowTitle aggiungiamo:

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

Quindi creiamo un pulsante:

buttonExportDocument = QPushButton("Esporta documento", mainWidget)

Per collegare ora il pulsante alla nostra funzione, dobbiamo guardare i segnali nella documentazione. QPushButton non possiede segnali univoci propri, ma eredita quattro segnali da QAbstractButton, il che significa che possiamo utilizzare anche quelli. Nel nostro caso vogliamo fare clic (clicked).

buttonExportDocument.clicked.connect(self.exportDocument)

Quando riavviamo Krita, avremo una nuova area di aggancio in cui è presente un pulsante. Se vi facciamo clic, esso richiamerà la funzione di esportazione.

Il pulsante, tuttavia, appare male allineato. Ciò è dovuto al fatto che il nostro mainWidget non possiede un layout. Eseguiamo rapidamente:

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

Qt contiene vari layout, ma QHBoxLayout e QVBoxLayout sono i più facili da usare, in quanto si limitano a disporre rispettivamente orizzontalmente o verticalmente gli oggetti.

Riavvia Krita e il pulsante ora dovrebbe apparire posizionato in maniera appropriata.

Segnali e slot PyQt

Abbiamo già utilizzato i segnali e gli slot PyQt, ma ci sono situazioni in cui vuoi creare segnali e slot personalizzati. Dato che la documentazione PyQt è ostica da capire, e il modo in cui vengono creati i segnali e gli slot è molto diverso da quello di Qt in C++, lo spieghiamo qui:

Tutte le funzioni python che crei in PyQt possono essere viste come slot, intendendo con questo che esse possono essere collegate ai segnali tipo Action.triggered o QPushButton.clicked. QCheckBox, tuttavia, possiede un segnale per la commutazione (toggled), che invia un booleano. Come procediamo affinché la nostra funzione accetti quel booleano?

Per prima cosa, devi accertarti di usare le istruzioni import corrette per creare slot personalizzati:

from PyQt5.QtCore import pyqtSlot

(se c’è già from PyQt5.QtCore import * nell’elenco delle importazioni, naturalmente questo comando non va creato).

Quindi devi aggiungere una definizione slot PyQt prima della funzione:

@pyqtSlot(bool)
def myFunction(self, enabled):
    enabledString = "disabled"
    if (enabled == True):
        enabledString = "enabled"
    print("La casella di controllo è"+enabledString)

Dopo che hai creato la casella di controllo puoi eseguire qualcosa tipo myCheckbox.toggled.connect(self.myFunction).

In modo simile, per creare i tuoi segnali PyQt personalizzati usa il codice seguente:

# il nome del segnale viene aggiunto alle variabili membro della classe
signal_name = pyqtSignal(bool, name='signalName')

def emitMySignal(self):
    # E questo è come attivi il segnale da emettere.
    self.signal_name.emit(True)

E utilizzi l’importazione corretta:

from PyQt5.QtCore import pyqtSignal

Per emettere o creare slot per gli oggetti che non sono oggetti python standard, devi solo inserire i loro nome tra virgolette.

Nota sui test di unità

Se vuoi scrivere test unità per la tua estensione, dai un’occhiata al modulo Krita per simulazioni.

Conclusione

Bene, tutto quello detto sopra tratta tutti i dettagli specifici di Krita per la creazione di estensioni python. Non tratta il modo in cui vanno analizzati i dati dei pixel, o i migliori metodi da utilizzare con i documenti, ma se possiedi un po” di esperienza con Python, dovresti essere in grado di creare le tue estensioni personalizzate.

Come sempre, leggi attentamente il codice e la documentazione delle API di Python, Krita e Qt in modo da capire cosa è possibile fare, e otterrai così molti risultati.