当前位置: 首页 > news >正文

yolo自动化项目实例解析(八)自建UI-键鼠录制回放

项目中关于键鼠的操作,不像我们之前自动化那样一步一步去定义的,而是用C++写了一个记录键鼠的操作,通过回放的方法来实现的

一、通讯系统

1、创建websocket服务器

首先通过事件循环asyncio 和websockets,创建一个持久化的服务端进程,允许客户端来请求

 vi wss.py

# -*- coding: utf-8 -*-
import asyncio
import concurrent.futures
import threading
import time
import websockets# 存储所有已连接的客户端
connected_clients = []# 处理客户端请求
async def handle_client(websocket):print("客户端连接成功")connected_clients.append(websocket)try:while True:message = await websocket.recv()if message is None:breakprint(f"客户端请求的消息: {message}")# 假设需要回应客户端await websocket.send(f"{message}")except websockets.exceptions.ConnectionClosed:print("Client 连接断开")finally:# 确保移除已断开连接的客户端connected_clients.remove(websocket)async def server_main():# 创建 WebSocket 服务器server = await websockets.serve(handle_client, "localhost", 29943)print("启动服务端成功")try:# 等待服务器关闭await asyncio.Future()except asyncio.CancelledError:print("WebSocket server was cancelled.")except Exception as e:print(f"An error occurred: {e}")import asyncio# 创建一个新的事件循环
loop = asyncio.new_event_loop()# 启动 WebSocket 服务器
try:loop.run_until_complete(server_main())except KeyboardInterrupt:# 当用户按下 Ctrl+C 时会引发此异常。通常用来处理用户中断程序的情况。print("Stopping WebSocket server...")finally:# 关闭事件循环loop.close()

说明

import asyncio
import concurrent.futures
import threading
import time
import websockets# 存储所有已连接的客户端
connected_clients = []#处理客户端请求
async def handle_client(websocket):print("客户端连接成功")connected_clients.append(websocket)try:while True:message = await websocket.recv()if message is None:breakprint(f"客户端请求的消息: {message}")# 假设需要回应客户端await websocket.send(f"{message}")except websockets.exceptions.ConnectionClosed:print("Client 连接断开")finally:# 确保移除已断开连接的客户端connected_clients.remove(websocket)async def server_main():'''websockets.serve:这个函数用于创建一个 WebSocket 服务器,它返回一个协程对象。await:等待这个协程对象完成。实际上,websockets.serve 创建了服务器并立即返回,不会等待服务器完全启动,因此这里的 await 主要是用来确保协程函数能够正确执行并返回结果。创建服务器:这个步骤创建了一个监听在本地地址 localhost 的端口 29943 上的 WebSocket 服务器,并指定了处理客户端连接的回调函数 handle_client。:return:'''# 创建 WebSocket 服务器#当有客户端连接到服务端的时候,触发handle_client 处理请求server = await websockets.serve(handle_client, "localhost", 29943)print("启动服务端成功")try:# 等待服务器关闭# asyncio.Future 会开启Future协程, 通过await异步等待他完成# 但是因为没有设置完成条件,所以只会在中断或错误的时候退出,期间一致保持接收客户端请求await asyncio.Future()except asyncio.CancelledError:print("WebSocket server was cancelled.")except Exception as e:print(f"An error occurred: {e}")import asyncio# 创建一个新的事件循环
loop = asyncio.new_event_loop()# 启动 WebSocket 服务器
try:#这个方法会启动事件循环并一直运行,直到提供的协程函数(在这里是#server_main())完成。如果协程函数中抛出了任何异常,run_until_complete()#会将这个异常重新抛出,这样就可以在try-except 块中捕获并处理它loop.run_until_complete(server_main())except KeyboardInterrupt:#当用户按下 Ctrl+C 时会引发此异常。通常用来处理用户中断程序的情况。print("Stopping WebSocket server...")finally:#close():这个方法用来关闭事件循环。关闭事件循环可以释放相关资源,并确保所有任务都已经完成或被适当地取消。#finally 块:无论前面的#try 块是否抛出异常,finally 块中的代码都会被执行。这确保了即使在发生异常或用户中断的情况下,事件循环也会被正确关闭# 关闭事件循环loop.close()

2、客户端请求测试

vi wsstest.py

# -*- coding: utf-8 -*
import asyncio
import websocketsasync def client():uri = "ws://localhost:29943"async with websockets.connect(uri) as websocket:print("已连接到服务端")# 发送消息给服务端await websocket.send("我是客户端发送的消息")print("已发送消息到服务端")# 接收服务端返回的消息response = await websocket.recv()print(f"服务端消息返回: {response}")async def main():await client()# 初始化事件循环
loop = asyncio.get_event_loop()# 启动客户端
try:loop.run_until_complete(main())
except KeyboardInterrupt:print("Stopping WebSocket client...")
finally:# 关闭事件循环loop.close()

 

服务端返回

启动服务端成功客户端连接成功
客户端请求的消息: 我是客户端发送的消息
Client 连接断开

客户端返回

已连接到服务端
已发送消息到服务端
服务端消息返回: 我是客户端发送的消息

3、服务端--守护进程运行

上面服务端和客户端正常用起来看着没什么问题,但是我们主要是在ui上面用的,所有东西必须都是异步,不能因为开个服务端就把主进程给占用了,这里服务端做下修改

下面改用threading.Thread 以协程形式运行服务端进程,并将该协程设置为守护进程模式,只有当主进程结束时,才会结束服务端, 方便测试我们先在结尾加个input作为程序的主进程(不退出或者指定方法退出的都行)

 vi wss.py

# -*- coding: utf-8 -*
import asyncio
import concurrent.futures
import sys
import threading
import time
import websockets# 存储所有已连接的客户端
connected_clients = []#处理客户端请求
async def handle_client(websocket):print("客户端连接成功")connected_clients.append(websocket)try:while True:message = await websocket.recv()if message is None:breakprint(f"客户端请求的消息: {message}")# 假设需要回应客户端await websocket.send(f"{message}")except websockets.exceptions.ConnectionClosed:print("Client 连接断开")finally:# 确保移除已断开连接的客户端connected_clients.remove(websocket)async def server_main():server = await websockets.serve(handle_client, "localhost", 29943, max_size=1024 * 1024 * 10)#搭配协程用# 等待 server 对象的 wait_closed 方法返回的一个协程完成。asyncio.gather 是一个用来并发运行多个异步任务的函数# 并且它可以返回这些任务的结果。在这个特定的情况下,gather 只接收了一个任务 server.wait_closed()# 因此它实际上是等待这个特定的任务完成# 等待 server 对象的 wait_closed 方法返回的一个协程完成。try:await asyncio.gather(server.wait_closed())except asyncio.CancelledError:print("WebSocket server was cancelled.")except Exception as e:print(f"An error occurred: {e}")import asyncio# 创建一个新的事件循环
loop = asyncio.new_event_loop()#以协程非主进程形式运行
t = threading.Thread(target=loop.run_until_complete, args=(server_main(),))#协程以守护进程运行
python_var = sys.version_info[1]
if python_var > 8:t.daemon = True
else:t.setDaemon(True)
t.start()print("服务端协程已守护进程模式运行,下面的代码需要去运行其他的主进程, 如果没有进程则会退出")
print("这个程序将在未来作为一个协程启动,而非主进程形式")
print("没有前台进程的话直接跑的话,主进程会结束,当主进程结束时,守护进程也会关闭")#主进程(这个我们后面是要把input改为我们ui中的某个触发点的,这里input只是用来演示)
input("回车退出")

4、添加键鼠录制插件

如果是本地主机,可能会报毒给删了,看情况自己做隔离,路径datas/JiaoBen/xxx.exe
exe文件
https://download.csdn.net/download/qq_42883074/89820151

源码
https://download.csdn.net/download/qq_42883074/89858644

5、部署插件服务

首先我们要先将录制的插件服务拉起来,通过subprocess.Popen('"' + "./datas/jiaoben/xxx.exe" + '" "' + "29943" + '"') 在启动插件的时候指定本地服务端端口

wss.py

async def server_main():server = await websockets.serve(handle_client, "localhost", 29943, max_size=1024 * 1024 * 10)#添加下面这个#运行插件服务import subprocesstry:time.sleep(5)# 启动exe程序subprocess.Popen('"' + "./datas/jiaoben/xxx.exe" + '" "' + "29943" + '"')print("插件服务已经启动!")except:print("文件没找到可以能被杀毒软件干掉了 " + "./datas/jiaoben/xxx.exe")#搭配协程用try:await asyncio.gather(server.wait_closed())except asyncio.CancelledError:print("WebSocket server was cancelled.")except Exception as e:print(f"An error occurred: {e}")

返回信息

