Hi,
Trying to understand why Newspaper plugin is not working anymore on Krita 4.4.0 & 5.0.0, after many tests and many hair lost, I finally found something…
I’m not sure, but practically convinced about origin of problem.
The way the plugin is working is to generate layers (color layer), apply some blending mode, merge, and process the halftone on layers…
In Krita 4.3.0 everything works fine.
In krita 4.4.0 & 5.0.0, result is like if some actions on layers were not applied or not applied properly.
So, trying to debug the process with some QMessageBox
between each layer action made the final result OK; difficult to understand where is the problem…
Remembered this topic Bugs - Ok I need some Help, then I tried to put sleep (125ms) between each action and final result is OK.
Here is the sleep method applied between each layer action (ie: using Qt event compatible method)
def sleep(value): """Do a sleep of `value` milliseconds""" loop = QEventLoop() QTimer.singleShot(value, loop.quit) loop.exec()
So, what I think is, with Krita 4.4.0 something has been changed with asynchronous layer management.
And where in topic Bugs - Ok I need some Help the problem ocurred when triggering a QAction, here the problem occurs when calling API.
As I can understand that from user interface it’s not a problem because user action are not fast enough to be in this case, for a script it’s problematic: next function call is made before current calculation has been finished…
And scripting ability with layers became completely useless in this case.
Here a simple example to illustrate the problem.
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)
for sv in sleepValues:
# loop over sleep values to illustrate result of asynchronous calculation
parentGroupLayer = newDocument.createGroupLayer(f'Group layer {sv}ms')
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)
sleep(sv)
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)
sleep(sv)
parentGroupLayer.addChildNode(newFLayer, newPLayer)
sleep(sv)
# 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)
sleep(sv)
newFLayer.setBlendingMode('add')
sleep(sv)
newLayer = newFLayer.mergeDown()
sleep(sv)
# returned layer from merge can't be used, so get the last one
currentProcessedLayer = parentGroupLayer.childNodes()[-1]
currentProcessedLayer.setBlendingMode('multiply')
sleep(sv)
newDocument.rootNode().addChildNode(parentGroupLayer, None)
newDocument.refreshProjection()
The script will generate a picture, and will try to decompose it in Cyan, Magenta and Yellow layers.
There 2 executions:
- One with a sleep set to 0ms
- One with a sleep set to 125ms
With Krita 4.3.0, result is the same in the both case.
And result is correct:
With Krita 4.4.0, result is not the the same:
- Incorrect result with a sleep of 0ms
- Correct result (like in Krita 4.3) with a sleep of 125ms
If someone can test this and confirm the problem exists (Linux, Windows, Mac? or only my computer is concerned?) and my analysis about origin…
Note: I currently can’t tell if all functions are concerned or only a subset…
If I’m right, I currently don’t think I have to find a solution for the plugin as the problem seems to be relative to Krita’s API.
Add a sleep evereywhere is not a good solution, for me when a call to an API function is made, the function shouldn’t return a result until the function has finished to process the data.
Grum999