【批量图片查找】在电脑上如何根据文件名清单一次性查找多张图片并复制到指定文件夹,基于Python的解决方案

一、应用场景

【批量图片查找】在电脑上如何根据文件名清单一次性查找多张图片并复制到指定文件夹,基于Python的解决方案_第1张图片

这个工具适用于以下场景:

  1. 设计师需要从大量素材中筛选特定图片复制并保存
  2. 摄影师需要根据文件名批量整理照片查找筛选复制
  3. 电商运营人员需要从产品库中提取特定商品图片复制到指定文件夹
  4. 数据分析师需要批量收集特定图片复制保存用于处理
  5. 任何需要从大量图片中快速找到并整理特定文件的场景

二、界面设计

【批量图片查找】在电脑上如何根据文件名清单一次性查找多张图片并复制到指定文件夹,基于Python的解决方案_第2张图片

应用采用直观的三部分布局:

  1. 顶部区域:源目录和目标目录选择,用户可以浏览文件系统选择相应目录
  2. 中间区域
    • 文本框用于输入要搜索的文件名,支持多行输入
    • 选项区域包含搜索选项:区分大小写、使用正则表达式、覆盖已存在文件、搜索子文件夹
    • 图片格式筛选,用户可以自定义要搜索的图片格式
  3. 底部区域
    • 状态显示和进度条,显示当前操作状态和进度
    • 结果文本框,显示搜索和复制操作的详细结果
    • 功能按钮:开始搜索、清空列表和退出

三、详细代码步骤

import os
import shutil
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import threading
import re
from pathlib import Path

