Bug? setActiveNode(n) fails to make n the action target

The little script (below) creates a new document, adds a paint layer, sets that layer to be the active node, enables animation on it, sets current time to 0 and then triggers the ‘add_blank_frame’ action.

My expectation is that this adds a blank frame to the paint layer at time 0 and that as a result when I right-click on that frame in the timeline the “Create Blank Frame” menu item is unavailable as the frame should already have been created and added.

That, however, is not always the case because sometimes the blank frame is added to the background layer instead of the paint layer. In fact whichever of the two layers I manually select by clicking on it in the current document will determine which layer gets the blank frame in the new document.

I wanted to log this behaviour and found that the animated() property of the background layer becomes True when it gets the blank frame. So when I create a random doc, select the paint layer in it and then run my script, everything is as it should be and the background layer of the new doc is not animated / does not have the frame created:

root: animated=False
    Background: animated=False
    PaintLayer: animated=True

However, when I select the background frame on a document and then run my script, the background frame of the newly created document becomes animated / gets the blank frame set:

root: animated=False
    Background: animated=True
    PaintLayer: animated=True

Clearly some variation of this is what @mallyj was struggling with, too, in Programmatically changing selected frame in Timeline docker (as opposed to active frame).

Would we expect the explicitly activated node to have the action applied, i.e. is this a bug?
How can I make sure my code always targets the paint layer?

Here’s my script:

inst = Krita.instance()
doc = inst.createDocument(60, 40, "TestDoc", "RGBA", "U8", "", 100.0)
inst.activeWindow().addView(doc)
newLayer = doc.createNode("PaintLayer", "paintlayer")
doc.rootNode().addChildNode(newLayer, None)
newLayer.enableAnimation()
doc.setActiveNode(newLayer)
doc.refreshProjection()
doc.setCurrentTime(0)
doc.waitForDone()
inst.action('add_blank_frame').trigger()

def printNodes(n, indent = 0):
    print(4*indent*' ' + n.name() + ': animated=' + str(n.animated()))
    for c in n.childNodes():
        printNodes(c, indent+1)
printNodes(doc.rootNode())

Hi

yes, “bug”
:frowning:

The Document.waitForDone() doesn’t work properly and when working with triggered action results are not really good…

Here a modified version of script:

from krita import *
from PyQt5.Qt import *

def sleep(value):
    loop = QEventLoop()
    QTimer.singleShot(value, loop.quit)
    loop.exec()

inst = Krita.instance()
doc = inst.createDocument(60, 40, "TestDoc", "RGBA", "U8", "", 100.0)
inst.activeWindow().addView(doc)
newLayer = doc.createNode("PaintLayer", "paintlayer")
doc.rootNode().addChildNode(newLayer, None)
newLayer.enableAnimation()
doc.setActiveNode(newLayer)
doc.refreshProjection()
doc.setCurrentTime(0)
doc.waitForDone()
sleep(500)
Krita.instance().action('add_blank_frame').trigger()

def printNodes(n, indent = 0):
    print(4*indent*' ' + n.name() + ': animated=' + str(n.animated()))
    for c in n.childNodes():
        printNodes(c, indent+1)
printNodes(doc.rootNode())

I use a sleep() with a duration long enough to be sure that action has been triggered (on my computer for this action, I need to wait 500ms)

You can take a look on this topic too:

I show examples to create animated document, and the one using triggered action and waitForDone() is (currently) clearly not a solution…

Grum999

1 Like

Is it reported already on bugzilla? Would be good to have it there.

1 Like

I don’t think, I only found 3 bugs where waitForDone() is in comments.

One of them was created by me, is fixed but it seems that fix wasn’t made on waitForDone() (I didn’t really understand the bug fix explanation :sweat_smile:)
https://bugs.kde.org/show_bug.cgi?id=426349

Two others are trace of crash where call to waitForDone() appears in crash log but I don’t if it’s really related to this function or not.

