【Python 实战】---- 实现一个可选择、配置操作的批量文件上传工具(一)上传界面 UI 实现

114 阅读6分钟

1. 前言

很早之前,我实现过一个【Python 实战】---- 接口自动化:60行代码,如何通过Python requests实现图片上传,这个的确简化了前端开发对图片操作的时间,但是随着项目开发的越多,遇到一个新的问题,就是图片需要上传到每个项目的自己服务器中,而且每个项目的参数也不一样,有的需要加密,有的不需要,这就会出现我们需要根据不同的项目,对代码进行上传的微调,然后再打包成工具使用,最后就会发现有很多工具,完全达不到一个程序员的优雅,因此就想到使用 GUI 配置管理,将所有的工具进行配置管理。

2. 简单的 UI 界面

2.1 上传界面

输入图片说明

2.2 配置管理界面

输入图片说明

2.3 编辑和新增配置管理界面

输入图片说明

3. 实现的基础结构

  1. UploadConfig 上传配置文件的操作;
  2. FileUploader 上传文件的操作方法;
  3. UploadGUI 上传界面的 UI 布局;
  4. ConfigManagerWindow 管理界面的 UI 布局;
  5. ConfigEditWindow 编辑和新增的 UI 布局。

输入图片说明

4. 对配置文件的增删改查操作类的实现

  1. init:构造函数,用于初始化 ConfigManager 实例。它会设置配置文件路径,初始化配置字典,并调用 load_configs() 方法加载配置。
  2. load_configs:从配置文件加载所有配置。如果配置文件不存在或为空,将创建一个包含默认配置的文件。该方法还会处理加载配置时可能出现的异常。
  3. save_configs:将所有配置保存到文件中。该方法会处理保存配置时可能出现的异常。
  4. list_configs:列出所有配置。返回一个包含所有配置的列表,每个配置都包含其名称和其他配置项。
  5. get_config:获取指定名称的配置。如果配置不存在,则返回 None 。
  6. get_all_config_names:获取所有配置名称。返回一个包含所有配置名称的列表。
  7. add_config:添加新配置。将新配置添加到配置字典中,并保存到文件。
  8. update_config:更新现有配置。将指定名称的配置更新为新配置,并保存到文件。
  9. delete_config:删除指定名称的配置。注意,默认配置不能被删除。如果成功删除配置,则返回 True ,否则返回 False 。
  10. 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 刷新配置

  1. 获取配置列表:通过config_manager.list_configs()方法获取所有可用的配置信息。
  2. 构建显示名称
    • 检查每个配置是否有title字段;
    • 如果有标题,显示格式为"标题 (配置名)",提高可读性;
    • 如果没有标题,则直接显示配置名。
  3. 建立映射关系:创建从显示名称到实际配置名称的映射字典,便于后续查找。
  4. 更新UI组件:将构建好的显示名称列表设置为下拉框的选项。
  5. 默认选择:如果存在配置,默认选择第一个配置并加载它。
  6. 边界处理:当没有配置时,清空下拉框并设置当前配置为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. 总结

  1. 目前就是配置文件设置读取等操作还有初始化基本的界面,比较简单;
  2. 由于必定还是一个完整的工具,代码还是有点多,因此决定分多篇进行讲解实现过程。