昔年科技 发表于 2024-9-6 14:19

160行代码实现文件检查器(GUI)

本帖最后由 昔年科技 于 2024-9-6 14:30 编辑

Python初学者,还望大佬多多指点!

正文
工作中,每个月都会对数据进行汇总,想要方便一些,就写了一个文件校验器。
不同于市面上常见的批量重命名,这个具有自动校验功能,可以检验文件名的连续性,会提示缺失的文件,同时,还会校验文件数量,MD5值,路径等。

功能
1.快速批量校验MD5值校验。
2.文件大小、文件路径显示
3.文件日期连续性校验,对缺失日期文件进行提示。(不完善)
4.搜索框支持中文搜索
5.对小于<10KB的文件会提示异常文件。方便快速定位异常文件
以下是界面截图&源代码&打包文件
1.界面及功能截图

2.源代码
import os
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
from datetime import datetime
import hashlib
import math
import re

class FileVerifier:
    def __init__(self, master):
      self.master = master
      master.title("文件校验")

      self.folder_path = tk.StringVar()
      self.filter_rule = tk.StringVar()

      self.create_widgets()

      self.tree = self.create_treeview()

      self.context_menu = self.create_context_menu()

      self.file_paths = {}

    def create_widgets(self):
      # 使用Frame来组织并集中管理其他所有Widget
      self.main_frame = tk.Frame(self.master)
      self.main_frame.pack(fill=tk.BOTH, expand=True)

      tk.Label(self.main_frame, text="文件夹路径:").grid(row=0, column=0, sticky="w")
      # 增加sticky参数以确保控件填满单元格并左对齐
      tk.Entry(self.main_frame, textvariable=self.folder_path).grid(row=0, column=1, sticky="we")
      tk.Button(self.main_frame, text="浏览", command=self.select_folder).grid(row=0, column=2, sticky="e")

      tk.Label(self.main_frame, text="文件名匹配规则:").grid(row=1, column=0, sticky="w")
      tk.Entry(self.main_frame, textvariable=self.filter_rule).grid(row=1, column=1, sticky="we")
      tk.Button(self.main_frame, text="开始校验", command=self.verify_files).grid(row=1, column=2, sticky="e")

    def create_treeview(self):
      columns = ('status', 'filename', 'size', 'modified', 'md5', 'path')
      tree = ttk.Treeview(self.master, columns=columns, show='headings')
      scrollbar = ttk.Scrollbar(self.master, orient=tk.VERTICAL, command=tree.yview)
      tree.configure(yscrollcommand=scrollbar.set)
      for col in columns:
            tree.heading(col, text=col.capitalize())
            tree.column(col, width=100)

      tree.pack(fill=tk.BOTH, expand=True)
      scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
      return tree

    def create_context_menu(self):
      context_menu = tk.Menu(self.master, tearoff=0)
      context_menu.add_command(label="打开文件", command=self.open_file)
      context_menu.add_command(label="在 Windows 资源管理器中打开", command=self.open_file_in_explorer)
      return context_menu

    def select_folder(self):
      folder_selected = filedialog.askdirectory()
      if folder_selected:
            self.folder_path.set(folder_selected)

    def show_context_menu(self, event):
      # 获取选中的项
      iid = self.tree.identify_row(event.y)
      if iid:
            self.tree.selection_set(iid)
            self.context_menu.post(event.x_root, event.y_root)

    def open_file(self):
      selected_item = self.tree.selection()
      if selected_item:
            file_path = self.file_paths]
            if os.path.isfile(file_path):
                os.startfile(file_path)

    def open_file_in_explorer(self):
      selected_item = self.tree.selection()
      if selected_item:
            file_path = self.file_paths]
            file_dir = os.path.dirname(file_path)
            os.startfile(file_dir)

    def get_file_size(self, size):
      if size >= 1024 * 1024:
            return f"{size / (1024 * 1024):.2f} MB"
      elif size >= 1024:
            return f"{size / 1024:.2f} KB"
      else:
            return f"{size} B"

    def calculate_md5(self, filename):
      hash_md5 = hashlib.md5()
      with open(filename, "rb") as f:
            for chunk in iter(lambda: f.read(4096), b""):
                hash_md5.update(chunk)
      return hash_md5.hexdigest()

    def verify_files(self):
      folder_path = self.folder_path.get()
      filter_rule = self.filter_rule.get()

      if not folder_path:
            messagebox.showerror("错误", "请选择文件夹路径!")
            return

      if not filter_rule:
            messagebox.showerror("错误", "请输入文件名匹配规则!")
            return

      file_count = 0
      self.tree.delete(*self.tree.get_children())# 清空Treeview
      self.file_paths.clear()# 清空文件路径字典

      for root, dirs, files in os.walk(folder_path):
            for name in files:
                if filter_rule in name:
                  file_path = os.path.join(root, name)
                  file_size = os.path.getsize(file_path)
                  file_size_str = self.get_file_size(file_size)
                  modification_time = os.path.getmtime(file_path)
                  formatted_modification_time = datetime.fromtimestamp(modification_time).strftime('%Y-%m-%d %H:%M:%S')
                  md5 = self.calculate_md5(file_path)
                  status = "Ok" if file_size >= 10 * 1024 else "异常文件"
                  self.tree.insert('', tk.END, values=(status, name, file_size_str, formatted_modification_time, md5, file_path))
                  self.file_paths] = file_path
                  file_count += 1

      self.master.update_idletasks()
      self.master.minsize(self.tree.winfo_width(), self.tree.winfo_height())

      messagebox.showinfo("完成", f"校验完成。共找到 {file_count} 个文件。")
      self.check_file_date_continuity(files)

    def check_file_date_continuity(self, file_list):
      date_list = []
      missing_dates = []

      for file_name in file_list:
            match = re.search(r'\d{8}', file_name)
            if match:
                date_str = match.group()
                date = datetime.strptime(date_str, '%Y%m%d')
                date_list.append(date)

      date_list.sort()

      for i in range(len(date_list) - 1):
            if (date_list - date_list).days > 1:
                missing_dates.append(date_list)

      if missing_dates:
            message = "以下日期的文件缺失:\n"
            for date in missing_dates:
                message += f"{date.strftime('%Y-%m-%d')}\n"
            messagebox.showwarning("警告", message)
      else:
            messagebox.showinfo("通知", "文件名日期连续,没有缺失。")

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