I can create a bug, but if I just tell “it seems that waitForDone() is not working” it might be rejected.
And I currently don’t know if problem with this method is a constant (ie: never works) or depends of previous called method or context (like it seems to be in bug 426349 but I’m finally not sure…)

Grum999

Thanks so much for the incredibly swift response! Unfortunately, the new script with the sleep invocation fails for me in the same way as the original version did, even when I increase the wait to 5 whole seconds.

:thinking:
Seems to be the same problem than described here

I’m able to reproduce your case but it’s weird.

  1. I have a document opened, with 2 layers
    – The background layer is active
    – The first frame in background layer is selected
  2. I execute the script
    – New document is created
    – New paint layer on new document is activated
    – Background layer keep the selection and blank frame is created on background layer instead of active layer

In the following case, the script works:

  1. No document opened when I execute script
  2. On an opened document, the first frame of layer above the background layer is selected

Is it the same on your side?

Note: it’s late here, I’m going to sleep :sleeping:

Grum999

When you say the background layer is active and selected, how do you differentiate between the two? I know what a selected layer looks like, but I don’t know how I can tell which one’s activated.

But what you’re describing is what I’m seeing, yes. In essence, when running the script I don’t reliably get the frame created in the desired layer but often in the background layer and that seems to be the case when the background layer is selected in the document I’m last in before running the script.

Night, night, it’s time indeed!

Like described in topic I gave:

On this screenshot, you can see the active layer “Paint layer 4”:

  • Row slightly blue
  • Blue lines on top+bottom of layer properties on left side
    And the selected frame is the yellow/orange square

One solution I found to avoid the problem is to activate layer and set current frame number, and then add document to view:

inst = Krita.instance()
doc = inst.createDocument(60, 40, "TestDoc", "RGBA", "U8", "", 100.0)
newLayer = doc.createNode("PaintLayer", "paintlayer")
doc.rootNode().addChildNode(newLayer, None)
doc.setActiveNode(newLayer)
doc.refreshProjection()
newLayer.enableAnimation()
doc.setCurrentTime(0)
doc.waitForDone()
inst.activeWindow().addView(doc)
doc.waitForDone()
Krita.instance().action('add_blank_frame').trigger()

In this case I don’t reproduce the bug, but if you have to process more than one frame not sure that’s a solution…

Grum999

It’s quite amazing, that solution of yours, thank you so much! When I go back to adding many frames by adding the lines below, right under your example, it still does work as I would hope:

for i in range(10):
    doc.setCurrentTime(i)
    Krita.instance().action('add_blank_frame').trigger()

Should we still report this as a bug? I think probably yes but what would be the longer term bug fix in Krita to render such a workaround obsolete?

It’s not clear to me what the ideal behaviour is, partly because I don’t understand why we’re ever differentiating between an activated vs a selected layer. Is it meant to be the same as in Excel and Blender which according to my quick googling seem to differentiate between a selection that may consist of several objects and an activation which targets only one object, usually the most recently selected of the bunch (or the last one deselected if nothing is selected anymore)?

There’s probably a bug to report, but I’m not sure about how to report it (by how, I mean how to explain it properly because I’m currently not sure to determinate exactly where is the bug)

And there’s 2 bugs:

  • One with waitForDone() that is not working as expected here
  • One with the active but not selected layer(?)

I think this is because when you’re working on an animation, you can draw an a layer (the active one) and decide to made some action on some frames that related or not related to active layer (you can do multiple frame selection on multiple layers to do insert/remove/other stuff)

Maybe the setCurrentTime()should ensure that when going to a frame, the selection of frame in current layer is also applied (and in this case this could be a solution to fix bug… I don’t know)

@tiar or @emmetpdx or @eoinoneill might have a better expertise than me about this problem.

Grum999

The difference is that you can only have one active layer, and multiple selected layers. When an action is applied that can only be applied to one single layer, but multiple layers are selected, it’s the active layer which this will be applied on.

2 Likes