Python项目73:自动化文件备份系统1.0(tkinter)

主要功能说明:
1.界面组件:源文件夹和目标文件夹选择(带浏览按钮),备份间隔时间设置(分钟),立即备份按钮,自动备份切换按钮,状态栏显示备份状态。
2.进度条显示:使用ttk.Progressbar实现精确进度显示,根据实际文件数量更新进度,实时显示已复制文件数量。
3.文件过滤功能:包含:*.docx, *.xlsx,排除:temp, ~$,支持包含/排除模式(支持通配符)。
4.日志记录系统:自动生成backup_log.txt,记录每次备份的详细信息,支持查看日志窗口(带滚动条)。
5.增量备份选项:通过MD5哈希比较文件内容,仅复制修改过的文件,可切换完整/增量备份模式。

6.使用说明:设置源目录和目标目录,配置过滤规则(可选),选择备份模式(完整/增量),设置自动备份间隔,通过按钮执行立即备份或自动备份,可随时查看备份日志。使用独立线程进行自动备份,避免界面卡顿,自动生成带时间戳的备份目录,实时状态反馈,错误处理机制。

7.注意事项:需要确保目标驱动器有足够空间,长时间运行的自动备份建议在后台运行,第一次使用时建议先进行手动备份测试,增量备份基于文件内容比对,大文件可能会影响性能,过滤规则支持标准shell通配符语法,日志文件保存在程序运行目录,进度条精度取决于文件数量而非大小。
在这里插入图片描述


# -*- coding: utf-8 -*-
# @Author : 小红牛
# 微信公众号:WdPython
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import shutil
import os
from datetime import datetime
import threading
import time
import fnmatch
import hashlib


