Skip to content

Document(v5.1 and below)

Monomux edited this page Nov 25, 2023 · 2 revisions

Warning: This document is out-of-date and only works with KeymouseGo v5.1 and versions below.

Table of content

Usage

Basic operation

Desktop mode

  1. Click Record button to start recording

  2. Do anything like clicking mouse or tapping keyboard, which will be recorded

  3. Click Finish button to stop recording

  4. Click Launch button to reproduce the operation recorded in step 2

Command line mode

usage: KeymouseGo.py [-h] [-rt RUNTIMES] [-sp SPEED] [-m MODULE]
                     sctipts [sctipts ...]

positional arguments:
  sctipts               Path for the scripts

optional arguments:
  -h, --help            show this help message and exit
  -rt RUNTIMES, --runtimes RUNTIMES
                        Run times for the script
  -sp SPEED, --speed SPEED
                        Run speed for the script, input in percentage form
  -m MODULE, --module MODULE
                        Extension for the program

Run specific script

> ./KeymouseGo scripts/0314_1452.txt

Run specific script for 3 times

> ./KeymouseGo scripts/0314_1452.txt -rt 3
> ./KeymouseGo scripts/0314_1452.txt --runtimes 3

Run specific script at the speed of 200%

> ./KeymouseGo scripts/0314_1452.txt -sp 200
> ./KeymouseGo scripts/0314_1452.txt --speed 200

Run specific script with extension MyExtension

> ./KeymouseGo scripts/0314_1452.txt -m MyExtension
> ./KeymouseGo scripts/0314_1452.txt --module MyExtension

Tips

  1. The program will endlessly run the script if run times is set to 0

  2. The default launch hotkey is F6, which functions the same as the launching button. The default stop hotkey is F9, which will terminate the running script

  3. Only mouse click operation and keyboard operation will be recorded. Mouse trail won't be recorded.

  4. A new script file will be generated in directory scripts at the end of recording.

  5. You can choose the script to run in choice list.

  6. The content of script can be edited with reference of Grammar of script.

  7. In hotkey setting, Middle refers mouse middle button and XButton refers mouse side button.

  8. Due to the limitation of execution speed, the running speed cannot be set too high.

In some system environment, there may be circumstances that the mouse events cannot be fully recorded. To settle this, you can run this program as administrator.

Grammar of script

Assume that the resolution of screen is 1920 * 1080

[
 [3000, "EM", "mouse right down", ["0.05208%", "0.1852%"]],    // Press mouse right button at the relative coordinates `(0.05208, 0.1852)`(i.e. absolute coordinates `(100,200)`) after 3000ms
 [50,   "EM", "mouse right up",   ["0.05208%", "0.1852%"]],    // Release mouse right button at the coordinates after 50ms
 [1000, "EK", "key down",         (70, 'F', 0)],                                   // Press key 'f' after 1000ms
 [50,   "EK", "key up",           (70, 'F', 0)],                                   // Release key 'f' after 50ms
 [100,  "EM", "mouse left down",  ["0.2604%", "0.4630%"]],      // Press mouse left button at the relative coordinates `(0.2604, 0.4630)`(i.e. absolute coordinates `(500,500)`) after 100ms
 [100,  "EM", "mouse move",       ["0.2604%", "0.5556%"]],       // Move mouse to the relative coordinates `(0.2604, 0.4630)`(i.e. absolute coordinates `(500,500)`) after 100ms
 [100,  "EM", "mouse left up",  ["0.3125%", "0.5556%"]],                   // Release mouse left button at the relative coordinates `(0.3125, 0.5556)`(i.e. absolute coordinates `(600,600)`) after 100ms
 [100,  "EX", "input",            "Hello world"],                                   // Input 'Hello world' at current coordinate after 100ms
]

The script is saved in json format, in which each line represents an operation

  • The first element of each line is time interval(ms), indicating the interval to previous operation
  • The second element of each line is operation type. EM represents mouse, EK represents keyboard, EX represents extended operation
  • The third element of each line is detailed operation type, including
    • mouse left down :press mouse left button mouse left up release mouse left button
    • mouse right down press mouse right buttonmouse right up release mouse right button
    • mouse middle down press mouse middle button mouse middle up release mouse middle button
    • mouse wheel up mouse wheel slide up mouse wheel down mouse wheel slide down
    • key down press key key up release key
    • mouse move move mouse to input input text
  • The fourth element of each line is action type
    • If operation type is EM, it is consisted of coordinates of mouse.(Both relative coordinates and absolute coordinates are supported)
    • If operation type is EK, it is consisted of coordinates of (key number, key name, extension flag)
    • If detailed operation type is Input, it is the text to input
  • In each line, comment can be added after //
  • It is recommended to back up script before editing. And make sure to follow the format while editing, otherwise it may result in failure of execution.
  • The mouse event will execute on the position that the cursor is currently in when the coordinate is set to [-1, -1].

Extension

What it can do:

  • Control execution process
  • Change events to execute during runtime
  • Fulfill custom feature

tips: the extension is written in Python, thus relative knowledge on programming is required

The extension class

The default extension is at assets/plugins/Extension.py, custom features can be achieved by extending class Extension.

The parameters in __init__:

  • runtimes:the execution times, which can influence the execution times during runtime
  • speed:execution speed(%)
  • thd:the thread object that runs script
  • swap:the data to swap, which is None by default. When PushProcess is raised, the data in swap will be passed to sub-extension object. When the sub-process is finished, the data in swap in sub-extension object will be passed to parent extension.

