python3+tkinter实践历程(一)——基于requests与tkinter的API工具

python3+tkinter实践历程(一)——基于requests与tkinter的API工具


系列文章目录

python3+tkinter实践历程(一)——基于requests与tkinter的API工具
python3+tkinter实践历程(二)——基于tkinter的日志检索工具
python3+tkinter实践历程(三)——基于requests与tkinter的模拟web登录工具
python3+tkinter实践历程(四)——模仿CRT完成基于socket通信与tkinter的TCP串口客户端

分享背景

①分享意图在于帮助新入门的朋友,提供思路,里面详细的注释多多少少能解决一些问题。欢迎大佬指点跟交流。
②2021年8月,开始陆续有需求制作一些工具,因为python语言跟tkinter工具相对简单,所以就基于这些做了好几个不同用处的工具。
③分享从完成的第一个工具开始分享,分享到最新完成的工具,对于tkinter的理解也从一开始的摸索入门,到后来逐渐熟练,完成速度也越来越快,所用到的tk的功能点也越来越多。

制作背景

实现让不会代码的人也能使用该图形化工具对API进行测试,或者调用API

最终功能

① 将加密过程、登录流程、各个API的body、headers、url全部封装好
② 使用者根据自己的需求修改工具给出的body即可

代码详解

代码中关于产品登录流程及加密方式不展示

# -*- coding=utf-8 -*-
"""API工具"""

import tkinter as tk
from tkinter import *
import json
import requests
import base64
import datetime
import hashlib
import tkinter.font as tf

# HTTP类,关于HTTP的操作均在此类中封装实现
class Http(object):
    def __init__(self, url='', username='', password='', api_name=''):
        """初始化类时传入url、账号、密码、API名称"""
        # 以下将一系列参数变成整个类中都可调用的属性
        self.check_url = url				# ip:端口
        self.check_username = username		# 账号	
        self.check_pwd = password		# 密码
        self.check_api_name = api_name		# API名称

        self.url = 'http://' + url + api_name		# 整个url:http://xx.xx.xx.xx:80/login
        self.encrypted_value = xx	# 此为某加密的结果
		
        # body字典  
        self.xx_body = """
    <pageIndex>1</pageIndex>     
    <pageSize>20</pageSize>		
    <condition>
        <logType type="list">
            <itemType type="logType"/>
            <item>				
                <![CDATA[LOG_ALL]]>
            </item>
        </logType>  
        <startTime>			
            <![CDATA[%s]]>
        </startTime>
        <endTime>			
            <![CDATA[%s]]>
        </endTime>
        <langId>			
            <![CDATA[0x0804]]>
        </langId>
    </condition>
"""

    def send_request(self, re_type='post', body='', authorization=''):
		# 发送接口请求的函数
        if not self.check_url or not self.check_username or not self.check_pwd or not self.check_api_name or not body or not authorization:
        # 检查所需数据是否已传入,未传入则返回
            print('请填写相关信息')
            return

        # basic 的鉴权值是“账号:密码” 的Base64加密值
        if authorization == 'basic':
            self.headers = {xx }
        elif authorization == 'digest':
            self.headers = {xx}
        else:
            return
        if re_type == 'get':
            return
        if re_type == 'post':
            print('给%s发送%s请求,body:%s' % (self.url, re_type, body))
            try:
            	# 进行请求
                respone = requests.post(self.url, headers=self.headers, data=body)
                print(respone)
                if not respone.content:
                    print('无data信息')
                    # 没有返回信息就返回状态码
                    return respone.status_code
                else:
                	# 返回以utf-8解码的报文信息
                    print(respone.content.decode('utf-8'))
                    return respone.content.decode('utf-8')
            except Exception as e:
                print('请求异常:%s' % e)
                return False

    def get_api_info(self, api_name):
        """获取API的默认参数、及默认的具体body"""
		# 有一些API的url会有后缀参数,比如http://xx.xx.xx.xx:80/login/1、http://xx.xx.xx.xx:80/login/up、http://xx.xx.xx.xx:80/login/xx
		# 有些API不需要body,有些需要body的需要用户自己去填写所需内容,所以body和url都需要展示在界面上供使用者更改
		# 这里是对某个API的body的预处理,自动生成两个常用时间然后放进body中,再输出给使用者
        xx_start_time = str(datetime.date.today())
        xx_end_time = xx_start_time + ' 23:59:59'
        xx_start_time = xx_start_time + ' 00:00:00'
		# url的对应字典,每个API的对应的具体url内容从这里取
        url_param = {
            "xx": "/xx",
        }
		# body的对应字典,每个API的对应的具体body内容从这里取
        body = {
            "xx": self.xx_bdoy% (xx_start_time, xx_end_time),
        }
		# 如果url字典和body字典未加入该API,则返回
        if api_name not in url_param or api_name not in body:
            print('api:%s,无url_parm或者body,请添加' % api_name)
            return {}
        api_info = {'url_param': url_param[api_name], 'body': body[api_name]}
        print('api:%s的信息为:' % api_name + json.dumps(api_info, ensure_ascii=False))
        # 取得了该API的url和body后,制作成api_info字典,返回给上层
        return api_info

    def get_digest_respone(self):
        """用于计算加密后的respone的值"""
        # 加密算法省略,加密结果返回上层
        return all_s

