Como criar um plugin Python para o Krita

Você pode ter alguns scripts interessantes escritos no editor de script Python, mas talvez queira fazer mais com eles e executá-los automaticamente, por exemplo. Encapsular seu script em um plugin pode lhe dar muito mais flexibilidade e poder do que executar scripts no editor de script.

Certo, mesmo que você conheça Python muito bem, existem alguns pequenos detalhes para fazer o Krita reconhecer um plugin Python. Portanto, esta página dará uma visão geral de como criar os vários tipos de script Python exclusivos do Krita.

Esses minitutoriais foram escritos para pessoas com conhecimento básico de Python e de forma a incentivar a experimentação em vez de simplesmente copiar e colar código. Portanto, leia o texto com atenção.

Fazendo o Krita reconhecer seu plugin

Um script no Krita tem dois componentes: a pasta do script (que contém os arquivos Python do seu script) e um arquivo “.desktop” que o Krita usa para carregar e registrar seu script. Para que o Krita carregue seu script, ambos devem estar na subpasta pykrita da sua pasta de recursos do Krita (consulte Gerenciamento de recursos para os caminhos por sistema operacional). Para encontrar sua pasta de recursos, inicie o Krita e clique no item de menu Configurações ‣ Gerenciar recursos…. Isso abrirá uma caixa de diálogo. Clique no botão Abrir pasta de recursos. Isso deve abrir o gerenciador de arquivos do seu sistema na sua pasta de recursos do Krita. Consulte a documentação API em “Inicialização automática de scripts”. Se não houver uma subpasta pykrita na pasta de recursos do Krita, use seu gerenciador de arquivos para criar uma.

Os scripts são identificados por um arquivo que termina com a extensão .desktop que contém informações sobre o próprio script.

Portanto, para cada plugin adequado, você precisará criar uma pasta e um arquivo na área de trabalho.

O arquivo desktop deve ter a seguinte aparência:

