I frequently use a process called Trapping for my artwork. This involves making a selection and filling the silhouette of the art with white on a layer below the line art. I attempted to create a script to automate this process, but it’s not working as intended. Here’s what I’ve written so far (it’s a bit messy—I’m clearly not an expert at scripting):
from krita import *
from PyQt5.QtCore import (
Qt,
QPoint,
QPointF,
QEvent)
from PyQt5.QtGui import (
QMouseEvent,
QCursor)
from PyQt5.QtWidgets import (
QWidget,
QApplication)
#===============
#=====Functios=====
#===============
def find_current_canvas():
application = Krita.instance()
q_window = application.activeWindow().qwindow()
q_stacked_widget = q_window.centralWidget()
q_mdi_area = q_stacked_widget.currentWidget()
q_mdi_sub_window = q_mdi_area.currentSubWindow()
view = q_mdi_sub_window.widget()
for c in view.children():
if c.metaObject().className() == 'KisCanvasController':
viewport = c.viewport()
canvas = viewport.findChild(QWidget)
return canvas
def click_canvas(global_pos=None):
canvas = find_current_canvas()
if global_pos is None:
global_pos = QCursor.pos()# cursor position
global_posF = QPointF(global_pos)
posF = QPointF(canvas.mapFromGlobal(global_pos))
else:
global_pos = QPoint(global_pos)
global_posF = QPointF(global_pos)
posF = QPointF(canvas.mapFromGlobal(global_pos))
button = Qt.LeftButton
buttons = Qt.LeftButton
key_state = Qt.NoModifier
mouse_press = QMouseEvent(
QEvent.MouseButtonPress,
posF,
button,
buttons,
key_state)
mouse_release = QMouseEvent(
QEvent.MouseButtonRelease,
posF,
button,
buttons,
key_state)
if not canvas.isActiveWindow():
canvas.activateWindow()
QApplication.sendEvent(canvas, mouse_press)
QApplication.sendEvent(canvas, mouse_release)
def create_layer_below():
"""Creates a new paint layer below the active layer in Krita and sets it as active."""
doc = Krita.instance().activeDocument()
if not doc:
return None # No active document, exit function
currentNode = doc.activeNode()
if not currentNode:
return None # No active layer selected, exit function
def getNextSibling(node):
"""Finds the previous sibling node to insert the new layer below the current one."""
previousNode = None
for foundNode in node.parentNode().childNodes():
if foundNode == node:
return previousNode
previousNode = foundNode
return previousNode
# Create new layer and insert it below the current layer
newNode = doc.createNode("White", "paintlayer")
currentNode.parentNode().addChildNode(newNode, getNextSibling(currentNode))
return newNode # Return the newly created layer in case it's needed
#===============
#=====Code main=====
#===============
# Activate contiguous selection tool
Krita.instance().action('KisToolSelectContiguous').trigger()
click_canvas() # Click at current cursor position (top-left corner if not moved)
create_layer_below()
Krita.instance().action('invert_selection').trigger()
Krita.instance().action('fill_selection_background_color').trigger()
The script kind of works, but it fills the white area on the line art layer instead of the white layer below it. I tried changing the active layer, but it doesn’t seem to work. I’m probably doing something wrong.
Here’s the process I’m trying to automate:
1 - The line art layer is the active layer.
2 - Use the Magic Wand to select an area outside the line art (e.g., the top-left corner of the image at coordinates (1, 1)).
3 - Grow the selection by 2 pixels. The selection should only consider the active layer (line art).
4 - Invert the selection (Ctrl+Shift+I).
5 - Create a new layer below the line art layer and name it “white”.
6 - With the new layer active, fill the selected area with white.
If anyone could help me improve this script or guide me on how to fix the main issue, I’d greatly appreciate it! Thank you in advance for your time and expertise.