使用Python开发测试小工具-录制回放工具的实现
程序员文章站
2022-07-13 15:38:59
...
Pyqt5 信号槽机制可参考:https://blog.51cto.com/9291927/2422187
信号槽是Qt的核心机制,也是PyQt编程中对象进行通信的机制。在Qt中,QObject对象和PyQt中所有继承自QWidget的控件都支持信号槽机制。当信号发射时,连接的槽函数会自动执行。在PyQt5中,信号与槽函数通过object.signal.connect()方法进行连接。
mymainwindow.py 通过QtDesigner设计并转换
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.centralwidget)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.verticalLayout = QtWidgets.QVBoxLayout()
self.verticalLayout.setObjectName("verticalLayout")
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.pushButton_1 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_1.setObjectName("pushButton_1")
self.horizontalLayout.addWidget(self.pushButton_1)
self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_2.setObjectName("pushButton_2")
self.horizontalLayout.addWidget(self.pushButton_2)
self.line = QtWidgets.QFrame(self.centralwidget)
self.line.setFrameShape(QtWidgets.QFrame.VLine)
self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
self.line.setObjectName("line")
self.horizontalLayout.addWidget(self.line)
self.pushButton_3 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_3.setObjectName("pushButton_3")
self.horizontalLayout.addWidget(self.pushButton_3)
self.pushButton_4 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_4.setObjectName("pushButton_4")
self.horizontalLayout.addWidget(self.pushButton_4)
self.verticalLayout.addLayout(self.horizontalLayout)
self.tableWidget = QtWidgets.QTableWidget(self.centralwidget)
self.tableWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.tableWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.tableWidget.setObjectName("tableWidget")
self.tableWidget.setColumnCount(0)
self.tableWidget.setRowCount(0)
self.tableWidget.horizontalHeader().setCascadingSectionResizes(False)
self.tableWidget.verticalHeader().setVisible(False)
self.verticalLayout.addWidget(self.tableWidget)
self.horizontalLayout_2.addLayout(self.verticalLayout)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 23))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "按键精灵"))
self.pushButton_1.setText(_translate("MainWindow", "录制"))
self.pushButton_2.setText(_translate("MainWindow", "停止"))
self.pushButton_3.setText(_translate("MainWindow", "回放"))
self.pushButton_4.setText(_translate("MainWindow", "停止"))
self.tableWidget.setSortingEnabled(True)
mylistener.py 文件,定义MyWindow类,继承自Ui_MainWindow,并在其中定义信号与槽
import sys
import os
import threading
import time
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QCheckBox, QTableWidgetItem
from mymainwindow import *
from pynput import mouse, keyboard
from record import Recorder
from playback import PlayBack
import ctypes
class MyWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MyWindow, self).__init__(parent)
self.setupUi(self)
self.inittable()
self.getscripts()
self.pushButton_1.clicked.connect(self.start_record)
self.pushButton_2.clicked.connect(self.getscripts)
self.pushButton_2.clicked.connect(self.stop_record)
self.pushButton_3.clicked.connect(self.start_playback)
self.pushButton_4.clicked.connect(self.stop_playback)
#self.pushButton_4.clicked.connect(self.reset_button)
self.__mouse_thread: mouse.Listener = None
self.__key_thread: keyboard.Listener = None
self.__playback_thread: PlayBack = None
def start_record(self):
self.setWindowState(Qt.WindowMinimized)
self.pushButton_1.setDisabled(True)
self.pushButton_3.setDisabled(True)
self.pushButton_4.setDisabled(True)
filename = time.strftime("%Y_%m_%d_%H_%M_%S_", time.localtime(time.time())) + "Record.txt"
file = os.path.join(os.getcwd(), filename)
mylistener = Recorder(file)
self.__mouse_thread = mouse.Listener(on_click=mylistener.on_click)
self.__key_thread = keyboard.Listener(on_press=mylistener.on_press, on_release=mylistener.on_release)
self.__mouse_thread.start()
self.__key_thread.start()
def stop_record(self):
if self.__mouse_thread is not None:
if self.__mouse_thread.isAlive():
self.__mouse_thread.stop()
if self.__mouse_thread is not None:
if self.__key_thread.isAlive():
self.__key_thread.stop()
self.pushButton_1.setDisabled(False)
self.pushButton_3.setDisabled(False)
self.pushButton_4.setDisabled(False)
def inittable(self):
# 设置行数和列数
self.tableWidget.setColumnCount(3)
self.tableWidget.setRowCount(1)
self.tableWidget.setColumnWidth(0, 100)
self.tableWidget.setColumnWidth(1, 300)
self.tableWidget.setColumnWidth(2, 300)
# 设置数据标题
self.tableWidget.horizontalHeader().setStyleSheet("QHeaderView::section{background:skyblue;}")
self.tableWidget.setHorizontalHeaderItem(0, QTableWidgetItem("序号"))
self.tableWidget.setHorizontalHeaderItem(1, QTableWidgetItem("文件名"))
self.tableWidget.setHorizontalHeaderItem(2, QTableWidgetItem("修改时间"))
self.tableWidget.horizontalHeader().setStretchLastSection(True)
def getscripts(self):
# 获取当前路径下的txt文件
cwdpath = os.getcwd()
filelist = []
for file in os.listdir(cwdpath):
if file.endswith(".txt"):
filelist.append(file)
row_num = len(filelist)
self.tableWidget.setRowCount(row_num)
# 设置数据条目
for row in range(row_num):
# 第一列为checkbox
item_checked = QTableWidgetItem()
item_checked.setText(str(row + 1))
item_checked.setCheckState(Qt.Unchecked)
item_checked.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
self.tableWidget.setItem(row, 0, item_checked)
# 第二列为文件名
item_name = QTableWidgetItem(filelist[row])
item_name.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
self.tableWidget.setItem(row, 1, item_name)
# 第三列为文件修改时间
filepath = os.path.join(cwdpath, filelist[row])
mtime = os.stat(filepath).st_mtime
mtime_str = time.strftime('%Y_%m_%d %H:%M:%S', time.localtime(mtime))
item_time = QTableWidgetItem(mtime_str)
item_time.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
self.tableWidget.setItem(row, 2, item_time)
def get_checkedfiles(self):
rownum = self.tableWidget.rowCount()
filelist = []
for i in range(rownum):
if self.tableWidget.item(i, 0).checkState() == Qt.Checked:
filename = self.tableWidget.item(i, 1).text()
file = os.path.join(os.getcwd(), filename)
filelist.append(file)
return filelist
def start_playback(self):
filelist = self.get_checkedfiles()
if len(filelist) != 0:
self.setWindowState(Qt.WindowMinimized)
self.pushButton_1.setDisabled(True)
self.pushButton_2.setDisabled(True)
self.pushButton_3.setDisabled(True)
self.__playback_thread = PlayBack(filelist)
self.__playback_thread.start()
self.__playback_thread.finished.connect(self.reset_button)
def reset_button(self):
self.pushButton_1.setDisabled(False)
self.pushButton_2.setDisabled(False)
self.pushButton_3.setDisabled(False)
self.pushButton_4.setDisabled(False)
def stop_playback(self):
if self.__playback_thread is not None:
if self.__playback_thread.isRunning():
self.__playback_thread.stop()
if __name__ == '__main__':
app = QApplication(sys.argv)
myWin = MyWindow()
myWin.show()
sys.exit(app.exec_())
record.py 启动线程对鼠标键盘进行监控
import os
import time
from pynput import mouse
from pynput.mouse import Button
from pynput import keyboard
from _datetime import datetime
class Recorder(object):
def __init__(self, file):
self.file = file
def on_click(self, x, y, button, pressed):
if button == Button.left:
button_name = 'Left'
elif button == Button.middle:
button_name = 'Middle'
elif button == Button.right:
button_name = 'Right'
else:
button_name = 'Unknown'
if pressed:
msg = 'mouse {0} Presses at {1} {2} \n'.format(button_name, x, y)
else:
msg = 'mouse {0} Released at {1} {2} \n'.format(button_name, x, y)
with open(self.file, "a") as f:
timestr = datetime.now().strftime("%Y_%m_%d_%H_%M_%S.%f")[:-3]
f.write(timestr)
f.write(" ")
f.write(msg)
def on_press(self, key):
# msg = 'key {0} pressed \n'.format(key)
try:
msg = 'key alphanumeric {0} pressed \n'.format(key.char)
except AttributeError:
msg = 'key special {0} pressed \n'.format(key)
with open(self.file, "a") as f:
timestr = datetime.now().strftime("%Y_%m_%d_%H_%M_%S.%f")[:-3]
f.write(timestr)
f.write(" ")
f.write(msg)
def on_release(self, key):
# msg = 'key {0} released \n'.format(key)
try:
msg = 'key alphanumeric {0} released \n'.format(key.char)
except AttributeError:
msg = 'key special {0} released \n'.format(key)
# Stop listener
if key == keyboard.Key.esc:
return False
with open(self.file, "a") as f:
timestr = datetime.now().strftime("%Y_%m_%d_%H_%M_%S.%f")[:-3]
f.write(timestr)
f.write(" ")
f.write(msg)
playback.py 对录制文件进行解析,并回放
import ctypes
import re
from _datetime import datetime
import time
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QMainWindow
from pynput.mouse import Button, Controller as MController
from pynput.keyboard import Key, Controller as KController
import threading
from mymainwindow import Ui_MainWindow
class PlayBack(QThread):
# trigger = pyqtSignal()
def __init__(self, filelist):
super(PlayBack, self).__init__()
self.filelist = filelist
self._running = True
def run(self):
time.sleep(3)
self.playback_allfiles()
# self.trigger.emit()
def stop(self):
self._running = False
def playback_allfiles(self):
for file in self.filelist:
self.playback_file(file)
def playback_file(self, file):
with open(file, "r") as f:
timestmpA = 0
for line in f:
if self._running:
if timestmpA != 0:
timeB = datetime.strptime(line.split(" ")[0], "%Y_%m_%d_%H_%M_%S.%f")
timestmpB = timeB.timestamp()
timedelta = timestmpB - timestmpA
time.sleep(timedelta)
if "mouse" in line:
# print("This is the mouse operation")
pattern = re.compile(r"(\d+) (\d+)")
match = re.search(pattern, line)
if match:
x = match.group(1)
y = match.group(2)
mouse = MController()
mouse.position = (x, y)
if "Presses" in line:
if "Left" in line:
mouse.press(button=Button.left)
print("Mouse Left press at ({0},{1})".format(x, y))
elif "Right" in line:
mouse.click(button=Button.right)
print("Mouse Right press at ({0},{1})".format(x, y))
elif "Middle" == line:
mouse.click(button=Button.middle)
print("Mouse Middle press at ({0},{1})".format(x, y))
else:
print("Unkonwn Mouse Action")
elif "Released" in line:
if "Left" in line:
mouse.release(button=Button.left)
print("Mouse Left release at ({0},{1})".format(x, y))
elif "Right" in line:
mouse.release(button=Button.right)
print("Mouse Right release at ({0},{1})".format(x, y))
elif "Middle" == line:
mouse.release(button=Button.middle)
print("Mouse Middle release at ({0},{1})".format(x, y))
else:
print("Unkonwn Mouse Action")
else:
print("Error:There is no mouse position")
elif "key" in line:
# print("This is the keyboard operation")
keyboard = KController()
if "special" in line:
pattern = re.compile(r"special (.+?) ")
match = re.search(pattern, line)
if match:
key = eval(match.group(1))
else:
print("Error:There is no key action")
elif "alphanumeric" in line:
pattern = re.compile(r"alphanumeric (.+?) ")
match = re.search(pattern, line)
if match:
key = match.group(1)
else:
print("Error:There is no key action")
if "pressed" in line:
keyboard.press(key)
print("Keyboard pressed {0}".format(key))
elif "released" in line:
keyboard.release(key)
print("Keyboard released {0}".format(key))
timeA = datetime.strptime(line.split(" ")[0], "%Y_%m_%d_%H_%M_%S.%f")
timestmpA = timeA.timestamp()
else:
break
实际效果如图