class ImageFinderApp:
    def __init__(self, root):
        self.root = root
        self.root.title("图片查找与复制工具")
        self.root.geometry("800x600")
        self.root.minsize(600, 500)
        
        # 设置中文字体支持
        self.font = ('SimHei', 10)
        
        # 创建主框架
        self.main_frame = ttk.Frame(root, padding="10")
        self.main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 源目录选择
        ttk.Label(self.main_frame, text="源目录:", font=self.font).grid(row=0, column=0, sticky=tk.W, pady=5)
        self.source_var = tk.StringVar()
        ttk.Entry(self.main_frame, textvariable=self.source_var, width=60, font=self.font).grid(row=0, column=1, pady=5, padx=5)
        ttk.Button(self.main_frame, text="浏览", command=self.browse_source, width=10).grid(row=0, column=2, pady=5)
        
        # 目标目录选择
        ttk.Label(self.main_frame, text="目标目录:", font=self.font).grid(row=1, column=0, sticky=tk.W, pady=5)
        self.target_var = tk.StringVar()
        ttk.Entry(self.main_frame, textvariable=self.target_var, width=60, font=self.font).grid(row=1, column=1, pady=5, padx=5)
        ttk.Button(self.main_frame, text="浏览", command=self.browse_target, width=10).grid(row=1, column=2, pady=5)
        
        # 文件名字段
        ttk.Label(self.main_frame, text="输入文件名(每行一个):", font=self.font).grid(row=2, column=0, sticky=tk.W, pady=5)
        self.filenames_text = tk.Text(self.main_frame, height=10, width=70, font=self.font)
        self.filenames_text.grid(row=3, column=0, columnspan=3, pady=5, sticky=tk.W+tk.E)
        
        # 滚动条
        scrollbar = ttk.Scrollbar(self.main_frame, command=self.filenames_text.yview)
        scrollbar.grid(row=3, column=3, sticky=tk.N+tk.S)
        self.filenames_text.config(yscrollcommand=scrollbar.set)
        
        # 选项
        options_frame = ttk.LabelFrame(self.main_frame, text="选项", padding="10")
        options_frame.grid(row=4, column=0, columnspan=3, pady=10, sticky=tk.W+tk.E)
        
        self.case_sensitive_var = tk.BooleanVar(value=False)
        ttk.Checkbutton(options_frame, text="区分大小写", variable=self.case_sensitive_var, font=self.font).grid(row=0, column=0, padx=10)
        
        self.use_regex_var = tk.BooleanVar(value=False)
        ttk.Checkbutton(options_frame, text="使用正则表达式", variable=self.use_regex_var, font=self.font).grid(row=0, column=1, padx=10)
        
        self.overwrite_var = tk.BooleanVar(value=False)
        ttk.Checkbutton(options_frame, text="覆盖已存在文件", variable=self.overwrite_var, font=self.font).grid(row=0, column=2, padx=10)
        
        self.search_subfolders_var = tk.BooleanVar(value=True)
        ttk.Checkbutton(options_frame, text="搜索子文件夹", variable=self.search_subfolders_var, font=self.font).grid(row=0, column=3, padx=10)
        
        # 图片格式筛选
        ttk.Label(options_frame, text="图片格式:", font=self.font).grid(row=1, column=0, sticky=tk.W, pady=5)
        self.image_formats = tk.StringVar(value="jpg,jpeg,png,gif,bmp")
        ttk.Entry(options_frame, textvariable=self.image_formats, width=40, font=self.font).grid(row=1, column=1, pady=5, padx=5)
        ttk.Label(options_frame, text="(逗号分隔)", font=self.font).grid(row=1, column=2, sticky=tk.W, pady=5)
        
        # 状态和进度条
        self.status_var = tk.StringVar(value="就绪")
        ttk.Label(self.main_frame, textvariable=self.status_var, font=self.font).grid(row=5, column=0, sticky=tk.W, pady=5)
        
        self.progress_var = tk.DoubleVar(value=0)
        self.progress_bar = ttk.Progressbar(self.main_frame, variable=self.progress_var, length=100, mode='determinate')
        self.progress_bar.grid(row=5, column=1, pady=5, sticky=tk.W+tk.E)
        
        # 结果显示
        ttk.Label(self.main_frame, text="结果:", font=self.font).grid(row=6, column=0, sticky=tk.W, pady=5)
        self.result_text = tk.Text(self.main_frame, height=10, width=70, font=self.font)
        self.result_text.grid(row=7, column=0, columnspan=3, pady=5, sticky=tk.W+tk.E+tk.N+tk.S)
        self.result_text.config(state=tk.DISABLED)
        
        # 结果滚动条
        result_scrollbar = ttk.Scrollbar(self.main_frame, command=self.result_text.yview)
        result_scrollbar.grid(row=7, column=3, sticky=tk.N+tk.S)
        self.result_text.config(yscrollcommand=result_scrollbar.set)
        
        # 按钮
        button_frame = ttk.Frame(self.main_frame)
        button_frame.grid(row=8, column=0, columnspan=3, pady=10, sticky=tk.E)
        
        ttk.Button(button_frame, text="开始搜索", command=self.start_search, width=15).grid(row=0, column=0, padx=5)
        ttk.Button(button_frame, text="清空列表", command=self.clear_list, width=15).grid(row=0, column=1, padx=5)
        ttk.Button(button_frame, text="退出", command=root.quit, width=15).grid(row=0, column=2, padx=5)
        
        # 配置网格权重,使界面可伸缩
        self.main_frame.columnconfigure(1, weight=1)
        self.main_frame.rowconfigure(7, weight=1)
        
        # 存储结果
        self.search_results = []
        self.total_files = 0
        self.processed_files = 0
        
    def browse_source(self):
        directory = filedialog.askdirectory(title="选择源目录")
        if directory:
            self.source_var.set(directory)
    
    def browse_target(self):
        directory = filedialog.askdirectory(title="选择目标目录")
        if directory:
            self.target_var.set(directory)
    
    def clear_list(self):
        self.filenames_text.delete(1.0, tk.END)
        self.result_text.config(state=tk.NORMAL)
        self.result_text.delete(1.0, tk.END)
        self.result_text.config(state=tk.DISABLED)
        self.status_var.set("就绪")
        self.progress_var.set(0)
    
    def start_search(self):
        # 获取用户输入
        source_dir = self.source_var.get()
        target_dir = self.target_var.get()
        filenames_text = self.filenames_text.get(1.0, tk.END).strip()
        
        # 验证输入
        if not source_dir:
            messagebox.showerror("错误", "请选择源目录")
            return
        
        if not os.path.isdir(source_dir):
            messagebox.showerror("错误", "源目录不存在")
            return
        
        if not target_dir:
            messagebox.showerror("错误", "请选择目标目录")
            return
        
        if not filenames_text:
            messagebox.showerror("错误", "请输入要搜索的文件名")
            return
        
        # 准备文件名列表
        filenames = [line.strip() for line in filenames_text.split('\n') if line.strip()]
        
        # 创建目标目录(如果不存在)
        os.makedirs(target_dir, exist_ok=True)
        
        # 清空结果显示
        self.result_text.config(state=tk.NORMAL)
        self.result_text.delete(1.0, tk.END)
        self.result_text.config(state=tk.DISABLED)
        
        # 启动搜索线程
        self.search_results = []
        self.total_files = len(filenames)
        self.processed_files = 0
        self.progress_var.set(0)
        
        thread = threading.Thread(target=self.search_and_copy_files, args=(source_dir, target_dir, filenames))
        thread.daemon = True
        thread.start()
    
    def search_and_copy_files(self, source_dir, target_dir, filenames):
        # 更新状态
        self.update_status("正在搜索文件...")
        
        # 获取图片格式列表
        image_extensions = [f".{ext.strip().lower()}" for ext in self.image_formats.get().split(',') if ext.strip()]
        
        # 搜索选项
        case_sensitive = self.case_sensitive_var.get()
        use_regex = self.use_regex_var.get()
        search_subfolders = self.search_subfolders_var.get()
        overwrite = self.overwrite_var.get()
        
        found_count = 0
        not_found_count = 0
        copied_count = 0
        skipped_count = 0
        
        # 遍历每个文件名进行搜索
        for filename in filenames:
            if not case_sensitive:
                filename_lower = filename.lower()
            else:
                filename_lower = filename
            
            # 存储当前文件的搜索结果
            found_files = []
            
            # 使用正则表达式搜索
            if use_regex:
                try:
                    pattern = re.compile(filename, re.IGNORECASE if not case_sensitive else 0)
                    
                    # 遍历源目录中的所有文件
                    if search_subfolders:
                        for root, _, files in os.walk(source_dir):
                            for file in files:
                                if not image_extensions or Path(file).suffix.lower() in image_extensions:
                                    if pattern.search(file):
                                        found_files.append(os.path.join(root, file))
                    else:
                        for file in os.listdir(source_dir):
                            if os.path.isfile(os.path.join(source_dir, file)):
                                if not image_extensions or Path(file).suffix.lower() in image_extensions:
                                    if pattern.search(file):
                                        found_files.append(os.path.join(source_dir, file))
                except re.error as e:
                    self.update_result(f"错误: 无效的正则表达式 '{filename}': {str(e)}")
                    not_found_count += 1
                    self.update_progress()
                    continue
            
            # 普通文件名搜索
            else:
                # 遍历源目录中的所有文件
                if search_subfolders:
                    for root, _, files in os.walk(source_dir):
                        for file in files:
                            if not image_extensions or Path(file).suffix.lower() in image_extensions:
                                compare_name = file if case_sensitive else file.lower()
                                if compare_name == filename_lower:
                                    found_files.append(os.path.join(root, file))
                else:
                    for file in os.listdir(source_dir):
                        if os.path.isfile(os.path.join(source_dir, file)):
                            if not image_extensions or Path(file).suffix.lower() in image_extensions:
                                compare_name = file if case_sensitive else file.lower()
                                if compare_name == filename_lower:
                                    found_files.append(os.path.join(source_dir, file))
            
            # 处理搜索结果
            if found_files:
                found_count += 1
                for found_file in found_files:
                    # 构建目标路径
                    target_file = os.path.join(target_dir, os.path.basename(found_file))
                    
                    # 检查文件是否已存在
                    if os.path.exists(target_file):
                        if overwrite:
                            try:
                                shutil.copy2(found_file, target_file)
                                self.update_result(f"已复制: {found_file} -> {target_file}")
                                copied_count += 1
                            except Exception as e:
                                self.update_result(f"错误: 无法复制 {found_file}: {str(e)}")
                        else:
                            self.update_result(f"已跳过: {found_file} (目标文件已存在)")
                            skipped_count += 1
                    else:
                        try:
                            shutil.copy2(found_file, target_file)
                            self.update_result(f"已复制: {found_file} -> {target_file}")
                            copied_count += 1
                        except Exception as e:
                            self.update_result(f"错误: 无法复制 {found_file}: {str(e)}")
            else:
                not_found_count += 1
                self.update_result(f"未找到: {filename}")
            
            # 更新进度
            self.update_progress()
        
        # 显示最终结果
        self.update_status(f"搜索完成。找到: {found_count}, 未找到: {not_found_count}, 已复制: {copied_count}, 已跳过: {skipped_count}")
    
    def update_result(self, message):
        self.root.after(0, self._update_result_ui, message)
    
    def _update_result_ui(self, message):
        self.result_text.config(state=tk.NORMAL)
        self.result_text.insert(tk.END, message + "\n")
        self.result_text.see(tk.END)
        self.result_text.config(state=tk.DISABLED)
    
    def update_status(self, message):
        self.root.after(0, self.status_var.set, message)
    
    def update_progress(self):
        self.processed_files += 1
        progress = (self.processed_files / self.total_files) * 100
        self.root.after(0, self.progress_var.set, progress)

if __name__ == "__main__":
    root = tk.Tk()
    app = ImageFinderApp(root)
    root.mainloop()    

四、总结优化

  1. 性能优化
    • 使用多线程处理,避免界面冻结
    • 支持正则表达式搜索,提高搜索灵活性
    • 可以限制搜索的文件类型为图片,提高搜索效率
  2. 用户体验优化
    • 直观的图形界面,易于操作
    • 实时显示搜索进度和结果
    • 提供多种搜索选项,满足不同需求
  3. 错误处理
    • 处理目录不存在、文件复制失败等异常情况
    • 提供明确的错误提示
  4. 扩展性
    • 可以轻松添加更多功能,如预览图片、批量重命名等
    • 代码结构清晰,易于维护和修改

这个工具通过简单易用的界面和强大的搜索功能,帮助用户快速从大量图片中找到并复制需要的文件,提高工作效率。

你可能感兴趣的:(文件操作类,python,开发语言,批量文件查找,批量图片查找,批量PDF查找)