基于PyAutoGUI图片定位的自动化截图工具--jmeter部分

1、计划

  压测完成后需要编写性能测试报告,报告中所需数据截图较多,使用自动化操作方便快捷,就编写一个界面工具以便后续复用。之前编写过loadrunner报告的自动化截图脚本,现在用jmeter也比较多,就编写jmeter部分,然后两个整合起来。
PyAutoGUI实现对LoadRunner报告自动化截图

2、功能分析

  • 需求:点击需要截图的监听器按钮,指定区域截图保存,对每个jmeter报告循环。
    在这里插入图片描述
  • 工具流程

在这里插入图片描述

3、主体界面设计

  还是使用Qt Designer编辑pyqt的基础界面,使用TabWidget来切换页面,现在先设计jmeter部分。

  1. 左边的监听器与报告展示,使用QTreeWidget展示
    在这里插入图片描述
  2. 修改样式,稍微好看点。
/* 设置表格水平表头(最上面一行) */
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;
}

在这里插入图片描述

  1. 右边增加个QGridLayout布局管理器,方便整合按钮输入框等。实现选择报告文件夹、下拉选择报告格式,如:csv、jtl,等功能。
  2. 下面放个TextBrowser,显示日志。
    在这里插入图片描述
# 按钮样式
QPushButton {background-color: #57bd6a;color: #f9ffff;font-size: 20px;font-weight: bold;border-radius: 5px;}QPushButton:pressed {background-color: #4eaa5f;}
  1. 主体界面完成,包含两个选择区域,右边一个按钮区域,下方日志显示区域。
    在这里插入图片描述

4、扫描文件功能实现

  1. 报告文件夹
    增加【选择路径】按钮信号槽,链接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)
  1. 扫描文件夹内报告:

  将扫描到的报告名称与地址放到字典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()                             # 清除其所有itemfor value in self.file_names.keys():parent_item = QTreeWidgetItem(self.mkliebiao_1)  # 创建父级项目parent_item.setText(0, value)

实现情况:
在这里插入图片描述

5、监听器设置页面

  1. 监听器信息页面设计
    页面填写监听器的名称、截图范围。
    然后对指定范围的按钮,截取识别图像。
    【3.动态高度识别】勾选了,才显示其设置项目。
    相应按钮增加信号槽。在这里插入图片描述
  2. 保存
    保存按钮只保存监听器名称与截图范围,数据量不大就存放到json文件中。
JM_JSON = 'Identify/jm.json'  # jm配置文件路径def save_jm(self):"""监听器,截图范围保存"""if len(self.biaoti.text()) == 0:self.ts.xinxi("请填写名称与截图范围")returndata = {'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}")
  1. 信息显示
    这又臭又长
    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):            # tu1t1 = 1self.label_19.setText("已截图")self.label_5.setStyleSheet(f'''background-color: rgb(97, 174, 255);image:url(./{tu1});''')      # 设置图,相对路径self.label_5.show()else:t1 = 0if '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):            # tu2t2 = 1self.label_23.setText("已截图")self.label_7.setStyleSheet(f'''background-color: rgb(97, 174, 255);image:url(./{tu2});''')      # 设置图,相对路径self.label_7.show()else:t2 = 0if '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)                   # 动态高度图 tu3if 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))
  1. 实现情况

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/610942.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

UNet网络学习记录

如下图所示,整个网络结构包括两部分,编码结构和解码结构,编码结构是对特征进行提取,解码结构是对特征进行还原;如下右图所示,这个步骤包括数据集的加载,网络的搭建,训练网络&#xf…

计算机网络常见面试总结

文章目录 1. 计算机网络基础1.1 网络分层模型1. OSI 七层模型是什么?每一层的作用是什么?2.TCP/IP 四层模型是什么?每一层的作用是什么?3. 为什么网络要分层? 1.2 常见网络协议1. 应用层有哪些常见的协议?2…

力扣HOT100 - 160. 相交链表

解题思路: /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) {* val x;* next null;* }* }*/ public class Solution {public ListNode getIntersectionNode(ListNode headA, ListNode headB) {if…

无人新零售引领的创新浪潮

无人新零售引领的创新浪潮 在数字化时代加速演进的背景下,无人新零售作为商业领域的一股新兴力量,正以其独特的高效性和便捷性重塑着传统的购物模式,开辟了一条充满创新潜力的发展道路。 依托人脸识别、物联网等尖端技术,无人新…

题目 2349: 信息学奥赛一本通T1437-扩散【二分答案】

信息学奥赛一本通T1437-扩散 - C语言网 (dotcpp.com) #include <iostream> #include <algorithm> using namespace std; #define int long long const int N2e2; int n; struct node{int x,y; }a[N]; int fa[N]; int dist[N][N];//记录两坐标间的曼哈顿距离 int fi…

玩机进阶教程------手机定制机 定制系统 解除系统安装软件限制的一些步骤解析

定制机 在于各工作室与商家合作定制rom中有一些定制机。限制用户私自安装第三方软件。或者限制解锁 。无法如正常机登陆账号等等。定制机一般用于固定行业或者一些部门。专机专用。例如很多巴枪扫描机型等等。或者一些小牌机型。对于没有官方包的机型首先要导出各个分区来制作…

代码随想录算法训练营Day52|LC300 最长递增子序列LC 674 最长连续递增序列LC 718 最长重复子数组

一句话总结&#xff1a;动规做多了就豁然开朗了。 原题链接&#xff1a;300 最长递增子序列 按照动规五部曲&#xff1a; 首先确定dp数组及下标的含义&#xff1a;dp[i]表示以nums[i]结尾的最长子序列的长度&#xff1b;确定状态转移方程&#xff1a;如果nums[i] > nums[j…

风储微网虚拟惯性控制系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 风储微网虚拟惯性控制系统simulink建模与仿真。风储微网虚拟惯性控制系统是一种模仿传统同步发电机惯性特性的控制策略&#xff0c;它通过集成风力发电系统、储能系统和其他分…

Java工具类:批量发送邮件(带附件)

​ 不好用请移至评论区揍我 原创代码&#xff0c;请勿转载&#xff0c;谢谢&#xff01; 一、介绍 用于给用户发送特定的邮件内容&#xff0c;支持附件、批量发送邮箱账号必须要开启 SMTP 服务&#xff08;具体见下文教程&#xff09;本文邮箱设置示例以”网易邮箱“为例&…

微服务学习(黑马)

学习黑马的微服务课程的笔记 导学 微服务架构 认识微服务 SpringCloud spring.io/projects/spring-cloud/ 服务拆分和远程调用 根据订单id查询订单功能 存在的问题 硬编码 eureka注册中心 搭建eureka 服务注册 在order-service中完成服务拉取 Ribbon负载均衡 Nacos注册中心…

ELK-Kibana 部署

目录 一、在 node1 节点上操作 1.1.安装 Kibana 1.2.设置 Kibana 的主配置文件 1.3.启动 Kibana 服务 1.4.验证 Kibana 1.5.将 Apache 服务器的日志&#xff08;访问的、错误的&#xff09;添加到 ES 并通过 Kibana 显示 1.6. 浏览器访问 二、部署FilebeatELK&…

使用 Axios 处理 AxiosError 的三种常见方法

在使用 Axios 时处理 AxiosError 有几种常见的方法: 使用 try-catch 语句捕获异常: try {const response await axios.get(/api/data);// 处理响应数据 } catch (error) {if (error.response) {// 请求成功但状态码不在 2xx 范围console.log(error.response.data);console.l…