Simulating Mouse Click on Canvas to Use Fill-and-Enclose Tool

All right, so by inverting one of AkiR’s other snippets that he posted here, I managed to make a function that clicks roughly at the intended document coordinates, regardless of zoom etc.

from krita import Krita
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

def get_q_view(view):
    window = view.window()
    q_window = window.qwindow()
    q_stacked_widget = q_window.centralWidget()
    q_mdi_area = q_stacked_widget.findChild(QMdiArea)
    for v, q_mdi_view in zip(window.views(), q_mdi_area.subWindowList()):
        if v == view:
            return q_mdi_view.widget()


def get_q_canvas(q_view):
    scroll_area = q_view.findChild(QAbstractScrollArea)
    viewport = scroll_area.viewport()
    for child in viewport.children():
        cls_name = child.metaObject().className()
        if cls_name.startswith('Kis') and ('Canvas' in cls_name):
            return child


def get_transform(view):
    def _offset(scroller):
        mid = (scroller.minimum() + scroller.maximum()) / 2.0
        return -(scroller.value() - mid)
    canvas = view.canvas()
    document = view.document()
    q_view = get_q_view(view)
    area = q_view.findChild(QAbstractScrollArea)
    zoom = (canvas.zoomLevel() * 72.0) / document.resolution()
    transform = QTransform()
    transform.translate(
            _offset(area.horizontalScrollBar()),
            _offset(area.verticalScrollBar()))
    transform.rotate(canvas.rotation())
    transform.scale(zoom, zoom)
    return transform

def get_global_from_document_coords(doc_x, doc_y):
    app = Krita.instance()
    view = app.activeWindow().activeView()
    document = view.document()
    if document:
        q_view = get_q_view(view)
        q_canvas = get_q_canvas(q_view)
        transform = get_transform(view)
        
        # Calculate the center of the document
        centerCanvas = QPointF(0.5 * document.width(), 0.5 * document.height())
        
        #Convert document coordinates to coordinates relative to the center
        local_pos = QPointF(doc_x, doc_y) - centerCanvas
        
        # Apply the transformation to convert to canvas coordinates
        transformed_pos = transform.map(local_pos)
        
        # Find the center of the canvas
        center = q_canvas.rect().center()
        
        # Convert canvas coordinates to local widget coordinates
        widget_local_pos = transformed_pos + QPointF(center)
        
        return QPoint(int(widget_local_pos.x()), int(widget_local_pos.y()))



def click_canvas(x0, y0):
    pos0 = get_global_from_document_coords(x0, y0)
    app = Krita.instance()
    view = app.activeWindow().activeView()
    document = view.document()
    if document is None:
        return

    q_view = get_q_view(view)
    canvas = get_q_canvas(q_view)    

    global_pos = QPointF(canvas.mapToGlobal(pos0))
    device = QTabletEvent.Stylus
    pointer_type = QTabletEvent.Pen
    pressure = 1
    x_tilt = 0
    y_tilt = 0
    tangential_pressure = 0.0
    rotation = 0.0
    z_pos = 0
    key_state = Qt.NoModifier
    unique_id = 1234  # ???
    button = Qt.LeftButton
    buttons = Qt.LeftButton

    canvas.activateWindow()
    
    table_press = QTabletEvent(
        QEvent.TabletPress,
        pos0,
        global_pos,
        device,
        pointer_type,
        pressure,
        x_tilt,
        y_tilt,
        tangential_pressure,
        rotation,
        z_pos,
        key_state,
        unique_id,
        button,
        buttons)

    table_release = QTabletEvent(
        QEvent.TabletRelease,
        pos0,
        global_pos,
        device,
        pointer_type,
        pressure,
        x_tilt,
        y_tilt,
        tangential_pressure,
        rotation,
        z_pos,
        key_state,
        unique_id,
        button,
        buttons)

    QApplication.sendEvent(canvas, table_press)
    QApplication.sendEvent(canvas, table_release)

app = Krita.instance()
view = app.activeWindow().activeView()
document = view.document()



click_canvas(100,100)

Krita.instance().activeDocument().refreshProjection()

However, just clicking is unfortunately not good enough. I’ve got the Enclose-and-Fill tool in Rectangle mode, so I’d like to drag it from 0,0 (pos0) to the max width and height of the document (pos1).

Setting the release coordinates to pos1 doesn’t work, even with throwing in a few hacky sleep statements that I stole from somewhere:

def sleep(value):
    loop = QEventLoop()
    QTimer.singleShot(value, loop.quit)
    loop.exec()

Similarly, clicking down in the four corners in lasso mode before releasing doesn’t work either.

Does anyone have an idea how I could make it properly drag accross the area?
If no, I guess I could switch the tool to brush mode, and just click across the entire canvas, but that’s a much more unreliable solution than dragging accross the entire canvas, and I’d have to figure out a way to set the pen size.