1.介绍说明
本程序类似于按键精灵,代替用户执行重复性的的按键、输入文本、鼠标点击功能,可设置时间延迟。
示例如图:
使用流程:
1.共有ABCDEF六套方案,用户编辑任意方案
2.点击打开,可打开之前保存的方案
点击保存,可保存方案。
点击生成之后将不可编辑,并且开始监听热键,启动热键执行方案,结束热键均为F12
点击修改之后可以修改,结束监听按键,按键将无法执行,修改完成点击生成
3.按键框可输入普通按键,如N,1,t等,执行时会按压此键
按键框可输入文本,如:adsaf,123234等,执行时会输入此文本
按键框激活时,按压TAB 或者回车键,按键框会相应,,执行时照此执行
按键框激活时,在按键框以外点击鼠标左键或者右键,按键框会相应显示,执行时照此执行
延迟框可输入上次按键后需要间隔的时间,单位是秒,如:1 或者 1.3 等,输入汉字会崩溃
2、开发构思
三个功能同时工作,互相影响,所以需要三个线程,分别是
1.主线程,ui界面需要ui.show,所以一直占用一个线程,如果其他动作时间过长,会无响应
使用qtdesigner.exe设置ui界面,然后调用,尽量多的在qtdesigner完成设置
2.监听线程,持续监听全局热键,以便控制方案的开启和关闭
使用QThread多线程, pyqtSignal线程间传输信号.
使用pyhooked import Hook, KeyboardEvent全局监听热键,本打算用PYHK,但是安装不了,听说pyhk也是有大佬打包好的,看来还有其他方法可以
3.自动化线程,收到接听到的信号之后,开始执行,自动化输入
使用pyautogui#实现自动化
3.代码部分
# -*- coding:utf-8 -*-
import sys#读取数据文件用
import time#控制按键的延迟
import pyautogui#实现自动化
from PyQt5.QtCore import Qt, QThread, pyqtSignal# QThread多线程, pyqtSignal线程间传输信号
from PyQt5.QtGui import QDoubleValidator#延迟框只能输入数字
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5 import sip#打包时显示WARNING: Hidden import "sip" not found!,加上也没啥用
from pyhooked import Hook, KeyboardEvent#全局监听热键,本打算用PYHK,但是安装不了
from anjian_ui_main import Ui_MainWindow # 调用qtdesigner.exe生成的主窗口,好省事
# 子线程1_执行命令,自动化线程
class BackendThread(QThread):
def __init__(self, e=0):
super().__init__()
self.e = e # 传入的按键值,F6=0,使用方案A
def run(self):
while 1: # 默认一直循环
# 执行A方案的文本框内容
for index, step in enumerate(ui.dataALL[self.e]):
if step: # 若不为空
if index % 2 != 1: # 除2无余数,也是就是偶数,按键信息框
if len(step) == 1: # 只是1个按键,那就是按键,按压它
pyautogui.press(step)
elif step == "左键":#那就点击左键
pyautogui.leftClick()
elif step == "右键":#那就是右键
pyautogui.rightClick()
elif step == "tab":#那就按压tab键
print(f"输入{step}")
pyautogui.press("tab")
elif step == "Enter":
pyautogui.press("Enter")
print(f"输入{step}")
else:
pyautogui.write(step) # 否则就是输入文本
print(f"输入{step}")
else: # 是时间框体
time.sleep(float(step))#延迟数秒
print(f"延迟{step}秒")
# 若是执行一次,索引为1,退出循环
if ui.comboBoxall[self.e].currentIndex() == 0:
print("仅执行一次")
break
print("再循环一次")
# 子线程2_hotkey
class hotkeynew(QThread):
sinOut = pyqtSignal(int) # 监听到热键后,传出的信号
def run(self):
def handle_events(args):
if isinstance(args, KeyboardEvent):
if args.current_key == "F5" and args.event_type == "key down": # 如果F5按下了,传出0,也就是方案A
self.sinOut.emit(0)
if args.current_key == "F6" and args.event_type == "key down":
self.sinOut.emit(1)
if args.current_key == "F7" and args.event_type == "key down":
self.sinOut.emit(2)
if args.current_key == "F8" and args.event_type == "key down":
self.sinOut.emit(3)
if args.current_key == "F9" and args.event_type == "key down":
self.sinOut.emit(4)
if args.current_key == "F10" and args.event_type == "key down":
self.sinOut.emit(5)
if args.current_key == "F12" and args.event_type == "key down":# 如果F12按下了,传出12,关闭线程
self.sinOut.emit(12)
hk = Hook()#实例化线程
hk.handler = handle_events
hk.hook()#启动热键监控
class MainWindow(QMainWindow, Ui_MainWindow): # 多重继承QMainWindow和Ui_MainWindow
def __init__(self):
super(MainWindow, self).__init__() # 先调用父类QMainWindow的初始化方法
self.setupUi(self) # 再调用setupUi方法
# 实例化热键线程
self.treadhotkey = hotkeynew()
self.treadhotkey.sinOut.connect(self.doit) # 热键后发送信号给执行
# 实例化执行线程
self.treaddoit = BackendThread()
self.dataALL = [[], [], [], [], [], []] # 用来存储6种方案
self.anjianlei = [self.anjianA, self.anjianB, self.anjianC, self.anjianD, self.anjianE, self.anjianF]
self.deleylei = [self.deleyA, self.deleyB, self.deleyC, self.deleyD, self.deleyE, self.deleyF]
self.comboBoxall = [self.comboBox1, self.comboBox2, self.comboBox3, self.comboBox4, self.comboBox5,
self.comboBox6]
self.saveaction.triggered.connect(self.savenow) # 保存连接到savenow
self.openaction.triggered.connect(self.opennow) # 打开连接到opennow
self.finishwriteaction.triggered.connect(self.finishwrite) # 生成按键连接到finishwrite
self.rewriteaction.triggered.connect(self.rewrite) # 修改按键连接到rewrite
#重新定义主页面的按键,去掉焦点,否则不能输入TAB。其实可以在qtdesigner.exe设置
for u in range(6): # 6种方案
for i in range(8): # 8个按键
self.anjianlei[u].itemAt(i).widget().setFocusPolicy(Qt.ClickFocus) # 单击成为焦点,为了tab能用
self.deleylei[u].itemAt(i).widget().setFocusPolicy(Qt.ClickFocus) # 单击成为焦点
self.deleylei[u].itemAt(i).widget().setValidator(QDoubleValidator()) # 时间框体只能输入数字
def savenow(self): # 保存的动作
self.dataALL = [[], [], [], [], [], []] # 6种方案,之前的清理掉,重新保存现在的
#现在的方案都保存进去
for u in range(6): # 6种方案
for i in range(8): # 8个按键
self.dataALL[u].append(self.anjianlei[u].itemAt(i).widget().text())
self.dataALL[u].append(self.deleylei[u].itemAt(i).widget().text())
file = open("anjiandata.txt", "w")#打开txt。数据较少,为了方便,习惯用txt保存数据
file.write(str(self.dataALL))#写入
print("写入成功")
file.close()#关闭
def opennow(self): # 打开之前保存的数据
with open("anjiandata.txt", "r") as file:
self.dataALL = eval(file.read()) # 之前保存的数据,file.read()是str格式,需要转化为list,用eval,字典转化用ast
#把打开的输入输入到各个文本框
for u in range(6): # 6种方案
for i in range(8): # 8个按键
self.anjianlei[u].itemAt(i).widget().setText(self.dataALL[u][i * 2])
self.deleylei[u].itemAt(i).widget().setText(self.dataALL[u][i * 2 + 1])
def finishwrite(self): # 生成之后不可写
if self.focusWidget(): # 保存以后有焦点就清除
self.focusWidget().clearFocus()
for u in range(6): # 6种方案
for i in range(8): # 8个按键
self.anjianlei[u].itemAt(i).widget().setReadOnly(1) # 设置不可写
self.deleylei[u].itemAt(i).widget().setReadOnly(1)# 设置不可写
self.anjianlei[u].itemAt(i).widget().setFocusPolicy(Qt.NoFocus) # 不能设为焦点
self.deleylei[u].itemAt(i).widget().setFocusPolicy(Qt.NoFocus) # 不能设为焦点
self.dataALL[u].append(self.anjianlei[u].itemAt(i).widget().text()) # 储存下文本
self.dataALL[u].append(self.deleylei[u].itemAt(i).widget().text()) # 储存下文本
print("生成之后开启监控热键线程")
self.treadhotkey.start()#生成之后开启监控热键线程
def rewrite(self): # 重写之后可以写
for u in range(6): # 6种方案
for i in range(8): # 8个按键
self.anjianlei[u].itemAt(i).widget().setReadOnly(0)#设置可以写
self.deleylei[u].itemAt(i).widget().setReadOnly(0)#设置可以写
self.anjianlei[u].itemAt(i).widget().setFocusPolicy(Qt.ClickFocus) # 设只能单击为焦点
self.deleylei[u].itemAt(i).widget().setFocusPolicy(Qt.ClickFocus) # 设只能单击为焦点
# 重写之后关闭监控热键线程
self.treadhotkey.terminate()
# 如过还有执行线程,关闭他
if self.treaddoit.isRunning():
self.treaddoit.terminate()
print("关闭线程")
# 重写鼠标事件
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton and self.focusWidget(): # 如果左键并且有焦点
self.focusWidget().setText("左键")#这个框体的文本设为左键
if event.button() == Qt.RightButton and self.focusWidget():
self.focusWidget().setText("右键")
# 执行热键的方案,重写事件
def keyPressEvent(self, event):
if event.key() == Qt.Key_Tab and self.focusWidget():#如果按了tab并且有焦点
self.focusWidget().setText("tab")#这个框体的文本设为tab
if event.key() == 16777220 and self.focusWidget(): #如果按了回车并且有焦点
self.focusWidget().setText("Enter")
def doit(self, e): # e是按键的表示,F6用6表示
if e == 12: # 按了F12
if self.treaddoit.isRunning():#若有线程就关闭他
self.treaddoit.terminate()
print("关闭线程")
else:
pass
else:
# 实例化线程
self.treaddoit = BackendThread(e)
self.treaddoit.start()
print(f"启动线程{e}")
if __name__ == '__main__':
app = QApplication(sys.argv)
ui = MainWindow()
ui.show()
sys.exit(app.exec_())
附赠qtdesigner.exe生成的代码,诸君可以拿去打包,这个不需要看,打包用
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'anjian_ui_main.ui'
#
# Created by: PyQt5 UI code generator 5.15.6
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(1298, 791)
MainWindow.setLocale(QtCore.QLocale(QtCore.QLocale.Chinese, QtCore.QLocale.China))
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout.setObjectName("gridLayout")
self.verticalLayoutA = QtWidgets.QVBoxLayout()
self.verticalLayoutA.setObjectName("verticalLayoutA")
self.label_15 = QtWidgets.QLabel(self.centralwidget)
self.label_15.setObjectName("label_15")
self.verticalLayoutA.addWidget(self.label_15)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.verticalLayout_19 = QtWidgets.QVBoxLayout()
self.verticalLayout_19.setObjectName("verticalLayout_19")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setObjectName("label")
self.verticalLayout_19.addWidget(self.label)
self.label_3 = QtWidgets.QLabel(self.centralwidget)
self.label_3.setObjectName("label_3")
self.verticalLayout_19.addWidget(self.label_3)
self.label_5 = QtWidgets.QLabel(self.centralwidget)
self.label_5.setObjectName("label_5")
self.verticalLayout_19.addWidget(self.label_5)
self.label_7 = QtWidgets.QLabel(self.centralwidget)
self.label_7.setObjectName("label_7")
self.verticalLayout_19.addWidget(self.label_7)
self.label_9 = QtWidgets.QLabel(self.centralwidget)
self.label_9.setObjectName("label_9")
self.verticalLayout_19.addWidget(self.label_9)
self.label_18 = QtWidgets.QLabel(self.centralwidget)
self.label_18.setObjectName("label_18")
self.verticalLayout_19.addWidget(self.label_18)
self.label_17 = QtWidgets.QLabel(self.centralwidget)
self.label_17.setObjectName("label_17")
self.verticalLayout_19.addWidget(self.label_17)
self.label_21 = QtWidgets.QLabel(self.centralwidget)
self.label_21.setObjectName("label_21")
self.verticalLayout_19.addWidget(self.label_21)
self.horizontalLayout.addLayout(self.verticalLayout_19)
self.anjianA = QtWidgets.QVBoxLayout()
self.anjianA.setObjectName("anjianA")
self.anjianA1 = QtWidgets.QLineEdit(self.centralwidget)
self.anjianA1.setText("")
self.anjianA1.setObjectName("anjianA1")
self.anjianA.addWidget(self.anjianA1)
self.anjianA2 = QtWidgets.QLineEdit(self.centralwidget)
self.anjianA2.setObjectName("anjianA2")
self.anjianA.addWidget(self.anjianA2)
self.anjianA3 = QtWidgets.QLineEdit(self.centralwidget)
self.anjianA3.setObjectName("anjianA3")
self.anjianA.addWidget(self.anjianA3)
self.anjianA4 = QtWidgets.QLineEdit(self.centralwidget)
self.anjianA4.setObjectName("anjianA4")
self.anjianA.addWidget(self.anjianA4)
self.anjianA5 = QtWidgets.QLineEdit(self.centralwidget)
self.anjianA5.setObjectName("anjianA5")
self.anjianA.addWidget(self.anjianA5)
self.anjianA6 = QtWidgets.QLineEdit(self.centralwidget)
self.anjianA6.