Python+PyQt5开发超全能的文件时间戳修改器
目录
- 概述
- 功能特色
- 1. 多模式时间编辑
- 2. 批量操作能力
- 3. 跨平台兼容性
- 4. 专业级UI设计
- 效果展示
- 开发步骤详解
- 1. 环境准备
- 2. 项目结构
- 3. 核心开发流程
- 代码深度解析
- 1. 时间处理核心逻辑
- 2. 时区转换算法
- 3. 批量递增时间实现
- 源码下载
- 总结与扩展
- 开发经验总结
- 扩展方向
概述
在日常开发中,我们经常需要批量修改文件的时间戳属性(创建时间、修改时间、访问时间)。无论是整理照片、管理文档,还是调试应用程序,时间戳操作都是一个常见需求。本文将详细介绍如何使用python的PyQt5库开发一个功能全面的图形化文件时间戳编辑器。
这个工具具有以下核心能力:
- 支持单个/批量文件操作
- 三种时间修改模式:绝对时间设置、相对时间调整、时区转换
- 跨平台支持(Windows/MACOS/linux)
- 现代化的暗色主题界面
- 批量递增时间功能
功能特色
1. 多模式时间编辑
- 绝对时间设置:精确设置到秒级的时间
- 相对时间调整:支持按小时、分钟、秒增减
- 时区转换:全球所有时区支持
2. 批量操作能力
- 文件列表管理(添加/删除/清空)
- 批量递增时间(按指定间隔自动递增)
3. 跨平台兼容性
- 自动识别操作系统(Windows/macOS/Linux)
- 针对不同系统适配时间属性(Windows支持创建时间修改)
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) android
代码深度解析
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('Fusionpython')) 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.aphpddWidget(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() www.devze.com 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文件时间戳修改的资料请关注编程客栈(www.devze.com)其它相关文章!
精彩评论