Storeroom of scripts

This function gives the active layer a outline layer style using the current foreground color.
Line thickness , transparency and etc can be manipulated by changing the number following “value=”.
Note that adding a layer style to a layer using this function will overwrite the original layer style.

from krita import *

def setLSLineFgColor():
    app = Krita.instance()
    win = app.activeWindow()
    view = win.activeView()

    fgColor = view.foregroundColor().toQString()
    fg2 = fgColor.split(" ")

    c1 = '    <node key="Rd  " value="' + fg2[1] + '" type="Double"/>'
    c2 = '    <node key="Grn " value="' + fg2[3] + '" type="Double"/>'
    c3 = '    <node key="Bl  " value="' + fg2[5] + '" type="Double"/>'

    node = Krita.instance().activeDocument().activeNode()
    style = ('<asl>'
    ' <node name="" classId="null" type="Descriptor">'
    '  <node unit="#Prc" key="Scl " value="100" type="UnitFloat"/>'
    '  <node key="masterFXSwitch" value="1" type="Boolean"/>'
    '  <node name="" key="FrFX" classId="FrFX" type="Descriptor">'
    '   <node key="enab" value="1" type="Boolean"/>'
    '   <node typeId="FStl" key="Styl" value="OutF" type="Enum"/>'### InsF/CtrF/OutF
    '   <node typeId="FrFl" key="PntT" value="SClr" type="Enum"/>'
    '   <node typeId="BlnM" key="Md  " value="Nrml" type="Enum"/>'### blendMode
    '   <node unit="#Prc" key="Opct" value="100" type="UnitFloat"/>'### opacity
    '   <node unit="#Pxl" key="Sz  " value="1" type="UnitFloat"/>'### size
    '   <node name="" key="Clr " classId="RGBC" type="Descriptor">'
    + c1
    + c2
    + c3
    + '   </node>'
    '  </node>'
    ' </node>'
    ' <node name="" classId="Styl" type="Descriptor">'
    '  <node name="" key="documentMode" classId="documentMode" type="Descriptor"/>'
    ' </node>'
    '</asl>')
    node.setLayerStyleFromAsl(style)
    
setLSLineFgColor()
7 Likes

Found a script by our new user @kernel for this topic:

Michelist

2 Likes

Code to select the next and previous preset among those displayed in the Preset Docker.
You can move the selection of presets irrespective of the “Favorites” tag. The first and last of the displayed presets are concatenated so that the selection loops.

# select next preset

from PyQt5.QtCore import (
        QItemSelectionModel,
        )
from PyQt5.QtWidgets import (
        QListView,
        )

qdock = next((w for w in Krita.instance().dockers() if w.objectName() == 'PresetDocker'), None)
listView = qdock.findChild(QListView,'ResourceItemview')
presetView = listView.findChild(QItemSelectionModel)
crrBrush = presetView.currentIndex()

presetViewModel = presetView.model()

presetCount = presetViewModel.rowCount()
crrPresetList = []
num = 0
for index in range(presetCount):
    item = presetViewModel.index(index, 0)

    if item == crrBrush:
        crrNum = num

    crrPresetList.append(item)
    num += 1

if presetView.hasSelection():
    if crrNum == presetCount-1:
        listView.setCurrentIndex(crrPresetList[0])
    else:
        listView.setCurrentIndex(crrPresetList[crrNum + 1])
else:
    listView.setCurrentIndex(crrPresetList[0])

# select previous preset

from PyQt5.QtCore import (
        QItemSelectionModel,
        )
from PyQt5.QtWidgets import (
        QListView,
        )

qdock = next((w for w in Krita.instance().dockers() if w.objectName() == 'PresetDocker'), None)
listView = qdock.findChild(QListView,'ResourceItemview')
presetView = listView.findChild(QItemSelectionModel)
crrBrush = presetView.currentIndex()

presetViewModel = presetView.model()

presetCount = presetViewModel.rowCount()
crrPresetList = []
num = 0
for index in range(presetCount):
    item = presetViewModel.index(index, 0)

    if item == crrBrush:
        crrNum = num

    crrPresetList.append(item)
    num += 1

if presetView.hasSelection():
    if crrNum == 0:
        listView.setCurrentIndex(crrPresetList[-1])
    else:
        listView.setCurrentIndex(crrPresetList[crrNum - 1])