服务端协程已守护进程模式运行,下面的代码需要去运行其他的主进程, 如果没有进程则会退出
这个程序将在未来作为一个协程启动,而非主进程形式
没有前台进程的话直接跑的话,主进程会结束,当主进程结束时,守护进程也会关闭回车退出插件服务已经启动!
客户端连接成功
客户端请求的消息: 是否禁止录制 真

可以看到当插件服务启动后,立刻给我们指定端口开始发送请求

这个插件本身就是支持通过按键来进行录制和回放,这里按F8其实就生效录制了,但我们没有输出所以看不出来

6、日志系统

在做服务端程序的时候,有很多时候我们都不方便直接print把信息打印出来,这里直接用日志模板,将服务端信息写到日志中

logger_module.py

import logging
import os
from logging.handlers import TimedRotatingFileHandler# 创建全局的 logger
logger = logging.getLogger("LenRenAI")
logger.setLevel(logging.DEBUG)
# 创建一个handler,用于将日志输出到控制台
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)log_dir = './log'
if not os.path.exists(log_dir):os.makedirs(log_dir)
# 创建一个handler,用于将日志输出到文件
file_handler = TimedRotatingFileHandler('./log/lanrenai.log', when='midnight', interval=1, backupCount=7)
file_handler.setLevel(logging.DEBUG)# 定义日志消息格式
class CustomFormatter(logging.Formatter):FORMATS = {logging.DEBUG: '%(asctime)s - %(name)s - %(levelname)s - %(message)s',logging.INFO: '%(asctime)s - %(name)s - %(levelname)s - %(message)s',logging.WARNING: '%(asctime)s - %(name)s - %(levelname)s - %(message)s',logging.ERROR: '%(asctime)s - %(name)s - %(levelname)s - %(message)s',logging.CRITICAL: '%(asctime)s - %(name)s - %(levelname)s - %(message)s',}def format(self, record):log_fmt = self.FORMATS.get(record.levelno)formatter = logging.Formatter(log_fmt)return formatter.format(record)# 创建一个formatter格式类
formatter = CustomFormatter()
# 设置消息格式
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)
# 将handler添加到logger
logger.addHandler(console_handler)
logger.addHandler(file_handler)

7、添加全局变量

状态_是否回放中 = False
状态_是否暂停 = False
状态_是否开始录制 = False
状态_是否禁止录制 = True
状态_开伞时间 = -1
录制_当前脚本名 = ""
录制_脚本文本 = ""
录制_path = ""
录制_当前任务索引 = -1

8、服务端与插件通讯交互

我们在项目中是无法直接判断插件的运行情况的,只能通过定义变量的形式保存在本地作为开关,当请求发生变化的时候,对插件发送请求信息,然后去变更本地的变量配置,进而通过对本地变量的配置来判断插件状态

 wss.py


# 处理客户端请求
from logger_module import logger
import state
async def handle_client(websocket):global new_msgtry:# 发送连接成功消息await websocket.send("连接成功")# 添加新连接的客户端到集合中connected_clients.append(websocket)while True:# 接收客户端发送的消息message = await websocket.recv()new_msg = message# print(f"收到消息:{message}")# 处理接收到的消息if message == "是否回放中 真":state.状态_是否回放中 = Trueelif message == "是否回放中 假":state.状态_是否回放中 = Falseelif message == "是否暂停 真":state.状态_是否暂停 = Trueelif message == "是否暂停 假":state.状态_是否暂停 = Falseelif message == "是否开始录制 假":state.状态_是否开始录制 = Falseelif message == "是否开始录制 真":if state.状态_是否禁止录制 == False:logger.info("请按F8结束录制")# state.QT_信号.mysig_tishi.emit(f"请按F8结束录制")state.状态_是否开始录制 = Trueelif message == "是否禁止录制 假":state.状态_是否禁止录制 = Falseelif message == "是否禁止录制 真":state.状态_是否禁止录制 = Trueelif message[:5] == "录制的脚本":state.录制_脚本文本 = message[6:]# 保存文件if state.录制_脚本文本 != "":logger.info(state.录制_脚本文本)with open("1111.txt", "w", encoding="gbk", newline='') as f:f.write(state.录制_脚本文本)logger.info("录制完毕!")finally:# 客户端断开连接后,将其移出集合connected_clients.remove(websocket)


9、添加发送请求函数

我们服务端是以一个事件循环的形式去运行的,如果我们想要对其中的请求连接做某项操作,就需要有一个外部能够调用的函数去对事件信息中的客户端(插件) 发送具体的消息,让插件知道该做什么了(录制、回放)

wss.py

#外部函数
def send_msg(msg="是否回放#@@#假"):'''给躺宝发送指令全部指令  :是否回放#@@#假  /真是否暂停#@@#假  /真是否开始录制#@@#假 /真是否禁止录制#@@#假 /真解析脚本#@@#jiaoben"  jiaoben就是录制的脚本文本 不是文件 是直接文字全局hwnd#@@#12345   12345就是游戏的窗口句柄:param msg: 指令 用 #@@# 分割:return:'''asyncio.run_coroutine_threadsafe(send_to_client(-1, msg), loop)#时间循环内发送请求
async def send_to_client(client_id, message):if len(connected_clients) > 0:# 查找指定的客户端await connected_clients[client_id].send(message)

下面的send_to_client函数是async,处于事件循环中的,相当于是拿着插件客户端的请求连接,然后通过send将消息 (msg="是否回放#@@#假") 发送给插件,让他做对应的操作

10、服务端全量代码

# -*- coding: utf-8 -*
import asyncio
import concurrent.futures
import sys
import threading
import time
import websockets# 存储所有已连接的客户端
connected_clients = []# 处理客户端请求
from logger_module import logger
import state
async def handle_client(websocket):global new_msgtry:# 发送连接成功消息await websocket.send("连接成功")# 添加新连接的客户端到集合中connected_clients.append(websocket)while True:# 接收客户端发送的消息message = await websocket.recv()new_msg = message# print(f"收到消息:{message}")# 处理接收到的消息if message == "是否回放中 真":state.状态_是否回放中 = Trueelif message == "是否回放中 假":state.状态_是否回放中 = Falseelif message == "是否暂停 真":state.状态_是否暂停 = Trueelif message == "是否暂停 假":state.状态_是否暂停 = Falseelif message == "是否开始录制 假":state.状态_是否开始录制 = Falseelif message == "是否开始录制 真":if state.状态_是否禁止录制 == False:logger.info("请按F8结束录制")# state.QT_信号.mysig_tishi.emit(f"请按F8结束录制")state.状态_是否开始录制 = Trueelif message == "是否禁止录制 假":state.状态_是否禁止录制 = Falseelif message == "是否禁止录制 真":state.状态_是否禁止录制 = Trueelif message[:5] == "录制的脚本":state.录制_脚本文本 = message[6:]# 保存文件if state.录制_脚本文本 != "":logger.info(state.录制_脚本文本)with open("1111.txt", "w", encoding="gbk", newline='') as f:f.write(state.录制_脚本文本)logger.info("录制完毕!")finally:# 客户端断开连接后,将其移出集合connected_clients.remove(websocket)
#外部函数
def send_msg(msg="是否回放#@@#假"):'''给躺宝发送指令全部指令  :是否回放#@@#假  /真是否暂停#@@#假  /真是否开始录制#@@#假 /真是否禁止录制#@@#假 /真解析脚本#@@#jiaoben"  jiaoben就是录制的脚本文本 不是文件 是直接文字全局hwnd#@@#12345   12345就是游戏的窗口句柄:param msg: 指令 用 #@@# 分割:return:'''asyncio.run_coroutine_threadsafe(send_to_client(-1, msg), loop)#时间循环内发送请求
async def send_to_client(client_id, message):if len(connected_clients) > 0:# 查找指定的客户端await connected_clients[client_id].send(message)async def server_main():server = await websockets.serve(handle_client, "localhost", 29943, max_size=1024 * 1024 * 10)# 添加下面这个# 运行插件服务import subprocesstry:time.sleep(5)# 启动exe程序subprocess.Popen('"' + "./datas/jiaoben/xxx.exe" + '" "' + "29943" + '"')print("插件服务已经启动!")except:print("文件没找到可以能被杀毒软件干掉了 " + "./datas/jiaoben/xxx.exe")# 搭配协程用try:await asyncio.gather(server.wait_closed())except asyncio.CancelledError:print("WebSocket server was cancelled.")except Exception as e:print(f"An error occurred: {e}")import asyncio# 创建一个新的事件循环
loop = asyncio.new_event_loop()#以协程非主进程形式运行
t = threading.Thread(target=loop.run_until_complete, args=(server_main(),))#协程以守护进程运行
python_var = sys.version_info[1]
if python_var > 8:t.daemon = True
else:t.setDaemon(True)
t.start()print("服务端协程已守护进程模式运行,下面的代码需要去运行其他的主进程, 如果没有进程则会退出")
print("这个程序将在未来作为一个协程启动,而非主进程形式")
print("没有前台进程的话直接跑的话,主进程会结束,当主进程结束时,守护进程也会关闭")#主进程(这个我们后面是要把input改为我们ui中的某个触发点的,这里input只是用来演示)
#input("回车退出")

 返回

