mcpField
K
krita

krita

github.com/jablonkai创意与媒体447 次安装今天更新
代码仓库

关于 krita

krita skill 通过 Krita Bridge 在运行中的 Krita 会话里执行 Python 脚本,面向需要自动构建、检查和导出图像的创作者。它利用 libkis 与 PyQt5 操作文档,并通过画布投影和脚本输出验证结果。

主要功能

  1. 01在实时 Krita 会话执行脚本
  2. 02创建并编辑文档与图层
  3. 03绘制像素、矢量和文字
  4. 04应用滤镜、选区与蒙版
  5. 05检查画布投影并导出文件

适用场景

  1. 01自动构建海报并导出
  2. 02批量导出图层或文件
  3. 03处理动画帧并导出视频

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jablonkai/skills --skill krita

安装后在 Claude Code 中输入/krita确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
api-reference.md
recipes.md
scripts/
example-poster.py
krita_bridge.desktop
krita-install-bridge.sh
krita-send.sh
scripts/krita_bridge/
__init__.py
krita_bridge.py

详细介绍

Krita 控制

Krita(/Applications/krita.app)内置 Python 3,并通过 libkis API(from krita import *)开放其整个文档模型,同时提供 PyQt5 用于像素处理。 通过 Krita Bridge 控制它——这是一个在 运行中的 Krita 内运行的 pykrita 插件,可执行 POST 到 127.0.0.1:8737 的任意脚本。由于脚本在运行中的会话的 GUI 线程上执行,画布会随着创作过程实时更新,用户可以看到操作过程,而且只能在主线程中调用的 API 部分也能安全使用。以下每个调用都在 Krita 5.3.4(Python 3.13,PyQt5 5.15.11)中运行过;该 skill 最初是在 5.3.3 上编写的。

  • scripts/krita-install-bridge.sh — 将桥接插件安装到用户的资源文件夹,并在 kritarc 中启用它(一次性操作)。
  • scripts/krita_bridge/ — 插件本身;也可粘贴到 Tools ▸ Scripts ▸ Scripter 中,用于单次会话。
  • scripts/krita-send.sh — 发送 .py 文件(或 -c 'inline code'),并打印捕获的输出;--ping 用于检查桥接是否已启动。
  • scripts/example-poster.py — 完整的构建 → 验证 → 导出脚本,可供参考。
  • references/api-reference.md — 经版本核验的 libkis API(Krita、Document、Node、Selection、滤镜、动画、导出)。在编写超出下方速查表范围的内容前,请先阅读。
  • references/recipes.md — 批量导出、图层提取、动画帧 → 视频、为何不应启动第二个 Krita,以及 API 无法实现的功能。

控制循环

  1. 确保桥接服务已启动:bash scripts/krita-send.sh --ping → {"ok": true, "bridge": "krita", "version": "5.3.4 (git e7e52a7)", "documents": [...]}。 如果没有响应,可能是 Krita 未运行(请用户启动它——安装后,桥接服务会随 Krita 自动启动),也可能是插件尚未安装:

    bash scripts/krita-install-bridge.sh     # Krita must be quit: it rewrites kritarc on exit
    

    该脚本会将插件复制到 pykrita 文件夹,并在 kritarc 的 [python] 组中写入 enable_krita_bridge=true——这与 Settings ▸ Configure Krita ▸ Python Plugin Manager 复选框的作用相同。修改其他应用的配置属于用户级更改,有些设置 会拒绝让你执行此操作;请将这一行命令交给用户,而不是绕过限制。如果当前会话完全不能 安装任何内容,用户可以将 krita_bridge.py 粘贴到 Tools ▸ Scripts ▸ Scripter,然后按 Ctrl+R——桥接服务功能相同,退出时即消失。

  2. 将构建脚本写入临时目录并发送: OUT=/path/to/outdir bash scripts/krita-send.sh /path/build.py。脚本的命名空间中会预先注入 Krita 和 OUT(OUT 也会作为 os.environ["OUT"] 注入),而 KRITA_SEND_TIMEOUT=600(秒)用于应对耗时较长的导出。

  3. **读取反馈。**桥接服务会返回脚本捕获的 stdout/stderr,失败时还会返回 Python 回溯信息——因此 print(...) 是你获取反馈的渠道。其他信息不会报告:Krita 自己的日志 窗口仍在应用内。

  4. 查看结果。doc.projection(0, 0, w, h) 会返回合并画布的 QImage——保存后读取 PNG。这比截图更省事,也更可靠,因为它与导出时生成的内容完全一致。

  5. 迭代,然后交付:修复脚本并重新发送。脚本必须可重复运行——每个版本都创建 新文档,而不是修改已打开的文档,这样重新发送时就不会在上一次尝试的结果上叠加图层。