else:
    listView.setCurrentIndex(crrPresetList[0])

4 Likes

Toggles the show-hide of the layer with name 0. Reactivate current layer after finished.

from PyQt5.QtGui import QIcon

node = Krita.instance().activeDocument().nodeByName("0")
if node:
    Krita.instance().activeDocument().setActiveNode(node)
    Krita.instance().action('toggle_layer_visibility').trigger()
    Krita.instance().action('switchToPreviouslyActiveNode').trigger()
    Krita.instance().activeWindow().activeView().showFloatingMessage("Successed",QIcon(),1000,1)
else:
    Krita.instance().activeWindow().activeView().showFloatingMessage("Failed",QIcon(),1000,1)
4 Likes

Fill layer with foreground color and keep alpha.
I wonder why Krita have not single action to do this. Maybe I didn’t find it?

lock_alpha = Krita.instance().action('preserve_alpha')
if not lock_alpha.isChecked():
    lock_alpha.trigger()
    Krita.instance().action('fill_selection_foreground_color_opacity').trigger()
    lock_alpha.trigger()
else:
    Krita.instance().action('fill_selection_foreground_color_opacity').trigger()
7 Likes

This is a small scripts that I used to check information in the develop of plugins.

It helpt when check diffence of tranform data in coordinate.

# ========================================
#  Get Transform Data from transformMask  
# ========================================
from krita import *
# Select and use to the paintlayer with transform masked
targ = Krita.instance().activeDocument().activeNode()
targ.move(targ.position().x()+10,targ.position().y()+10)
print(targ.property('transformedCenter'))
print(targ.toXML())

Next,a quick way to direct import and export SVG data in Krita.

# ========================================
#  Export Vector data example script  
# ========================================
from krita import *
doc = Krita.instance().activeDocument()
root = doc.rootNode()

# Need Select a vector layer (= It's Active Node)
# Get SVG data of the layer
targ = doc.activeNode()
print(targ.toSvg())
# ========================================
#   Import Vector data example script  
# ========================================
from krita import *
doc = Krita.instance().activeDocument()
root = doc.rootNode()

# Add SVG data as new vector layer (A red line from left top to right bottom direction)
# Element have to enclosed by <svg> tag
svgdata = '<svg><path id="line00" stroke-width="14" fill="none" stroke="#ff3b02" d="M 0 0 L 350 350"/></svg>'
vect = doc.createVectorLayer('New vector layer')
vect.addShapesFromSvg(svgdata)
root.addChildNode( vect , None )

# Get SVG data from new vector layer
print(doc.nodeByName('New vector layer').toSvg())
1 Like

This function is useful for get debug message when can’t use scripter even.

from krita import *
from PyQt5.QtWidgets import *

def message(mes):
    mb = QMessageBox()
    mb.setText(str(mes))
    mb.setStandardButtons(QMessageBox.Ok)
    ret = mb.exec()
    if ret == QMessageBox.Ok:
        pass # OK clicked
 
message('hello!')

hi

3 Likes

This is a script that adds the specified layer to the selection. This allows you, for example, to add layers with the same color labels to the selection and make them the target of free transformations.
However, this may be useful in limited situations…

from krita import *

application = Krita.instance()
activeDoc = application.activeDocument()
activeNode = activeDoc.activeNode()

qwin = application.activeWindow().qwindow()
layerBox = qwin.findChild(QDockWidget, "KisLayerBox")
layerList = layerBox.findChild(QTreeView,"listLayers")

layerListSM = layerList.selectionModel()
selectList = layerListSM.selectedIndexes()

# input layer name into "X"
targetNode = activeDoc.nodeByName("X")

# activate the target node temporarily
activeDoc.setActiveNode(targetNode)
index = layerList.currentIndex()# QModelIndex
selectList.append(index)

# back to ActiveNode
activeDoc.setActiveNode(activeNode)

for target in selectList:
    layerListSM.select(target, QItemSelectionModel.Toggle)
    # layerListSM.select(target, QItemSelectionModel.Clear)
    # layerListSM.select(target, QItemSelectionModel.Select)
    # layerListSM.select(target, QItemSelectionModel.Deselect)
1 Like