class BackupApp:
    def __init__(self, root):
        self.root = root
        self.root.title("高级自动备份工具")
        self.root.geometry("650x450")

        # 初始化变量
        self.is_auto_backup_running = False
        self.total_files = 0
        self.copied_files = 0
        self.backup_mode = tk.StringVar(value="full")  # 备份模式

        # 创建界面组件
        self.create_widgets()
        self.setup_logging()

    def setup_logging(self):
        """初始化日志系统"""
        self.log_path = os.path.join(os.getcwd(), "backup_log.txt")
        if not os.path.exists(self.log_path):
            with open(self.log_path, "w") as f:
                f.write("备份日志记录\n\n")

    def create_widgets(self):
        # 创建主容器
        main_frame = ttk.Frame(self.root)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        # 源文件夹选择
        source_frame = ttk.LabelFrame(main_frame, text="源目录设置")
        source_frame.grid(row=0, column=0, columnspan=3, sticky="we", pady=5)

        self.source_entry = ttk.Entry(source_frame, width=50)
        self.source_entry.grid(row=0, column=0, padx=5, pady=5)

        ttk.Button(source_frame, text="浏览...", command=self.select_source).grid(row=0, column=1, padx=5)

        # 目标文件夹选择
        target_frame = ttk.LabelFrame(main_frame, text="目标目录设置")
        target_frame.grid(row=1, column=0, columnspan=3, sticky="we", pady=5)

        self.target_entry = ttk.Entry(target_frame, width=50)
        self.target_entry.grid(row=0, column=0, padx=5, pady=5)

        ttk.Button(target_frame, text="浏览...", command=self.select_target).grid(row=0, column=1, padx=5)

        # 过滤设置
        filter_frame = ttk.LabelFrame(main_frame, text="文件过滤设置")
        filter_frame.grid(row=2, column=0, sticky="we", pady=5, padx=5)

        ttk.Label(filter_frame, text="包含模式(逗号分隔):").grid(row=0, column=0)
        self.include_pattern = ttk.Entry(filter_frame)
        self.include_pattern.grid(row=0, column=1, padx=5)

        ttk.Label(filter_frame, text="排除模式(逗号分隔):").grid(row=1, column=0)
        self.exclude_pattern = ttk.Entry(filter_frame)
        self.exclude_pattern.grid(row=1, column=1, padx=5)

        # 备份设置
        setting_frame = ttk.LabelFrame(main_frame, text="备份设置")
        setting_frame.grid(row=2, column=1, sticky="we", pady=5, padx=5)

        ttk.Label(setting_frame, text="间隔(分钟):").grid(row=0, column=0)
        self.interval_entry = ttk.Entry(setting_frame, width=8)
        self.interval_entry.grid(row=0, column=1, padx=5)
        self.interval_entry.insert(0, "60")

        ttk.Radiobutton(setting_frame, text="完整备份", variable=self.backup_mode, value="full").grid(row=1, column=0)
        ttk.Radiobutton(setting_frame, text="增量备份", variable=self.backup_mode, value="incremental").grid(row=1,
                                                                                                         column=1)

        # 进度条
        self.progress = ttk.Progressbar(main_frame, orient=tk.HORIZONTAL, mode='determinate')
        self.progress.grid(row=3, column=0, columnspan=3, sticky="we", pady=10)

        # 按钮区域
        btn_frame = ttk.Frame(main_frame)
        btn_frame.grid(row=4, column=0, columnspan=3, pady=10)

        ttk.Button(btn_frame, text="立即备份", command=self.manual_backup).pack(side=tk.LEFT, padx=5)
        self.auto_btn = ttk.Button(btn_frame, text="开始自动备份", command=self.toggle_auto_backup)
        self.auto_btn.pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text="查看日志", command=self.show_logs).pack(side=tk.LEFT, padx=5)

        # 状态栏
        self.status_label = ttk.Label(main_frame, text="状态: 就绪", relief=tk.SUNKEN)
        self.status_label.grid(row=5, column=0, columnspan=3, sticky="we")

    def select_source(self):
        path = filedialog.askdirectory()
        if path:
            self.source_entry.delete(0, tk.END)
            self.source_entry.insert(0, path)

    def select_target(self):
        path = filedialog.askdirectory()
        if path:
            self.target_entry.delete(0, tk.END)
            self.target_entry.insert(0, path)

    def manual_backup(self):
        threading.Thread(target=self.perform_backup, daemon=True).start()

    def should_include(self, file_path):
        """文件过滤逻辑"""
        include = self.include_pattern.get().strip()
        exclude = self.exclude_pattern.get().strip()
        filename = os.path.basename(file_path)

        # 包含规则
        if include:
            patterns = [p.strip() for p in include.split(",")]
            if not any(fnmatch.fnmatch(filename, p) for p in patterns):
                return False

        # 排除规则
        if exclude:
            patterns = [p.strip() for p in exclude.split(",")]
            if any(fnmatch.fnmatch(filename, p) for p in patterns):
                return False

        return True

    def get_file_hash(self, file_path):
        """计算文件哈希值(用于增量备份)"""
        hasher = hashlib.md5()
        with open(file_path, 'rb') as f:
            while chunk := f.read(8192):
                hasher.update(chunk)
        return hasher.hexdigest()

    def perform_backup(self):
        """执行备份的核心方法"""
        try:
            source = self.source_entry.get()
            target = self.target_entry.get()

            if not source or not target:
                messagebox.showerror("错误", "请先选择源文件夹和目标文件夹")
                return

            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            backup_folder = os.path.join(target, f"backup_{timestamp}")
            os.makedirs(backup_folder, exist_ok=True)

            # 获取文件列表并过滤
            all_files = []
            for root, dirs, files in os.walk(source):
                for file in files:
                    full_path = os.path.join(root, file)
                    if self.should_include(full_path):
                        all_files.append(full_path)

            self.total_files = len(all_files)
            self.copied_files = 0
            self.progress['maximum'] = self.total_files
            self.progress['value'] = 0

            # 记录日志
            log_entry = {
                'timestamp': datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                'source': source,
                'target': backup_folder,
                'mode': self.backup_mode.get(),
                'status': 'success',
                'files': 0
            }

            # 执行备份
            for file_path in all_files:
                if not self.is_auto_backup_running and self.backup_mode.get() == "incremental":
                    break  # 允许取消增量备份

                rel_path = os.path.relpath(file_path, source)
                dest_path = os.path.join(backup_folder, rel_path)
                os.makedirs(os.path.dirname(dest_path), exist_ok=True)

                # 增量备份检查
                if self.backup_mode.get() == "incremental":
                    if os.path.exists(dest_path):
                        src_hash = self.get_file_hash(file_path)
                        dst_hash = self.get_file_hash(dest_path)
                        if src_hash == dst_hash:
                            continue

                shutil.copy2(file_path, dest_path)
                self.copied_files += 1
                self.update_progress()

            log_entry['files'] = self.copied_files
            self.log_backup(log_entry)
            self.update_status(f"备份完成:复制 {self.copied_files}/{self.total_files} 个文件")

        except Exception as e:
            self.update_status(f"备份失败:{str(e)}")
            messagebox.showerror("错误", str(e))
            log_entry['status'] = 'failed'
            log_entry['error'] = str(e)
            self.log_backup(log_entry)

    def update_progress(self):
        """更新进度条"""
        self.progress['value'] = self.copied_files
        self.root.update_idletasks()

    def log_backup(self, entry):
        """记录备份日志"""
        with open(self.log_path, "a") as f:
            f.write(f"[{entry['timestamp']}] {entry['mode'].upper()}备份 "
                    f"源目录: {entry['source']}\n目标目录: {entry['target']}\n"
                    f"状态: {entry['status'].upper()} 文件数: {entry['files']}\n")
            if 'error' in entry:
                f.write(f"错误信息: {entry['error']}\n")
            f.write("-" * 50 + "\n")

    def show_logs(self):
        """显示日志窗口"""
        log_win = tk.Toplevel(self.root)
        log_win.title("备份日志")

        text_area = tk.Text(log_win, wrap=tk.WORD, width=80, height=20)
        scroll = ttk.Scrollbar(log_win, command=text_area.yview)
        text_area.configure(yscrollcommand=scroll.set)

        with open(self.log_path, "r") as f:
            text_area.insert(tk.END, f.read())

        text_area.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scroll.pack(side=tk.RIGHT, fill=tk.Y)

    def toggle_auto_backup(self):
        if not self.is_auto_backup_running:
            try:
                interval = int(self.interval_entry.get()) * 60
                if interval <= 0:
                    raise ValueError
            except ValueError:
                messagebox.showerror("错误", "请输入有效的正整数分钟数")
                return

            self.is_auto_backup_running = True
            self.auto_btn.config(text="停止自动备份")
            self.update_status("自动备份已启动...")

            self.auto_backup_thread = threading.Thread(
                target=self.auto_backup_loop, args=(interval,), daemon=True
            )
            self.auto_backup_thread.start()
        else:
            self.is_auto_backup_running = False
            self.auto_btn.config(text="开始自动备份")
            self.update_status("自动备份已停止")

    def auto_backup_loop(self, interval):
        while self.is_auto_backup_running:
            self.perform_backup()
            for _ in range(interval):
                if not self.is_auto_backup_running:
                    return
                time.sleep(1)

    def update_status(self, message):
        self.status_label.config(text=f"状态: {message}")


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