服务端协程已守护进程模式运行,下面的代码需要去运行其他的主进程, 如果没有进程则会退出
这个程序将在未来作为一个协程启动,而非主进程形式
没有前台进程的话直接跑的话,主进程会结束,当主进程结束时,守护进程也会关闭Process finished with exit code 0

因为我们是从外部调用事件循环,我们要先将服务端中的那个input主进程去掉,防止调用的时候input阻塞还需要按回车

11、模拟录制键鼠

from wss import *#打开插件的录制功能
input("###########是否开启禁止录制\n")
send_msg("是否禁止录制#@@#假")   #允许录制input("########回车开始录制\n")
send_msg("是否开始录制#@@#真")  #开始录制input("########回车结束录制\n")
send_msg("是否开始录制#@@#假")  #关闭录制
send_msg("是否禁止录制#@@#真")  #开启禁止

 第一次回车取消禁止录制,第二次回车开启录制功能,但需要在按一次F8去激活插件中的录制功能,再按一次F8停止录制并返回消息给服务端(这步可以反复操作),第三次回车,关闭录制功能

 返回

C:\Users\Administrator\PycharmProjects\yolo8test\.venv\Scripts\python.exe C:\Users\Administrator\PycharmProjects\yolo8test\test\test.py 
服务端协程已守护进程模式运行,下面的代码需要去运行其他的主进程, 如果没有进程则会退出
这个程序将在未来作为一个协程启动,而非主进程形式
没有前台进程的话直接跑的话,主进程会结束,当主进程结束时,守护进程也会关闭
回车退出插件服务已经启动!
收到消息:是否禁止录制 真###########是否开启禁止录制2024-10-15 17:21:10,416 - LenRenAI - INFO - 请求对插件客户端发送请求: 是否禁止录制#@@#假
########回车开始录制
收到消息:是否禁止录制 假
收到消息:是否禁止录制 假2024-10-15 17:21:14,896 - LenRenAI - INFO - 请求对插件客户端发送请求: 是否开始录制#@@#真
2024-10-15 17:21:14,898 - LenRenAI - INFO - 请按F8结束录制
########回车结束录制
收到消息:是否开始录制 真
2024-10-15 17:21:15,189 - LenRenAI - INFO - 请按F8结束录制
收到消息:是否开始录制 真
收到消息:是否开始录制 假
收到消息:录制的脚本 ##测试脚本##
鼠标初始 413 935
等待键 0.0229151
等待鼠 0.02292892024-10-15 17:21:19,095 - LenRenAI - INFO - ##测试脚本##
鼠标初始 413 935
等待键 0.0229151
等待鼠 0.02292892024-10-15 17:21:19,095 - LenRenAI - INFO - 录制完毕!
收到消息:是否开始录制 真
2024-10-15 17:21:20,297 - LenRenAI - INFO - 请按F8结束录制
收到消息:是否开始录制 假
收到消息:录制的脚本 ##测试脚本##
鼠标初始 413 935
等待键 3.993411
等待鼠 3.9934232024-10-15 17:21:24,202 - LenRenAI - INFO - ##测试脚本##
鼠标初始 413 935
等待键 3.993411
等待鼠 3.9934232024-10-15 17:21:24,202 - LenRenAI - INFO - 录制完毕!
收到消息:是否开始录制 真
2024-10-15 17:21:33,517 - LenRenAI - INFO - 请按F8结束录制
收到消息:是否开始录制 假
收到消息:录制的脚本 ##测试脚本##
鼠标初始 625 914
等待鼠 1.1647663
鼠标 0 0 21316 55553 0
等待鼠 0.0002612
鼠标 0 0 21282 55553 0
等待鼠 0.0398962
鼠标 0 0 21248 55675 0
等待鼠 0.0604424
鼠标 0 0 21248 55735 0
等待鼠 0.0098287
鼠标 0 0 21282 55735 0
等待鼠 0.0251939
鼠标 0 0 21350 55675 0
等待鼠 0.0002929
鼠标 0 0 21486 55432 0
等待鼠 0.0195504
鼠标 0 0 21623 55189 0
等待鼠 0.4348741
鼠标 0 0 21657 55129 0
等待鼠 0.0148643
鼠标 0 0 21725 55007 0
等待鼠 0.0182851
鼠标 0 0 21760 55007 0
等待鼠 0.1272844
鼠标 1 1 21760 55007 0
等待鼠 0.0874505
鼠标 1 2 21760 55007 0
等待鼠 0.1222998
鼠标 0 0 21794 55007 0
等待键 3.3952953
等待鼠 1.27003472024-10-15 17:21:47,638 - LenRenAI - INFO - ##测试脚本##
鼠标初始 625 914
等待鼠 1.1647663
鼠标 0 0 21316 55553 0
等待鼠 0.0002612
鼠标 0 0 21282 55553 0
等待鼠 0.0398962
鼠标 0 0 21248 55675 0
等待鼠 0.0604424
鼠标 0 0 21248 55735 0
等待鼠 0.0098287
鼠标 0 0 21282 55735 0
等待鼠 0.0251939
鼠标 0 0 21350 55675 0
等待鼠 0.0002929
鼠标 0 0 21486 55432 0
等待鼠 0.0195504
鼠标 0 0 21623 55189 0
等待鼠 0.4348741
鼠标 0 0 21657 55129 0
等待鼠 0.0148643
鼠标 0 0 21725 55007 0
等待鼠 0.0182851
鼠标 0 0 21760 55007 0
等待鼠 0.1272844
鼠标 1 1 21760 55007 0
等待鼠 0.0874505
鼠标 1 2 21760 55007 0
等待鼠 0.1222998
鼠标 0 0 21794 55007 0
等待键 3.3952953
等待鼠 1.27003472024-10-15 17:21:47,639 - LenRenAI - INFO - 录制完毕!
收到消息:是否开始录制 真
2024-10-15 17:21:47,939 - LenRenAI - INFO - 请按F8结束录制收到消息:是否开始录制 假
收到消息:是否禁止录制 真
2024-10-15 17:22:08,845 - LenRenAI - INFO - 请求对插件客户端发送请求: 是否开始录制#@@#假
2024-10-15 17:22:08,847 - LenRenAI - INFO - 请求对插件客户端发送请求: 是否禁止录制#@@#真Process finished with exit code 0

操作记录

鼠标初始 625 914
等待鼠 1.1647663
鼠标 0 0 21316 55553 0
等待鼠 0.0002612
鼠标 0 0 21282 55553 0
等待鼠 0.0398962
鼠标 0 0 21248 55675 0
等待鼠 0.0604424
鼠标 0 0 21248 55735 0
等待鼠 0.0098287
鼠标 0 0 21282 55735 0
等待鼠 0.0251939
鼠标 0 0 21350 55675 0
等待鼠 0.0002929
鼠标 0 0 21486 55432 0
等待鼠 0.0195504
鼠标 0 0 21623 55189 0
等待鼠 0.4348741
鼠标 0 0 21657 55129 0
等待鼠 0.0148643
鼠标 0 0 21725 55007 0
等待鼠 0.0182851
鼠标 0 0 21760 55007 0
等待鼠 0.1272844
鼠标 1 1 21760 55007 0
等待鼠 0.0874505
鼠标 1 2 21760 55007 0
等待鼠 0.1222998
鼠标 0 0 21794 55007 0
等待键 3.3952953
等待鼠 1.2700347

12、键盘鼠标监听

上面使用时发现一个问题,我们好像无法观测他到底开没开始录制,按下F8没有任何提示,录制完成了也不知道从哪里整的,我们先做一个键盘按键监听器,监听我们确实按下按键并且开始录制了

# -*- coding: gbk -*-
import timefrom pynput import keyboarddef on_press(key):# 这里写处理按键的逻辑print(f'{key} pressed')if key == keyboard.Key.esc:  # 如果按下Esc键,则停止监听return Falsedef on_release(key):# 可选:处理按键释放的逻辑print(f'{key} released')# 使用上下文管理器来创建一个监听器
with keyboard.Listener(on_press=on_press,on_release=on_release) as listener:# 监听器在此范围内活动# 其他代码可以在这里执行print("现在开始监听键盘事件,请按Esc键退出。")try:# 无限循环,保持程序运行while True:time.sleep(1)except KeyboardInterrupt:# 捕获Ctrl+C中断信号print("\n用户中断了程序。")