打包好的EXE(不放心的,可以审查源码。打包只是方便使用)
virscan检测报告:https://www.virscan.org/report/aaf8c24604039827cd9b95a40550b893a01052a61b85469dfd6d051ee0f80533
蓝奏:https://wwi.lanzoup.com/iYcdr29c201c       密码:52pj

mytomsummer 发表于 2024-9-6 15:15

感谢分享学习了

zlqhysy 发表于 2024-9-6 19:55


感谢分享

taokuo 发表于 2024-9-7 00:30

感谢分享

angelicryil 发表于 2024-9-7 07:38

感谢大佬分享

AuroraVerses 发表于 2024-9-7 22:41

感谢分享

jellycici 发表于 2024-9-8 01:05

日常工作有用,谢谢

a761199721 发表于 2024-9-13 09:20

本帖最后由 a761199721 于 2024-9-13 09:31 编辑

1.文件过多时候,没有滚动条,不是很方便查看
2.界面好像就显示330条就不显示了,也不提示完成。。。
运行了下源码,报错了
Traceback (most recent call last):
File "C:\Users\swu\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 1892, in __call__
    return self.func(*args)
File "G:\Python\python_Project\图片处理\4_文件校验\文件校验.py", line 119, in verify_files
    file_size = os.path.getsize(file_path)
File "C:\Users\swu\AppData\Local\Programs\Python\Python39\lib\genericpath.py", line 50, in getsize
    return os.stat(filename).st_size
OSError: 文件或目录损坏且无法读取。: 'H:/DCIM/100MEDIA\\SYFW00325.jpg'
页: [1]
查看完整版本: 160行代码实现文件检查器(GUI)