Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

The environment and the harness

✅ verified · installing, self-checking and the common reasons it will not start are in “Installing and self-checking”; this chapter is the mechanism and the reference.

Why it has to be Krita 6, and why it has to be the RPM

Node.paintLine and paintPath are new in 6.0 and are the foundation of all of this. 5.x leaves injected tablet events, every one of which has to be debugged on its own (coordinate calibration unknowable, strokes landing only on the first layer, saveAs segfaulting, throughput a few hundred strokes a minute). Flatpak’s sandboxed Python has no numpy, and it moves the paths to ~/.var/app/org.kde.krita/.

The harness: a separate HOME

The harness script (appendix A) creates krita-bot/. It is idempotent and copies no library code:

krita-bot/
  run_job.sh                                   the launcher (below)
  .config/kritarc                              undoStackLimit=3, EnableAutosave=false, AutoSaveInterval=0, [python] enable_autopaint=true
  .local/share/krita/pykrita/autopaint/        the extension: createActions does QTimer.singleShot(3000, run) -- it has to wait for the
                                               main window and the resource system, because paintLine takes its brush preset from the
                                               window; run() reads $KRITA_JOB, execs it, prints JOB OK / JOB FAIL, then os._exit(0)

os._exit(0) rather than exiting normally: a normal exit goes through Krita’s shutdown and sometimes hangs. Do not add other memory-related keys to kritarc; measured, they break painting outright. And undoStackLimit does nothing for memory, since what grows is the tiles (ch. 18).

run_job.sh:

export HOME=<harness>  XDG_RUNTIME_DIR=/tmp/xdg-kritabot  LANG=C.UTF-8  PYTHONIOENCODING=utf-8
export DTP_SRC="${DTP_SRC:-<pkg>/core/src:<pkg>/craft/src:...}"   # one src per package, colon-separated
export PYTHONPATH=$DTP_SRC${PYTHONPATH:+:$PYTHONPATH}      # what a job imports is the library's own source
cd "$HOME"; rm -f *autosave*.kra           # a leftover autosave pops up the recovery dialog and hangs every later run
xvfb-run -a -s "-screen 0 1600x1200x24" krita --nosplash     # --gui runs it on a real desktop (tools/gui.py)

harness.run_job(module, args, ...): writes the job’s arguments as JSON into DTP_JOB, strips PYTHONHOME, PYTHONPATH, PYTHONSAFEPATH, VIRTUAL_ENV, UV_PROJECT_ENVIRONMENT and .venv/bin before starting Krita, and raises JobFailed on seeing JOB FAIL – a failed job has to fail its caller, or the criteria run on the previous round’s output (ch. 17, case 35).

A job’s skeleton

A job is imported only inside Krita, and the shell the autopaint extension execs which reads DTP_JOB, does import dtp.jobs.<module> and calls job(**args).

from dtp.krita.session import *            # only at module level (a job is exec'd and has no __file__)

def job(plan_dir, out_dir, scale=4, name="x"):
    s = Session(W, H, name, journal=True, out_dir=out_dir)   # document, view and output directory
    ...                                                       # build the tree (from ops/layers.py's building blocks) and paint
    s.stage("label")                                           # stages/NN-label.png
    s.final()                                                 # final.png + final.kra + layers.json

A job is passed its output directory explicitly; a standalone probe script uses DTP_OUT (the Krita process boundary). Apart from the machine-level settings and that boundary, the library reads no environment variables.

Self-checking

The self-check script (appendix A) runs 31 checks inside Krita, each of which really runs and reads the pixels back (they are listed in “Installing and self-checking”). After changing the session wrapper, all of them have to pass.

ComfyUI: the foundation for the other models

Two jobs, one model each:

jobmodelnodefirst runhow far to trust it
lineart extractionManga2Anime(comfyui_controlnet_aux)Manga2Anime_LineArt_Preprocessora dozen seconds or sodiscriminative, and steady
text-prompted segmentationGroundingDINO SwinT + SAM ViT-B(comfyui_segment_anything)GroundingDinoSAMSegmentabout 2 s per worddepends on the word

The driver (appendix B) is a thin HTTP client: POST /prompt plus polling /history; LoadImage accepts only a filename under ComfyUI/input/, so the file is copied there first; It produces one lineart image per extractor and one mask per word (appendix F). Of the three linearts, manga is the cleanest (anime is soft with uneven line widths, and std, being a gradient method, catches bright edges too and is noisy). The completion model (See-through) does not go through ComfyUI and runs as its own process (ch. 3).

Environment: ComfyUI itself is not modified, only custom_nodes and models are added; its location is comfy_dir in dtp.toml; and the ComfyUI script (appendix A) starts it on 127.0.0.1:8188, logging to work/comfy.log. The two patches for transformers 5 are in “Installing and self-checking”.

Pitfalls: kill the service by port with fuser -k 8188/tcp and Krita with pkill -x krita – pkill -f "<a pattern that matches your own command line>" kills the shell that issued the command. A working directory cd-ed into in one command carries over to later calls, so a background chain uses absolute paths throughout. And a process left by an earlier session queues work into the same queue, which is cleared with POST /queue {"clear": true} and /interrupt.