python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python文件时间戳修改

Python+PyQt5开发超全能的文件时间戳修改器

作者:创客白泽

在日常开发中,我们经常需要批量修改文件的时间戳属性,本文将使用Python的PyQt5库开发一个功能全面的图形化文件时间戳编辑器,感兴趣的小伙伴可以了解下

概述

在日常开发中,我们经常需要批量修改文件的时间戳属性(创建时间、修改时间、访问时间)。无论是整理照片、管理文档,还是调试应用程序,时间戳操作都是一个常见需求。本文将详细介绍如何使用Python的PyQt5库开发一个功能全面的图形化文件时间戳编辑器

这个工具具有以下核心能力:

功能特色

1. 多模式时间编辑 

2. 批量操作能力

3. 跨平台兼容性

4. 专业级UI设计

效果展示

主界面截图

批量操作页面

开发步骤详解

1. 环境准备

pip install PyQt5 pytz pywin32

2. 项目结构

FileTimeEditor/
│── main.py          # 主程序入口
│── requirements.txt # 依赖文件
└── README.md        # 使用说明

3. 核心开发流程

3.1 初始化主窗口

class TimeStampEditor(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("文件时间戳编辑器")
        self.setGeometry(100, 100, 800, 600)
        self.set_dark_theme()  # 设置暗色主题
        self.init_ui()         # 初始化UI

3.2 实现暗色主题

def set_dark_theme(self):
    dark_palette = QPalette()
    dark_palette.setColor(QPalette.Window, QColor(53, 53, 53))
    dark_palette.setColor(QPalette.WindowText, Qt.white)
    # ...更多颜色设置
    QApplication.setPalette(dark_palette)

3.3 构建主界面

采用QTabWidget实现标签页分类:

3.4 文件操作实现

def add_files(self):
    files, _ = QFileDialog.getOpenFileNames(self, "选择文件")
    if files:
        for file in files:
            if file not in self.files:
                self.files.append(file)
                self.file_list.addItem(file)

代码深度解析

1. 时间处理核心逻辑

def set_file_time(self, file_path, atime=None, mtime=None, ctime=None):
    # 基础时间设置
    if atime_ts is not None or mtime_ts is not None:
        os.utime(file_path, (atime_ts, mtime_ts))
    
    # Windows专属创建时间设置
    if self.system == 'Windows' and ctime:
        import win32file
        wintime = pywintypes.Time(ctime)
        handle = win32file.CreateFile(...)
        win32file.SetFileTime(handle, wintime, None, None)

2. 时区转换算法

def convert_timezone(self, target_tz):
    if hasattr(datetime, 'astimezone'):
        # Python 3.3+现代语法
        mtime = mtime.astimezone(target_tz)
    else:
        # 旧版Python兼容处理
        mtime = local_tz.localize(mtime).astimezone(target_tz)

3. 批量递增时间实现

current_time = self.increment_start.dateTime().toPyDateTime()
interval = self.increment_interval.value()

for file in self.files:
    self.set_file_time(file, current_time)
    current_time += timedelta(seconds=interval)

源码下载

import sys
import os
import platform
from datetime import datetime, timedelta
import pytz
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, 
                            QLabel, QPushButton, QListWidget, QDateTimeEdit, QComboBox, 
                            QRadioButton, QButtonGroup, QGroupBox, QFileDialog, QMessageBox,
                            QSpinBox, QCheckBox, QTabWidget, QStyleFactory)
from PyQt5.QtCore import Qt, QDateTime, QTimer
from PyQt5.QtGui import QIcon, QPalette, QColor

