1. 前言
很早之前,我实现过一个【Python 实战】---- 接口自动化:60行代码,如何通过Python requests实现图片上传,这个的确简化了前端开发对图片操作的时间,但是随着项目开发的越多,遇到一个新的问题,就是图片需要上传到每个项目的自己服务器中,而且每个项目的参数也不一样,有的需要加密,有的不需要,这就会出现我们需要根据不同的项目,对代码进行上传的微调,然后再打包成工具使用,最后就会发现有很多工具,完全达不到一个程序员的优雅,因此就想到使用 GUI 配置管理,将所有的工具进行配置管理。
2. 简单的 UI 界面
2.1 上传界面
2.2 配置管理界面
2.3 编辑和新增配置管理界面
3. 实现的基础结构
- UploadConfig 上传配置文件的操作;
- FileUploader 上传文件的操作方法;
- UploadGUI 上传界面的 UI 布局;
- ConfigManagerWindow 管理界面的 UI 布局;
- ConfigEditWindow 编辑和新增的 UI 布局。
4. 对配置文件的增删改查操作类的实现
- init:构造函数,用于初始化 ConfigManager 实例。它会设置配置文件路径,初始化配置字典,并调用 load_configs() 方法加载配置。
- load_configs:从配置文件加载所有配置。如果配置文件不存在或为空,将创建一个包含默认配置的文件。该方法还会处理加载配置时可能出现的异常。
- save_configs:将所有配置保存到文件中。该方法会处理保存配置时可能出现的异常。
- list_configs:列出所有配置。返回一个包含所有配置的列表,每个配置都包含其名称和其他配置项。
- get_config:获取指定名称的配置。如果配置不存在,则返回 None 。
- get_all_config_names:获取所有配置名称。返回一个包含所有配置名称的列表。
- add_config:添加新配置。将新配置添加到配置字典中,并保存到文件。
- update_config:更新现有配置。将指定名称的配置更新为新配置,并保存到文件。
- delete_config:删除指定名称的配置。注意,默认配置不能被删除。如果成功删除配置,则返回 True ,否则返回 False 。
- rename_config:重命名配置。将指定名称的配置重命名为新名称,并保存到文件。如果成功重命名配置,则返回 True ,否则返回 False 。
import json
import os
class ConfigManager:
def __init__(self, config_file='upload_configs.json'):
self.config_file = config_file
self.configs = {}
self.load_configs()
def load_configs(self):
"""从配置文件加载所有配置"""
# 默认配置模板
default_config = {
"url": "xxx",
"headers": {
"appkey": "xxx",
"source": "xxx",
"seq": "xxx",
"sign": "xxx"
},
"file_field_name": "file",
"file_type_field": "fileType",
"name_field": "name",
"name_value": "file",
"success_code": 200,
"content_field": "content",
"prefix": ""
}
if os.path.exists(self.config_file):
try:
with open(self.config_file, 'r', encoding='utf-8') as f:
self.configs = json.load(f)
# 如果配置文件为空或不包含默认配置,添加默认配置
if not self.configs or "default" not in self.configs:
self.configs["default"] = default_config
self.save_configs()
except Exception as e:
print(f"加载配置文件失败: {e}")
self.configs = {"default": default_config}
self.save_configs()
else:
# 如果配置文件不存在,创建默认配置
self.configs = {"default": default_config}
self.save_configs()
def save_configs(self):
"""保存所有配置到文件"""
try:
with open(self.config_file, 'w', encoding='utf-8') as f:
json.dump(self.configs, f, ensure_ascii=False, indent=2)
except Exception as e:
print(f"保存配置文件失败: {e}")
def get_config(self, name):
"""获取指定名称的配置"""
return self.configs.get(name)
def list_configs(self):
"""列出所有配置"""
return [{'name': name, **config} for name, config in self.configs.items()]
def get_all_config_names(self):
"""获取所有配置名称"""
return list(self.configs.keys())
def add_config(self, name, config):
"""添加新配置"""
self.configs[name] = config
self.save_configs()
def update_config(self, name, config):
"""更新配置"""
self.configs[name] = config
self.save_configs()
def delete_config(self, name):
"""删除配置"""
# 不允许删除默认配置
if name == "default":
return False
if name in self.configs:
del self.configs[name]
self.save_configs()
return True
return False
def rename_config(self, old_name, new_name):
"""重命名配置"""
if old_name in self.configs and new_name not in self.configs:
self.configs[new_name] = self.configs[old_name]
del self.configs[old_name]
self.save_configs()
return True
return False
5. 上传配置类的读取和默认实现
如果 config_data 配置数据不 None,那么就使用配置数据,否则就使用默认的上传配置。
from config_manager import ConfigManager
class UploadConfig:
def __init__(self, config_data=None):
if config_data:
self.url = config_data.get('url', 'xxx')
self.headers = config_data.get('headers', {
"appkey": 'xxx',
"source": 'xxx',
"seq": 'xxx',
"sign": 'xxx'
})
self.file_field_name = config_data.get('file_field_name', 'file')
self.file_type_field = config_data.get('file_type_field', 'fileType')
self.name_field = config_data.get('name_field', 'name')
self.name_value = config_data.get('name_value', 'file')
self.success_code = config_data.get('success_code', 200)
self.content_field = config_data.get('content_field', 'content')
else:
self.url = 'xxx'
self.headers = {
"appkey": 'xxx',
"source": 'xxx',
"seq": 'xxx',
"sign": 'xxx'
}
self.file_field_name = 'file'
self.file_type_field = 'fileType'
self.name_field = 'name'
self.name_value = 'file'
self.success_code = 200
self.content_field = 'content'
6. UploadGUI 实现
6.1 管理器初始化
def __init__(self, root):
self.root = root
self.root.title("文件上传工具")
self.root.geometry("800x600")
# 初始化配置管理器
self.config_manager = ConfigManager()
self.current_config = None
self.setup_ui()
6.2 界面布局
def setup_ui(self):
# 创建主框架
main_frame = ttk.Frame(self.root)
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 创建配置选择框架
config_frame = ttk.LabelFrame(main_frame, text="配置管理")
config_frame.pack(fill=tk.X, padx=5, pady=5)
# 配置选择下拉框
ttk.Label(config_frame, text="选择配置:").pack(side=tk.LEFT, padx=5)
self.config_combobox = ttk.Combobox(config_frame, state="readonly", width=30)
self.config_combobox.pack(side=tk.LEFT, padx=5)
self.config_combobox.bind('<<ComboboxSelected>>', self.on_config_selected)
# 刷新配置按钮
refresh_btn = ttk.Button(config_frame, text="刷新配置", command=self.refresh_configs)
refresh_btn.pack(side=tk.LEFT, padx=5)
# 配置管理按钮
config_manage_btn = ttk.Button(config_frame, text="管理配置", command=self.open_config_manager)
config_manage_btn.pack(side=tk.LEFT, padx=5)
# 创建上传框架
upload_frame = ttk.LabelFrame(main_frame, text="文件上传")
upload_frame.pack(fill=tk.X, padx=5, pady=5)
# 目录选择
dir_frame = ttk.Frame(upload_frame)
dir_frame.pack(fill=tk.X, padx=5, pady=5)
ttk.Label(dir_frame, text="选择目录:").pack(side=tk.LEFT)
self.dir_entry = ttk.Entry(dir_frame, width=50)
self.dir_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
browse_btn = ttk.Button(dir_frame, text="浏览", command=self.browse_directory)
browse_btn.pack(side=tk.LEFT, padx=5)
# 文件类型
type_frame = ttk.Frame(upload_frame)
type_frame.pack(fill=tk.X, padx=5, pady=5)
ttk.Label(type_frame, text="文件类型:").pack(side=tk.LEFT)
self.type_entry = ttk.Entry(type_frame, width=50)
self.type_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
self.type_entry.insert(0, "jpg,png,jpeg,gif,bmp")
# 前缀输入
prefix_frame = ttk.Frame(upload_frame)
prefix_frame.pack(fill=tk.X, padx=5, pady=5)
ttk.Label(prefix_frame, text="文件前缀:").pack(side=tk.LEFT)
self.prefix_entry = ttk.Entry(prefix_frame, width=50)
self.prefix_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
# 操作按钮
button_frame = ttk.Frame(upload_frame)
button_frame.pack(fill=tk.X, padx=5, pady=5)
self.upload_btn = ttk.Button(button_frame, text="开始上传", command=self.start_upload)
self.upload_btn.pack(side=tk.LEFT, padx=5)
# 导出按钮
export_btn = ttk.Button(button_frame, text="导出icon.js", command=self.export_icon_js)
export_btn.pack(side=tk.LEFT, padx=5)
# 日志显示
log_frame = ttk.LabelFrame(main_frame, text="上传日志")
log_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
# 日志操作按钮
log_button_frame = ttk.Frame(log_frame)
log_button_frame.pack(fill=tk.X, padx=5, pady=5)
clear_log_btn = ttk.Button(log_button_frame, text="清空日志", command=self.clear_log)
clear_log_btn.pack(side=tk.LEFT, padx=5)
self.log_text = tk.Text(log_frame, height=15)
scrollbar = ttk.Scrollbar(log_frame, orient=tk.VERTICAL, command=self.log_text.yview)
self.log_text.configure(yscrollcommand=scrollbar.set)
self.log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
6.3 刷新配置
- 获取配置列表:通过
config_manager.list_configs()方法获取所有可用的配置信息。 - 构建显示名称:
- 检查每个配置是否有
title字段; - 如果有标题,显示格式为"标题 (配置名)",提高可读性;
- 如果没有标题,则直接显示配置名。
- 检查每个配置是否有
- 建立映射关系:创建从显示名称到实际配置名称的映射字典,便于后续查找。
- 更新UI组件:将构建好的显示名称列表设置为下拉框的选项。
- 默认选择:如果存在配置,默认选择第一个配置并加载它。
- 边界处理:当没有配置时,清空下拉框并设置当前配置为None。
def refresh_configs(self):
"""刷新配置列表"""
configs = self.config_manager.list_configs()
# 使用title作为显示名称,如果没有title则使用配置名称
config_display_names = []
config_name_map = {} # 映射显示名称到实际配置名称
for config in configs:
title = config.get('title', '')
if title:
display_name = f"{title} ({config['name']})"
else:
display_name = config['name']
config_display_names.append(display_name)
config_name_map[display_name] = config['name']
self.config_combobox['values'] = config_display_names
# 保存配置名称映射
self.config_name_map = config_name_map
# 如果有配置,选择第一个
if configs:
title = configs[0].get('title', '')
if title:
display_name = f"{title} ({configs[0]['name']})"
else:
display_name = configs[0]['name']
self.config_combobox.set(display_name)
self.load_selected_config()
else:
self.config_combobox.set('')
self.current_config = None
6.4 加载默认配置
def load_default_config(self):
"""加载默认配置"""
configs = self.config_manager.list_configs()
if configs:
self.config_combobox.set(configs[0]['name'])
self.load_selected_config()
7. 实现的初始化 UI 界面
8. 总结
- 目前就是配置文件设置读取等操作还有初始化基本的界面,比较简单;
- 由于必定还是一个完整的工具,代码还是有点多,因此决定分多篇进行讲解实现过程。