A small script to open the location where your work is saved as a folder in Windows

import krita
import os

active_document = krita.Krita.instance().activeDocument()
if active_document:
    file_path = active_document.fileName()

    if os.path.exists(file_path):
        os.startfile(os.path.dirname(file_path))



6 Likes

Script to launch other applications. By entering the address of any application in the “add” field, you can launch other applications from Krita.
I have confirmed that it works on Windows, but are not sure on other operating systems. If it does not work, please check the “subprocess” module of Python.

import subprocess

# address of the target application
add = r"D:\CCC\BBB\AAA.exe"

subprocess.Popen(add, shell=True)
5 Likes

This test script tell you what shapes are selected in VectorLayer now.
Also it output SVG data for current selected shape.
How to use? : Make some shapes and select in a VectorLayer.

# ================================================
#  Get selected shapes info in VectorLayer
# ================================================
# More information is there
# https://api.kde.org/krita/html/classVectorLayer.html
# https://api.kde.org/krita/html/classShape.html

from krita import Krita
app = Krita.instance()
doc = app.activeDocument()
lay = doc.activeNode()

if lay.type() == 'vectorlayer':
    app.action('InteractionTool').trigger()# Select shape tool
    shapes = lay.shapes()
    print(" "+str(len(shapes))+" shapes found in this active VectorLayer")
    
    selected_shapes = []
    print("-- ↑ Front -- ") 
    
    # Get All shape info
    # Range = len()-1 .... 0 
    for i in range(len(shapes)-1,-1,-1):
        sp = shapes[i]
        print(f'* Shape({i}), Name: {sp.name()}  ,Type: {sp.type()} , isSelected?: {sp.isSelected()} , ID :{sp} ')

        # Get the selected shape
        if sp.isSelected() == True:
            selected_shapes.append(sp)
    print("-- ↓ Back -- ")
    
    print(" ")
    print(f" {len(selected_shapes)} / {len(shapes)} shapes selected")

    # Detail of the selected shapes
    for j in range(len(selected_shapes)-1,-1,-1):
        s = selected_shapes[j]
        name = s.name()
        type = s.type()
        s.update()
        print(" ------------------ ")
        print(f'* Shape({j}), Type:{type} , \n '+s.toSvg())
2024 04 19 Fixed

shapes → selected_shapes
# Detail of the selected shapes
for j in range(len(selected_shapes)-1,-1,-1):

Example of a log output :

==== Warning: Script not saved! ====
 3 shapes found in this active VectorLayer
-- ↑ Front -- 
* Shape(2), Name:   ,Type: KoPathShape , isSelected?: True , ID :<PyKrita.krita.Shape object at 0x1268cd000> 
* Shape(1), Name:   ,Type: KoPathShape , isSelected?: True , ID :<PyKrita.krita.Shape object at 0x1268cce50> 
* Shape(0), Name:   ,Type: KoPathShape , isSelected?: True , ID :<PyKrita.krita.Shape object at 0x1268ccf70> 
-- ↓ Back -- 
 
 3 / 3 shapes selected
 ------------------ 
* Shape(2), Type:KoPathShape , 
 <path id="shape0" transform="translate(87.8400016565994, 480.96000907056)" fill="none" stroke="#55aaaa" stroke-width="4.21" stroke-linecap="square" stroke-linejoin="bevel" d="M57.6 0L54.72 76.32L0 28.08L130.32 1.44" sodipodi:nodetypes="cccc"/>
 ------------------ 
* Shape(1), Type:KoPathShape , 
 <rect id="shape0" transform="translate(114.48, 426.764124223602)" fill="#16ff41" fill-rule="evenodd" stroke="#ff1730" stroke-width="0.2376" stroke-linecap="square" stroke-linejoin="bevel" width="76.32" height="86.4"/>
 ------------------ 
* Shape(0), Type:KoPathShape , 
 <ellipse id="shape0" transform="translate(152.85286974174, 394.877515405742)" rx="36.7848447204969" ry="31.32" cx="36.7848447204969" cy="31.32" fill="#ff1730" fill-rule="evenodd" stroke="#55aaaa" stroke-width="0.2376" stroke-linecap="square" stroke-linejoin="bevel"/>

1 Like

