New View on Second Screen : Script for Actuator

Showcase the ability of Actuator to create and place additional View for Krita in a second display and generate Key Stroke to place the window into a FanzyZone

In case if you want to do somethings fancy. For instance you have a second display and FancyZones utility to manage windows. Suppose you want to create a new view and send it into a particular zone on a second screen.

With Actuator it is possible. To do that you will need to create a new View and send the window to a second display. Then using system call simulate the keyboard shortcut.

The following script does that. Keep in mind that this script works only for Windows.

from PyQt5 import QtTest
from PyQt5.QtWidgets import QApplication

from PyQt5.QtCore import qDebug
from krita import Krita

import ctypes
from ctypes import wintypes

def run():
    app = Krita.instance()

    doc = app.activeDocument()
    if doc is None:
        return
    new_window = app.openWindow()
    new_window.qwindow().show()
    new_window.addView(doc)
    doc.waitForDone()
    QtTest.QTest.qWait(1000)

    windows = app.windows()

    print(QApplication.screens())
    screens = QApplication.screens()

    SCREEN_NO = 2

    if len(screens) == 2:
        screen = screens[SCREEN_NO - 1]
        print(screen)

        qr = screen.geometry()
        win = windows[1]
        win.qwindow().move(qr.left(), qr.top())
        win.qwindow().show()

    app.action("view_show_canvas_only").trigger()
    QtTest.QTest.qWait(1000)

run()

user32 = ctypes.WinDLL('user32', use_last_error=True)

INPUT_MOUSE    = 0
INPUT_KEYBOARD = 1
INPUT_HARDWARE = 2

KEYEVENTF_EXTENDEDKEY = 0x0001
KEYEVENTF_KEYUP       = 0x0002
KEYEVENTF_UNICODE     = 0x0004
KEYEVENTF_SCANCODE    = 0x0008

MAPVK_VK_TO_VSC = 0

# msdn.microsoft.com/en-us/library/dd375731
VK_TAB    = 0x09
VK_MENU   = 0x12
VK_LWIN   = 0x5B
VK_LSHIFT = 0xA0
VK_LEFT   = 0x25
VK_UP     = 0x26
VK_RIGHT  = 0x27
VK_DOWN   = 0x28
VK_TAB = 0x09

# C struct definitions

wintypes.ULONG_PTR = wintypes.WPARAM

class MOUSEINPUT(ctypes.Structure):
    _fields_ = (("dx",          wintypes.LONG),
                ("dy",          wintypes.LONG),
                ("mouseData",   wintypes.DWORD),
                ("dwFlags",     wintypes.DWORD),
                ("time",        wintypes.DWORD),
                ("dwExtraInfo", wintypes.ULONG_PTR))

class KEYBDINPUT(ctypes.Structure):
    _fields_ = (("wVk",         wintypes.WORD),
                ("wScan",       wintypes.WORD),
                ("dwFlags",     wintypes.DWORD),
                ("time",        wintypes.DWORD),
                ("dwExtraInfo", wintypes.ULONG_PTR))

    def __init__(self, *args, **kwds):
        super(KEYBDINPUT, self).__init__(*args, **kwds)
        # some programs use the scan code even if KEYEVENTF_SCANCODE
        # isn't set in dwFflags, so attempt to map the correct code.
        if not self.dwFlags & KEYEVENTF_UNICODE:
            self.wScan = user32.MapVirtualKeyExW(self.wVk,
                                                 MAPVK_VK_TO_VSC, 0)

class HARDWAREINPUT(ctypes.Structure):
    _fields_ = (("uMsg",    wintypes.DWORD),
                ("wParamL", wintypes.WORD),
                ("wParamH", wintypes.WORD))

class INPUT(ctypes.Structure):
    class _INPUT(ctypes.Union):
        _fields_ = (("ki", KEYBDINPUT),
                    ("mi", MOUSEINPUT),
                    ("hi", HARDWAREINPUT))
    _anonymous_ = ("_input",)
    _fields_ = (("type",   wintypes.DWORD),
                ("_input", _INPUT))

LPINPUT = ctypes.POINTER(INPUT)

def _check_count(result, func, args):
    if result == 0:
        raise ctypes.WinError(ctypes.get_last_error())
    return args

user32.SendInput.errcheck = _check_count
user32.SendInput.argtypes = (wintypes.UINT, # nInputs
                             LPINPUT,       # pInputs
                             ctypes.c_int)  # cbSize

# Functions

def PressKey(hexKeyCode):
    x = INPUT(type=INPUT_KEYBOARD,
              ki=KEYBDINPUT(wVk=hexKeyCode))
    user32.SendInput(1, ctypes.byref(x), ctypes.sizeof(x))

def ReleaseKey(hexKeyCode):
    x = INPUT(type=INPUT_KEYBOARD,
              ki=KEYBDINPUT(wVk=hexKeyCode,
                            dwFlags=KEYEVENTF_KEYUP))
    user32.SendInput(1, ctypes.byref(x), ctypes.sizeof(x))


# Win+Left
def shortcut():
    wait_time = 100 # ms
    qDebug(f"Wait time: {wait_time}")
    PressKey(VK_LWIN)
    QtTest.QTest.qWait(wait_time)
    PressKey(VK_LEFT)
    QtTest.QTest.qWait(wait_time)
    ReleaseKey(VK_LEFT)
    QtTest.QTest.qWait(wait_time)
    ReleaseKey(VK_LWIN)

qDebug("Engage shortcut")
shortcut()

Download this sequence.