class TimeStampEditor(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("文件时间戳编辑器")
        self.setGeometry(100, 100, 800, 600)
        
        # 设置应用样式和配色
        self.setStyle(QStyleFactory.create('Fusion'))
        self.set_dark_theme()
        
        # 初始化UI
        self.init_ui()
        
        # 初始化变量
        self.files = []
        self.system = platform.system()
        
        # 设置窗口图标
        self.setWindowIcon(QIcon(self.style().standardIcon(getattr(self.style(), 'SP_FileDialogListView'))))

    def set_dark_theme(self):
        """设置暗色主题"""
        dark_palette = QPalette()
        
        # 基础颜色
        dark_palette.setColor(QPalette.Window, QColor(53, 53, 53))
        dark_palette.setColor(QPalette.WindowText, Qt.white)
        dark_palette.setColor(QPalette.Base, QColor(35, 35, 35))
        dark_palette.setColor(QPalette.AlternateBase, QColor(53, 53, 53))
        dark_palette.setColor(QPalette.ToolTipBase, QColor(25, 25, 25))
        dark_palette.setColor(QPalette.ToolTipText, Qt.white)
        dark_palette.setColor(QPalette.Text, Qt.white)
        dark_palette.setColor(QPalette.Button, QColor(53, 53, 53))
        dark_palette.setColor(QPalette.ButtonText, Qt.white)
        dark_palette.setColor(QPalette.BrightText, Qt.red)
        dark_palette.setColor(QPalette.Link, QColor(42, 130, 218))
        dark_palette.setColor(QPalette.Highlight, QColor(42, 130, 218))
        dark_palette.setColor(QPalette.HighlightedText, QColor(35, 35, 35))
        
        # 禁用颜色
        dark_palette.setColor(QPalette.Disabled, QPalette.ButtonText, Qt.darkGray)
        dark_palette.setColor(QPalette.Disabled, QPalette.WindowText, Qt.darkGray)
        dark_palette.setColor(QPalette.Disabled, QPalette.Text, Qt.darkGray)
        
        QApplication.setPalette(dark_palette)
        self.setPalette(dark_palette)

    def init_ui(self):
        """初始化用户界面"""
        main_widget = QWidget()
        main_layout = QVBoxLayout()
        
        # 创建标签页
        tab_widget = QTabWidget()
        
        # 第一个标签页 - 基本设置
        basic_tab = QWidget()
        basic_layout = QVBoxLayout()
        
        # 文件选择部分
        file_group = QGroupBox("📁 选择文件")
        file_layout = QVBoxLayout()
        
        self.file_list = QListWidget()
        self.file_list.setSelectionMode(QListWidget.ExtendedSelection)
        
        btn_layout = QHBoxLayout()
        self.add_file_btn = QPushButton("➕ 添加文件")
        self.add_file_btn.clicked.connect(self.add_files)
        self.add_folder_btn = QPushButton("📂 添加文件夹")
        self.add_folder_btn.clicked.connect(self.add_folder)
        self.remove_btn = QPushButton("❌ 移除选中")
        self.remove_btn.clicked.connect(self.remove_files)
        self.clear_btn = QPushButton("🧹 清空列表")
        self.clear_btn.clicked.connect(self.clear_files)
        
        btn_layout.addWidget(self.add_file_btn)
        btn_layout.addWidget(self.add_folder_btn)
        btn_layout.addWidget(self.remove_btn)
        btn_layout.addWidget(self.clear_btn)
        
        file_layout.addLayout(btn_layout)
        file_layout.addWidget(self.file_list)
        file_group.setLayout(file_layout)
        
        # 时间设置部分
        time_group = QGroupBox("⏰ 时间设置")
        time_layout = QVBoxLayout()
        
        # 时间操作类型
        self.time_op_group = QButtonGroup()
        self.set_time_rb = QRadioButton("设置为指定时间")
        self.adjust_time_rb = QRadioButton("调整时间 (加减)")
        self.timezone_rb = QRadioButton("时区转换")
        
        self.set_time_rb.setChecked(True)
        self.time_op_group.addButton(self.set_time_rb)
        self.time_op_group.addButton(self.adjust_time_rb)
        self.time_op_group.addButton(self.timezone_rb)
        
        # 时间选择器
        self.datetime_edit = QDateTimeEdit()
        self.datetime_edit.setDateTime(QDateTime.currentDateTime())
        self.datetime_edit.setDisplayFormat("yyyy-MM-dd HH:mm:ss")
        self.datetime_edit.setCalendarPopup(True)
        
        # 时间调整控件
        adjust_layout = QHBoxLayout()
        self.hours_spin = QSpinBox()
        self.hours_spin.setRange(-24, 24)
        self.minutes_spin = QSpinBox()
        self.minutes_spin.setRange(-60, 60)
        self.seconds_spin = QSpinBox()
        self.seconds_spin.setRange(-60, 60)
        
        adjust_layout.addWidget(QLabel("小时:"))
        adjust_layout.addWidget(self.hours_spin)
        adjust_layout.addWidget(QLabel("分钟:"))
        adjust_layout.addWidget(self.minutes_spin)
        adjust_layout.addWidget(QLabel("秒:"))
        adjust_layout.addWidget(self.seconds_spin)
        
        # 时区选择
        self.timezone_combo = QComboBox()
        self.timezone_combo.addItems(pytz.all_timezones)
        current_tz = datetime.now().astimezone().tzinfo
        self.timezone_combo.setCurrentText(str(current_tz))
        
        # 堆叠控件
        self.time_stack = QWidget()
        stack_layout = QVBoxLayout()
        stack_layout.addWidget(self.datetime_edit)
        
        adjust_widget = QWidget()
        adjust_widget.setLayout(adjust_layout)
        stack_layout.addWidget(adjust_widget)
        
        stack_layout.addWidget(self.timezone_combo)
        self.time_stack.setLayout(stack_layout)
        
        # 默认显示第一个控件
        self.datetime_edit.show()
        adjust_widget.hide()
        self.timezone_combo.hide()
        
        # 连接信号
        self.set_time_rb.toggled.connect(lambda: self.switch_time_mode(0))
        self.adjust_time_rb.toggled.connect(lambda: self.switch_time_mode(1))
        self.timezone_rb.toggled.connect(lambda: self.switch_time_mode(2))
        
        # 时间类型选择布局
        op_layout = QHBoxLayout()
        op_layout.addWidget(self.set_time_rb)
        op_layout.addWidget(self.adjust_time_rb)
        op_layout.addWidget(self.timezone_rb)
        
        time_layout.addLayout(op_layout)
        time_layout.addWidget(self.time_stack)
        
        # 时间属性选择
        attr_layout = QHBoxLayout()
        self.modified_cb = QCheckBox("修改时间")
        self.modified_cb.setChecked(True)
        self.access_cb = QCheckBox("访问时间")
        self.created_cb = QCheckBox("创建时间")
        
        attr_layout.addWidget(self.modified_cb)
        attr_layout.addWidget(self.access_cb)
        attr_layout.addWidget(self.created_cb)
        
        time_layout.addLayout(attr_layout)
        time_group.setLayout(time_layout)
        
        # 应用按钮
        self.apply_btn = QPushButton("✅ 应用更改")
        self.apply_btn.clicked.connect(self.apply_changes)
        self.apply_btn.setStyleSheet("background-color: #2a82da; color: white; font-weight: bold;")
        
        # 添加到基本标签页
        basic_layout.addWidget(file_group)
        basic_layout.addWidget(time_group)
        basic_layout.addWidget(self.apply_btn)
        basic_tab.setLayout(basic_layout)
        
        # 第二个标签页 - 批量操作
        batch_tab = QWidget()
        batch_layout = QVBoxLayout()
        
        batch_group = QGroupBox("🔢 批量操作")
        batch_inner_layout = QVBoxLayout()
        
        # 批量递增时间
        increment_group = QGroupBox("⏱️ 批量递增时间")
        increment_layout = QVBoxLayout()
        
        self.increment_start = QDateTimeEdit()
        self.increment_start.setDateTime(QDateTime.currentDateTime())
        self.increment_start.setDisplayFormat("yyyy-MM-dd HH:mm:ss")
        
        self.increment_interval = QSpinBox()
        self.increment_interval.setRange(1, 3600)
        self.increment_interval.setValue(60)
        self.increment_interval.setSuffix(" 秒")
        
        increment_layout.addWidget(QLabel("起始时间:"))
        increment_layout.addWidget(self.increment_start)
        increment_layout.addWidget(QLabel("时间间隔:"))
        increment_layout.addWidget(self.increment_interval)
        
        self.increment_btn = QPushButton("🔄 应用递增时间")
        self.increment_btn.clicked.connect(self.apply_incremental)
        self.increment_btn.setStyleSheet("background-color: #2a82da; color: white;")
        
        increment_layout.addWidget(self.increment_btn)
        increment_group.setLayout(increment_layout)
        
        batch_inner_layout.addWidget(increment_group)
        batch_group.setLayout(batch_inner_layout)
        
        batch_layout.addWidget(batch_group)
        batch_tab.setLayout(batch_layout)
        
        # 添加标签页
        tab_widget.addTab(basic_tab, "🛠️ 基本设置")
        tab_widget.addTab(batch_tab, "🔢 批量操作")
        
        main_layout.addWidget(tab_widget)
        main_widget.setLayout(main_layout)
        self.setCentralWidget(main_widget)
        
        # 状态栏
        self.status_bar = self.statusBar()
        self.status_bar.showMessage("就绪 🚀")
        
        # 连接信号
        self.file_list.itemSelectionChanged.connect(self.update_status)
        
        # 定时更新当前时间显示
        self.time_updater = QTimer(self)
        self.time_updater.timeout.connect(self.update_current_time)
        self.time_updater.start(1000)
    
    def switch_time_mode(self, index):
        """切换时间操作模式"""
        for i in range(self.time_stack.layout().count()):
            widget = self.time_stack.layout().itemAt(i).widget()
            widget.setVisible(i == index)
    
    def update_current_time(self):
        """更新状态栏中的当前时间"""
        current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        self.status_bar.showMessage(f"就绪 🚀 | 当前时间: {current_time}", 1000)
    
    def update_status(self):
        """更新状态栏显示选中文件数量"""
        selected = len(self.file_list.selectedItems())
        total = self.file_list.count()
        self.status_bar.showMessage(f"已选 {selected} 个文件 | 总计 {total} 个文件", 3000)
    
    def add_files(self):
        """添加文件到列表"""
        files, _ = QFileDialog.getOpenFileNames(self, "选择文件", "", "所有文件 (*.*)")
        if files:
            for file in files:
                if file not in self.files:
                    self.files.append(file)
                    self.file_list.addItem(file)
            self.update_status()
    
    def add_folder(self):
        """添加文件夹中的所有文件到列表"""
        folder = QFileDialog.getExistingDirectory(self, "选择文件夹")
        if folder:
            for root, _, files in os.walk(folder):
                for file in files:
                    file_path = os.path.join(root, file)
                    if file_path not in self.files:
                        self.files.append(file_path)
                        self.file_list.addItem(file_path)
            self.update_status()
    
    def remove_files(self):
        """从列表中移除选中的文件"""
        for item in self.file_list.selectedItems():
            self.files.remove(item.text())
            self.file_list.takeItem(self.file_list.row(item))
        self.update_status()
    
    def clear_files(self):
        """清空文件列表"""
        self.files.clear()
        self.file_list.clear()
        self.update_status()
    
    def apply_changes(self):
        """应用时间戳更改"""
        if not self.files:
            QMessageBox.warning(self, "警告", "请先添加文件! ⚠️")
            return
        
        if not (self.modified_cb.isChecked() or self.access_cb.isChecked() or self.created_cb.isChecked()):
            QMessageBox.warning(self, "警告", "请至少选择一个时间属性! ⚠️")
            return
        
        try:
            if self.set_time_rb.isChecked():
                # 设置为指定时间
                new_time = self.datetime_edit.dateTime().toPyDateTime()
                self.set_custom_time(new_time)
            
            elif self.adjust_time_rb.isChecked():
                # 调整时间
                delta = timedelta(
                    hours=self.hours_spin.value(),
                    minutes=self.minutes_spin.value(),
                    seconds=self.seconds_spin.value()
                )
                self.adjust_time(delta)
            
            elif self.timezone_rb.isChecked():
                # 时区转换
                tz = pytz.timezone(self.timezone_combo.currentText())
                self.convert_timezone(tz)
            
            QMessageBox.information(self, "成功", "时间戳修改成功! ✅")
        
        except Exception as e:
            QMessageBox.critical(self, "错误", f"修改时间戳时出错: {str(e)} ❌")
    
    def apply_incremental(self):
        """应用递增时间"""
        if not self.files:
            QMessageBox.warning(self, "警告", "请先添加文件! ⚠️")
            return
        
        if not (self.modified_cb.isChecked() or self.access_cb.isChecked() or self.created_cb.isChecked()):
            QMessageBox.warning(self, "警告", "请至少选择一个时间属性! ⚠️")
            return
        
        try:
            current_time = self.increment_start.dateTime().toPyDateTime()
            interval = self.increment_interval.value()
            
            for file in self.files:
                if os.path.exists(file):
                    self.set_file_time(file, current_time)
                    current_time += timedelta(seconds=interval)
            
            QMessageBox.information(self, "成功", "批量递增时间应用成功! ✅")
        
        except Exception as e:
            QMessageBox.critical(self, "错误", f"应用递增时间时出错: {str(e)} ❌")
    
    def set_custom_time(self, new_time):
        """设置自定义时间"""
        for file in self.files:
            if os.path.exists(file):
                self.set_file_time(file, new_time)
    
    def adjust_time(self, delta):
        """调整时间"""
        for file in self.files:
            if os.path.exists(file):
                # 获取当前时间戳
                stat = os.stat(file)
                atime = datetime.fromtimestamp(stat.st_atime)
                mtime = datetime.fromtimestamp(stat.st_mtime)
                
                # 如果是Windows系统,可以获取创建时间
                if self.system == 'Windows':
                    ctime = datetime.fromtimestamp(stat.st_ctime)
                else:
                    ctime = datetime.fromtimestamp(stat.st_mtime)
                
                # 应用调整
                new_atime = atime + delta if self.access_cb.isChecked() else atime
                new_mtime = mtime + delta if self.modified_cb.isChecked() else mtime
                new_ctime = ctime + delta if self.created_cb.isChecked() else ctime
                
                # 设置新时间
                self.set_file_time(file, new_atime, new_mtime, new_ctime)
    
    def convert_timezone(self, target_tz):
        """转换时区"""
        for file in self.files:
            if os.path.exists(file):
                # 获取当前时间戳
                stat = os.stat(file)
                atime = datetime.fromtimestamp(stat.st_atime)
                mtime = datetime.fromtimestamp(stat.st_mtime)
                
                # 如果是Windows系统,可以获取创建时间
                if self.system == 'Windows':
                    ctime = datetime.fromtimestamp(stat.st_ctime)
                else:
                    ctime = datetime.fromtimestamp(stat.st_mtime)
                
                # 假设原始时间是本地时区,转换为目标时区
                local_tz = pytz.timezone('UTC')
                if hasattr(datetime, 'astimezone'):
                    # Python 3.3+
                    atime = atime.astimezone(target_tz)
                    mtime = mtime.astimezone(target_tz)
                    ctime = ctime.astimezone(target_tz)
                else:
                    # 旧版Python
                    atime = local_tz.localize(atime).astimezone(target_tz)
                    mtime = local_tz.localize(mtime).astimezone(target_tz)
                    ctime = local_tz.localize(ctime).astimezone(target_tz)
                
                # 转换为naive datetime
                atime = atime.replace(tzinfo=None)
                mtime = mtime.replace(tzinfo=None)
                ctime = ctime.replace(tzinfo=None)
                
                # 设置新时间
                new_atime = atime if self.access_cb.isChecked() else datetime.fromtimestamp(stat.st_atime)
                new_mtime = mtime if self.modified_cb.isChecked() else datetime.fromtimestamp(stat.st_mtime)
                new_ctime = ctime if self.created_cb.isChecked() else datetime.fromtimestamp(stat.st_ctime if self.system == 'Windows' else stat.st_mtime)
                
                self.set_file_time(file, new_atime, new_mtime, new_ctime)
    
    def set_file_time(self, file_path, atime=None, mtime=None, ctime=None):
        """设置文件时间戳"""
        if atime is None and mtime is None and ctime is None:
            return
        
        # 如果只提供了一个时间,则所有选中的时间属性都使用该时间
        if mtime is None and ctime is None:
            mtime = atime if self.modified_cb.isChecked() else None
            atime = atime if self.access_cb.isChecked() else None
            ctime = atime if self.created_cb.isChecked() else None
        
        # 转换为时间戳
        atime_ts = atime.timestamp() if atime is not None else None
        mtime_ts = mtime.timestamp() if mtime is not None else None
        
        # 设置访问和修改时间
        if atime_ts is not None or mtime_ts is not None:
            os.utime(file_path, (atime_ts or os.stat(file_path).st_atime, 
                                mtime_ts or os.stat(file_path).st_mtime))
        
        # 设置创建时间 (Windows only)
        if self.system == 'Windows' and ctime is not None and self.created_cb.isChecked():
            try:
                import win32file
                import win32con
                import pywintypes
                
                # 转换为Windows文件时间格式
                wintime = pywintypes.Time(ctime)
                
                # 打开文件句柄
                handle = win32file.CreateFile(
                    file_path, win32con.GENERIC_WRITE,
                    win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE | win32con.FILE_SHARE_DELETE,
                    None, win32con.OPEN_EXISTING,
                    win32con.FILE_ATTRIBUTE_NORMAL, None)
                
                # 设置创建时间
                win32file.SetFileTime(handle, wintime, None, None)
                
                # 关闭句柄
                handle.Close()
            except ImportError:
                QMessageBox.warning(self, "警告", "需要pywin32库来修改创建时间 (仅Windows)")
            except Exception as e:
                QMessageBox.warning(self, "警告", f"修改创建时间时出错: {str(e)}")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setStyle('Fusion')
    
    # 设置应用信息
    app.setApplicationName("文件时间戳编辑器")
    app.setApplicationDisplayName("文件时间戳编辑器")
    
    window = TimeStampEditor()
    window.show()
    sys.exit(app.exec_())

总结与扩展

开发经验总结

跨平台兼容性处理是核心难点

PyQt5的信号槽机制大幅简化了UI交互开发

合理的状态管理对用户体验至关重要

扩展方向

以上就是Python+PyQt5开发超全能的文件时间戳修改器的详细内容,更多关于Python文件时间戳修改的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文