The interfaces :

  • onbeginp():trigger when extension object is initialized
  • onbeforeeachloop(currentloop):trigger when runs a single script. Current run will be skipped if False is returned
  • onrunbefore(event, currentindex):trigger when runs a single event. Current event will be skipped if False is returned
  • onrunafter(event, currentindex):trigger after running a single event, no matter what onrunbefore returns
  • onaftereachloop(currentloop):trigger after running a single script, no matter what onbeforeeachloop returns
  • onrecord(event, currentindex):trigger when records a single event. Current event will be saved if True is returned
  • onendp():trigger when all loop execution is complete

currentindex:index of current event(begin at 0) currentloop:index of current loop(begin at 0)

event:the opration to execute, with parameters below:

  • delay:time to wait before execution(ms)
  • event_type:operation type
  • message:detailed operation type
  • action:action type
  • addon:custom content

Create an extension

When creating a subclass of Extension, make sure that the name of subclass is the same as module name. (i.e. define class subclassname in subclassname.py)

To specify an extension for a script, you can select the extension in desktop mode or input subclass name in command line mode. Or, you can add the subclassname at the beginning of script:

[
    "subclassname",
    // Script content
]

Pirority

Specified in script > Specified in desktop/command line mode > default

The plugins directory is added to import search path when launching program. If you want to add other directory to import search path, it can be achieved by adding the content below to the beginning of module.

Example

Add /plugins to import search path

import os
from KeymouseGo import add_lib_path
add_lib_path([
    os.path.join(os.getcwd(), "plugins")
])

After adding the import search path, the modules in relative folder can be directly imported through 'import' or 'from... import ... `

Assuming that 'util. py' exists in the 'plugins' folder, the module can be imported directly by

import Util

Process control

Several exception are defined for process controlling, which can be raised during execution to fulfill relative functions:

  • JumpProcess(index):Jump to event with index or label named index
  • BreakProcess:End current program loop
  • EndProcess:End the execution

These exception can be raised in specific interfaces:

- onbeginp onbeforeeachloop onrunbefore onrunafter onafterloop onendp onrecord
Jump × × × × ×
Break × × ×
End × × ×

Declare a label

You can add the label a line before the specific script:

[
    // content
    "label",
    // content
]

When raise JumpProcess("label"), the process will jump to the content below the label.

Warning: Jumping to a given index is not supported in version 5.1, instead it can only jump to a given label.

The label of the first line is "START".

Debug via log

loguruis used as the logging module:

from loguru import logger

Example

There are some recorded script 1.txt, 2.txt at directory Scripts with content more than 3 lines. The program will execute content in 1.txt at first.

Custom operation:

  1. In the first loop, jump at index 2 after executing event at index 0
  2. In the first loop, run sub-script 2.txt with the same speed twice after executing event at index 2. During the execution of sub-script:
    1. In the first loop, skip current loop after executing event at index 0
    2. In the first loop, end execution after executing event at index 1
    3. Print the data from parent extension at the end of process
  3. In the second loop, skip the event at index 0
  4. In the second loop, repeat the event once after execuing event at index 1
  5. Limit the execution times to 3

Create file MyExtension.py at plugins/ with content listed below:

from assets.plugins.Extension import *
from assets.plugins.ProcessException import *
from UIFunc import RunScriptClass
from loguru import logger

logger.info('Import MyExtension')


class MyExtension(Extension):
    def __init__(self, runtimes, speed, thd=None, swap=None):
        # Defalut __init__
        super().__init__(runtimes, speed, thd, swap)
        # Index of urrent loop
        self.currentloop = 0
        
        # Content to sub-extension
        # self.swap = 'Helloworld'
        
        """
        Fulfill demand 5
        """ 
        # self.runtimes = 3

    def onbeforeeachloop(self, currentloop):
        self.currentloop = currentloop
        return True

    def onrunbefore(self, event, currentindex):
        return True
        # Fulfill demand 3
        # if self.currentloop == 1 and currentindex == 0:
        #     return False
        # else:
        #     return True

    def onrunafter(self, event, currentindex):
        pass
        # Fulfill demand 1
        # if self.currentloop == 0 and currentindex == 0:
        #     raise JumpProcess(2)
        # Fulfill demand 2
        # elif self.currentloop == 0 and currentindex == 2:
        #     RunScriptClass.run_sub_script(self, 'scripts/0603_1013.txt', 
        #                                    speed=self.speed, 
        #                                    runtimes=2, 
        #                                    subextension_name='MyExtension2',
        #                                    thd=self.thd
        #                                    )
        # Fulfill demand 4
        # elif self.currentloop == 1 and currentindex == 1:
        #     event.execute()

Create file MyExtension2.py at plugins/ with content listed below:

from assets.plugins.Extension import *
from assets.plugins.ProcessException import *
from loguru import logger

logger.info('Import MyExtension2')


class MyExtension2(Extension):
    def __init__(self, runtimes, speed, thd=None, swap=None):
        super().__init__(runtimes, speed, thd, swap)
        self.currentloop = 0

    def onbeforeeachloop(self, currentloop):
        self.currentloop = currentloop
        return True

    def onrunafter(self, event, currentindex):
        pass
        # Fulfill demand 2.i
        # if self.currentloop == 0:
        #     raise BreakProcess()
        # Fulfill demand 2.ii
        # elif self.currentloop == 1 and currentindex == 1:
        #     raise EndProcess()

    def onendp(self):
        pass
        # Fulfill demand 2.iii
        # logger.info(self.swap)