Skip to content

Commit

Permalink
增加图片处理模块 增加单元测试
Browse files Browse the repository at this point in the history
  • Loading branch information
5656565566 committed Feb 2, 2025
1 parent afcaa88 commit b61e4d4
Show file tree
Hide file tree
Showing 14 changed files with 534 additions and 25 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# rose-script-actuator
一个使用 lua 语言编写简单脚本的 自动化控制 支持 adb windows 等

通过 textual 实现了简单的 TUI 实现跨平台的用户交互方式

使用 Poetry 包管理器

# 运行
Expand All @@ -13,7 +15,7 @@
从当前仓库克隆代码,并安装依赖。

## 环境依赖
安装 Python 3.10 及以上版本,并安装 Poetry 包管理器。可以参考以下链接进行安装:[Poetry 官方文档](https://python-poetry.org/docs/#installation)+
安装 Python 3.10 及以上版本,并安装 Poetry 包管理器。可以参考以下链接进行安装:[Poetry 官方文档](https://python-poetry.org/docs/#installation)

- 开发
```shell
Expand Down
5 changes: 5 additions & 0 deletions actuator/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from consts import threadings

from typing import Callable
from platform import system

import keyboard
import ctypes
Expand Down Expand Up @@ -41,5 +42,9 @@ def main() -> None:

if __name__ == "__main__":
read_conifg()

if system() == "Windows":
ctypes.windll.kernel32.SetConsoleTitleW("脚本执行器")

main()
exit()
13 changes: 8 additions & 5 deletions actuator/devices/adb/execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ def appName(self):
res = self.device.shell("dumpsys activity activities | grep \"mResumedActivity\"", timeout=2)

return f"{self.device} 前台 APP {res}", res


def get_screenshot(self) -> bytes:
return self.screenshot_file.getvalue()

def screenshot(self, filePath: Path= None):

Expand All @@ -82,10 +84,11 @@ def screenshot(self, filePath: Path= None):

try:
self.device.screenshot().save(save_object)

if isinstance(save_object, BytesIO):
return f"对 {self.device} 进行截图并保存到内存", save_object.getvalue()

except:
return f"无法为 {self.device.serial} 截图 请检查设备状态", False
else:
if get_config().save_screenshot:
return f"{self.device} 截图并保存到了 {save_object}"
else:
return f"对 {self.device} 进行截图"
return f"{self.device} 截图并保存到了 {save_object}"
10 changes: 8 additions & 2 deletions actuator/devices/windows/execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,10 @@ def activateWindow(self, name: str):
def get_all_windows_titles(self):
return gw.getAllTitles()

def get_screenshot(self) -> bytes:
return self.screenshot_file.getvalue()

def screenshot(self, filePath: Path= None) -> Path:

save_object = self.screenshot_file

if get_config().save_screenshot:
Expand All @@ -317,7 +319,11 @@ def screenshot(self, filePath: Path= None) -> Path:

try:
screenshot = pyautogui.screenshot()
screenshot.save(save_object)
screenshot.save(save_object, "png")

if isinstance(save_object, BytesIO):
return "Windows 截图并保存到内存", save_object.getvalue()

return f"Windows 的截图并保存到了 {save_object}"
except Exception as e:
return f"无法为 Windows 设备 截图 请检查设备状态 {e}", False
101 changes: 94 additions & 7 deletions actuator/run_script/exec_lua.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,23 @@

import lupa
import time
import inspect

from log import logger

from utils.requests import Requests
from utils.image import (
crop_image,
get_image_resolution,
image_ocr,
exact_match,
simple_fuzzy_match,
fuzzy_match,
regex_match,
template_matching,
diff_size_template_matching
)

from consts import devices_manager

class VirtualFile:
Expand Down Expand Up @@ -36,9 +51,7 @@ class LuaExit(LuaError):

class LuaPath:
def __init__(self, path: Union[str, Path]):

self._path = path

if isinstance(path, str):
self._path = Path(path)

Expand All @@ -49,9 +62,53 @@ def add(self, path: str):
self._path = self._path / path
return self._path

def dynamic_call(func, args):

sig = inspect.signature(func)
param_info = sig.parameters
has_varargs = any(
param.kind == inspect.Parameter.VAR_POSITIONAL for param in param_info.values()
)

if has_varargs:
return func(*args)

flat_args = []
for arg in args:
if isinstance(arg, (list, set)):
flat_args.extend(arg)

else:
flat_args.append(arg)

param_names = list(param_info.keys())
required_params = [
p for p in param_info.values() if p.default == inspect.Parameter.empty
]
if len(flat_args) < len(required_params):
raise Exception(
f"参数不足,函数 {func.__name__} 需要至少 {len(required_params)} 个参数,实际传入 {len(flat_args)} 个参数"
)

bound_args = {}
for i, param_name in enumerate(param_names):
if i < len(flat_args):
bound_args[param_name] = flat_args[i]
elif param_info[param_name].default != inspect.Parameter.empty:
bound_args[param_name] = param_info[param_name].default
else:
raise Exception(f"参数 {param_name} 未传入且无默认值")

result = func(**bound_args)

if isinstance(result, list):
return tuple(result)

return result

def output_result(output, callable: Callable):
def wrapper(*args, **kwargs):
result = callable(*args, **kwargs)
result = dynamic_call(callable, args)
if isinstance(result, tuple):
output(result[0])
return result[1:]
Expand All @@ -78,12 +135,36 @@ def __getitem__(self, name: str):

return output_result(self.output, getattr(self.device, name))

class LuaImage:

function_maps = {
"crop_image": crop_image,
"get_image_resolution": get_image_resolution,
"ocr": image_ocr,
"exact_match": exact_match,
"simple_fuzzy_match": simple_fuzzy_match,
"fuzzy_match": fuzzy_match,
"regex_match": regex_match,
"template_matching": template_matching,
"diff_size_template_matching": diff_size_template_matching,
}

def __getitem__(self, name: str):

if func := self.function_maps.get(name):
return func

raise LuaError(f"Image 不存在 {name} 操作 !")

class LuaScriptRuntime:

def output_handler(self, message: str = ""):
"""打印的映射"""
self.buffer.write(f"{message}\n")


def print_handler(self, *args):
self.output_handler(" ".join(map(str, args)))

def user_input_handler(self, prompt: str = "", description: str = ""):
if self.user_input_callback is None:
self.notify_handler("脚本使用了用户输入, 但当前运行环境不支持 !")
Expand Down Expand Up @@ -133,7 +214,7 @@ def init_lua(self):

self.buffer.clear()

self.lua.globals()["print"] = self.output_handler
self.lua.globals()["print"] = self.print_handler
self.lua.globals()["input"] = self.user_input_handler
self.lua.globals()["notify"] = self.notify_handler if self.notify is not None else self.output_handler
self.lua.globals()["clear_buffer"] = self.buffer.clear
Expand All @@ -147,6 +228,7 @@ def init_lua(self):

self.lua.globals()["select_device"] = self.select_device
self.lua.globals()["device"] = LuaDevice(None, self.output_handler)
self.lua.globals()["image"] = LuaImage()

self.register_func()

Expand Down Expand Up @@ -198,9 +280,14 @@ def run(self, script: str):
return None

except Exception as e:
#import traceback
#logger.error(f"发生错误 {e.args[0]}")
#error_message = "".join(traceback.format_exception(e))
#logger.debug(error_message)

self.output_handler("本程序异常 !")
self.output_handler(f"错误 {e}")
return e
self.output_handler(f"错误 {e if str(e) else '未知错误'}")
return e if str(e) else "未知错误"

else:
if result not in [0, None]:
Expand Down
39 changes: 32 additions & 7 deletions actuator/run_script/old/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from log import logger
from config import get_config
from consts import devices_manager
from utils.file import FileHelper

from .model import *

Expand Down Expand Up @@ -482,7 +483,18 @@ def wrapper(*args, **kwargs):

return wrapper

def pathSplicing(*paths: list) -> str:
"""路径拼接"""
paths = [str(path) for path in paths]
return "/".join(paths)

def formatString(*paths: list) -> str:
"""格式化字符串"""
paths = [str(path) for path in paths]
return "".join(paths)

class InternalMethods:

def __init__(
self,
device,
Expand All @@ -495,6 +507,16 @@ def __init__(
self.path = path
self.output = output
self.notify = notify

self.methods_maps = {
"select_device": self.select_device,
"fileMove" : FileHelper.file_move,
"fileRename" : FileHelper.file_rename,
"folderCreate" : FileHelper.folder_create,
"pathSplicing" : pathSplicing,
"formatString" : formatString,

}

def select_device(self, name: str):
devices_manager.init_platforms()
Expand All @@ -507,16 +529,16 @@ def select_device(self, name: str):

def get(self, name: str):

if name == "select_device":
return self.select_device
if method := self.methods_maps.get(name):
return method

if not self.device:
raise RunException("请先使用 call select_device(设备名称) 选择设备 !")

if not hasattr(self.device, name):
raise RunException(f"设备不存在 {name} 操作 !")
if hasattr(self.device, name):
return output_result(self.output, getattr(self.device, name))

return output_result(self.output, getattr(self.device, name))
raise RunException(f"设备不存在 {name} 操作 !")

class Interpreter:
"""解释器"""
Expand All @@ -530,7 +552,7 @@ def __init__(
):
self.path = path.parent
self.parser = parser
self.variables = {}
self.variables = {"work_path": path}
self.user_func: dict[str, UserFunction] = {}

self.used_func: list[UserFunction] = []
Expand Down Expand Up @@ -908,8 +930,11 @@ def __init__(
self.notify: Callable = notify

def run(self, script: str, name: str, path: Path):

script = f"name \"{name}\"\n" + script

try:
return self.run_script(script, name, path)
return self.run_script(script, name, path.parent)
except Exception as e:
return e

Expand Down
8 changes: 5 additions & 3 deletions actuator/tui/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,9 +298,8 @@ def action_get_script_list(self) -> None:
)

self.refresh()

list_container = self.query_one("#file-list")

list_container.remove_children()
if self.lua_files:
for file in self.lua_files:
Expand All @@ -313,13 +312,16 @@ def action_get_script_list(self) -> None:
)
list_container.refresh(layout=True)
self.notify("成功刷新了脚本列表")


except Exception as e:
self.notify(
f"未知错误:{str(e)}",
f"请尝试返回主页重新进入",
title="刷新失败",
severity="error"
)

logger.warning(e)

@work(thread=True)
def run_lua_scripts(self, code: str, name: str, path: str):
Expand Down
Loading

0 comments on commit b61e4d4

Please sign in to comment.