需求
- 能够选择两个目录,比较这两个目录下所有的图片文件。
- 比较图片文件时,记录文件路径、文件名和文件哈希值,并找出具有相同哈希值和不同哈希值的图片。
- 将比较结果记录到两个不同的日志文件中,一个包含相同的图片,另一个包含不同的图片。
- 能够将不同的文件从一个目录复制到另一个目录。
- 复制文件时应保留文件夹结构,并创建目标文件夹(如果不存在)。
- 在界面上显示统计结果,包括相同文件数和不同文件数。
- 在界面上显示日志文件的路径。
程序代码
import os
import tkinter as tk
from tkinter import filedialog
import hashlib
import shutil
def calculate_hash(file_path):
"""计算文件的MD5哈希值"""
md5 = hashlib.md5()
with open(file_path, 'rb') as f:
while True:
data = f.read(8192)
if not data:
break
md5.update(data)
return md5.hexdigest()
def select_directory(entry_widget):
"""使用文件对话框选择目录并将目录路径填入文本框"""
directory = filedialog.askdirectory()
if directory:
entry_widget.delete(0, tk.END)
entry_widget.insert(0, directory)
def scan_directory(directory):
"""扫描目录下的所有图片文件,并记录路径、文件名和hash"""
image_files = []
for root, dirs, files in os.walk(directory):
for file in files:
if file.lower().endswith(('.jpg', '.jpeg', '.png', '.gif', '.bmp')):
file_path = os.path.join(root, file)
file_hash = calculate_hash(file_path)
image_files.append((file_path, file, file_hash))
return image_files
def compare_directories():
"""比较两个目录的图片文件,并记录结果到日志文件"""
directory1 = entry1.get()
directory2 = entry2.get()
if not directory1 or not directory2:
result_label.config(text="请提供两个目录路径")
return
image_files1 = scan_directory(directory1)
image_files2 = scan_directory(directory2)
same_images = []
different_images = []
for file1 in image_files1:
for file2 in image_files2:
if file1[2] == file2[2]:
same_images.append((file1[0], file2[0]))
break
else:
different_images.append(file1[0])
with open('same_images.txt', 'w') as same_file:
for pair in same_images:
same_file.write(f"{pair[0]}\t{pair[1]}\n")
with open('different_images.txt', 'w') as different_file:
for file_path in different_images:
different_file.write(f"{file_path}\n")
same_count = len(same_images)
different_count = len(different_images)
log_path = os.path.abspath('different_images.txt')
# 更新统计结果和日志路径
stats_label.config(text=f"相同文件数: {same_count}\n不同文件数: {different_count}")
log_label.config(text=f"日志路径: {log_path}")
def copy_different_to_directory1():
"""复制不同的文件到目录1"""
directory1 = entry1.get()
directory2 = entry2.get()
if not directory1 or not directory2:
result_label.config(text="请提供两个目录路径")
return
copy_different_files(directory2, directory1)
result_label.config(text="复制不同的文件到目录1完成")
def copy_different_to_directory2():
"""复制不同的文件到目录2"""
directory1 = entry1.get()
directory2 = entry2.get()
if not directory1 or not directory2:
result_label.config(text="请提供两个目录路径")
return
copy_different_files(directory1, directory2)
result_label.config(text="复制不同的文件到目录2完成")
def copy_different_files(source_dir, dest_dir):
"""复制source_dir中的不同文件到dest_dir中,保留文件夹结构"""
for root, _, files in os.walk(source_dir):
for file in files:
source_file_path = os.path.join(root, file)
relative_path = os.path.relpath(source_file_path, source_dir)
dest_file_path = os.path.join(dest_dir, relative_path)
dest_dir_path = os.path.dirname(dest_file_path)
os.makedirs(dest_dir_path, exist_ok=True) # 创建目标文件夹
if not os.path.exists(dest_file_path):
shutil.copy2(source_file_path, dest_file_path)
# 创建GUI窗口
root = tk.Tk()
root.title("图片比较和复制工具")
# 创建GUI元素
frame1 = tk.Frame(root)
frame1.pack(fill=tk.BOTH, expand=True)
label1 = tk.Label(frame1, text="目录1:")
label1.grid(row=0, column=0, padx=10, pady=10, sticky="w")
entry1 = tk.Entry(frame1)
entry1.grid(row=0, column=1, padx=10, pady=10, sticky="ew")
entry1.insert(0, "请输入目录1的路径")
browse1_button = tk.Button(frame1, text="选择文件夹", command=lambda: select_directory(entry1))
browse1_button.grid(row=0, column=2, padx=10, pady=10, sticky="e")
# 设置entry1的最小宽度为50个字符
entry1.config(width=50)
frame2 = tk.Frame(root)
frame2.pack(fill=tk.BOTH, expand=True)
label2 = tk.Label(frame2, text="目录2:")
label2.grid(row=0, column=0, padx=10, pady=10, sticky="w")
entry2 = tk.Entry(frame2)
entry2.grid(row=0, column=1, padx=10, pady=10, sticky="ew")
entry2.insert(0, "请输入目录2的路径")
browse2_button = tk.Button(frame2, text="选择文件夹", command=lambda: select_directory(entry2))
browse2_button.grid(row=0, column=2, padx=10, pady=10, sticky="e")
frame_buttons = tk.Frame(root)
frame_buttons.pack(fill=tk.BOTH, expand=True)
compare_button = tk.Button(frame_buttons, text="比较图片", command=compare_directories)
compare_button.grid(row=0, column=0, padx=10, pady=10)
copy_to_dir1_button = tk.Button(frame_buttons, text="复制不同文件到目录1", command=copy_different_to_directory1)
copy_to_dir1_button.grid(row=0, column=1, padx=10, pady=10)
copy_to_dir2_button = tk.Button(frame_buttons, text="复制不同文件到目录2", command=copy_different_to_directory2)
copy_to_dir2_button.grid(row=0, column=2, padx=10, pady=10)
# 创建结果标签
result_label = tk.Label(root, text="")
result_label.pack()
# 创建统计结果和日志路径标签
result_frame = tk.Frame(root)
result_frame.pack(fill=tk.BOTH, expand=True)
stats_label = tk.Label(result_frame, text="", anchor="w")
stats_label.pack(fill=tk.BOTH, expand=True)
log_label = tk.Label(result_frame, text="", anchor="w")
log_label.pack(fill=tk.BOTH, expand=True)
# 设置窗口最小宽度为label1、entry1和browse1_button的宽度之和
min_width = label1.winfo_reqwidth() + entry1.winfo_reqwidth() + browse1_button.winfo_reqwidth() + 60 # 添加一些额外空间
root.update_idletasks() # 等待窗口更新,确保winfo_reqwidth()能够正确获取值
root.minsize(min_width, 300) # 设置最小宽度
# 居中显示窗口
root.geometry(
f"{min_width}x300+{root.winfo_screenwidth() // 2 - min_width // 2}+{root.winfo_screenheight() // 2 - 150}")
# 配置列权重,使entry1可以随窗口变宽
frame1.grid_columnconfigure(1, weight=1)
frame2.grid_columnconfigure(1, weight=1)
# 运行GUI主循环
root.mainloop()