**所有操作都通过正在运行的会话完成。**不要从 shell 启动第二个 Krita——不要使用 krita --export 或 --export-sequence,也不要在后台启动无头实例。两个 Krita 进程会争用 同一个资源数据库和 kritarc,用户正在使用的进程会因此受影响;批处理任务若弹出自己的 应用,还会抢占焦点,并让你们双方都看不到它的错误。普通格式转换也要通过桥接脚本完成—— 在已经运行的会话中打开、导出并关闭(参见 references/recipes.md)。

脚本速查

Krita.instance() 就是应用程序;所有内容都从它开始。尺寸单位为像素,颜色取决于文档的 色彩空间,而 doc.setBatchmode(True) 可以防止导出和保存操作弹出对话框打扰用户。

文档和图层堆栈
from krita import Krita, InfoObject
app = Krita.instance()
doc = app.createDocument(1200, 1600, "poster_v1", "RGBA", "U8", "", 300.0)
app.activeWindow().addView(doc)          # show it — the user sees the build happen
doc.setBatchmode(True)                   # no dialogs on export/save

layer = doc.createNode("background", "paintlayer")   # grouplayer, filterlayer,
doc.rootNode().addChildNode(layer, None)             # filllayer, clonelayer, …
layer.setBlendingMode("multiply")        # Krita's blend-mode ids, not CSS names
layer.setOpacity(180)                    # 0–255, not a percentage

新建文档时已包含一个绘画图层,该图层填充为不透明白色,并且使用用户语言命名 (匈牙利语版 Krita 中为 Háttér)——应通过 doc.rootNode().childNodes()[0] 获取, 绝不要按名称获取;如果结果需要透明背景,则隐藏该图层或为其填充颜色。也可以直接处理已打开 的文档:doc = app.activeDocument();或者使用 doc = app.openDocument(path) 打开文件 (添加视图以使其可见)。完成后请关闭批处理文档——每个打开的文档都会将整张图像保留在内存中—— 但请先阅读关闭文档:不慎调用 close() 会导致挂起或崩溃。

使用真实绘画引擎绘制笔触

Node.paint* 会通过与数位板笔触相同的绘画操作,使用当前笔刷预设,因此纹理、水彩和图案预设的表现会与手绘时一致:

from krita import ManagedColor
from PyQt5.QtCore import QPoint, QRectF
from PyQt5.QtGui import QColor

view = app.activeWindow().addView(doc)           # paint* needs a view
presets = app.resources("preset")
view.setCurrentBrushPreset(presets["b) Basic-5 Size"])   # names include the "b) " prefix
view.setBrushSize(40); view.setPaintingOpacity(0.9)
view.setForeGroundColor(ManagedColor.fromQColor(QColor("#ff6b35")))

print(layer.paintAbility())              # expect "PAINT" before a long stroke loop
layer.paintLine(QPoint(120, 200), QPoint(900, 640), 1.0, 0.3, "ForegroundColor")
layer.paintEllipse(QRectF(300, 300, 400, 400), "ForegroundColor", "None")

paintLine 接受 QPoint——传入 QPointF 会引发 TypeError,尽管基于矩形的调用需要 QRectF。在调用 paintAbility() 或任何 paint* 方法之前,先添加视图:对于没有视图的文档,这些方法不会友好地报错,而是会导致 Krita 崩溃(Krita 5.3.4 会解引用缺失的视图)。所有内容都会沿用用户当前的预设、颜色和混合模式,因此请明确设置需要的属性。如果几何图形必须在每次运行时看起来完全一致,请改用 QPainter 绘制:

像素:使用 QPainter 绘制,一次性推送

确定性的图像内容会绘制到 QImage 中,然后一次性推送到绘画图层。Krita 的 RGBA8 色彩空间需要 BGRA 字节;而在小端机器上,这正是 QImage.Format_ARGB32 的内存布局,因此无需交换通道:

from PyQt5.QtCore import QByteArray, QPointF, Qt
from PyQt5.QtGui import QColor, QImage, QPainter