# 核心类,整个工具的核心
class Application(tk.Frame):
    def __init__(self, master=None):
    # 继承于tkinter的Frame类,可使用Frame的布局方式
        tk.Frame.__init__(self, master)
        self.grid()
        # API列表(一个元组),用于显示在界面。与下面的self.api_name_lb这个tk的Listbox列表框配合使用
        self.api_list = ('GetxxxxInfo',)
		# 初始化时执行下面create_widgets函数构造出GUI界面
        self.create_widgets()

    def create_widgets(self):
        """创建界面"""
        # 设备IP端口
        # 创建一个Label控件,指定text
        dev_url = tk.Label(self, text="设备IP及端口(xx:xx)")
        # 左右间距为1,row/column 默认初始值是0,即第1行第1列
        dev_url.grid(padx=1, row=0, column=0)
        # 创建一个tk下的字符串类型的容器,容器用set方法赋值,用get方法获取值
        dev_url = tk.StringVar()
        dev_url.set("10.100.10.108:80")         # set的作用的预置的文字(在标签内可变的文本)
        # 创建一个Entry框,即小型输入框,用textvariable绑定dev_url容器,容器类的值即Entry的默认显示值,在界面上可以随意更改
        self.dev_url = tk.Entry(self, textvariable=dev_url, width=20)
        self.dev_url.grid(padx=1, row=0, column=1)
		# 账号密码的创建方法与设备IP端口同理
        # 账号
        user_name = tk.Label(self, text="账号")
        user_name.grid(padx=1, row=0, column=2)
        user_name = tk.StringVar()
        user_name.set("")
        self.user_name = tk.Entry(self, textvariable=user_name, width=20)
        self.user_name.grid(padx=1, row=0, column=3)

        # 密码
        password = tk.Label(self, text="密码")
        password.grid(padx=1, row=0, column=4)
        password = tk.StringVar()
        password.set("")
        self.password = tk.Entry(self, textvariable=password, width=20)
        self.password.grid(padx=1, row=0, column=5)
	
        # 两种认证方式的单选框
        # 创建一个tk下的int类型的容器,容器用set方法赋值,用get方法获取值
        self.authentication = tk.IntVar()
        self.authentication.set(1)
        # 创建两个Radiobutton控件,variable与self.authentication容器绑定,默认是为1,两个按钮互相冲突,必有一个被选择。选择按钮2时,self.authentication的值就会改变成按钮2的value,即2
        self.select_basic = Radiobutton(self, variable=self.authentication, text='basic', value=1)
        self.select_basic.grid(padx=1, row=0, column=6)
        self.select_digest = Radiobutton(self, variable=self.authentication, text='digest', value=2)
        self.select_digest.grid(padx=1, row=0, column=7)

        # 接口列表
        self.log_label = tk.Label(self, text="接口列表")
        self.log_label.grid(padx=1, row=1, column=0)
        # 创建字符串类型的容器
        self.api_name = tk.StringVar()
        self.api_name.set(self.api_list)
        # 创建Listbox控件,用于显示一组文本选项,与self.api_name容器绑定,后续调用curselection()就知道容器元组中哪个下标的文本
        self.api_name_lb = tk.Listbox(self, listvariable=self.api_name, height=32)
        self.api_name_lb.grid(padx=1, row=2, column=0)

        # 获取接口请求体按钮
        # 创建Button控件,绑定show_api_body函数,点击按钮则执行此函数
        self.get_body_btn = tk.Button(self, text='获取接口请求体', command=self.show_api_body)
        self.get_body_btn.grid(padx=1, row=1, column=1)

        # 发送接口按钮
        # button控件,同理
        self.get_body_btn = tk.Button(self, text='发送接口', command=self.send_api_request)
        self.get_body_btn.grid(padx=1, row=1, column=5)

        # 接口url具体参数输入框
        api_param = tk.Label(self, text="接口及参数输入")
        api_param.grid(padx=1, row=1, column=2)
        api_param = tk.StringVar()
        api_param.set("")
        # Entry控件,预置为空,选择接口后会把url输出到界面
        self.api_param = tk.Entry(self, textvariable=api_param, width=20)
        self.api_param.grid(padx=1, row=1, column=3)

        # body框 + 滚动条
        # Font 调整Text框内的字体大小
        ft = tf.Font(size=9)
        # Text框-用于打印API的body
        self.api_body = tk.Text(self, width=57, height=47, font=ft)
        # Scrollbar控件,滚动条
        body_slide_bar = tk.Scrollbar(self, command=self.api_body.yview, orient="vertical")
        body_slide_bar.grid(padx=1, row=2, column=4,  sticky='ns')
        self.api_body.grid(padx=1, row=2, column=1, columnspan=4)
        # 将body框与滚动条绑定
        self.api_body.config(font=ft, yscrollcommand=body_slide_bar.set)
		

        # respone框 + 滚动条
        self.api_respone = tk.Text(self, width=80, height=45)
        respone_slide_bar = tk.Scrollbar(self, command=self.api_respone.yview, orient="vertical")
        respone_slide_bar.grid(padx=1, row=2, column=11, sticky='ns')
        self.api_respone.grid(padx=1, row=2, column=5, columnspan=6)
        self.api_respone.config(yscrollcommand=respone_slide_bar.set)

        # 使用说明
        row = Frame(self)
        row.grid(padx=1, row=3, column=0, columnspan=100, sticky='w')
        instructions = """
        使用说明:输入设备IP及端口,用IP:端口的形式,输入正确的账号密码,选择测试的认证方式basic/digest,
                  然后于接口列表选择要测试的接口,选择后点击"获取接口请求体"按钮,"接口及参数输入"框中会出现接口的url,
                  正中间的框会出现接口的请求体(body),两个框的内容均修改为所需内容后,点击"发送接口"按钮,右方框会出现接口返回的内容。
        """
        text_param = tk.Label(row, text=instructions, font=('宋体', 12), justify='left')
        text_param.pack(side='left')

    def show_api_body(self):
        """点击这个按钮必须检查url、账号、密码是否符合要求,获取接口请求体"""
        index = self.api_name_lb.curselection()		# 获取该列表选择框的选择项
        if not index:	
            print('未选择')
            return
        else:
            print('序号是:%s' % index[0])			# index[0]为self.api_list这个元组的下标
            api_name = self.api_list[int(index[0])]		# 通过下标确定选择了哪个API
            print('选择的接口是:%s' % api_name)
            # 一系列必填项判断操作
            if not self.dev_url.get():
                print('设备IP端口不能为空')
                return
            else:
                url = self.dev_url.get()
            if not self.user_name.get():
                print('账号不能为空')
                return
            else:
                username = self.user_name.get()
            if not self.password.get():
                print('密码不能为空')
                return
            else:
                password = self.password.get()
            # 创建HTTP类对象
            dev_http = Http(url, username, password, api_name)
            # 调用HTTP类中的get_api_info函数,传入API名称,返回API预置的url与body
            api_info = dev_http.get_api_info(api_name)
            if not api_info:
                print('无api的信息,请检查')
                return
            # 传输url到接口参数框
            self.api_param.delete(0, 'end')         # Entry框的清除方式
            self.api_param.insert(0, api_info['url_param'])         # Entry框的添加方式
            # 传输body到body框
            if self.api_body.get(0.0):				# 如果Text框已有值,则先清除
                self.api_body.delete(0.0, 'end')        # Text框的清除方式
            self.api_body.insert('end', api_info['body'])       # Text框的添加方式

    def send_api_request(self):
        """发送接口请求"""
        # 从界面上获取一系列的数据
        api_param = self.api_param.get()
        api_body = self.api_body.get(0.0, 'end')
        url = self.dev_url.get()
        username = self.user_name.get()
        password = self.password.get()
        authentication = {'1': 'basic', '2': 'digest'}.get(str(self.authentication.get()))
        print('发送接口请求,传入数据:url:%s,账号:%s,密码:%s,认证方式:%s' % (url, username, password, authentication))
        # 创建HTTP类对象,调用send_request函数发送请求
        dev_http = Http(url, username, password, api_param)
        respone = dev_http.send_request(body=api_body, authorization=authentication)
        if respone:
            if self.api_respone.get(0.0):
                self.api_respone.delete(0.0, 'end')
            self.api_respone.insert('end', respone)
        else:
            print('接口返回数据为空')
            if self.api_respone.get(0.0):
                self.api_respone.delete(0.0, 'end')
            self.api_respone.insert('end', '接口发送异常')

root = tk.Tk()			# 创建Tk对象
app = Application(master=root)		# 创建Application类的对象app,主界面为root
root.title("第三方API测试工具")		# 工具标题
root.resizable(False, False)			# 禁止调整窗口大小

sw = root.winfo_screenwidth()   # 得到屏幕宽度
sh = root.winfo_screenheight()  # 得到屏幕高度
# 窗口宽高为1200/800
ww = 1200
wh = 800
x = (sw-ww) / 2
y = (sh-wh) / 2
root.geometry("%dx%d+%d+%d" % (ww, wh, x, y))       # 设置窗口宽高为1200x800,且自适应居中

root.deiconify()        # 显示窗口
app.mainloop()          # 进入消息循环



工具截图展示

在这里插入图片描述
在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值