13、添加监听

# -*- coding: gbk -*-
import time
from pynput import keyboard
import state  # 假设 state 模块已经定义并包含相关变量
from wss import send_msg  # 假设 wss 模块已经定义并包含 send_msg 函数# 检查按键
def on_press(key):global stop_listener  # 声明 stop_listener 为全局变量# 如果按下 Esc,则视为停止# print(f'{key} pressed')if key == keyboard.Key.esc:  # 如果按下Esc键,则设置停止标志send_msg("是否禁止录制#@@#真")  # 禁止录制录制send_msg("是否开始录制#@@#假")  # 关闭录制开关state.录制_脚本文本 = ""stop_listener = True  # 设置停止标志# 按键 F8if key == keyboard.Key.media_play_pause:# print(key)print("按下F8")if state.状态_是否开始录制 == False:print("录制中,再次按下F8关闭")send_msg("是否禁止录制#@@#假")  #取消禁止录制send_msg("是否开始录制#@@#真")  #允许开始录制hwnd = 47449172send_msg(f"全局hwnd#@@#{hwnd}")else:print("录制关闭")send_msg("是否禁止录制#@@#真")  # 禁止录制send_msg("是否开始录制#@@#假")  # 关闭的录制开关# 初始化状态
send_msg("是否禁止录制#@@#真")  # 禁止录制
send_msg("是否开始录制#@@#假")  # 关闭的录制开关# 使用上下文管理器来创建一个监听器
with keyboard.Listener(on_press=on_press,
) as listener:# 监听器在此范围内活动# 其他代码可以在这里执行print("现在开始监听键盘事件,请按Esc键退出。")# 判断是否停止循环stop_listener = Falsewhile not stop_listener :time.sleep(1)# print("正在监听...")#检查文本是否存在数据if state.录制_脚本文本 != "":# print(f"已经有数据:{state.录制_脚本文本 }")stop_listener = True# 当离开 with 块时,监听器会自动停止

14、修改监听为后台运行

我们后面是要给ui用的,这里将键盘按键监听改为线程模式运行

# -*- coding: gbk -*-
import time
from pynput import keyboard
import threading
import state  # 假设 state 模块已经定义并包含相关变量
from wss import send_msg  # 假设 wss 模块已经定义并包含 send_msg 函数# 初始化标志变量
stop_listener = False# 检查按键
def on_press(key):global stop_listener  # 声明 stop_listener 为全局变量# 如果按下 Esc,则视为停止if key == keyboard.Key.esc:  # 如果按下Esc键,则设置停止标志send_msg("是否禁止录制#@@#真")  # 禁止录制录制send_msg("是否开始录制#@@#假")  # 关闭录制开关state.录制_脚本文本 = ""stop_listener = True  # 设置停止标志# 按键 F8if key == keyboard.Key.media_play_pause:if state.状态_是否开始录制 == False:print("录制中,再次按下F8关闭")send_msg("是否禁止录制#@@#假")  # 取消禁止录制send_msg("是否开始录制#@@#真")  # 允许开始录制hwnd = 47449172send_msg(f"全局hwnd#@@#{hwnd}")else:print("录制关闭")send_msg("是否禁止录制#@@#真")  # 禁止录制send_msg("是否开始录制#@@#假")  # 关闭的录制开关# 启动按键监听器
def start_keyboard_listener():global stop_listenerstop_listener = Falsedef handle_on_press(key):on_press(key)if stop_listener:listener.stop()# 使用上下文管理器来创建一个监听器with keyboard.Listener(on_press=handle_on_press) as listener:print("现在开始监听键盘事件,请按Esc键退出。")listener.join()  # 等待监听器结束# 启动监听器
def start_listener_thread():thread = threading.Thread(target=start_keyboard_listener)thread.start()return thread# 示例使用
if __name__ == "__main__":# 初始化状态send_msg("是否禁止录制#@@#真")  # 禁止录制send_msg("是否开始录制#@@#假")  # 关闭的录制开关# 启动监听器线程listener_thread = start_listener_thread()# UI 主进程可以在这里继续运行while True:# UI 主进程逻辑time.sleep(1)if state.录制_脚本文本 != "":print(f"已经有数据:{state.录制_脚本文本}")break# 如果需要停止监听器# listener_thread.join()

15、键鼠回放


# 检查按键
def on_press(key):global stop_listener  # 声明 stop_listener 为全局变量# 如果按下 Esc,则视为停止if key == keyboard.Key.esc:  # 如果按下Esc键,则设置停止标志send_msg("是否禁止录制#@@#真")  # 禁止录制录制send_msg("是否开始录制#@@#假")  # 关闭录制开关state.录制_脚本文本 = ""stop_listener = True  # 设置停止标志# 按键 F8if key == keyboard.Key.media_play_pause:if state.状态_是否开始录制 == False:print("录制中,再次按下F8关闭")send_msg("是否禁止录制#@@#假")  # 取消禁止录制send_msg("是否开始录制#@@#真")  # 允许开始录制send_msg(f"全局脚本名#@@#1111.txt")  # 文件名称定义hwnd = 21431468send_msg(f"全局hwnd#@@#{hwnd}")elif state.状态_是否开始录制 == True:print("录制关闭")send_msg("是否禁止录制#@@#真")  # 禁止录制send_msg("是否开始录制#@@#假")  # 关闭的录制开关print(state.录制_脚本文本)#按下F9回放if key == keyboard.Key.media_next:print("开始回放")config = configparser.ConfigParser()# 加载 INI 文件state.状态_是否回放中 = Truemessage = f"解析脚本#@@#C:/Users/Administrator/PycharmProjects/yolo8test/test/1111.txt"send_msg(message)hwnd = 21431468set_window_activate(hwnd)send_msg("脚本执行#@@#1")#当状态_是否回放中 为False就是回放任务返回的消息while True:if state.状态_是否回放中 == False:logger.info("回放结束")break

15、服务端全量

import asyncio
import concurrent.futures
import os
import sys
import threading
import time
import websocketsimport state# 存储所有已连接的客户端
connected_clients = []# 处理客户端请求
# 处理客户端请求
from logger_module import loggerfile_lock = threading.Lock()
def safe_write_to_file(text):with file_lock:with open("1111.txt", "w", encoding="gbk", newline='', buffering=1) as f:f.write(text)async def handle_client(websocket, path):global new_msgtry:# 发送连接成功消息await websocket.send("连接成功")# 添加新连接的客户端到集合中connected_clients.append(websocket)while True:# 接收客户端发送的消息message = await websocket.recv()new_msg = messageprint(f"收到消息:{message}")# 处理接收到的消息if message == "是否回放中 真":state.状态_是否回放中 = Trueelif message == "是否回放中 假":state.状态_是否回放中 = Falseelif message == "是否暂停 真":state.状态_是否暂停 = Trueelif message == "是否暂停 假":state.状态_是否暂停 = Falseelif message == "是否开始录制 假":state.状态_是否开始录制 = Falseelif message == "是否开始录制 真":if state.状态_是否禁止录制 == False:logger.info("请按F8结束录制")# state.QT_信号.mysig_tishi.emit(f"请按F8结束录制")state.状态_是否开始录制 = Trueelif message == "是否禁止录制 假":state.状态_是否禁止录制 = Falseelif message == "是否禁止录制 真":state.状态_是否禁止录制 = Trueelif message[:5] == "录制的脚本":state.录制_脚本文本 = message[6:]safe_write_to_file(state.录制_脚本文本)finally:# 客户端断开连接后,将其移出集合connected_clients.remove(websocket)async def server_main():server = await websockets.serve(handle_client, "localhost", 29943, max_size=1024 * 1024 * 10)# 添加下面这个# 运行插件服务import subprocesstry:# 启动exe程序subprocess.Popen('"' + "./datas/jiaoben/xxx.exe" + '" "' + "29943" + '"')print("插件服务已经启动! 按下F8开始录制")except:print("文件没找到可以能被杀毒软件干掉了 " + "./datas/jiaoben/xxx.exe")try:await asyncio.gather(server.wait_closed())except asyncio.CancelledError:print("WebSocket server was cancelled.")except Exception as e:print(f"An error occurred: {e}")def send_msg(msg="是否回放#@@#假"):'''给躺宝发送指令全部指令  :是否回放#@@#假  /真是否暂停#@@#假  /真是否开始录制#@@#假 /真是否禁止录制#@@#假 /真解析脚本#@@#jiaoben"  jiaoben就是录制的脚本文本 不是文件 是直接文字全局hwnd#@@#12345   12345就是游戏的窗口句柄:param msg: 指令 用 #@@# 分割:return:'''logger.info("请求对插件客户端发送请求: " + msg)asyncio.run_coroutine_threadsafe(send_to_client(-1, msg), loop)async def send_to_client(client_id, message):if len(connected_clients) > 0:# 查找指定的客户端await connected_clients[client_id].send(message)import asyncio# 创建一个新的事件循环
loop = asyncio.new_event_loop()# 以协程非主进程形式运行
t = threading.Thread(target=loop.run_until_complete, args=(server_main(),))# 协程以守护进程运行
python_var = sys.version_info[1]
if python_var > 8:t.daemon = True
else:t.setDaemon(True)
t.start()# print("服务端协程已守护进程模式运行,下面的代码需要去运行其他的主进程, 如果没有进程则会退出")
# print("这个程序将在未来作为一个协程启动,而非主进程形式")
# print("没有前台进程的话直接跑的话,主进程会结束,当主进程结束时,守护进程也会关闭")# input("回车退出")