PyQt has no SeparateLine Widget,
So I often had been create it from FrameShape.

    hl0 = QFrame()
    hl0.setFrameShape(QFrame.HLine)
    hl0.setFrameShadow(QFrame.Sunken)

This way 3 line need for create 1 hair line.

But prepare it many become the code messy.
It is more compact and shorter if it create as custom widget.

# ------------------------------------------------------------------------
# Custom Widget (Horizontal Line, Vertical Line)
# ------------------------------------------------------------------------
from PyQt5.QtWidgets import *

class QHLine(QWidget):
    def __init__(self):
        super().__init__()
        self.initWidget()
        
    def initWidget(self):
        self.hl = QFrame()
        self.hl.setFrameShape(QFrame.HLine)
        self.hl.setFrameShadow(QFrame.Sunken)
        layout = QHBoxLayout()
        layout.addWidget(self.hl)
        layout.setContentsMargins(0,0,0,0)
        self.setLayout(layout) # It nessesasry

class QVLine(QWidget):
    def __init__(self):
        super().__init__()
        self.initWidget()
        
    def initWidget(self):
        self.hl = QFrame()
        self.hl.setFrameShape(QFrame.VLine)
        self.hl.setFrameShadow(QFrame.Sunken)
        layout =QVBoxLayout()
        layout.addWidget(self.hl)
        layout.setContentsMargins(0,0,0,0)
        self.setLayout(layout) # It nessesasry

# Set to use
hl = QHLine()
hl2 = QHLine()
gui_layout01 = QVBoxLayout() # Vertical Layout

#  (Add some GUI Widget/Layout to gui_layout01)

gui_layout01.addWidget(hl)

# (Add some GUI Widget/Layout to gui_layout01)

gui_layout01.addWidget(hl2)

2 Likes

This script selects all clone layers that have the same layer as their source and layer that is the source of the clone layers.
It may be used to delete or hide the clone layer and the source layer all together.

from krita import *

app = Krita.instance()
activeDoc = app.activeDocument()
activeNode = activeDoc.activeNode()

root = activeDoc.rootNode()
allNode = root.findChildNodes("", True)

qwin = app.activeWindow().qwindow()
layerBox = qwin.findChild(QDockWidget, "KisLayerBox")
layerList = layerBox.findChild(QTreeView,"listLayers")
layerListSM = layerList.selectionModel()
selectList = layerListSM.selectedIndexes()

if activeNode.type() == "clonelayer":
    activeNode = activeNode.sourceNode()

activeId = activeNode.uniqueId()

for node in allNode:
    if node.type() == "clonelayer":
        if node.sourceNode().uniqueId() == activeId:
            activeDoc.setActiveNode(node)
            index = layerList.currentIndex()
            selectList.append(index)      

activeDoc.setActiveNode(activeNode)
index = layerList.currentIndex()
selectList.append(index)

for target in selectList:
    layerListSM.select(target, QItemSelectionModel.Select)
5 Likes

Note for position and transform treat of Vector shape by Python on Krita

When getting a shape, processing it, and return it back to Krita.
It might will seems the shape smaller than original shape on Krita
This is due to the different units used,px and pt.

So you might wonder for want scale 1:1 0.72 * 1.3888888888888889 = 1 (about)
apply each path points.

But also should to apply the magic number 1.38… to transform attribute,so it very hard.
For easy workaround solution is use pt for width and height of svg tag,and arge it to addShapesFromSvg()

    current_doc = Krita.instance().activeDocument()
    layer = current_doc.activeNode() # VectorLayer
    # px → pt
    w_pt = current_doc.width()*0.72
    h_pt = current_doc.height()*0.72
    
    svg_src = "some svg elements"
    
    newsrc = f'<svg width="{w_pt}pt" height="{h_pt}pt" viewBox="0 0 {wpt} {hpt}">'+svg_src+'</svg>'
    layer.addShapesFromSvg(newsrc)

This may be different if the document uses units other than px.

1 Like

It tell simple info to user,then after close automatically.
For tell to finished something process, or check of simple debug message.
This is similar that info display when in Krita user do zoom rotate canvas,on left top corner itself.

auto_close_dialog

# =================================
#  Auto-close notice dialog
# =================================
from krita import *
from PyQt5.QtWidgets import *
from PyQt5.Qt import *
from PyQt5.QtGui import *
from PyQt5 import QtCore
import re,math

