SUMMARY Detailled description of problem is available on krita-artist.org https://krita-artists.org/t/layers-manipulation-is-buggy/11566?u=grum999 In few words, with Krita Plus (4.4.0) and Next (5.0.0), some layer manipulation made from script are totaly asynchronous and then, as script execution is fast, when an instructions on layers is made, the next one are executed before the previous ones are finished. @Tiar may have found origin of problem: https://krita-artists.org/t/layers-manipulation-is-buggy/11566/7?u=grum999 >> It looks like a regression from the multi-threaded fill layers: >> 3f85089fa36ad371ce1322117c0c420e806b580f is the first bad commit >> commit 3f85089fa36ad371ce1322117c0c420e806b580f STEPS TO REPRODUCE Please read topic in Krita artist (sorry, but formatting in KDE bugtracking system is not good enough to be able to copy all explanation properly provided on krita-artist.org) OBSERVED RESULT When execution of script is made with Krita 4.3.0, layers content is correct When execution of script is made with Krita Plus and/or Next, layers content: - is correct when a delay (sleep) is applied after each layer instruction - is not correct when no delay is applied after each layer instruction EXPECTED RESULT Layers content must be correct when no delay is applied after each layer instruction (like it was with Krita 4.3.0) SOFTWARE/OS VERSIONS Windows: not tested macOS: not tested Qt Version (compiled): 5.12.9 Version (loaded): 5.12.9 OS Information Build ABI: x86_64-little_endian-lp64 Build CPU: x86_64 CPU: x86_64 Kernel Type: linux Kernel Version: 4.19.0-10-amd64 Pretty Productname: Debian GNU/Linux 10 (buster) Product Type: debian Product Version: 10 Desktop: KDE ADDITIONAL INFORMATION -
I'm not sure about Component / Version to set (4.4.0 and 5.0.0 are not in version list) Grum999
It's technically a regression caused by making the Fill Layer multi-threaded, but I think just adding waitForDone() in the FillLayer.cpp should solve this. I haven't checked it yet though. (If you want @Grum check it, please see if adding waitForDone() instead of sleep() would make it work). d52e55f6cc0567ac75c8
In script example, I've replaced > sleep(sv) by > newDocument.waitForDone() There's no change in results, it's still incorrect Grum999
Created attachment 131537 [details] Improved Python script showing all three cases I attached an improved script that shows all three cases: - using doc.waitForDone() - using 1 ms sleep (not enough for Krita to finish generating) - using 125 ms sleep (enough) First two cases give bad results. Steps to reproduce: 1. Open Krita. 2. Go to Tools -> Scripts -> Scripter. 3. Paste the script and execute.
I've made some other tests. On my side it seems that to get expected results, call to sleepOrWait() is needed only after: - a call to layer.setGenerator() - a call to layer.setBlendingMode() When other call to sleepOrWait() function are in comment, expected result is still OK. With this I can't affirm that problem doesn't occurs for mergeDown(), addChildNode() or other layer/document manipulation, but this tends to confirm that problem, as suspected bu @Tiar, is relative to fill layer. Grum999
Git commit 74dbabc3636fd5a1c18967d9fed105fb2ddaf7b2 by Dmitry Kazakov. Committed on 16/09/2020 at 11:37. Pushed by dkazakov into branch 'master'. Fix Python API for Node::addChildNode() The function should use the command, run it in a stroke and wait for it to finish. That ensures that 1) updates are issued; 2) undo item is created; 3) no threading issues arise. M +12 -4 libs/libkis/Node.cpp https://invent.kde.org/graphics/krita/commit/74dbabc3636fd5a1c18967d9fed105fb2ddaf7b2
Git commit 57dbf8a946d022a86855b95f278f1013f29d3148 by Dmitry Kazakov. Committed on 16/09/2020 at 11:37. Pushed by dkazakov into branch 'master'. Fix Python API for Node::setBlendingMode() The function should use the command, run it in a stroke and wait for it to finish. That ensures that 1) updates are issued; 2) undo item is created; 3) no threading issues arise. M +9 -1 libs/libkis/Node.cpp https://invent.kde.org/graphics/krita/commit/57dbf8a946d022a86855b95f278f1013f29d3148
Git commit 623c5114e009485391be9fbe943a1d9cd4da6bd3 by Dmitry Kazakov. Committed on 16/09/2020 at 11:37. Pushed by dkazakov into branch 'master'. Fix KisGeneratorLayer support KisDelayedUpdateNodeInterface It is needed to make sure the layer is updated when some synchronous non-gui provoked actions are requested. E.g. saving or changing via python API. The path also fixes FillLayer's Pythong API to be synchronous using this fix. M +15 -0 libs/image/generator/kis_generator_layer.cpp M +7 -1 libs/image/generator/kis_generator_layer.h M +6 -0 libs/libkis/FillLayer.cpp https://invent.kde.org/graphics/krita/commit/623c5114e009485391be9fbe943a1d9cd4da6bd3
After my patches the following script works fine: =========================================================== from krita import * from PyQt5.Qt import * from PyQt5 import QtCore from PyQt5.QtCore import ( QByteArray, QPoint ) from PyQt5.QtGui import ( QColor, QImage, QPixmap ) def buildQImage(color, w, h): """Generate a QImage to use for example""" img=QImage(w, h, QImage.Format_ARGB32_Premultiplied) img.fill(Qt.transparent) pxmTgt = QPixmap.fromImage(img) gradientb = QRadialGradient(QPointF(w/3, h/4), 2*w/3) gradientb.setColorAt(0, color) gradientb.setColorAt(1, Qt.black) gradientf = QRadialGradient(QPointF(w/3, h/4), 2*w/3) gradientf.setColorAt(0, Qt.white) gradientf.setColorAt(0.15, Qt.transparent) gradientf.setColorAt(1, Qt.transparent) canvas = QPainter() canvas.begin(pxmTgt) canvas.setRenderHint(QPainter.Antialiasing) canvas.setPen(Qt.NoPen) canvas.setBrush(gradientb) canvas.drawEllipse(QRect(20,20,w-40, h-40)); canvas.setBrush(gradientf) canvas.drawEllipse(QRect(20,20,w-40, h-40)); canvas.end() return pxmTgt.toImage() def setLayerFromQImage(layerNode, image): """Set QImage as layer content""" position = QPoint(0, 0) ptr = image.bits() ptr.setsize(image.byteCount()) layerNode.setPixelData(QByteArray(ptr.asstring()), position.x(), position.y(), image.width(), image.height()) def sleep(value): """Do a sleep of `value` milliseconds""" loop = QEventLoop() QTimer.singleShot(value, loop.quit) loop.exec() dWidth=500 dHeight=500 # Cyan, Magenta, Yellow: colors used to generate layers colors=[QColor(0,255,255), QColor(255,0,255), QColor(255,255,0)] sleepValues=[0, 125] image=buildQImage(QColor(255,0,0), dWidth, dHeight) newDocument = Krita.instance().createDocument(dWidth, dHeight, "Test", "RGBA", "U8", "", 300.0) Krita.instance().activeWindow().addView(newDocument) parentGroupLayer = newDocument.createGroupLayer('Group layer') newDocument.rootNode().addChildNode(parentGroupLayer, None) for i in range(len(colors)): # loop over colors and apply basics actions like plugin Newspaper (and Channel2Layers) does newPLayer = newDocument.createNode(f"PaintLayer{i}", 'paintlayer') setLayerFromQImage(newPLayer, image) parentGroupLayer.addChildNode(newPLayer, None) infoObject = InfoObject(); infoObject.setProperty("color", colors[i]) selection = Selection(); selection.select(0, 0, dWidth, dHeight, 255) newFLayer = newDocument.createFillLayer(f"Color{i}", "color", infoObject, selection) parentGroupLayer.addChildNode(newFLayer, newPLayer) # mandatory as when provided to createFillLayer(), infoObject is not applied # must also be applied after node has been added to parent... newFLayer.setGenerator("color", infoObject) newFLayer.setBlendingMode('add') newLayer = newFLayer.mergeDown() # returned layer from merge can't be used, so get the last one currentProcessedLayer = parentGroupLayer.childNodes()[-1] currentProcessedLayer.setBlendingMode('multiply') =========================================================== Though other methods of Python API still have the same problems
Git commit 39bf729d88e70168e1e9ce58cac08fcb5f11f6f3 by Dmitry Kazakov. Committed on 16/09/2020 at 12:19. Pushed by dkazakov into branch 'krita/4.3'. Fix KisGeneratorLayer support KisDelayedUpdateNodeInterface It is needed to make sure the layer is updated when some synchronous non-gui provoked actions are requested. E.g. saving or changing via python API. The path also fixes FillLayer's Pythong API to be synchronous using this fix. # Conflicts: # libs/libkis/FillLayer.cpp M +15 -0 libs/image/generator/kis_generator_layer.cpp M +7 -1 libs/image/generator/kis_generator_layer.h M +7 -0 libs/libkis/FillLayer.cpp https://invent.kde.org/graphics/krita/commit/39bf729d88e70168e1e9ce58cac08fcb5f11f6f3
Git commit 7be91fc9922182bf02a3fc95feb9462ae8948f72 by Dmitry Kazakov. Committed on 16/09/2020 at 12:20. Pushed by dkazakov into branch 'krita/4.3'. Fix Python API for Node::setBlendingMode() The function should use the command, run it in a stroke and wait for it to finish. That ensures that 1) updates are issued; 2) undo item is created; 3) no threading issues arise. M +9 -1 libs/libkis/Node.cpp https://invent.kde.org/graphics/krita/commit/7be91fc9922182bf02a3fc95feb9462ae8948f72
Git commit 7013fef0227028316c8788e828e28a66f026c08d by Dmitry Kazakov. Committed on 16/09/2020 at 12:20. Pushed by dkazakov into branch 'krita/4.3'. Fix Python API for Node::addChildNode() The function should use the command, run it in a stroke and wait for it to finish. That ensures that 1) updates are issued; 2) undo item is created; 3) no threading issues arise. M +12 -4 libs/libkis/Node.cpp https://invent.kde.org/graphics/krita/commit/7013fef0227028316c8788e828e28a66f026c08d
Now it is just a bug, not a regression
Reassigning back to Amyspark :)