16、客户端全量

# -*- coding: gbk -*-
import configparser
import os
import timeimport win32con
import win32gui
from pynput import keyboard
import threading
import state  # 假设 state 模块已经定义并包含相关变量
from logger_module import logger
from wss import send_msg  # 假设 wss 模块已经定义并包含 send_msg 函数# 初始化标志变量
stop_listener = False# 检查按键
def on_press(key):global stop_listener  # 声明 stop_listener 为全局变量# 如果按下 Esc,则视为停止if key == keyboard.Key.esc:  # 如果按下Esc键,则设置停止标志send_msg("是否禁止录制#@@#真")  # 禁止录制录制send_msg("是否开始录制#@@#假")  # 关闭录制开关state.录制_脚本文本 = ""stop_listener = True  # 设置停止标志# 按键 F8if key == keyboard.Key.media_play_pause or key == keyboard.Key.f8:if state.状态_是否开始录制 == False:print("录制中,再次按下F8关闭")send_msg("是否禁止录制#@@#假")  # 取消禁止录制send_msg("是否开始录制#@@#真")  # 允许开始录制send_msg(f"全局脚本名#@@#1111.txt")  # 文件名称定义hwnd = 36701828set_window_activate(hwnd)send_msg(f"全局hwnd#@@#{hwnd}")elif state.状态_是否开始录制 == True:print("录制关闭")send_msg("是否禁止录制#@@#真")  # 禁止录制send_msg("是否开始录制#@@#假")  # 关闭的录制开关print(state.录制_脚本文本)#按下F6回放if key == keyboard.Key.f6:print("开始回放")config = configparser.ConfigParser()# 加载 INI 文件state.状态_是否回放中 = Truemessage = f"解析脚本#@@#C:/Users/Administrator/PycharmProjects/yolo8test/test/1111.txt"send_msg(message)hwnd = 36701828set_window_activate(hwnd)send_msg("脚本执行#@@#1")#当状态_是否回放中 为False就是回放任务返回的消息while True:time.sleep(1)if state.状态_是否回放中 == False:logger.info("回放结束")break# time.sleep(5)def set_window_activate(hwnd):"""强制激活窗口"""try:#判断窗口是否为最小化if win32gui.IsIconic(hwnd):# 还原窗口win32gui.ShowWindow(hwnd, win32con.SW_RESTORE)# 将窗口置顶win32gui.SetWindowPos(hwnd, win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)# 取消置顶win32gui.SetWindowPos(hwnd, win32con.HWND_NOTOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)win32gui.SetForegroundWindow(hwnd)except:pass# 启动按键监听器
def start_keyboard_listener():global stop_listenerstop_listener = Falsedef handle_on_press(key):on_press(key)if stop_listener:listener.stop()# 使用上下文管理器来创建一个监听器with keyboard.Listener(on_press=handle_on_press) as listener:print("现在开始监听键盘事件,请按Esc键退出。")listener.join()  # 等待监听器结束# 启动监听器
def start_listener_thread():thread = threading.Thread(target=start_keyboard_listener)thread.start()return thread# 示例使用
if __name__ == "__main__":# 初始化状态send_msg("是否禁止录制#@@#真")  # 禁止录制send_msg("是否开始录制#@@#假")  # 关闭的录制开关# 启动监听器线程listener_thread = start_listener_thread()# UI 主进程可以在这里继续运行while True:# 保存文件time.sleep(1)

17、录制、回放、验证

先获取下窗口句柄,运行后把鼠标放到窗口上然后等待窗口句柄显示

import time
import win32api
import win32guitime.sleep(2)point = win32api.GetCursorPos()  #win32api.GetCursorPos 获取鼠标当前的坐标(x,y)hwnd = win32gui.WindowFromPoint(point)  #查看坐标位置窗口的句柄print(hwnd)  #输出句柄

效果图

注意上面代码中回放按键改成F6了,下图没换

最好别录制中文输入,有时候会有奇奇怪怪的影响,尽量单独字母按键即可

二、UI调用

现在开始我们又需要回到main.py中的主窗口继续编写

1、添加顶层菜单

    def connect_set(self):......# 创建一个顶级菜单self.menu_file = self.menu.addMenu("文件")#添加录制动作self.action_addlianzhao = QAction("录制/编辑连招", self)self.action_addlianzhao.triggered.connect(self.hotkey_addlianzhao)self.menu_file.addAction(self.action_addlianzhao)# 创建一个动作任务回放self.action_addtaskjiaoben = QAction("新增-脚本回放任务", self)self.action_addtaskjiaoben.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_3))self.action_addtaskjiaoben.triggered.connect(self.hotkey_addtaskjiaoben)self.menu_file.addAction(self.action_addtaskjiaoben)#录制任务def hotkey_addlianzhao(self):pass#主窗口添加回放uidef hotkey_addtaskjiaoben(self):pass

2、添加导入录制脚本函数

我们这个ui的项目模拟开始是新开的目录有很多项目中前面已经整好的函数需要用到,我们将前面项目中的的lanrenauto 整个拷贝到我们test项目下面,这个函数主要是为了将我们在其他地方录制好的脚本文件导入到ui项目中

 

from lanrenauto.moni.moni import *class MainWindow(QMainWindow, Ui_mainWindow):def __init__(self):...def hotkey_addlianzhao(self):# 弹出一个选择弹窗 选项为  录制还是文件中选择msgBox = QMessageBox(self)msgBox.setWindowTitle("配置连招")  # 设置消息框标题msgBox.setIcon(QMessageBox.Information)msgBox.setText("当前连招:" + state.LIANZHAO+"\n请选择一个配置方式:" )msgBox.addButton("录制", QMessageBox.AcceptRole)msgBox.addButton("回放", QMessageBox.ApplyRole)msgBox.addButton("文件中选择", QMessageBox.RejectRole)result = msgBox.exec_()if result == QMessageBox.AcceptRole:try:text, ok = QInputDialog.getText(self, '提示', '请输入脚本名:',text=state.LIANZHAO)if not ok:returnif text=="":returnelif text[-4:]!=".txt":state.LIANZHAO=text+".txt"else:state.LIANZHAO = textlogger.info("可以开始录制了,按F8开始/停止")self.sg.mysig_tishi.emit("可以开始录制了,按F8开始/停止")state.状态_是否禁止录制 = Falsestate.状态_是否开始录制 = Falsestate.录制_脚本文本 = ""send_msg("是否禁止录制#@@#假")send_msg("是否开始录制#@@#假")send_msg(f"全局脚本名#@@#{state.LIANZHAO.replace('.txt','')}")hwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE)  # 替换成你实际的窗口句柄send_msg(f"全局hwnd#@@#{hwnd}")self.timer_luzhi_lianzhao.start(200)except:passelif result == QMessageBox.RejectRole:if state.状态_是否回放中 == True:returnif not os.path.exists(state.PATH_JIAOBEN):os.makedirs(state.PATH_JIAOBEN)state.状态_是否回放中 = Truestate.状态_循环开关 = Truejiaoben = os.path.abspath(os.path.join(state.PATH_JIAOBEN,state.LIANZHAO ))message = f"解析脚本#@@#{jiaoben}"send_msg(message)hwnd = win32gui.FindWindow("UnityWndClass", state.GAME_TITLE)  # 替换成你实际的窗口句柄set_window_activate(hwnd)send_msg("脚本执行#@@#1")self.timer_huifang.start(200)elif result == QMessageBox.Warning:self.hotkey_setlianzhao()

3、添加任务脚本