# create dialog  and show it
def notice_autoclose_dialog(message):
    wait = 1500 # msec

    # Get application window position
    app = Krita.instance()
    qwin = app.activeWindow().qwindow()
    qq = qwin.size()
    wpos = math.ceil(qq.width() * 0.45)
    hpos = math.ceil(qq.height() * 0.45)
    
    # GUI : no window style
    noticeDialog = QDialog()
    noticeDialog.setWindowFlags(QtCore.Qt.FramelessWindowHint) 

    label = QLabel(message)
    label.setAlignment(Qt.AlignCenter)
    label.setFont(QFont('Arial black', 15))
    
    hboxd = QHBoxLayout()
    hboxd.addWidget(label)
    noticeDialog.setLayout(hboxd)
    noticeDialog.setWindowTitle("noTitle")
    
    # Set size and show position 
    w = 240;h = 60
    noticeDialog.setMinimumSize(w,h)
    noticeDialog.resize(w,h)

    print(qwin.x(),wpos,hpos)
    noticeDialog.move(qwin.x()+wpos,qwin.y()+hpos)
    # Wait Timer
    QtCore.QTimer.singleShot(wait, noticeDialog.close)
    noticeDialog.exec_() # show


notice_autoclose_dialog('Some process finished!')
3 Likes

I want to add this one from @AkiR:

Michelist

A quick script to convert all layers to an image’s colorspace. It seems like ‘Convert Image Color Space’ doesn’t work if some layers are already in that colorspace or something (I didn’t check its code to see what’s really happening there).

# Script to convert layers to an image's colorspace.

from krita import *

# These can be used to convert layers to a specified colorspace instead of the image's.
colorModel = ""
colorDepth = ""
colorProfile = ""

def changeColorspace(node, colorModel, colorDepth, colorProfile):
    if node.colorModel() != colorModel or \
    node.colorDepth() != colorDepth or \
    node.colorProfile() != colorProfile:
        print(f"Changing {node.name()} from {node.colorModel()} {node.colorDepth()} {node.colorProfile()}" + \
               f" to {colorModel}, {colorDepth}, {colorProfile}.")
        node.setColorSpace(colorModel, colorDepth, colorProfile)

doc = Krita.instance().activeDocument()

if colorModel == "":
    colorModel = doc.colorModel()
if colorDepth == "":
    colorDepth = doc.colorDepth()
if colorProfile == "":
    colorProfile = doc.colorProfile()

topNodes = doc.topLevelNodes()

def checkLayersRecursive(topNodes, colorModel, colorDepth, colorProfile):
    for parent in topNodes:
        changeColorspace(parent, colorModel, colorDepth, colorProfile)
        children = parent.childNodes()
        if len(children) > 0:
            checkLayersRecursive(children, colorModel, colorDepth, colorProfile)

checkLayersRecursive(topNodes, colorModel, colorDepth, colorProfile)

It was mentioned before that it might be nice to have a subcategory for scripts like there is for plugins. Maybe it would make them easier to find than in this thread?

5 Likes

Script to fill current layer (probably should be a paint layer) to middle gray, and set the blending mode to “Soft Light (Photoshop)”. Afterwards, the current color is restored. I very often use such a layer for dodging and burning. Based on this script (thanks! would have never thought of the eraser action setting).
Script was tested with 8 and 16 bit grayscale and RGB images.

from krita import ManagedColor
application = Krita.instance()
window = application.activeWindow()
view = window.activeView()

currentBlend = view.currentBlendingMode()

kritaEraserAction = application.action("erase_action")
eraseCheck = kritaEraserAction.isChecked()##TorF
kritaEraserAction.setChecked(False)

node = application.activeDocument().activeNode()
colorDepth = node.colorDepth()
colorProfile = node.colorProfile()
colorModel = node.colorModel()

currentColor = view.foregroundColor()
gray = ManagedColor(colorModel, colorDepth, colorProfile)
components = gray.components()
n_components = len(components)
for n in range(n_components - 1):
    components[n] = 0.5
components[n_components - 1] = 1.0
gray.setComponents(components)