img = QImage(doc.width(), doc.height(), QImage.Format_ARGB32)
img.fill(Qt.transparent)
p = QPainter(img)
p.setRenderHint(QPainter.Antialiasing)
p.setBrush(QColor("#ff6b35")); p.setPen(Qt.NoPen)
p.drawEllipse(QPointF(600, 500), 260, 260)
p.end()

ptr = img.constBits(); ptr.setsize(img.byteCount())
layer.setPixelData(QByteArray(ptr.asstring()), 0, 0, img.width(), img.height())

node.pixelData(x, y, w, h) 会读回同一缓冲区,因此可以将现有图层通过 QImage 往返传递,以便分析或修饰。

矢量和文本

在 Krita 中,文本属于矢量内容,可靠的导入方式是使用 SVG:

vec = doc.createVectorLayer("headline")
doc.rootNode().addChildNode(vec, None)
vec.addShapesFromSvg('<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1600">'
                     '<text x="90" y="1320" fill="#f5f5f5" font-family="Helvetica" '
                     'font-size="150">KRITA</text></svg>')
滤镜、选区和蒙版
f = app.filter("blur")                   # bind it: a chained temporary takes its
cfg = f.configuration()                  # InfoObject down with it (see below)
cfg.setProperty("halfWidth", 40); cfg.setProperty("halfHeight", 40)
f.setConfiguration(cfg)
f.apply(layer, 0, 0, doc.width(), doc.height())    # destructive, on that node

from krita import Selection
sel = Selection(); sel.select(100, 100, 400, 400, 255)   # x, y, w, h, selectedness
doc.setSelection(sel)                    # filters and edits then respect it

滤镜 ID 是 app.filters() 返回的字面字符串,其中有些包含空格("gaussian blur");ID 错误时会返回 None,而不是抛出异常。各滤镜的属性名称各不相同——f.configuration().properties() 会打印默认值,这是了解属性最快的方法。非破坏性替代方案是使用滤镜蒙版或滤镜图层(doc.createFilterMask / doc.createFilterLayer)——请参阅 API 参考。

查看前先刷新

Krita 以异步方式合成图像。任何读取或写入合并图像的操作——projection()、thumbnail()、exportImage()、save()——都需要先确保投影为最新状态:

doc.refreshProjection()
doc.waitForDone()
导出和保存
doc.projection(0, 0, doc.width(), doc.height()).save(OUT + "/check.png")   # for you to Read
doc.exportImage(OUT + "/poster.png", InfoObject())    # PNG/JPEG/TIFF/WEBP by extension
doc.saveAs(OUT + "/poster.kra")                       # re-editable, keeps the layer stack
doc.waitForDone()

动画、逐图层导出和批量文件夹处理请参阅 references/recipes.md。

关闭文档
doc.setModified(False)   # or save first — then close() can't ask anything
doc.close()

setBatchmode(True) 不适用于关闭文档:有视图的已修改文档会弹出模态的 “保存更改?” 对话框,发送操作会阻塞,直到 KRITA_SEND_TIMEOUT 超时,而 Krita 会一直等待用户点击。并且,绝不要在创建动画关键帧的那次发送中关闭动画文档——即使调用了 setModified(False)、排空了事件循环或执行了 file_close,Krita 仍会在片刻后于“图层”面板中崩溃。请让文档保持打开状态供用户使用,或在后续发送中将其关闭。

验证

  • 返回的标准输出是即时信号——print(...) 的内容会通过发送器直接返回;失败时会返回回溯信息,并以非零状态退出。
  • 投影 PNG可用于目视检查:将 doc.projection(...)(缩小尺寸也可以)保存到 $OUT,然后读取。请在每个构建脚本末尾执行此操作——只需一行,就能捕获断言无法发现的错误。
  • 数值数据:将尺寸、分辨率和图层列表(名称、类型、边界、混合模式、不透明度)导出为 metrics.json 并读取,不要只凭目测判断图层堆栈。
  • 交付:导出的 PNG/JPEG 可在任何地方打开;保存的 .kra 会保留所有图层,供用户继续编辑。