我们在ui主窗口中添加一个任务窗口,让我们能够通过窗口去播放录制的操作

    def hotkey_addtaskjiaoben(self):dir_ = os.path.join(state.PATH_TASK, "任务" + str(int(time.time())) + str(random.randint(10, 100)))if not os.path.exists(dir_):os.makedirs(dir_)# 创建 ConfigParser 对象config = configparser.ConfigParser()# 加载 INI 文件config.read(os.path.join(dir_, "jiaoben.ini"))self.add_taskjiaoben(dir_, config)self.save_ini(dir_, self.datas[-1])def add_taskjiaoben(self, dir_, config):'''添加脚本任务:return:'''# 根据导入的文件# 获取主窗口的宽度width = self.centralWidget().width()# 计算每行可以容纳的组件数量num_per_row = (width ) // (self.column_step * ratio)  # 假设每个组件的宽度为200if num_per_row < 1:num_per_row = 1self.column += 1if self.column == num_per_row:self.column = 0self.row += 1# 加载ini配置文件f_item = FrameItemJiaoBen()f_item.setObjectName("frame_cont")f_item.raise_()f_item.setStyleSheet("""#frame_cont:hover {border: 1px solid red;}#frame_cont {background-color: rgba(0, 0, 0, 0);}""")d = {}d["type"] = "3"dir_ = dir_.replace("/", "\\")if dir_[-1]=="\\":dir_=dir_[:-1]try:config.set('seting', 'name', dir_.split("\\")[-1].strip())except:# 添加一个名为 'seting' 的配置节config.add_section('seting')config.set('seting', 'name', dir_.split("\\")[-1].strip())d["name"] = config.get('seting', 'name', fallback=dir_.split("\\")[-1]).strip()d["cb_auto_f"] = config.get('seting', 'cb_auto_f', fallback='True')d["chuansong"] = config.get('seting', 'chuansong', fallback='')d["cbb_address"] = config.get('seting', 'cbb_address', fallback='手动录制')d["jiaoben"] = config.get('seting', 'jiaoben', fallback='')d["chuansongmoban"] = config.get('seting', 'chuansongmoban', fallback='')d["maodianmoban"] = config.get('seting', 'maodianmoban', fallback='')d["cb_is_checked"] = config.get('seting', 'cb_is_checked', fallback='True')f_item.bt_chuansong.setProperty("OK_chuansong", d["chuansong"] != "")f_item.bt_jiaoben.setProperty("OK_jiaoben", d["jiaoben"] != "")f_item.bt_moban.setProperty("OK_chuansongmoban", d["chuansongmoban"] != "")f_item.bt_moban_maodian.setProperty("OK_maodianmoban", d["maodianmoban"] != "")f_item.bt_chuansong.setObjectName("bt_chuansong")f_item.bt_moban.setObjectName("bt_moban")f_item.bt_moban_maodian.setObjectName("bt_moban_maodian")f_item.led_name.setText(d["name"])f_item.bt_jiaoben.setStyleSheet("""#bt_jiaoben[OK_jiaoben="true"] {color: rgb(237,182,43);  border-color: rgb(237,182,43); }""")f_item.bt_chuansong.setStyleSheet("""#bt_chuansong[OK_chuansong="true"] {color: rgb(237,182,43);  border-color: rgb(237,182,43); }""")f_item.bt_moban.setStyleSheet("""#bt_moban[OK_chuansongmoban="true"] {color: rgb(237,182,43); border-color: rgb(237,182,43); }""")f_item.bt_moban_maodian.setStyleSheet("""#bt_moban_maodian[OK_maodianmoban="true"] {color: rgb(237,182,43); border-color: rgb(237,182,43); }""")if d["cb_is_checked"] == "True":f_item.cb_is_checked.setChecked(True)else:f_item.cb_is_checked.setChecked(False)if d["cb_auto_f"] == "True":f_item.cb_auto_f.setChecked(True)else:f_item.cb_auto_f.setChecked(False)idex = len(self.datas)f_item.bt_jiaoben.clicked.connect(functools.partial(self.clickd_bt_jiaoben, idex))f_item.bt_chuansong.clicked.connect(functools.partial(self.clickd_bt_chuansong, idex))f_item.bt_moban.clicked.connect(functools.partial(self.clickd_bt_moban, idex))f_item.bt_moban_maodian.clicked.connect(functools.partial(self.clickd_bt_moban_maodian, idex))f_item.bt_start.clicked.connect(functools.partial(self.clickd_bt_start, idex))f_item.bt_del.clicked.connect(functools.partial(self.clickd_bt_del, idex))d["f_item"] = f_itemf_item.bt_start.setVisible(False)f_item.bt_jiaoben.setVisible(False)f_item.bt_chuansong.setVisible(False)f_item.bt_moban_maodian.setVisible(False)f_item.bt_moban.setVisible(False)f_item.bt_start.setVisible(False)f_item.bt_del.setVisible(False)f_item.cb_auto_f.setVisible(False)f_item.bt_moban.setToolTip(self.tanChu_img(dir_ + "/" + d["chuansongmoban"]))f_item.bt_moban_maodian.setToolTip(self.tanChu_img(dir_ + "/" + d["maodianmoban"]))self.datas.append(d)self.g_box.addWidget(f_item, self.row, self.column)

添加任务会直接在主菜单栏中添加新的任务,并且依赖于ui页面

4、添加ui页面

formitemjiaoben.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"><class>formitemjiaoben</class><widget class="QWidget" name="formitemjiaoben"><property name="geometry"><rect><x>0</x><y>0</y><width>229</width><height>155</height></rect></property><property name="windowTitle"><string>Form</string></property><layout class="QGridLayout" name="gridLayout"><item row="0" column="0" colspan="4"><layout class="QHBoxLayout" name="horizontalLayout"><item><widget class="QLineEdit" name="led_name"><property name="minimumSize"><size><width>0</width><height>0</height></size></property><property name="maximumSize"><size><width>999</width><height>9999</height></size></property><property name="styleSheet"><string notr="true">color: rgb(255, 255, 255);</string></property><property name="text"><string>蒙德锄地</string></property><property name="alignment"><set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set></property></widget></item><item><widget class="QCheckBox" name="cb_is_checked"><property name="minimumSize"><size><width>0</width><height>0</height></size></property><property name="maximumSize"><size><width>999999</width><height>9999999</height></size></property><property name="layoutDirection"><enum>Qt::RightToLeft</enum></property><property name="styleSheet"><string notr="true"/></property><property name="text"><string/></property><property name="checked"><bool>true</bool></property></widget></item></layout></item><item row="1" column="0" colspan="2"><widget class="QCheckBox" name="cb_auto_f"><property name="minimumSize"><size><width>50</width><height>50</height></size></property><property name="font"><font><family>ADMUI3Lg</family><pointsize>14</pointsize></font></property><property name="focusPolicy"><enum>Qt::TabFocus</enum></property><property name="text"><string>自动按F</string></property><property name="checked"><bool>true</bool></property></widget></item><item row="1" column="2"><widget class="QPushButton" name="bt_jiaoben"><property name="minimumSize"><size><width>50</width><height>50</height></size></property><property name="maximumSize"><size><width>999999</width><height>999999</height></size></property><property name="text"><string>脚本</string></property></widget></item><item row="1" column="3"><widget class="QPushButton" name="bt_start"><property name="minimumSize"><size><width>50</width><height>50</height></size></property><property name="maximumSize"><size><width>999999</width><height>999999</height></size></property><property name="text"><string>启动</string></property></widget></item><item row="2" column="0"><widget class="QPushButton" name="bt_chuansong"><property name="minimumSize"><size><width>50</width><height>50</height></size></property><property name="maximumSize"><size><width>999999</width><height>999999</height></size></property><property name="text"><string>传送
脚本</string></property></widget></item><item row="2" column="1"><widget class="QPushButton" name="bt_moban"><property name="minimumSize"><size><width>50</width><height>50</height></size></property><property name="maximumSize"><size><width>999999</width><height>999999</height></size></property><property name="text"><string>传送
模板</string></property></widget></item><item row="2" column="2"><widget class="QPushButton" name="bt_moban_maodian"><property name="minimumSize"><size><width>50</width><height>50</height></size></property><property name="maximumSize"><size><width>999999</width><height>999999</height></size></property><property name="text"><string>锚点
模板</string></property></widget></item><item row="2" column="3"><widget class="QPushButton" name="bt_del"><property name="minimumSize"><size><width>50</width><height>50</height></size></property><property name="maximumSize"><size><width>999999</width><height>999999</height></size></property><property name="text"><string>删除
任务</string></property></widget></item></layout></widget><resources/><connections/>
</ui>

转换

pyuic5 -o ./formitemjiaoben.py ./formitemjiaoben.ui

5、添加ui调用类