view.setForeGroundColor(gray)
view.setCurrentBlendingMode("normal")
application.action('fill_selection_foreground_color').trigger()
node.setBlendingMode("soft_light")

# back to pre-setting
kritaEraserAction.setChecked(eraseCheck)
view.setCurrentBlendingMode(currentBlend)
view.setForeGroundColor(currentColor)
3 Likes

Script to batch convert all Krita files in one folder to JPEG files in another:

import os
from krita import *
from PyQt5.QtWidgets import QFileDialog


# Function to convert a Krita file to JPG
def convert_kra_to_jpg(krita_file):
    doc_path = os.path.join(input_folder, krita_file)
    doc = krita.Krita.instance().openDocument(doc_path)
    doc.setBatchmode(True)

    if doc:
        # Set the export path
        output_path = os.path.join(output_folder, krita_file.replace(".kra", ".jpg"))

        # Export the document as JPG
        # Set export options according to preferences.
        # Note that writing metadata may lead to JPG files that cannot be opened by Photoshop:
        # https://bugs.kde.org/show_bug.cgi?id=491267
        info = InfoObject()
        info.setProperty("baseline", True)
        info.setProperty("exif", False)
        info.setProperty("filters", False)
        info.setProperty("forceSRGB", True)
        info.setProperty("iptc", False)
        info.setProperty("is_sRGB", True)
        info.setProperty("optimize", True)
        info.setProperty("progressive", False)
        info.setProperty("quality", 90)
        info.setProperty("saveProfile", True)
        info.setProperty("smoothing", 0)
        info.setProperty("subsampling", 3)
        info.setProperty("transparencyFillcolor", [255, 255, 255])
        info.setProperty("xmp", False)

        doc.exportImage(output_path, info)

        # Close the document
        doc.close()
    else:
        print(f"Failed to open {krita_file}")


def process_folder(input_folder, output_folder):
    krita_instance = krita.Krita.instance()

    # Ensure the output folder exists
    if not os.path.exists(output_folder):
        os.makedirs(output_folder)

    # Get a list of all .kra files in the input folder
    krita_files = [f for f in os.listdir(input_folder) if f.endswith(".kra")]
    krita_instance.setBatchmode(True)
    for index, krita_file in enumerate(krita_files, start=1):
        # Convert the document and report progress
        print(f"{krita_file}: {index} / {len(krita_files)}")
        convert_kra_to_jpg(krita_file)


# Set the input and output folder paths
input_folder = QFileDialog.getExistingDirectory(None, "Browse to source folder")
print(f"input folder: {input_folder}")
if input_folder is not None and input_folder != "":
    output_folder = QFileDialog.getExistingDirectory(None, "Browse to destination folder")
    print(f"output folder: {output_folder}")
    if output_folder is not None and output_folder != "":

        print(f"Converting Krita files from {input_folder} to {output_folder}")

        # Convert all .kra files in the input folder to JPG
        process_folder(input_folder, output_folder)
        print("Done.")

You could also hard code the paths and skip the directory browsing dialogs.

7 Likes

This script duplicates a group by converting all layers inside a certain grouplayer together into a clone layer. Layers that cannot be cloned, such as filterlayer, are ignored.
Please execute the code with the group layer active.

def groupClone():
    def cloning(groupNode, group):
        childs = groupNode.childNodes()

        for node in childs:
            typ = node.type()
            alpha = node.inheritAlpha()

            if typ == "paintlayer" or typ == "filllayer"  or typ == "clonelayer":
                cloneNode = activeDoc.createCloneLayer(node.name(), node)
                cloneNode.setInheritAlpha(alpha)

                group.addChildNode(cloneNode, None)

            elif typ == "grouplayer":
                cloneGroup2 = activeDoc.createGroupLayer(node.name())
                cloneGroup2.setInheritAlpha(alpha)
                group.addChildNode(cloneGroup2, None)
                cloning(node, cloneGroup2)

    activeDoc=Krita.instance().activeDocument()
    activeNode = activeDoc.activeNode()
    cloneGroup = activeDoc.createGroupLayer("Clone_" + activeNode.name())

    activeNode.parentNode().addChildNode(cloneGroup, activeNode)

    if activeNode.type() == "grouplayer":
        cloning(activeNode, cloneGroup)

groupClone()
4 Likes