Gap Closing Selection Script (WIP)

from krita import *
from PyQt5.QtCore import QPoint, QPointF, QEvent, QTimer
from PyQt5.QtGui import QImage,QMouseEvent,QCursor

def find_current_canvas():
    q_window = application.activeWindow().qwindow()
    q_stacked_widget = q_window.centralWidget()
    canvas = q_stacked_widget.findChild(QOpenGLWidget)

    return canvas

def click_canvas():

    mouse_press = QMouseEvent(
            QEvent.MouseButtonPress,
            posF,
            Qt.LeftButton,
            Qt.LeftButton,
            Qt.NoModifier)
    mouse_release = QMouseEvent(
            QEvent.MouseButtonRelease,
            posF,
            Qt.LeftButton,
            Qt.LeftButton,
            Qt.NoModifier)

    QApplication.sendEvent(canvas, mouse_press)
    QApplication.sendEvent(canvas, mouse_release)

def clipFillColor():
    def fillColor(node):
        image = QImage(w, h, QImage.Format_ARGB32)
        image.fill(Qt.red)
        pixel_ptr = image.constBits()
        pixels = bytes(pixel_ptr.asarray(image.byteCount()))
        node.setPixelData(pixels, 0, 0, w, h)
    # clip red color
    redNode = activeDoc.createNode("red", "paintlayer")
    fillColor(redNode)
    allSe.copy(redNode)
    redNode.remove()

def makeSelection():
    global lineSe

    startSe = activeDoc.selection()#selection by first Click
    application.action('deselect').trigger()

    invertSe = startSe.duplicate()
    invertSe.invert()

    lineSe = invertSe.duplicate()
    lineSe.border(1,1)#selection of line art

    # selection that close gaps
    gapSe = lineSe.duplicate()
    gapSe.grow(5,5)
    gapSe.shrink(5,5,True)
    gapSe.grow(1,1)
    gapSe.border(1,1)

    # selection that can make hole in the gap selection
    needleSe = lineSe.duplicate()
    needleSe.grow(5,5)
    needleSe.subtract(lineSe)
    needleSe.shrink(3,3,True)
    needleSe.grow(1,1)

    # make hole in the gapSelection
    gapSe.subtract(needleSe)

    gapSe.paste(closerNode, 0, 0)# gap closer

    # if aaBool:# restore state and ready for secondClick
    #     aaBox.setChecked(True)

def expand():
    def parameter(selection):
        # grow of selectoin
        if box_grow_v >= 0:
            selection.grow(box_grow_v, box_grow_v)

        elif box_grow_v < 0:
            selection.shrink(abs(box_grow_v), abs(box_grow_v), True)
        
        # feathering of selection
        if box_feather_v > 0:
            selection.feather(box_feather_v)
        
        return selection

    global compSe

    if activeDoc.selection():
        currentSe = activeDoc.selection()

        se = currentSe.duplicate()
        se.subtract(lineSe)
        for i in range(3):# fill inside
            se.grow(1,1)
            se.subtract(lineSe)
        
        se.intersect(allSe)

        compSe = parameter(se)

def mixSelection():
    global compSe
    global preSe
    # Compatible with selection mode
    if preSe != None:
        if controlMode == 2:
            preSe = compSe

        elif controlMode == 3:
            preSe.intersect(compSe)

        elif controlMode == 4:
            preSe.add(compSe)

        elif controlMode == 5:
            preSe.subtract(compSe)

        elif controlMode == 6:
            preSe.symmetricdifference(compSe)

        compSe = preSe

def afterCare():
    # restore state
    buttons[controlMode].setChecked(True)
    closerNode.remove()
    activeDoc.setActiveNode(activeLayer)

    box_grow.setValue(box_grow_v)
    box_feather.setValue(box_feather_v)

def visualizer():
    # to visualize the selection(krita has bag?)
    if compSe == None:
        activeDoc.setSelection(preSe)
    else:
        activeDoc.setSelection(compSe)

    application.action('invert_selection').trigger()
    application.action('invert_selection').trigger()

def settingTimer(ms, func):

    timer = QTimer()
    timer.setInterval(ms)
    timer.setSingleShot(True)
    timer.timeout.connect(func)

    return timer

application = Krita.instance()
activeDoc = application.activeDocument()
activeLayer = activeDoc.activeNode()
rootNode = activeDoc.rootNode()
w = activeDoc.width()
h = activeDoc.height()

allSe = Selection()
allSe.select(0, 0, w, h, 255)
lineSe = Selection()
compSe = Selection()
compSe = None

if activeDoc.selection() != None:
    preSe = activeDoc.selection()# memory original selection
    application.action('deselect').trigger()
else:
    preSe = None

canvas = find_current_canvas()
if not canvas.isActiveWindow():
    canvas.activateWindow()

# click point
pos = QCursor.pos()#起動時点のカーソル位置
posF = QPointF(canvas.mapFromGlobal(pos))

# pick contiguous selection tool
tooldock = next((w for w in application.dockers() if w.objectName() == 'ToolBox'), None)
sebutton = tooldock.findChild(QToolButton,'KisToolSelectContiguous')
sebutton.click()

# the tool option docker
qdock = next((w for w in application.dockers() if w.objectName() == 'sharedtooldocker'), None)
wobj = qdock.findChild(QWidget,'KisToolSelectContiguousoption widget')
buttons = wobj.findChildren(QToolButton)
boxs = wobj.findChildren(QSpinBox)# input box
aaBox = wobj.findChild(QCheckBox)# antialiasing check box

n = 2
while n < 7:
    if buttons[n].isChecked():
        num = n
    n += 1
controlMode = num # original mode
buttons[2].setChecked(True)# pick "replace"

# input to the box
box_grow = boxs[0]
box_feather = boxs[1]

box_grow_v = box_grow.value()
box_feather_v = box_feather.value()
box_grow.setValue(0)
box_feather.setValue(0)

# # check antiAliasing
# aaBool = aaBox.isChecked()
# if aaBool:
#     aaBox.setChecked(False)# set AA "False" for running speed

closerNode = activeDoc.createNode("closerNode", "paintlayer")
closerNode.setBlendingMode("not_converse")
rootNode.addChildNode(closerNode, None)# add layer for closer

clipFillColor()# clip red color

timer1 = settingTimer(800, makeSelection)# selection
timer2 = settingTimer(1000, click_canvas)# selection
timer3 = settingTimer(1800, expand)# selection
timer4 = settingTimer(2200, mixSelection)# selection
timer5 = settingTimer(2400, afterCare)# selection
timer6 = settingTimer(2600, visualizer)# selection


def runAll():
    click_canvas()
    # timer0.start()# firstClick
    activeDoc.waitForDone()

    if activeDoc.selection():
        timer1.start()#makeSelection()
        # activeDoc.waitForDone()
        timer2.start()#click_canvas()# secondClick
        # activeDoc.waitForDone()
        timer3.start()#expand()
        # activeDoc.waitForDone()
        timer4.start()#mixSelection()
    # activeDoc.waitForDone()
    timer5.start()#afterCare()
    # activeDoc.waitForDone()
    timer6.start()# Time difference required to visualize the selection
runAll()

This script is designed to run very slowly. If the selection does not appear, it is probably an environmental issue. (Note that it is a script that creates a selection, not fills it). Does anyone besides me get this script to work correctly in the first place?

Implementing it directly in Krita is not possible with my current knowledge.