from ui.formitemjiaoben import Ui_formitemjiaoben
class FrameItemJiaoBen(QFrame, Ui_formitemjiaoben):def __init__(self, parent=None):super(QFrame, self).__init__(parent)self.setupUi(self)self.set_ui()self.is_zd=Falsew = int(item_width * ratio)h = int(item_height_min * ratio)self.setMinimumSize(w + int(1 * ratio), h)self.setMaximumSize(w, h)# self.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowCloseButtonHint | Qt.WindowStaysOnTopHint)def set_ui(self):passdef mousePressEvent(self, event):if not self.is_zd:self.is_zd=not self.is_zd# 在这里添加鼠标进入时的逻辑w = int(item_width* ratio)h = int(item_height* ratio)self.setMinimumSize(w + int(1* ratio), h)self.setMaximumSize(w, h)self.bt_start.setVisible(True)self.bt_jiaoben.setVisible(True)self.bt_chuansong.setVisible(True)self.bt_moban_maodian.setVisible(True)self.bt_moban.setVisible(True)self.bt_start.setVisible(True)self.bt_del.setVisible(True)self.cb_auto_f.setVisible(True)else:self.is_zd = not self.is_zdw = int(item_width * ratio)h = int(item_height_min * ratio)self.setMinimumSize(w + int(1* ratio), h)self.setMaximumSize(w, h)self.bt_start.setVisible(False)self.bt_jiaoben.setVisible(False)self.bt_chuansong.setVisible(False)self.bt_moban_maodian.setVisible(False)self.bt_moban.setVisible(False)self.bt_start.setVisible(False)self.bt_del.setVisible(False)self.cb_auto_f.setVisible(False)

 

6、添加定时器及相关任务

class MainWindow(QMainWindow, Ui_mainWindow):def __init__(self):......#添加定时器self.timer_luzhi = QTimer(self)self.timer_luzhi.timeout.connect(self.timeout_luzhi)self.timer_luzhi_chuansong = QTimer(self)self.timer_luzhi_chuansong.timeout.connect(self.timeout_luzhi_chuansong)self.timer_luzhi_lianzhao = QTimer(self)self.timer_luzhi_lianzhao.timeout.connect(self.timeout_luzhi_lianzhao)self.timer_huifang = QTimer(self)self.timer_huifang.timeout.connect(self.timeout_huifang)def timeout_luzhi(self):if state.录制_脚本文本 != "":send_msg("是否禁止录制#@@#真")state.状态_是否禁止录制 = Truedir_ = os.path.join(state.PATH_TASK, self.datas[state.录制_当前任务索引]["name"])if not os.path.exists(dir_):os.makedirs(dir_)# 并且将jiaoben.ini文件也填入数据然后生成path = dir_ + "/脚本.txt"with open(path, "w", encoding="gbk", newline='') as f:f.write(state.录制_脚本文本)self.datas[state.录制_当前任务索引]["jiaoben"] = "脚本.txt"self.datas[state.录制_当前任务索引]["f_item"].bt_jiaoben.setProperty("OK_jiaoben", True)logger.info(f"导入脚本成功:{path}")self.sg.mysig_tishi.emit(f"导入脚本成功:{path}")self.datas[state.录制_当前任务索引]["f_item"].bt_jiaoben.setStyleSheet("""#bt_jiaoben[OK_jiaoben="true"] {color: rgb(237,182,43);  border-color: rgb(237,182,43); }""")state.录制_脚本文本 = ""self.save_ini(dir_, self.datas[state.录制_当前任务索引])self.timer_luzhi.stop()def timeout_luzhi_chuansong(self):if state.录制_脚本文本 != "":send_msg("是否禁止录制#@@#真")state.状态_是否禁止录制 = Truedir_ = os.path.join(state.PATH_TASK, self.datas[state.录制_当前任务索引]["name"])if not os.path.exists(dir_):os.makedirs(dir_)# 并且将jiaoben.ini文件也填入数据然后生成path = dir_ + "/传送脚本.txt"with open(path, "w", encoding="gbk", newline='') as f:f.write(state.录制_脚本文本)self.datas[state.录制_当前任务索引]["chuansong"] = "传送脚本.txt"self.datas[state.录制_当前任务索引]["f_item"].bt_chuansong.setProperty("OK_chuansong", True)logger.info(f"导入脚本成功:{path}")self.sg.mysig_tishi.emit(f"导入脚本成功:{path}")self.datas[state.录制_当前任务索引]["f_item"].bt_chuansong.setStyleSheet("""#bt_chuansong[OK_chuansong="true"] {color: rgb(237,182,43);  border-color: rgb(237,182,43); }""")state.录制_脚本文本 = ""self.save_ini(dir_, self.datas[state.录制_当前任务索引])self.timer_luzhi_chuansong.stop()def timeout_luzhi_lianzhao(self):if state.录制_脚本文本 != "":send_msg("是否禁止录制#@@#真")state.状态_是否禁止录制 = Truedir_ = os.path.abspath(state.PATH_JIAOBEN)if not os.path.exists(dir_):os.makedirs(dir_)# 并且将jiaoben.ini文件也填入数据然后生成path = os.path.join(dir_, state.LIANZHAO)with open(path, "w", encoding="gbk", newline='') as f:f.write(state.录制_脚本文本)state.录制_脚本文本 = ""logger.info("录制并选择连招脚本成功!")self.sg.mysig_tishi.emit("录制并选择连招脚本成功!")self.save_ini_seting()self.timer_luzhi_lianzhao.stop()def timeout_huifang(self):if state.状态_循环开关 == False:state.状态_需重新传送 = Falsesend_msg("是否回放#@@#假")logger.info("回放结束1")self.sg.mysig_tishi.emit("回放结束1")self.timer_huifang.stop()returnif state.状态_是否回放中 == False:logger.info("回放结束2")self.sg.mysig_tishi.emit("回放结束2")self.timer_huifang.stop()

好麻烦,先到这了ε=ε=ε=(~ ̄▽ ̄)~

相关文章:

yolo自动化项目实例解析(八)自建UI-键鼠录制回放

项目中关于键鼠的操作&#xff0c;不像我们之前自动化那样一步一步去定义的&#xff0c;而是用C写了一个记录键鼠的操作&#xff0c;通过回放的方法来实现的 一、通讯系统 1、创建websocket服务器 首先通过事件循环asyncio 和websockets&#xff0c;创建一个持久化的服务端进程…...

C++ 面向对象知识汇总(超详细)

学习交流&#xff1a;0voice GitHub 1.什么是类&#xff1f; 在C中&#xff0c;类&#xff08;Class&#xff09; 是一种用户定义的数据类型&#xff0c;用来描述具有相同特征和行为的一组对象。类是面向对象编程&#xff08;OOP&#xff09;的核心概念&#xff0c;它通过将…...

stm32使用SIM900A模块实现MQTT对接远程服务器

SIM900A模块是一种GSM/GPRS无线通信模块,它可以通过SIM卡连接移动通信网络,并通过串口或USB接口与微控制器或计算机进行通信。 SIM900A驱动代码如下: #include "stm32f10x.h" #include "stdio.h" #include "stdlib.h" #include "sim900a…...

MATLAB Simulink (一)直接序列扩频通信系统

MATLAB & Simulink &#xff08;一&#xff09;直接序列扩频通信系统 写在前面1 系统原理1.1 扩频通信系统理论基础1.1.1 基本原理1.1.2 扩频通信系统处理增益和干扰容限1.1.3 各种干扰模式下抗干扰性能 1.2 直接序列扩频通信系统理论基础1.2.1 基本原理1.2.2 物理模型 2 方…...

标准数字隔离器主要特性和应用---腾恩科技

在现代电子系统中&#xff0c;不同电路部分之间需要可靠的隔离&#xff0c;尤其是在高压环境或必须保持敏感信号完整性的情况下。一种这样的解决方案是使用标准数字隔离器。这些组件在电路的不同部分之间提供电气隔离&#xff0c;确保安全、降噪和可靠的信号传输。本文深入探讨…...

Spring事务的七种传播行为

Spring事务的七种传播行为 1.事务的传播行为是什么&#xff1f;2.具体传播行为2.1 REQUIRED &#xff0c;默认&#xff0c;存在事务则加入该事务&#xff0c;不存在则新建一个事务2.2 REQUIRES_NEW&#xff0c;每次新开启事务&#xff0c;新老事务相互独立2.3 NESTED&#xff0…...

win10怎么卸载软件干净?电脑彻底删除软件的方法介绍,一键清理卸载残留!

电脑上经常会下载各种各样的软件来协助我们办公&#xff0c;不同的软件能够满足不同的需求。 但是不少软件可能使用频率没有那么高&#xff0c;甚至完全不使用。这个时候就需要将这些不常用的电脑软件卸载掉了&#xff0c;卸载软件能够释放一定的存储空间&#xff0c;提高电脑…...

excel中,将时间戳(ms或s)转换成yyyy-MM-dd hh:mm.ss或毫秒格式