[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=meuplugin
X-Python-2-Compatible=false
X-Krita-Manual=ManualmeuPlugin.html
Name=Meu Próprio Plugin
Comment=Nosso próprio plugin.
Type (Tipo)

Isso sempre deve ser um serviço.

ServiceTypes (Tipos de Serviço)

Isso deve ser sempre Krita/PythonPlugin para plugins Python.

X-KDE-Library

Este deve ser o nome da pasta do plugin que você acabou de criar.

X-Python-2-Compatible

Se é compatível com o Python 2. Se o Krita foi criado com o Python 2 em vez do 3 (-DENABLE_PYTHON_2=ON na configuração do cmake), este plugin não aparecerá na lista.

X-Krita-Manual

Um valor opcional que apontará para o item manual. Isso é exibido no gerenciador de plugins do Python. Se for um arquivo HTML, será exibido como rich text, caso contrário, será exibido como texto simples.

Name (Nome)

O nome que aparecerá no gerenciador de plugins Python.

Comment (Comentário)

A descrição que será exibida no gerenciador de plugins Python.

Os plugins Python do Krita precisam ser módulos Python, então certifique-se de que haja um script __init__.py contendo algo como…

from .meuplugin import *

Onde .meuplugin é o nome do arquivo principal do seu plugin. Se você reiniciar o Krita, ele deverá exibir isso no gerenciador de plugins Python nas configurações, mas ficará esmaecido, pois não há meuplugin.py. Se você passar o mouse sobre os plugins desativados, poderá ver o erro com eles.

Nota

Você precisa habilitar seu plugin explicitamente. Acesse o menu Configurações, abra a caixa de diálogo Configurar Krita e acesse a página do Gerenciador de plugins Python para habilitar seu plugin.

Resumo

Em resumo, se você quiser criar um script chamado meuplugin:

  • na sua pasta resources/pykrita do Krita crie
    • uma pasta chamada meuplugin

    • um arquivo chamado meuplugin.desktop

  • na pasta meuplugin crie
    • um arquivo chamado __init__.py

    • um arquivo chamado meuplugin.py

  • no arquivo __init__.py coloque este código:

from .meuplugin import *
  • no arquivo desktop coloque este código:

    [Desktop Entry]
    Type=Service
    ServiceTypes=Krita/PythonPlugin
    X-KDE-Library=meuplugin
    X-Python-2-Compatible=false
    Name=Meu Próprio Plugin
    Comment=Nosso próprio plugin.
    
  • escreva seu script no arquivo meuplugin/meuplugin.py.

Criando uma extensão

Extensões são scripts Python relativamente simples que rodam na inicialização do Krita. Elas são criadas estendendo a classe Extension, e a extensão mais básica se parece com isto:

from krita import *

class MinhaExtensao(Extension):

    def __init__(self, parent):
        # Isso está inicializando o pai, sempre importante ao criar subclasses.
        super().__init__(parent)

    def setup(self):
        pass

    def createActions(self, window):
        pass

# E adiciona a extensão à lista de extensões do Krita:
Krita.instance().addExtension(MinhaExtensao(Krita.instance()))

Este código, claro, não faz nada. Normalmente, em createActions, adicionamos ações ao Krita, para que possamos acessar nosso script a partir do menu Ferramentas.

Primeiro, vamos criar uma ação. Podemos fazer isso facilmente com Window.createAction(). O Krita chamará createActions para cada janela criada e passará o objeto de janela correto que precisamos usar.

Então…

def createActions(self, window):
    action = window.createAction("minhaAcao", "Meu Script", "tools/scripts")
“minhaAcao”

Isso deve ser substituído por um ID exclusivo que o Krita usará para encontrar a ação.

“Meu Script”

Isto é o que ficará visível no Menu Ferramentas.

Se você reiniciar o Krita agora, terá uma ação chamada “Meu Script”. Ela ainda não faz nada, porque não a conectamos a um script.

Então, vamos criar um script simples para exportar documentos. Adicione o seguinte à classe de extensão, certificando-se de que esteja acima de onde você adicionou a extensão ao Krita:

def exportDocument(self):
    # Obtém o documento:
    doc =  Krita.instance().activeDocument()
    # Salvar um documento inexistente causa travamentos, então vamos verificar isso primeiro.
    if doc is not None:
        # Isso abre a caixa de diálogo de salvamento. A caixa de diálogo de salvamento retorna uma tupla.
        fileName = QFileDialog.getSaveFileName()[0]
        # E exporta o documento para a localização de fileName.
        # InfoObject é um dicionário com opções de exportação específicas, mas quando criamos um vazio, o Krita usará os padrões de exportação.
        doc.exportImage(fileName, InfoObject())

E adicione a importação para o QFileDialog acima com as importações:

from krita import *
from PyQt5.QtWidgets import QFileDialog

Em seguida, para conectar a ação ao novo documento de exportação:

def createActions(self, window):
    action = window.createAction("minhaAcao", "Meu Script")
    action.triggered.connect(self.exportDocument)

Este é um exemplo de uma conexão sinal/slot, muito utilizada em aplicativos Qt como o Krita. Veremos como criar nossos próprios sinais e slots mais adiante.

Reinicie o Krita e sua nova ação deverá exportar o documento.

Criando atalhos de teclado configuráveis

Agora, sua nova ação não aparece em Configurações ‣ Configurar Krita ‣ Atalhos de teclado.

O Krita, por vários motivos, só adiciona ações ao Configurações de atalho quando elas estão presentes em um arquivo .action. O arquivo de ação para que nossa ação seja adicionada aos atalhos deve ser semelhante a este:

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

        <Action name="myAction">
        <icon></icon>
        <text>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>Meus Scripts</text>

Isso criará uma subcategoria em scripts chamada “Meus scripts” para adicionar seus atalhos.

name

Isto deve ser o ID exclusivo que você criou para sua ação ao criá-la na configuração da extensão.

icon

O nome de um possível ícone. Eles só aparecerão no KDE Plasma, porque usuários do Gnome e do Windows reclamaram que eles são feios.

text

O texto que será exibido no editor de atalhos.

whatsThis

O texto que será exibido quando um aplicativo Qt solicitar especificamente “o que é isso”, que é uma ação de ajuda.

toolTip

A dica que aparecerá ao passar o mouse.

iconText

O texto que será exibido quando exibido em uma barra de ferramentas. Por exemplo, “Redimensionar imagem para novo tamanho” poderia ser abreviado para “Redimensionar imagem” para economizar espaço, então colocaríamos isso aqui.

activationFlags

Isso determina quando uma ação é desabilitada ou não.

activationConditions

Isso determina as condições de ativação (por exemplo, ativar somente quando a seleção for editável). Veja o código para exemplos.

shortcut

Atalho padrão.

isCheckable

Se é uma caixa de seleção ou não.

statusTip

A dica de status que é exibida em uma barra de status.

Salve este arquivo como meuplugin.action, onde meuplugin é o nome do seu plugin. O arquivo de ação deve ser salvo não na pasta de recursos do pykrita, mas sim em uma pasta de recursos chamada “actions”. (Portanto, share/pykrita é onde ficam os plugins Python e os arquivos do desktop, e share/actions é onde ficam os arquivos de ação.) Reinicie o Krita. O atalho agora deve aparecer na lista de ações de atalho.

Criando um painel

Criar um painel personalizado é muito parecido com criar uma extensão. Os painéis são, em alguns aspectos, um pouco mais fáceis, mas também exigem mais uso de widgets. Este é o código básico de um painel:

from PyQt5.QtWidgets import *
from krita import *

class MeuDocker(DockWidget):

    def __init__(self):
        super().__init__()
        self.setWindowTitle("Meu Docker")

    def canvasChanged(self, canvas):
        pass

Krita.instance().addDockWidgetFactory(DockWidgetFactory("meuDocker", DockWidgetFactoryBase.DockRight, MeuDocker))

O título da janela (setWindowTitle) é como ele aparecerá na lista de painéis no Krita. canvasChanged sempre precisa estar presente, mas você não precisa fazer nada com ele, então basta “passar”.

Para o addDockWidgetFactory…

“meuDocker”

Substitua isso por um ID exclusivo para seu painel que o Krita usa para rastreá-lo.

DockWidgetFactoryBase.DockRight

A localização. Podem ser DockTornOff, DockTop, DockBottom, DockRight, DockLeft ou DockMinimized

MeuDocker

Substitua isso pelo nome da classe do painel que você deseja adicionar.

Então, se adicionarmos a função de exportação de documentos que criamos na seção de extensões a este código de painel, como permitimos que o usuário a ative? Primeiro, precisamos codificar a interface gráfica do usuário em Qt: vamos adicionar um botão!

Por padrão, o Krita usa PyQt, mas sua documentação é bem ruim, principalmente porque a documentação regular do Qt é muito boa, e você frequentemente descobrirá que a documentação PyQt de uma classe, digamos, QWidget é como uma cópia estranha da documentação regular do Qt para essa classe.

De qualquer forma, o que precisamos fazer primeiro é criar um QWidget. Não é muito complicado. Em setWindowTitle, adicione:

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

Então, criamos um botão:

buttonExportDocument = QPushButton("Exportar documento", mainWidget)

Agora, para conectar o botão à nossa função, precisamos consultar os sinais na documentação. QPushButton não possui sinais únicos, mas diz que herda 4 sinais de QAbstractButton, o que significa que também podemos usá-los. No nosso caso, queremos que seja clicado.

buttonExportDocument.clicked.connect(self.exportDocument)

Se reiniciarmos o Krita agora, teremos um novo painel e, nesse painel, há um botão. Clicar no botão abrirá a função de exportação.

No entanto, o botão parece alinhado de forma um pouco estranha. Isso ocorre porque nosso mainWidget não tem layout. Vamos fazer isso rapidamente:

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

O Qt tem vários layouts, mas o QHBoxLayout e o QVBoxLayout são os mais fáceis de usar, eles apenas organizam os widgets horizontalmente ou verticalmente.

Reinicie o Krita e o botão deverá estar bem disposto.

Sinais e Slots do PyQt

Já usamos sinais e slots do PyQt, mas há momentos em que você deseja criar seus próprios sinais e slots. Como a documentação do PyQt é bastante difícil de entender, e a maneira como os sinais e slots são criados é muito diferente do Qt em C++, estamos explicando aqui:

Todas as funções Python que você cria em PyQt podem ser entendidas como slots, o que significa que podem ser conectadas a sinais como Action.triggered ou QPushButton.clicked. No entanto, QCheckBox tem um sinal para toggled, que envia um booleano. Como fazemos para que nossa função aceite esse booleano?

Primeiro, certifique-se de ter a importação correta para criar slots personalizados:

from PyQt5.QtCore import pyqtSlot

(Se já houver from PyQt5.QtCore import * na lista de importações, você não precisará fazer isso, é claro.)

Então, você precisa adicionar uma definição de slot PyQt antes da sua função:

@pyqtSlot(bool)
def myFunction(self, enabled):
    enabledString = "disabled"
    if (enabled == True):
        enabledString = "enabled"
    print("A caixa de seleção está"+enabledString)

Então, quando você tiver criado sua caixa de seleção, você pode fazer algo como myCheckbox.toggled.connect(self.myFunction).

Da mesma forma, para criar seus próprios sinais PyQt, faça o seguinte:

# o nome do sinal é adicionado às variáveis ​​​​membro da classe
signal_name = pyqtSignal(bool, name='signalName')

def emitMySignal(self):
    # E é assim que você aciona o sinal a ser emitido.
    self.signal_name.emit(True)

E use a importação correta:

from PyQt5.QtCore import pyqtSignal

Para emitir ou criar slots para objetos que não são objetos Python padrão, você só precisa colocar seus nomes entre aspas.

Uma nota sobre testes unitários

Se você quiser escrever testes unitários para seu plugin, dê uma olhada no módulo krita mock.

Conclusão

Certo, isso abrange todos os detalhes específicos do Krita para a criação de plugins em Python. Não aborda como analisar os dados de pixel nem as melhores práticas com documentos, mas se você tiver um pouco de experiência com Python, poderá começar a criar seus próprios plugins.

Como sempre, leia o código com atenção e leia a documentação da API para Python, Krita e Qt com atenção para ver o que é possível, e você chegará bem longe.