Was going to make a topic asking how to get current node before/after actions are triggered, but after some back and forth with copilot apparently using lambdas containing a tuple (expression) can work. There are a couple downsides like variables being local to each expression, but you could use functions instead and time them in a similar way. Probably solves the global variable issue but then you got to edit the function names in multiple places. I’ve not tried using doc.waitForDoc() here but if this allows mixing api/actions then maybe it will work now…?
from krita import *
inst = Krita.instance()
doc = inst.activeDocument()
def func():
if not doc:
print("no active document")
return
if not doc.selection():
print("no active selection")
return
steps = [
lambda: (
print("convert_selection_to_shape"),
inst.action("convert_selection_to_shape").trigger()
),
lambda: (
layer := doc.activeNode(),
print(layer),
svg := layer.toSvg(),
print(svg),
inst.action("deselect").trigger(),
),
]
delay_per_step = 100 # milliseconds
for i, step in enumerate(steps):
QTimer.singleShot(delay_per_step * (i + 1), step)
func()
Here is how I (and many others apparently) assumed it would work, but doc.activeNode() gets the previous node. This has been discussed over a bunch of topics.
from krita import *
inst = Krita.instance()
doc = inst.activeDocument()
def func():
if not doc:
print("no active document")
return
if not doc.selection():
print("no active selection")
return
inst.action("convert_selection_to_shape").trigger()
inst.action("deselect").trigger()
layer = doc.activeNode() #active layer is not the new vector layer
print(layer)
print(layer.toSvg()) # AttributeError: 'Node' object has no attribute 'toSvg'
func()
edit:
Here is a version that a list of functions. For whatever reason the prints go to the command prompt (I launched krita from there). the dictionary var is used to store values.
from krita import *
from PyQt5 import QtCore
inst = Krita.instance()
doc = inst.activeDocument()
var = {}
def func():
if not doc:
print("no active document")
return
if not doc.selection():
print("no active selection")
return
def func1():
print("convert_selection_to_shape"),
inst.action("convert_selection_to_shape").trigger()
# var['var1'] = "hello"
def func2():
print("func2")
layer = doc.activeNode()
var['svg'] = layer.toSvg()
print(var['svg'])
def func3():
print("func3")
inst.action("deselect").trigger()
pass
funcs = [func1,func2,func3]
delay_per_step = 100 # milliseconds
for i, func in enumerate(funcs):
QTimer.singleShot(delay_per_step * (i + 1), func)
#
func()
2025-06-09
I’ve been testing this for a while, now I’m using yield for each delay, it’s much more easy to read, variables are all in the same scope, and loops even work. Save your work before trying this
Not all commands will undo!
Besides the decorator function, the script I included here will select pixels from a transparency mask and apply it to a group above, then delete the layer and deselect.
from krita import *
inst = Krita.instance()
doc = inst.activeDocument()
node = doc.activeNode()
view = inst.activeWindow().activeView()
def async_runner(func):
"""Decorator to automatically handle function execution with pauses."""
import functools
@functools.wraps(func)
def wrapper():
generator = func()
def step():
try:
delay = next(generator)
QTimer.singleShot(delay, step) # Schedule next step
except StopIteration:
print('--- DELAYED EXECUTION COMPLETE ---')
step() # Start execution
return wrapper
@async_runner
def func():
layer_type = "transparencymask"
if len(view.selectedNodes()) != 1:
print(f"must select only 1 {layer_type}")
return
if node.type() != layer_type:
print(f"need to select a {layer_type}")
return
parent_type = "grouplayer"
parent = node.parentNode()
if parent.type() != parent_type:
print(f"parent node must be {parent_type}")
mask = node
yield 10
inst.action("krita_filter_invert").trigger()
yield 25
inst.action("selectopaque").trigger()
yield 25
inst.action("remove_layer").trigger()
yield 50
doc.setActiveNode(parent)
yield 25
inst.action("clear").trigger()
yield 25
inst.action("deselect").trigger()
yield 50
func()
Depending on document size and complexity of layers obviously it will not always work, maybe this can be mitigated with a function that tweaks the delay based on document size… I would really like to be able to call other functions with their own delays so I can reuse some of these, but it seems tricky. Maybe inspect the function and count the values of yields? I think I might have tried it and it didn’t work ![]()