问题 在一些输出为时间戳的文本中&#xff0c;按照某种格式显示更便于查看。 如下&#xff0c;第一列为时间戳(s)&#xff0c;第二列是转换后的格式。 解决方案&#xff1a; 在公式输入框中输入&#xff1a;yyyy/mm/dd hh:mm:ss TEXT((A18*3600)/8640070*36519, "yyy…...

机房巡检机器人有哪些功能和作用

随着数据量的爆炸式增长和业务的不断拓展&#xff0c;数据中心面临诸多挑战。一方面&#xff0c;设备数量庞大且复杂&#xff0c;数据中心内服务器、存储设备、网络设备等遍布&#xff0c;这些设备需时刻保持良好运行状态&#xff0c;因为任何一个环节出现问题都可能带来严重后…...

Redis Search系列 - 第一讲 创建索引

目录 一、引言二、全文检索基本概念三、创建索引 一、引言 Redis Search 是 Redis 的一个模块&#xff0c;用于提供全文搜索和二级索引功能。它允许在 Redis 数据库中执行复杂的搜索查询&#xff0c;并支持多种数据类型和查询操作。以下是 Redis Search 的一些关键特性&#x…...

bat 重置 Navicat 试用

bat 脚本文件 echo off set dnInfo set dn2ShellFolder set rpHKEY_CURRENT_USER\Software\Classes\CLSID :: reg delete HKEY_CURRENT_USER\Software\PremiumSoft\NavicatPremium\Registration14XCS /f %针对<strong><font color"#FF0000">navicat<…...

【真题笔记】09-12年系统架构设计师要点总结

【真题笔记】09-12年系统架构设计师要点总结 41 视图DSSA&#xff08;特定领域架构&#xff09;集成系统数据库管理设计模式操作符运算符综合布线备份数据库集成工作流技术软件质量保证需求管理需求开发结构化方法企业战略数据模型事务数据库主题数据库系统设计原型开发静态分析…...

Node + HTML搭建自己的ChatGPT [基础版]

文章目录 明明外面的ChatGPT产品那么多了&#xff0c;为什么要在本地搭建自己的ChatGPT呢&#xff1f;整体架构流程1. 获取APIKey1.1 常见的AI模型1.2 为什么选DeepSeek1.3 怎么获取DeepSeek的APIKey1.3.1 注册并登录DeepSeek开放平台1.3.2 选择API keys1.3.3 创建API key1.3.4…...

关于小程序审核需要提交订单列表页面path的修改办法

小程序又又又又又搞事情啦&#xff5e;&#xff5e;&#xff5e; 从12月31号起&#xff0c;所有有订单生成逻辑的小程序在审核过程中&#xff0c;必须要填写订单列表页面的path才可以进行审核 在代码层面上会有一些小的改动&#xff0c;下面就告诉大家怎么去修改吧。 第一步…...

使用 Nginx 在同一端口部署两个前端项目并配置子路径

在现代 Web 开发中&#xff0c;我们经常需要在同一台服务器上部署多个前端项目。这不仅可以节省资源&#xff0c;还可以简化管理。本文将指导你如何使用Nginx在同一端口上部署两个前端项目&#xff0c;并通过配置子路径来区分它们。 环境准备 首先&#xff0c;我们需要准备两…...

怎么选择独立站SEO效果好的wordpress模板

选择独立站SEO效果好的WordPress模板需要考虑多个因素&#xff0c;包括模板的代码质量、加载速度、SEO友好性以及与SEO插件的兼容性。以下是一些具体的建议&#xff1a; 1. 代码简洁&#xff1a;选择代码简洁的WordPress主题&#xff0c;因为干净的代码不仅使网站更加安全可靠…...

深度学习速通系列:超长法律文件隐私过滤(基于预训练模型Bert)

法律文件隐私过滤 网上使用bert的中文模型进行命名识别教程少的可怜,摸索了一周的时间,硬是把法律文书的人名全部识别出来了,目前可以达到98.9999%(开玩笑的,不过准确率保守估计是有90%以上).注意:这个法律文书目前只是针对裁决书,其他还没测试过,可支持超长文本识别 github仓…...

【数据结构与算法】之队列详解

队列&#xff08;Queue&#xff09;是一种重要的线性数据结构&#xff0c;遵循先进先出、后进后出的原则。本文将更详细地介绍队列的概念、特点、Java 实现以及应用场景。 模运算小复习&#xff1a; a % b 的值总是小于b 5 % 4 1 5 % 2 1 1 % 5 1 4 % 5 4 1. 队列…...

python最新h5st4.9.1调用源码(2025-10-25)

废话不多说&#xff0c;直接上源码&#xff0c;需要技术支持的私。 一、调用js方法&#xff1a; # -*- coding: utf-8 -*- """ -------------------------------------------------Author: byc6352File: jdh5st.pyTime: 2024/10/25 08:03Technical Support:by…...

微软投资比特币:将总资产1%投资于BTC?股东投票决定最终结果!

随着比特币及其他加密货币在全球金融市场中的影响力不断增加&#xff0c;科技巨头微软&#xff08;Microsoft&#xff09;也开始考虑是否在其资产负债表上纳入比特币。根据近期提交给美国证券交易委员会&#xff08;SEC&#xff09;的文件&#xff0c;微软将在2024年12月10日举…...

vue中标签的ref和id的用法和区别优缺点

Vue 3 中 ref 和 id 的用法详解&#xff1a;区别、优缺点及使用场景 在 Vue 3 开发中&#xff0c;我们经常需要获取 DOM 元素或组件实例来进行交互。Vue 提供了 ref 和原生 HTML 属性 id 来实现这种操作。虽然 ref 和 id 都能标识并操作元素&#xff0c;但它们的使用方式、优缺…...

Python基础知识-文件篇

Python 的文件操作是指与文件进行交互的各种技术和方法&#xff0c;包括读取、写入、关闭文件等。以下是对 Python 文件操作的详细介绍&#xff1a; 打开文件 要进行文件操作&#xff0c;首先需要打开文件。Python 提供了内置的 open() 函数。 file open(example.txt, r) …...

MacOS 环境下 VSCode 的 C++ 环境搭建

MacOS 环境下 VSCode 的 C 环境搭建 编译器安装 编译器可以选择 Clang 或者 GCC&#xff0c;在 MacOS 上 Clang 的安装更为简单一些。 Clang(推荐) 打开终端输入命令&#xff0c; clang -v 查看是否已经安装。 如果已经安装&#xff0c;会输出类似于如下的信息&#xff1…...

WPF样式

WPF&#xff08;Windows Presentation Foundation&#xff09;是微软推出的一种用于构建Windows应用程序的UI框架。它提供了一套丰富的控件、图形和动画功能&#xff0c;允许开发者创建具有丰富视觉效果的现代用户界面。WPF中的样式&#xff08;Styles&#xff09;是一种强大的…...

Vue Router 如何配置 404 页面?

在 Vue 项目中&#xff0c;如果你想配置一个 404 页面&#xff08;即找不到页面提示&#xff09;&#xff0c;你需要通过 Vue Router 来设置。这通常通过将路由配置中的 *&#xff08;通配符&#xff09;指向一个 404 组件来实现。 // 定义路由部分 const routes [{path: /,c…...

【C++:智能指针】

什么是内存泄漏 内存泄漏是指因为疏忽或者错误造成程序对一部分不再使用的内存没有进行释放的情况&#xff0c;内存释放不是指内存在物理上的消失&#xff0c;而是应用程序分配某段内存时&#xff0c;因设计错误&#xff0c;失去了对该内存的控制&#xff0c;从而造成内存浪费 …...

onlyoffice docker启用jwt并生成jwt

一、说明 本文是docker教程&#xff0c;linux/win的安装版本也类似&#xff0c;只需要修改配置文件中的secrt就可以了【Configuring JWT for ONLYOFFICE Docs - ONLYOFFICE】 二、正文开始 docker启动时候如果不想使用jwt&#xff0c;加上参数-e JWT_ENABLEDfalse就可以了&…...

希尔贝壳受邀参加首届“数据标注产业大会暨供需对接会”

为推动数据标注产业高质量发展&#xff0c;促进数据标注基地快速形成面向产业的规模化服务能力。10月22日&#xff0c;由国家数据局数字科技和基础设施建设司指导的首届“数据标注产业大会暨供需对接会”在北京召开&#xff0c;希尔贝壳受邀参加。 大会旨在进一步推动数据标注…...

35.第二阶段x86游戏实战2-C++遍历技能

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要…...

Jenkins发布vue项目,版本不一致导致build错误

问题一 yarn.lock文件的存在导致在自动化的时候&#xff0c;频频失败问题二 仓库下载的资源与项目资源版本不一致 本地跑好久的一个项目&#xff0c;现在需要部署在Jenkins上面进行自动化打包部署&#xff1b;想着部署后今后可以省下好多时间&#xff0c;遂兴高采烈地去部署&am…...