I’m not entirely sure but my guess is that core actions run in a different thread than the UI (and therefore the scripter too), therefore waitForDone will always return instantly because there is nothing to wait for in its thread.
Refresh projections will only refresh things like the thumbnails and the intermedieate results of groups (which composes to the image shown on the canvas)
processEvents processes events only for the calling thread, and returns after all available events have been processed. So this also does nothing since it is only for the thread the scripter runs in and the actions are ultimately run by the C++ code that doesn’t use any events.
So the only way I see is to use a timer to wait for hard coded amount of time and hope for the best. It’s a terrible way I have seen used by a few Krita scripts and my guess is there is simply no better workaround until Krita exposes signals for Python to slot into.
As far as I remember, merge_layer action merges all selected layers together. If you somehow manage to select those three layers with a script, calling merge_layer could do what you want with a single call.
Timer works. But man I can’t believe Krita plugin developers were working with such an unreliable implementation (and it’s not documented in Krita scripting school or anywhere… that’s just crazy.)
In the scripting school slots and signals are mentioned but only in relation with Qt, not with Krita’s core functions. It’s really something Krita should provide, in my opinion. Would make a lot of things so much easier.
For operations like those actions will always fall short because it is always locked to the active layer when you start the script compared to running the commands with breaks. You need to go into the API. And even there things remains stuck. I created a way to do copy paste and it is literally the same issue as the operation half way refers to another layer to complete the course but it does not like that at all.
My code is on the tela plugin on mirror fix to bypass the fixation on the active layer of the start of the script. But I don’t know honestly how long it will last they keep breaking functionality to the point of non functionality. Actions broke it then the API broke it. Now I am literally forcing it to exist and I am just waiting for the next break to happen. my code is awful and should not exist but works somehow.
Also I have a suspicion repeating commands on a couple of actions bypass it. It did happen with selections. Apply selection once does not work but if you do it twice it is convinced and applies it. Maybe it works with merge too.
so because of all that I am just in maintaining mode now. No new things. Qt6 is coming and it will break the plugins in one way or another. Last time took months to fix hopefully my code is more resilient this time.
I tried node.mergeDown() but it has similar issues so I don’t think it’s about Action. So far only Timer works.
Anyway… thanks for sharing your experience. It’s quite eye-opening to know the whole Krita ecosystem is built upon such a shaking foundation. I was so naive when I thought it’s “Blender for 2D”.
Even though the current situation is like this, I really think Krita will overcome the others and cease to be the alternative and be the main option. and have many other workflows besides just illustration with pixels.
Despite the changes Qt6 will bring many good things.
Krita’s “ecosystem” is not just python plugins though. And to this day I can not understand how people still think it’s “just like Blender but for 2D”, a quite common expectation but the differences are massive and it begins with the funding already, which is in the millions for Blender and only a few thousand for Krita. However it’s also quite honoring if you think about what the small team does pull off with its limited resources.
I wish. But I guess ultimately there isn’t that much demain: Photoshop is much cheaper than Maya, and CSP is even cheaper, so there isn’t a strong incentive that pushing users to look for free alternative.
from krita import *
app = Krita.instance()
doc = app.activeDocument()
def func():
if not doc:print("no active document");return
steps = [
lambda: (
app.action('merge_layer').trigger(), #make sure to put commas in the expression
print("merging first time")
),
lambda: (
action := app.action('merge_layer'), #create a variable with "walrus operator"
action.trigger(),
print("merging 2nd time")
),
]
delay_per_step = 100 # milliseconds
for i, step in enumerate(steps):
QTimer.singleShot(delay_per_step * (i + 1), step)
func()
It uses timers. Save your your work before trying it! I’ve not found things to execute out of order yet, but 100ms is a long time for most operations in Krita. Adjust the delay accordingly.
See this post for another example that uses regular functions not lambda expressions, so you don’t need the walrus operator. Same timer rule applies. Everything within function/lambda expressions is in its own scope. Ask any chatbot to explain what it all means
Actually Krita is indeed the closest to “Blender but for 2D”, there are simply not a lot of alternatives exist with extensive python scripting. I searched and Krita is most comprehensive
most of UI stuff works ok, and with less-popular things there is pillow/numpy/scikit/svgtools/whatever, works like a charm with projection pixels. Just get content of layers, put in PIL, overlay as you want and put back. like 10 lines of code without timers and this is actually faster than in Krita - since no intermediate refreshProjection stuff