注意事项

  • 桥接程序在当前会话中运行,因此状态会在多次发送之间保留:打开的文档、当前视图和选区都会保持不变。为文档添加版本号(createDocument(..., "poster_v2", ...)),以免重新发送时与之前的文档冲突。
  • 在导出或保存前调用 setBatchmode(True),否则 Krita 可能会弹出格式选项对话框,但无人可以点击,导致发送超时。它不适用于 close()——请参阅关闭文档。
  • 发送超时通常意味着出现了模态对话框,而不是桥接程序失效:对话框会运行嵌套事件循环,因此桥接程序仍会响应,且 QApplication.activeModalWidget() 会指出该对话框——请参阅 references/recipes.md。
  • 每次读取或导出前调用 refreshProjection() + waitForDone();否则导出的画布可能比最新合成结果落后一版。
  • 不透明度范围为 0–255,混合模式使用 Krita 自有的 ID("normal"、"multiply"、 "add"、"erase" 等)。5.3.x 中没有列出这些 ID 的 API——app.blendingModes() 仅存在于 master 版本中,因此请从图层停靠面板获取 ID。
  • 不要链式调用 app.filter("x").configuration()——Filter 临时对象会被回收,其 InfoObject 也会随之销毁:代码行看起来没问题,却会报错 RuntimeError: wrapped C/C++ object of type InfoObject has been deleted。请将过滤器绑定到一个变量名。
  • 预期为浮点数的地方却收到整数:xRes()/yRes() 会返回浮点数,但 scaleImage 和 setResolution 接受整数,PyQt 会引发 TypeError,而不是进行舍入——请使用 int(...)。
  • node.save() 返回 None,而不是文档所述的布尔值;请检查文件是否存在。
  • 无法通过 API 创建关键帧——setCurrentTime + setPixelData 始终不会创建关键帧,尽管每次保存看起来都正常。每帧都要触发 add_blank_frame,并在之后调用 doc.waitForDone()——该操作是异步的,不等待的话,每帧像素都会提前落在前一帧;请参阅 references/api-reference.md。
  • setPixelData 需要的是节点的色彩空间,而不是任意顺序的 RGBA:对于 "RGBA" / "U8" 文档,Format_ARGB32 缓冲区可以直接使用;对于 U16/F32 文档,请构建匹配的字节数据,或先转换文档。
  • 图层边界不等于文档边界——绘画图层的 bounds() 是已绘制内容的范围,在像素写入前可能为空;处理完整画布时请使用 doc.width()/height()。
  • Krita 退出时会重写 kritarc,因此在运行期间安装或启用插件会被悄悄还原——请先退出 Krita(否则安装程序会拒绝继续)。
  • 通过 GUI 启动的 Krita(open -a krita)无法获取环境变量,因此只有从终端启动 Krita 时,KRITA_BRIDGE_PORT 才会生效。
  • paint* 需要视图,并会继承用户的笔刷状态——没有视图时,即使调用 paintAbility() 也会导致 Krita 崩溃;有视图时,预设、大小、不透明度、混合模式和前景色都取自当前视图,因此请设置所需属性,并先检查 paintAbility() == "PAINT"。paintLine 要求使用 QPoint,而矩形相关调用接受 QRectF。工具、变换控件和画布输入都无法通过脚本操作;菜单命令只能通过 app.action(id).trigger() 触发,该命令会作用于当前文档,且不会返回任何信息。
  • 通过脚本进行的编辑无法可靠地撤销——对用户自己的文档执行破坏性操作前,请复制图层或另存一份副本;请参阅 references/recipes.md。

安全性

安装桥接程序意味着在用户的计算机上运行一个代码执行服务。请在要求用户安装之前说明这一点。

  • 桥接程序绑定到 127.0.0.1:8737(KRITA_BRIDGE_PORT),并会在实时会话中执行 POST 到该地址的任意 Python 代码,使用用户的权限,操作用户已打开的文档。请求不带身份验证:本地的每个进程,以及共享计算机上的其他每位用户,都可以通过它操控 Krita。
  • 网页无法访问。带有 Origin 标头或跨站点 Sec-Fetch-Site 的请求会被拒绝并返回 403,因此用户浏览器中的网页无法访问桥接程序。此检查是唯一的防护措施——没有令牌。
  • 安装方式决定端口会保持开放多久。 krita-install-bridge.sh 会启用一个 pykrita 插件,因此从此以后每次启动 Krita 都会开始监听,无论是否有 Agent 在操控它。通过 Scripter 粘贴代码的方式只会持续到退出 Krita 为止——如果用户不想让端口永久保持监听,请提供这种方式。
  • 要停止桥接程序,请退出 Krita;要移除它,请在 Settings ▸ Configure Krita ▸ Python Plugin Manager 中取消勾选 Krita Bridge。无法远程关闭。

评论

支持 Markdown