Unusable, crashing, or silently doing nothing
✅ verified
This chapter holds one of the most valuable tables in the book. A great many of Krita’s setters do not validate their arguments, do not raise, and silently do nothing, and a few kill the process outright in the C++ layer with no Python traceback.
It crashes
| thing | behaviour |
|---|---|
paint* with no view | segfault, with no traceback |
QT_QPA_PLATFORM=offscreen | Krita 6 segfaults on startup; Xvfb is required |
node.save() on a vector layer | segfault, killing the process. Take the pixels from the composite |
createFillLayer(..., "simplenoise", ...) | segfault ("gradient" and "pattern" are fine) |
| the layers’ tiles eat all the memory | the OOM killer takes the process without a word. It is the number of layers times the area painted on each (once wrongly recorded as “233 KB of undo stack per paint*”, ch. 18) |
Six probes (FilterMask, ColorizeMask, Preset XML, Scratchpad, delivery, Shape) added nothing new that crashes – everything that failed did so by honestly returning False or by silently doing nothing.
It silently does nothing
| thing | behaviour | how it was found |
|---|---|---|
doc.setSelection(Selection()) / sel.clear() | “select nothing”, after which every stroke paints 0 px | “suddenly no brush works” |
setBlendingMode("any string at all") | reads back unchanged and composites as normal | measure the composite |
scaleImage(..., strategy="xyzzy") | the size changes anyway, by an unknown interpolation | validate against filterStrategies() first |
setPaintingOpacity(255) | saturates to 1.0 | measure the peak alpha |
f) Bristles-5 Flat + opacity | ignored entirely | as above |
| a mypaint preset painting a 1 px dab | leaves nothing at all | count the pixels |
setBrushSize(n) | paints between 0.2n and 0.93n | measure the width |
| a transparency mask attached to the rootNode | an orphan node with no effect | attach it under the target layer |
group passthrough=True + clip | clipping stops working | look at alpha |
a selection against filter.apply() | does not confine it | the whole canvas changed |
| an autosave left by a crash | every later run hangs | rm -f *autosave*.kra |
| an environment variable holding a relative path | stops working after a cd | realpath it first |
Document.setBatchmode(True) against the undo stack | does nothing (291 against 297 strokes a second, and RSS grows regardless) | — |
kritarc’s undoStackLimit against paint* | does nothing | RSS grows to gigabytes regardless |
filter.apply()’s return value | None on success too | check the pixels yourself |
pixelData() reading stale data | a single read can give the opposite conclusion | s.settle() until it is stable |
createFilterMask(name, f, **node**) | confines nothing and processes the whole canvas | measure the profile: there is no transition band |
Scratchpad.resize() / setGeometry() | does nothing, and it stays 640x480 | show() before resize() |
changing diameter on a preset with a predefined tip | does nothing | measure the brush width |
Preset.fromXML(toXML()) on a mypaint preset | lossy: 14 px becomes 442 px | measure the brush width after the round trip |
Shape.setTransformation | replaces rather than composes, wiping out the original placement | write T0 * QTransform().translate(...) |
| a shape’s geometry is in points | asking shapeAtPosition in px returns None | PT = 72 / doc.resolution() |
| a group’s projection leaves a ghost after a shape moves | the centroid of a large box is dragged off | judge a translation by boundingBox() or against an analytic ground truth |
forgetting setEditKeyStrokes(False) on a ColorizeMask | the output is the editing preview, blended 50% with white | measure the patches’ colours |
It does not exist
| thing | instead |
|---|---|
Krita.instance().createInfoObject() | from krita import InfoObject |
Node.setFilter() | a FilterLayer has one |
| an API for creating a keyframe | one document per frame, exported as a PNG sequence |
| an API for clearing the undo stack | make fewer calls (ch. 18) |
setPassThroughMode on createNode(..., "grouplayer") | use createGroupLayer |
node.setLayerStyleFromAsl | Nothing can be fed in: a hand-written PSD descriptor XML returns False (the XML is valid; the number of styles parsed out is not 1), layerStyleToAsl() returns an empty string when no style is set so there is no skeleton to be had, and there is not one .asl on this machine in asl/, layerstyles/ or any of the four bundles |
painting on a Scratchpad | There is none. Only fill* and loadScratchpadImage; paintEngine, paintEvent and paintingActive come from QWidget |
Palette.save() | returns False and writes nothing, and with no document-level palette API it cannot get into a .kra |
a hand-written transform_params | TransformMask.fromXML returns False. Read the factory toXML() skeleton, edit that, and it returns True |
documentation for ColorizeMask.initializeKeyStrokeColors | it does not appear in api.kde.org’s method list and exists only in the source. Without it, setKeyStrokePixelData returns False every time |
Judged wrongly
These were recorded as unusable and later turned out to be measurement mistakes:
| recorded as | truth |
|---|---|
setInheritAlpha does nothing (the earliest edition) | the base and top were built under the root node, where a fully opaque Background makes the accumulated alpha 255 everywhere. Inside a group it works |
| a transparency mask has no effect (the earliest edition) | it was attached in the wrong place |
| the erase blending mode removed 2198 px | stale pixels were read. Once settled it reads 0 |
| filters are not confined by a selection (the old conclusion) | that was a conclusion about apply(). createFilterMask(name, f, **Selection**) is confined by the selection and feathers with it, with a transition band about 1.5 times the feather (ch. 15) |
| ColorizeMask “computes nothing” | initializeKeyStrokeColors was never called and no keystroke was ever created; with it, all three regions’ areas match at 0.98 |
| a shape “does not move all the way” | the measuring box was smaller than the shape and what moved outside it was cut off; measured against an analytically translated ground truth, the deviation is 0.0861 |
What they have in common: the criterion was looking at the wrong thing. Ch. 16.