Main
# encoding=utf8
from Action_command import *
from Main_method import *
import time
from datetime import datetime, date, timedelta
import datetime
import os
import threading
from pynput import keyboard
import _thread
from PIL import ImageGrab
import cv2
import cv2 as cv
from cv2 import VideoWriter, VideoWriter_fourcc, imread, resize, VideoCapture
import numpy as np
module = get_config("module", "module")
test_time = get_config("test_time", "time")
test_times = datetime.datetime.now() + datetime.timedelta(hours=float(test_time.strip('h')))
def main(start_tab):
print("测试时间:", test_time,test_times)
global flag
# global test_times
if check_client_working():
os.system('taskkill /f /im FTNN.exe')
time.sleep(1)
if start_tab == '牛牛圈':
nnq_window = start_connect_nnq(start_tab)
while datetime.datetime.now() < test_times:
check_top(start_tab, nnq_window)
check_minmax(nnq_window, '牛牛圈')
if not (check_client_working() and nnq_window.exists()):
logger2.info("客户端或牛牛圈被关闭")
flag = True
nnq_window = start_connect_nnq(start_tab)
try:
choice_order_execute(choice_ctrl(nnq_window))
except Exception as e:
logger2.info("动作执行失败,尝试重新关联{}".format(e))
flag = True
nnq_window = start_connect_nnq(start_tab)
time.sleep(0.5)
elif start_tab == '资讯' or start_tab == '消息':
zx_window = start_connect_zx(start_tab)
while datetime.datetime.now() < test_times:
check_top(start_tab, zx_window)
check_minmax(zx_window, '消息')
if not check_client_working():
logger2.info("客户端被关闭")
flag = True
zx_window = start_connect_zx(start_tab)
try:
choice_order_execute(choice_ctrl(zx_window))
except Exception as e:
logger2.info("动作执行失败,尝试重新关联{}".format(e))
flag = True
zx_window = start_connect_zx(start_tab)
time.sleep(1)
def on_press(key): # 监听按键
# global test_times
if key == keyboard.Key.home or datetime.datetime.now() > test_times:
logger2.info("稳定性测试结束")
return False # 返回False,键盘监听结束!
def video_record(): # 录⼊视频
# global test_times
global flag
global logger3
#test_time = get_config("test_time", "time")
video_time = get_config("test_time", "video_time")
more_time = get_config("test_time", "more_time")
video_address = get_config("path", "video_address")
# test_times = datetime.datetime.now() + datetime.timedelta(hours=float(test_time.strip('h')))
video_times = int(video_time.strip('s'))
more_times = int(more_time.strip('s'))
while datetime.datetime.now() < test_times:
flag = False
name = datetime.datetime.now().strftime('%Y-%m-%d %H-%M-%S') # 当前的时间(当文件名)
screen = ImageGrab.grab() # 获取当前屏幕
width, high = screen.size # 获取当前屏幕的大小
fourcc = VideoWriter_fourcc('X', 'V', 'I', 'D') # MPEG-4编码,文件后缀可为.avi .asf .mov等
video = VideoWriter(str(video_address)+'\\%s.avi' % name, fourcc, 15, (width, high)) # (文件名,编码器,帧率,视频宽⾼)
# print('3秒后开始录制----') # 可选
# time.sleep(3)
logger3.info("开始录制")
start_time = time.time()
while True:
if time.time() - start_time >= video_times:
# logger3.info("视频自然结束,删除")
video.release() # 释放
# time.sleep(1)
# os.remove('%s.avi' % name)
break
elif flag:
logger3.info("flag!多录{}秒".format(more_times))
now_time = time.time()
now_time_show = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
logger3.info('发生时间:{},文件名:{}'.format(now_time_show, name))
while True:
if time.time() - now_time >= more_times:
video.release() # 释放
time.sleep(1)
# video_info()
break
im = ImageGrab.grab() # 图片为RGB模式
imm = cv.cvtColor(np.array(im), cv.COLOR_RGB2BGR) # 转为opencv的BGR模式
video.write(imm) # 写入
break
im = ImageGrab.grab() # 图片为RGB模式
imm = cv.cvtColor(np.array(im), cv.COLOR_RGB2BGR) # 转为opencv的BGR模式
video.write(imm) # 写入
if __name__ == '__main__':
t = threading.Thread(target=main, args=(module,))
t1 = threading.Thread(target=video_record)
t.setDaemon(True) # 设置守护线程必须写在start之前,否则不生效
t1.setDaemon(True)
t1.start()
t.start()
with keyboard.Listener(on_press=on_press) as listener:
listener.join()
Main_method
# encoding=utf8
from Action_command import *
from pywinauto.application import Application
import random
import logging
import win32com.client
import pythoncom
import os
import configparser
import time
import win32gui
import win32con
import win32gui as w
global logger3
logging.basicConfig(level=logging.INFO, filename="testlog.levellog", filemode="w",
format='%(asctime)s - %(name)s - %(filename)s - %(lineno)d - %(levelname)s - %(message)s')
logger1 = logging.getLogger("start_APP")
logger2 = logging.getLogger("action")
logger3 = logging.getLogger("video_record")
# def get_location(start_tab):
# """
# 获取指定模块window信息
# :param start_tab:
# 指定模块("牛牛圈","资讯","消息"...)
# :return:
# """
# proDir = os.path.split(os.path.realpath(__file__))[0]
# configPath = os.path.join(proDir, "config.cfg")
# path = os.path.abspath(configPath)
# cf = configparser.ConfigParser()
# cf.read(path, encoding="utf-8")
# tab_location = cf.get("TAB", start_tab)
# dic_location = {}
# a1 = tab_location.split(',')
# for i in a1:
# j, k = i.split("=")
# dic_location[j] = k
# return dic_location
def check_minmax(window, start_tab):
"""
判断测试窗口是否最小化了,恢复窗口
:param window: 测试窗口
:param start_tab: 窗口名
:return:
"""
try:
if window.rectangle().left <= -30000 \
and window.rectangle().right <= -30000 \
and window.rectangle().top <= -30000 \
and window.rectangle().bottom <= -30000 and (start_tab == '资讯' or start_tab == '消息'):
hwnd = win32gui.FindWindow("#32770", "富途牛牛")
win32gui.ShowWindow(hwnd, win32con.SW_SHOWMAXIMIZED)
elif window.rectangle().left <= -30000 \
and window.rectangle().right <= -30000 \
and window.rectangle().top <= -30000 \
and window.rectangle().bottom <= -30000 and start_tab == '牛牛圈':
hwnd = win32gui.FindWindow("SOUIHOST", "牛牛圈")
win32gui.ShowWindow(hwnd, win32con.SW_SHOWNORMAL)
except Exception as e:
logger1.warning("判断测试窗口是否最小化失败{}".format(e))
def check_top(start_tab, window):
"""
判断窗口是否是在置顶
:param start_tab:
:return:
"""
try:
if start_tab == '资讯' or start_tab == '消息':
if w.GetWindowText(w.GetForegroundWindow()) != "富途牛牛":
hwnd = win32gui.FindWindow("#32770", "富途牛牛")
win32gui.SetWindowPos(hwnd, win32con.HWND_TOPMOST, 0, 0, 800, 600, win32con.SWP_SHOWWINDOW)
time.sleep(1)
win32gui.SetWindowPos(hwnd, win32con.HWND_NOTOPMOST, 0, 0, 800, 600, win32con.SWP_SHOWWINDOW)
window.maximize()
elif start_tab == '牛牛圈':
if w.GetWindowText(w.GetForegroundWindow()) != "牛牛圈":
hwnd = win32gui.FindWindow("SOUIHOST", "牛牛圈")
win32gui.SetWindowPos(hwnd, win32con.HWND_TOPMOST, 0, 0, 800, 600, win32con.SWP_SHOWWINDOW)
time.sleep(1)
win32gui.SetWindowPos(hwnd, win32con.HWND_NOTOPMOST, 0, 0, 800, 600, win32con.SWP_SHOWWINDOW)
except Exception as e:
logger1.warning("判断窗口是否是在置顶失败{}".format(e))
def get_config(tab_name, key_name):
"""
获取配置文件里对应的value值
:param tab_name: 配置文件[]里的值
:param key_name: 配置项key
:return: 配置项key对应的value
"""
try:
proDir = os.path.split(os.path.realpath(__file__))[0]
configPath = os.path.join(proDir, "config.cfg")
path = os.path.abspath(configPath)
cf = configparser.ConfigParser()
cf.read(path, encoding="utf-8")
value = cf.get(tab_name, key_name)
return value
except Exception as e:
logger1.warning("获取配置文件里对应的value值失败{}".format(e))
def check_client_working():
"""
判断牛牛是否在运行
:return:
"""
try:
pythoncom.CoInitialize()
wmi = win32com.client.GetObject('winmgmts:')
processCodeCov = wmi.ExecQuery('select * from Win32_Process where name=\"%s\"' % ('FTNN.exe'))
if len(processCodeCov) > 0:
return True
else:
return False
except Exception as e:
logger1.warning("判断牛牛进程是否还在运行失败{}".format(e))
global_app = [1]
def start_connect_nnq(start_tab):
"""
启动客户端关联模块
:param start_tab:
指定模块("牛牛圈","资讯","消息"...)
:return:
window窗口信息
"""
global global_app
global flag
nn_path = get_config("path", "nn")
if not check_client_working():
try:
logger1.info("*启动富途牛牛app")
app = Application(backend="uia").start(nn_path)
global_app[0] = app
ft_window = app.window(class_name="#32770", title="富途牛牛")
ft_window.wait('ready', timeout=20, retry_interval=2)
ft_window.maximize()
ft_tab_ctrl = ft_window.children()[ft_window.children_texts().index('FTabCtrl')]
nnq_ctrl = ft_tab_ctrl.children()[ft_tab_ctrl.children_texts().index(start_tab)]
logger1.info("**打开{}tab".format(start_tab))
nnq_ctrl.click_input()
nnq_window = app.window(class_name='SOUIHOST', auto_id='0_0_LAYOUTncircle_main:')
nnq_window.wait('ready', timeout=20, retry_interval=2)
flag = False
except Exception as e:
logger1.warning("***打开{}tab失败,重试关联tab窗口{}".format(start_tab, e))
ft_window = app.window(class_name="#32770", title="富途牛牛")
ft_window.wait('ready', timeout=20, retry_interval=2)
ft_window.maximize()
ft_tab_ctrl = ft_window.children()[ft_window.children_texts().index('FTabCtrl')]
nnq_ctrl = ft_tab_ctrl.children()[ft_tab_ctrl.children_texts().index(start_tab)]
nnq_ctrl.click_input()
nnq_window = app.window(class_name='SOUIHOST', auto_id='0_0_LAYOUT:ncircle_main')
nnq_window.wait('ready', timeout=20, retry_interval=2)
flag = False
else:
ft_window = global_app[0].window(class_name="#32770", title="富途牛牛")
try:
if not ft_window.exists():
logger1.info("****app被关闭,重新启动富途牛牛app")
app = Application(backend="uia").start(nn_path)
global_app[0] = app
ft_window = app.window(class_name="#32770", title="富途牛牛")
ft_window.wait('ready', timeout=20, retry_interval=2)
ft_window.maximize()
ft_tab_ctrl = ft_window.children()[ft_window.children_texts().index('FTabCtrl')]
nnq_ctrl = ft_tab_ctrl.children()[ft_tab_ctrl.children_texts().index(start_tab)]
logger1.info("*****打开{}tab".format(start_tab))
nnq_ctrl.click_input()
nnq_window = app.window(class_name='SOUIHOST', auto_id='0_0_LAYOUT:ncircle_main')
nnq_window.wait('ready', timeout=20, retry_interval=2)
flag = False
else:
logger1.warning("******{}tab窗口已关闭,重新打开并关联".format(start_tab))
ft_window = global_app[0].window(class_name="#32770", title="富途牛牛")
ft_window.wait('ready', timeout=20, retry_interval=2)
ft_window.maximize()
ft_tab_ctrl = ft_window.children()[ft_window.children_texts().index('FTabCtrl')]
nnq_ctrl = ft_tab_ctrl.children()[ft_tab_ctrl.children_texts().index(start_tab)]
nnq_ctrl.click_input()
nnq_window = global_app[0].window(class_name='SOUIHOST', auto_id='0_0_LAYOUT:ncircle_main')
nnq_window.wait('ready', timeout=20, retry_interval=2)
flag = False
except Exception as e:
logger1.warning("*******重新打开并关联失败{}".format(e))
return nnq_window
def start_connect_zx(start_tab):
"""
启动客户端关联模块
:param start_tab:
指定模块("牛牛圈","资讯","消息"...)
:return:
window窗口信息
"""
global global_app
global flag
nn_path = get_config("path", "nn")
if not check_client_working():
try:
logger1.info("*启动富途牛牛app")
app = Application(backend="uia").start(nn_path)
global_app[0] = app
ft_window = app.window(class_name="#32770", title="富途牛牛")
ft_window.wait('ready', timeout=20, retry_interval=2)
ft_window.maximize()
ft_tab_ctrl = ft_window.children()[ft_window.children_texts().index('FTabCtrl')]
zx_ctrl = ft_tab_ctrl.children()[ft_tab_ctrl.children_texts().index(start_tab)]
logger1.info("**打开{}tab".format(start_tab))
zx_ctrl.click_input()
zx_window = ft_window.children()[1]
flag = False
except Exception as e:
logger1.warning("***打开{}tab失败,重试关联tab窗口{}".format(start_tab, e))
ft_window = app.window(class_name="#32770", title="富途牛牛")
ft_window.wait('ready', timeout=20, retry_interval=2)
ft_window.maximize()
ft_tab_ctrl = ft_window.children()[ft_window.children_texts().index('FTabCtrl')]
zx_ctrl = ft_tab_ctrl.children()[ft_tab_ctrl.children_texts().index(start_tab)]
zx_ctrl.click_input()
zx_window = ft_window.children()[1]
flag = False
else:
ft_window = global_app[0].window(class_name="#32770", title="富途牛牛")
try:
if not ft_window.exists():
logger1.info("****app被关闭,重新启动富途牛牛app")
app = Application(backend="uia").start(nn_path)
global_app[0] = app
ft_window = app.window(class_name="#32770", title="富途牛牛")
ft_window.wait('ready', timeout=20, retry_interval=2)
ft_window.maximize()
ft_tab_ctrl = ft_window.children()[ft_window.children_texts().index('FTabCtrl')]
zx_ctrl = ft_tab_ctrl.children()[ft_tab_ctrl.children_texts().index(start_tab)]
logger1.info("*****打开{}tab".format(start_tab))
zx_ctrl.click_input()
zx_window = ft_window.children()[1]
flag = False
else:
logger1.warning("******{}tab窗口已关闭,重新打开并关联".format(start_tab))
ft_window = global_app[0].window(class_name="#32770", title="富途牛牛")
ft_window.wait('ready', timeout=20, retry_interval=2)
ft_window.maximize()
ft_tab_ctrl = ft_window.children()[ft_window.children_texts().index('FTabCtrl')]
zx_ctrl = ft_tab_ctrl.children()[ft_tab_ctrl.children_texts().index(start_tab)]
zx_ctrl.click_input()
zx_window = ft_window.children()[1]
flag = False
except Exception as e:
logger1.warning("*******重新打开并关联失败{}".format(e))
return zx_window
def choice_ctrl(window):
"""
获取window下所有控件并选取任意一个
:param window:
window窗口信息(start_connect_app()返回的数据)
:return:
此窗口下面所有控件的一个列表
"""
logger2.info("获取此窗口下面所有控件信息:[{}] ".format(window.get_properties()))
ctrl_list = []
def recursion(window):
if len(window.children()) > 0:
for i in window.children():
if i.rectangle().left + 1 >= window.rectangle().left \
and i.rectangle().right <= window.rectangle().right \
and i.rectangle().top + 1 >= window.rectangle().top \
and i.rectangle().bottom <= window.rectangle().bottom:
ctrl_list.append(i)
recursion(i)
recursion(window)
return ctrl_list
def choice_order_execute(control_list):
"""
:param control_list: 控件列表
:return:
"""
logger2.info("选取此窗口下面任意一个控件")
ctrl = random.choice(control_list)
proDir = os.path.split(os.path.realpath(__file__))[0]
configPath = os.path.join(proDir, "config.cfg")
path = os.path.abspath(configPath)
cf = configparser.ConfigParser()
cf.read(path, encoding="utf-8")
orders = []
for action in cf.items('order'):
orders.append(list(action)[1])
k = random.randint(1, len(orders))
logger2.info("随机选取[{}]种动作对控件进行操作".format(k))
for avtion in random.choices(orders, k=k):
eval(avtion)(ctrl)
time.sleep(0.5)
Action_command
# encoding=utf8
import random
from pywinauto import mouse
import logging
import time
logging.basicConfig(level=logging.INFO, filename="testlog.levellog", filemode="w",
format='%(asctime)s - %(name)s - %(filename)s - %(lineno)d - %(levelname)s - %(message)s')
logger1 = logging.getLogger("start_APP")
logger2 = logging.getLogger("action")
def click_ctrl(control):
"""
左键点击
:param control: 控件
:return:
"""
try:
logger2.info("对此控件进行左键单击:[{}]".format(control.get_properties()))
rect = control.rectangle()
cx = int((rect.left + rect.right) / 2)
cy = int((rect.top + rect.bottom) / 2)
mouse.click(button='left', coords=(cx, cy))
except Exception as e:
logger1.warning("左键点击失败{}".format(e))
def double_click_ctrl(control):
"""
左键双击命令
:param control:控件
:return:
"""
try:
logger2.info("对此控件进行左键双击:[{}]".format(control.get_properties()))
rect = control.rectangle()
cx = int((rect.left + rect.right) / 2)
cy = int((rect.top + rect.bottom) / 2)
mouse.double_click(button='left', coords=(cx, cy))
except Exception as e:
logger1.warning("左键双击失败{}".format(e))
def mouse_scroll(control):
"""
鼠标任意滚动滚轮
:param control:控件
:return:
"""
try:
distance = random.choice(range(-10, 10))
rect = control.rectangle()
cx = int((rect.left + rect.right) / 2)
cy = int((rect.top + rect.bottom) / 2)
logger2.info("对此控件鼠标滚轮({}):[{}]".format(control.get_properties(), distance))
mouse.scroll(coords=(cx, cy), wheel_dist=distance)
except Exception as e:
logger1.warning("鼠标滚动失败{}".format(e))
def mouse_press_release(control):
"""
鼠标拖拽
:param control:
:return:
"""
try:
ccx = random.choice(range(0, 1910))
ccy = random.choice(range(0, 1020))
rect = control.rectangle()
cx = int((rect.left + rect.right) / 2)
cy = int((rect.top + rect.bottom) / 2)
logger2.info("对此控件拖拽({},{}):[{}]".format(ccx, ccy, control.get_properties()))
mouse.press(button='left', coords=(cx, cy))
time.sleep(1)
mouse.release(button='left', coords=(ccx, ccy))
except Exception as e:
logger1.warning("鼠标拖拽失败{}".format(e))
def right_click_ctrl(control):
"""
右键点击
:param control: 控件
:return:
"""
try:
logger2.info("对此控件进行右键单击:[{}]".format(control.get_properties()))
rect = control.rectangle()
cx = int((rect.left + rect.right) / 2)
cy = int((rect.top + rect.bottom) / 2)
mouse.right_click(coords=(cx, cy))
except Exception as e:
logger1.warning("右键点击失败{}".format(e))
def keyboard_send_keys(control):
"""
键盘输入
:param control: 控件
:return:
"""
# if 'edit' in control.class_name().lower():
try:
control.type_keys('稳定性测试键盘输入')
logger2.info("对此控件进行键盘输入:[{}]".format(control.get_properties()))
except Exception as e:
logger1.warning("键盘输入失败,不可输入控件{}".format(e))
# else:
# logger2.info("此控件非edit,不进行键盘输入:[{}]".format(control.get_properties()))
# if __name__ == '__main__':
# dfdf_main()
config.cfg
[order]
order1 : click_ctrl
order2 : double_click_ctrl
order3 : mouse_scroll
order4 : mouse_press_release
order5 : right_click_ctrl
order6 : keyboard_send_keys
[test_time]
#执行总时长
time : 24h
#单个视频录制时间
video_time : 300s
#遇到客户端退出时再多录制时间
more_time : 30s
[path]
#被测客户端安装位置
nn : C:\Program Files (x86)\FTNN\FTNN.exe
#录屏保存地址(安装的稳定性工具里的video文件地址)
video_address : D:\Usopp\python\StabilityTest\stability_test\video
[module]
#需执行的模块 牛牛圈、消息
module : 消息