1、计划
压测完成后需要编写性能测试报告,报告中所需数据截图较多,使用自动化操作方便快捷,就编写一个界面工具以便后续复用。之前编写过loadrunner报告的自动化截图脚本,现在用jmeter也比较多,就编写jmeter部分,然后两个整合起来。
PyAutoGUI实现对LoadRunner报告自动化截图
2、功能分析
- 需求:点击需要截图的监听器按钮,指定区域截图保存,对每个jmeter报告循环。
- 工具流程
3、主体界面设计
还是使用Qt Designer编辑pyqt的基础界面,使用TabWidget来切换页面,现在先设计jmeter部分。
- 左边的监听器与报告展示,使用QTreeWidget展示
- 修改样式,稍微好看点。
/* 设置表格水平表头(最上面一行) */
QHeaderView::section:horizontal {
background-color: rgb(255, 245, 233); /* 色背景 */
color: black; /* 文本颜色为黑色 */
border: none; /* 隐藏边框 */
font: 11pt "微软雅黑";
}
QTreeView {
/*border:none;*/
border: 1px solid lightgray;
outline:0px;
background: #FFFFFF;
show-decoration-selected: 1;
}
QTreeView::item {
height: 30px;
border: none;
color: black;
background: #FFFFFF;
}
QTreeView::item:hover {
background: rgb(255, 210, 183);
}
QTreeView::item:selected{
background-color: rgb(255, 170, 127);
color: #f9ffff;
}
- 右边增加个QGridLayout布局管理器,方便整合按钮输入框等。实现选择报告文件夹、下拉选择报告格式,如:csv、jtl,等功能。
- 下面放个TextBrowser,显示日志。
# 按钮样式
QPushButton {
background-color: #57bd6a;
color: #f9ffff;
font-size: 20px;
font-weight: bold;
border-radius: 5px;
}
QPushButton:pressed {
background-color: #4eaa5f;
}
- 主体界面完成,包含两个选择区域,右边一个按钮区域,下方日志显示区域。
4、扫描文件功能实现
- 报告文件夹
增加【选择路径】按钮信号槽,链接select_folder()。点击按钮打开Windows资源管理器窗口,选择文件夹,路径回显到QLineEdit。
def select_folder(self):
"""报告文件夹"""
directory = QFileDialog.getExistingDirectory(self, "选择文件夹")
self.daoruwenjian.setText(directory)
self.show_folder() # 扫描文件夹
下面的保存文件夹同理,不同的是增加了默认打开地址【下载文件夹】:
def report_folder(self):
"""选择保存路径,默认下载文件夹"""
downloads_folder = QStandardPaths.writableLocation(QStandardPaths.StandardLocation.DownloadLocation)
directory = QFileDialog.getExistingDirectory(self, "选择文件夹", downloads_folder)
self.report.setText(directory)
- 扫描文件夹内报告:
将扫描到的报告名称与地址放到字典self.file_names,结果显示到日志框里面。
扫描到了格式文件,就调用select_bg(),显示到页面上。
def show_folder(self):
"""扫描文件夹内报告"""
directory = self.daoruwenjian.text() # 获取路径
self.file_names = {} # 全部报告,名称:地址
self.daochulog.clear() # 清空说明
for root, dirs, files in os.walk(directory, topdown=True): # 遍历文件夹及其子文件夹中的.格式文件
for file in files:
if file.endswith(f'.{self.geshi.currentText()}'): # 条件筛选
file_name = os.path.basename(root) + '-' + file # 获取文件名(含扩展名)
self.file_names[file_name] = os.path.join(root, file)
count = len(self.file_names) # 统计报告数量
if count == 0:
self.daochulog.append(f'扫描完成----------没有发现.{self.geshi.currentText()}格式的文件')
self.mkliebiao_1.clear()
else:
self.daochulog.append(f'扫描完成----------发现{count}个{self.geshi.currentText()}文件')
self.select_bg()
self.checkBox_bg.setChecked(False) # 取消全选
def select_bg(self):
"""显示报告列表"""
self.mkliebiao_1.clear() # 清除其所有item
for value in self.file_names.keys():
parent_item = QTreeWidgetItem(self.mkliebiao_1) # 创建父级项目
parent_item.setText(0, value)
实现情况:
5、监听器设置页面
- 监听器信息页面设计
页面填写监听器的名称、截图范围。
然后对指定范围的按钮,截取识别图像。
【3.动态高度识别】勾选了,才显示其设置项目。
相应按钮增加信号槽。 - 保存
保存按钮只保存监听器名称与截图范围,数据量不大就存放到json文件中。
JM_JSON = 'Identify/jm.json' # jm配置文件路径
def save_jm(self):
"""监听器,截图范围保存"""
if len(self.biaoti.text()) == 0:
self.ts.xinxi("请填写名称与截图范围")
return
data = {'lisener': (self.spinBox_1.value(),
self.spinBox_2.value(),
self.spinBox_3.value(),
self.spinBox_4.value())}
try:
if not os.path.exists(JM_JSON):
# 文件不存在,直接写入新数据
with open(JM_JSON, 'w', encoding='utf-8') as f:
json.dump(data, f)
else:
# 文件存在,读取并合并数据
with open(JM_JSON, 'r', encoding='utf-8') as f:
existing_data = json.load(f)
if self.biaoti.text() not in existing_data:
existing_data[self.biaoti.text()] = {}
existing_data[self.biaoti.text()].update(data)
# 写回更新后的数据
with open(JM_JSON, 'w', encoding='utf-8') as f:
json.dump(existing_data, f)
self.ts.xinxi(f"保存成功")
self.name = self.biaoti.text()
self.edit() # 反显信息
self.show()
except Exception as e:
self.ts.xinxi(f"保存出错:{e}")
- 信息显示
这又臭又长
def edit(self):
"""反显编辑页面信息"""
self.biaoti.setText(self.name)
self.biaoti.setReadOnly(True) # name 不可编辑
self.biaoti.setStyleSheet("""
QLineEdit[readOnly="true"] {
color: gray; /* 文本颜色设为灰色 */
background-color: #f0f0f0; /* 背景颜色设为浅灰色 */
border: 1px solid gray; 边框颜色设为灰色 */
}""")
with open(JM_JSON, 'r', encoding='utf-8') as f:
data = json.load(f)
value = data.get(self.name, {}) # 获取name的数据
lisener = value.get('lisener', (0, 0, 0, 0))
tu1 = value.get('tu1', 0) # 识别图
tu2 = value.get('tu2', 0)
if tu1 != 0 and os.path.exists(tu1): # tu1
t1 = 1
self.label_19.setText("已截图")
self.label_5.setStyleSheet(f'''
background-color: rgb(97, 174, 255);
image:url(./{tu1});''') # 设置图,相对路径
self.label_5.show()
else:
t1 = 0
if 'tu1' in value: # 如果没图片,则删除图片记录
del value['tu1']
with open(JM_JSON, 'w', encoding='utf-8') as f1:
json.dump(data, f1)
if tu2 != 0 and os.path.exists(tu2): # tu2
t2 = 1
self.label_23.setText("已截图")
self.label_7.setStyleSheet(f'''
background-color: rgb(97, 174, 255);
image:url(./{tu2});''') # 设置图,相对路径
self.label_7.show()
else:
t2 = 0
if 'tu2' in value: # 如果没图片,则删除图片记录
del value['tu2']
with open(JM_JSON, 'w', encoding='utf-8') as f1:
json.dump(data, f1)
self.tu.setCurrentIndex(t1 and t2) # 设置页面下拉项
tu3 = value.get('tu3', 0) # 动态高度图 tu3
if tu3 != 0 and os.path.exists(tu3):
self.checkBox.setChecked(True)
self.label_6.setText("")
self.label_6.setStyleSheet(f'''
background-color: rgb(97, 174, 255);
image:url(./{tu3});''') # 设置图,相对路径
self.label_6.show()
else:
if 'tu3' in value: # 无图删除记录,按钮不显示
del value['tu3']
with open(JM_JSON, 'w', encoding='utf-8') as f2:
json.dump(data, f2)
self.checkBox.setChecked(False)
self.label_40.setVisible(False)
self.label_6.setVisible(False)
self.widget_4.setVisible(False)
self.tu3.setVisible(False)
for i, value in enumerate(lisener, start=1):
spin_box = getattr(self, f"spinBox_{i}") # 坐标赋值
spin_box.setValue(int(value))
- 实现情况