完毕!!感谢您的收看

----------★★跳转到历史博文集合★★----------
我的零基础Python教程,Python入门篇 进阶篇 视频教程 Py安装py项目 Python模块 Python爬虫 Json Xpath 正则表达式 Selenium Etree CssGui程序开发 Tkinter Pyqt5 列表元组字典数据可视化 matplotlib 词云图 Pyecharts 海龟画图 Pandas Bug处理 电脑小知识office自动化办公 编程工具 NumPy Pygame

<think>嗯,用户已经用Python开发了一个UI界面,现在需要在打开程序时自动创建SQLite数据库和相应的表。这个问题看起来是需要在程序启动时初始化数据库。那我得想想该怎么一步步指导用户实现这个功能。 首先,用户可能已经有一个主脚本,比如用Tkinter、PyQt或者其他UI框架写的。他们需要在程序启动的时候检查数据库是否存在,如果不存在就创建,并建表。所以关键点在于如何连接SQLite,执行建表语句,并且把这个过程集成到启动过程中。 接下来,我需要确定用户是否了解SQLite的基本操作,比如连接数据库、执行SQL语句。可能需要给出一个简单的例子,比如使用sqlite3模块,这是Python自带的,不需要额外安装。用户可能已经知道,但作为回答还是需要涵盖这些基础步骤。 然后,要考虑数据库文件的位置。通常会在程序的工作目录或者某个固定位置创建,比如和脚本同一目录。可能需要建议将数据库路径设置为相对路径,这样无论程序在哪里运行都能正确创建。 另外,建表的SQL语句需要正确无误,否则会出错。可能需要提醒用户注意表结构的设计,以及是否已经存在表,避免重复创建。通常的做法是使用CREATE TABLE IF NOT EXISTS语句,这样即使数据库已经存在,也不会报错。 还有异常处理的问题。在连接数据库或执行SQL时可能会出现错误,比如权限问题、磁盘空间不足等。需要建议用户添加try-except块来捕获异常,并给出友好的错误提示,尤其是在UI程序中,可能需要弹出对话框提示用户。 用户可能还希望数据库初始化过程在后台静默完成,不影响UI的启动速度。所以可能需要将初始化过程放在程序启动的一个单独函数中,在显示UI之前调用,或者异步执行,但如果是轻量级的操作,同步执行可能更简单。 另外,是否需要关闭数据库连接?使用with语句或者显式关闭连接可能更安全,避免资源泄露。但sqlite3在连接对象被销毁时会自动关闭,不过显式关闭是个好习惯。 可能还需要考虑多线程问题,如果UI是事件驱动的,比如PyQt,数据库操作应该在主线程还是子线程中执行?一般来说,简单的初始化操作可以在主线程中快速完成,但如果操作耗时,可能需要放在子线程中以避免界面卡顿。不过SQLite的建表操作通常很快,所以可能不需要。 最后,可能需要提供一个完整的代码示例,展示如何在UI程序启动时调用数据库初始化函数。比如,在Tkinter的__init__方法中调用,或者PyQt的MainWindow的构造函数中调用。 总结一下,步骤应该是: 1. 导入sqlite3模块。 2. 编写一个初始化数据库的函数,检查数据库是否存在,不存在则创建,并创建表。 3. 在UI程序启动时调用这个函数。 4. 添加异常处理,确保错误能被捕获并处理。 5. 测试代码,确保数据库和表正确创建。 可能用户还需要知道如何验证数据库是否创建成功,比如使用SQLite浏览器查看,或者在程序中添加日志输出。这部分也可以提一下,比如在初始化完成后打印日志或更新UI状态。 另外,用户可能在开发过程中需要修改表结构,这时候可能需要考虑数据库迁移的问题,但问题目前可能不涉及,所以暂时不需要讨论。但可以提醒用户,如果未来需要修改表结构,可能需要使用ALTER TABLE语句或版本管理。 最后,代码示例要简洁明了,符合PEP8规范,并且适合集成到现有的UI代码中。可能用户使用的是不同的UI框架,所以示例需要通用一些,或者注明如何适配不同的框架。</think>在Python中实现启动UI程序时自动创建SQLite数据库及表的完整方案如下: 1. **基础实现代码**(以Tkinter为例): ```python import sqlite3 import os from tkinter import Tk, Label def init_database(): db_path = 'my_database.db' # 推荐使用相对路径 # 自动检测并创建数据库 is_new_db = not os.path.exists(db_path) try: conn = sqlite3.connect(db_path) cursor = conn.cursor() # 当检测到新数据库或需要强制初始化时执行建表 if is_new_db: # 用户表样例 cursor.execute('''CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE NOT NULL, email TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)''') # 日志表样例 cursor.execute('''CREATE TABLE IF NOT EXISTS logs ( log_id INTEGER PRIMARY KEY, event_type TEXT, details TEXT, user_id INTEGER, FOREIGN KEY(user_id) REFERENCES users(id))''') conn.commit() print("数据库初始化成功!创建了2张新表") else: print("已存在数据库,跳过初始化") except sqlite3.Error as e: print(f"数据库初始化失败: {str(e)}") # 此处可添加UI错误提示 raise finally: if conn: conn.close() class MyApp(Tk): def __init__(self): super().__init__() self.title("自动建库示例") # 先初始化数据库再加载UI init_database() # 正常UI初始化 Label(self, text="数据库已就绪!").pack(pady=20) if __name__ == "__main__": app = MyApp() app.mainloop() ``` 2. **关键点说明**: - **路径管理**: - 推荐使用`os.path`处理路径,确保跨平台兼容性 - 可考虑将数据库存储在用户专属目录(如`appdata`): ```python from pathlib import Path db_path = Path.home() / '.myapp' / 'data.db' db_path.parent.mkdir(parents=True, exist_ok=True) # 自动创建目录 ``` - **表结构版本控制**(进阶): ```python # 在数据库中添加版本管理表 cursor.execute('''CREATE TABLE IF NOT EXISTS db_info ( key TEXT PRIMARY KEY, value TEXT)''') # 检查当前版本 cursor.execute("SELECT value FROM db_info WHERE key='version'") current_version = cursor.fetchone() if not current_version: # 初始版本 cursor.execute("INSERT INTO db_info VALUES ('version', '1.0')") # 执行1.0版本的建表语句... elif current_version[0] == '1.0': # 执行版本升级逻辑... ``` - **异常处理增强**: - 添加具体错误类型捕获: ```python except sqlite3.OperationalError as oe: print(f"操作错误: {oe}") except sqlite3.IntegrityError as ie: print(f"数据完整性错误: {ie}") ``` - **UI集成建议**: - 在初始化期间显示加载提示 - 使用进度条组件反馈初始化进度 - 对于耗时操作,建议使用线程避免界面冻结 3. **验证方法**: ```python # 在init_database()末尾添加验证代码 cursor.execute("SELECT name FROM sqlite_master WHERE type='table'") tables = cursor.fetchall() print("现有表:", [t[0] for t in tables]) # 或使用PRAGMA命令验证 cursor.execute("PRAGMA table_info(users)") print("用户表结构:", cursor.fetchall()) ``` 4. **部署注意事项**: - 打包时确保数据库文件路径可写(如使用PyInstaller打包时需处理路径问题) - 对敏感数据考虑加密方案(如使用sqlcipher) - 定期备份机制建议 5. **扩展建议**: - 使用ORM工具(如SQLAlchemy、Peewee)进行更规范的数据库管理 - 实现数据库迁移工具(类似Django Migrations) - 添加数据库连接池管理(适用于复杂应用) 该方案通过先执行数据库初始化再加载UI的流程,确保后续操作始终基于有效的数据库环境。开发者可根据实际需求扩展版本控制、数据加密等高级功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值