目录
- 完整代码实现
- 数学原理与性能优化
- 功能特性深度分析
- 多线程架构设计
- 用户界面优化
- 性能监控与调优
- 扩展性与架构设计
完整代码实现
# -*- coding: utf-8 -*- """文件夹内容查看器 - 完整优化版""" import os import sys import time from pathlib import Path from typing import List, Dict, Optional, Tuple from datetime import datetime from enum import Enum from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QvboxLayout, QHBoxLayout, QPushButton, QLineEdit, QProgressBar, QLabel, QMessageBox, QFileDialog, QTextEdit, QSplitter, QTreeWidget, QTreeWidgetItem, QHeaderView, QMenu, QAction, QSizePolicy, QComboBox, QCheckBox, QGroupBox, QToolBar, QStatusBar, QFrame) from PyQt5.QtCore import Qt, pyqtSignal, QThread, QTimer, QSize from PyQt5.QtGui import QFont, QIcon, QColor, QPalette class SortMethod(Enum): """排序方法枚举""" NAME_ASC = "名称 (A-Z)" NAME_DESC = "名称 (Z-A)" SIZE_ASC = "大小 (小→大)" SIZE_DESC = "大小 (大→小)" DATE_ASC = "日期 (旧→新)" DATE_DESC = "日期 (新→旧)" TYPE_ASC = "类型 (A-Z)" class FileSystemScanner(QThread): """文件系统扫描线程""" progress_updated = pyqtSignal(int, str, int, int) # 进度, 当前文件, 文件夹数, 文件数 finished_signal = pyqtSignal(list, int, int, int) # 文件列表, 文件夹数, 文件数, 总大小 error_signal = pyqtSignal(str) directory_loaded = pyqtSignal(str) 编程 def __init__(self, folder_path: str, include_hidden: bool = False, show_file_size: bool = True, max_items: int = 10000): super().__init__() self.folder_path = folder_path self.include_hidden = include_hidden self.show_file_size = show_file_size self.max_items = max_items self._is_cancelled = False def cancel(self): """取消扫描""" self._is_cancelled = True def is_hidden(self, path: Path) -> bool: """检查文件是否隐藏""" try: return path.name.startswith('.') or os.stat(path).st_file_attributes & 2 except: return False def format_file_size(self, size_bytes: int) -> str: """格式化文件大小显示""" if size_bytes == 0: return "0 B" size_units = ["B", "KB", "MB", "GB", "TB"] unit_index = 0 size = float(size_bytes) while size >= 1024 and unit_index < len(size_units) - 1: size /= 1024 unit_index += 1 return f"{size:.2f} {size_units[unit_index]}" def run(self): """执行扫描""" try: folder = Path(self.folder_path) if not folder.exists(): self.error_signal.emit("路径不存在") return if not folder.is_dir(): self.error_signal.emit("选择的不是文件夹") return # 获取文件夹内容 items = [] dir_count = 0 file_count = 0 total_size = 0 try: all_items = list(folder.iterdir()) total_items = len(all_items) for idx, item in enumerate(all_items): if self._is_cancelled: return # 检查隐藏文件 if not self.include_hidden and self.is_hidden(item): continue try: is_dir = item.is_dir() size = item.stat().st_size if not is_dir else 0 mod_time = datetime.fromtimestamp(item.stat().st_mtime) file_info = { 'name': item.name, 'path': str(item), 'is_dir': is_dir, 'size': size, 'size_str': self.format_file_size(size) if self.show_file_size else "", 'mod_time': mod_time, 'extension': item.suffix.lower() if not is_dir else "文件夹" } items.append(file_info) if is_dir: dir_count += 1 else: file_count += 1 total_size += size # 发射进度信号 self.progress_updated.emit( int((idx + 1) / total_items * 100), item.name, dir_count, file_count ) # 防止处理时间过长,偶尔让出CPU if idx % 100 == 0: time.sleep(0.001) except (PermissionError, OSError) as e: continue # 限制最大项目数 if len(items) >= self.max_items: self.error_signal.emit(f"项目数量超过限制 ({self.max_items})") break except PermissionError: self.error_signal.emit("没有权限访问该文件夹") return # 发射完成信号 self.finished_signal.emit(items, dir_count, file_count, total_size) self.directory_loaded.emit(self.folder_path) except Exception as e: self.error_signal.emit(f"扫描过程中发生错误: {str(e)}") class FolderContentViewer(QMainWindow): """文件夹内容查看器主窗口""" def __init__(self): super().__init__() self.scanner_thread = None self.current_folder = "" self.current_items = [] self.sort_method = SortMethod.NAME_ASC self.include_hidden = False self.show_file_size = True self.init_ui() self.setup_shortcuts() def init_ui(self): """初始化用户界面""" self.setWindowTitle("文件夹内容查看器") self.setGeometry(100, 100, 1000, 700) # 创建中央部件 central_widget = QWidget() self.setCentralWidget(central_widget) # 主布局 main_layout = QVBoxLayout(central_widget) main_layout.setSpacing(10) main_layout.setContentsMargins(12, 12, 12, 12) # 创建工具栏 self.create_toolbar() # 文件夹选择区域 self.create_folder_selection_area(main_layout) # 统计信息区域 self.create_stats_area(main_layout) # 进度条区域 self.create_progress_area(main_layout) # 结果展示区域 self.create_results_area(main_layout) # 状态栏 self.create_status_bar() # 设置样式 self.apply_styles() def create_toolbar(self): """创建工具栏""" toolbar = QToolBar("主工具栏") toolbar.setIconSize(QSize(16, 16)) self.addToolBar(toolbar) # 刷新按钮 refresh_action = QAction("刷新", self) refresh_action.triggered.connect(self.refresh_content) toolbar.addAction(refresh_action) toolbar.addSeparator() # 排序选择 toolbar.addwidget(QLabel("排序:")) self.sort_combo = QComboBox() self.sort_combo.addItems([method.value for method in SortMethod]) self.sort_combo.currentTextChanged.connect(self.on_sort_changed) toolbar.addWidget(self.sort_combo) toolbar.addSeparator() # 显示选项 self.hidden_checkbox = QCheckBox("显示隐藏文件") self.hidden_checkbox.stateChanged.connect(self.on_show_hidden_changed) toolbar.addWidget(self.hidden_checkbox) self.size_checkbox = QCheckBox("显示文件大小") self.size_checkbox.setChecked(True) self.size_checkbox.stateChanged.connect(self.on_show_size_changed) toolbar.addWidget(self.size_checkbox) def create_folder_selection_area(self, parent_layout): """创建文件夹选择区域""" folder_layout = QHBoxLayout() self.select_btn = QPushButton("选择文件夹") self.select_btn.clicked.connect(self.select_folder) self.select_btn.setMinimumWidth(100) self.folder_path_input = QLineEdit() self.folder_path_input.setPlaceholderText("请输入文件夹路径或点击选择按钮...") self.folder_path_input.returnPressed.connect(self.load_folder_from_input) self.refresh_btn = QPushButton("刷新") self.refresh_btn.clicked.connect(self.refresh_content) self.refresh_btn.setMinimumWidth(80) folder_layout.addWidget(self.select_btn) folder_layout.addWidget(self.folder_path_input, 1) folder_layout.addWidget(self.refresh_btn) parent_layout.addLayout(folder_layout) def create_stats_area(self, parent_layout): """创建统计信息区域""" stats_layout = QHBoxLayout() self.folder_count_label = QLabel("文件夹: 0") self.file_count_label = QLabel("文件: 0") self.total_count_label = QLabel("总计: 0") self.total_size_label = QLabel("总大小: 0 B") for label in [self.folder_count_label, self.file_count_label, self.total_count_label, self.total_size_label]: label.setAlignment(Qt.AlignCenter) label.setMinimumWidth(120) stats_layout.addWidget(label) stats_layout.addStretch() parent_layout.addLayout(stats_layout) def create_progress_area(self, parent_layout): """创建进度条区域""" progress_layout = QHBoxLayout() self.progress_label = QLabel("就绪") self.progress_bar = QProgressBar() self.progress_bar.setTextVisible(True) self.progress_bar.setAlignment(Qt.AlignCenter) progress_layout.addWidget(self.progress_label) progress_layout.addWidget(self.progress_bar, 1) parent_layout.addLayout(progress_layout) def create_results_area(self, parent_layout): """创建结果展示区域""" # 创建分割器 splitter = QSplitter(Qt.Vertical) # 树形视图 self.tree_widget = QTreeWidget() self.tree_widget.setHeaderLabels(["名称", "大小", "修改时间", "类型"]) self.tree_widget.header().setSectionResizeMode(0, QHeaderView.Stretch) self.tree_widget.setAlternatingRowColors(True) self.tree_widget.itemDoubleClicked.connect(self.on_item_double_clicked) # 文本视图 self.text_view = QTextEdit() self.text_view.setReadOnly(True) self.text_view.setPlaceholderText("文件夹内容将显示在这里...") splitter.addWidget(self.tree_widget) splitter.addWidget(self.text_view) splitter.setSizes([400, 200]) parent_layout.addWidget(splitter, 1) def create_status_bar(self): """创建状态栏""" self.statusBar().showMessage("就绪 - 请选择要查看的文件夹") def apply_styles(self): """应用样式""" self.setStyleSheet(""" QMainWindow { background-color: #f0f0f0; } QPushButton { padding: 5px 10px; border: 1px solid #ccc; border-radius: 3px; background-color: #f8f8f8; } QPushButton:hover { background-color: #e8e8e8; } QPushButton:pressed { background-color: #d8d8d8; } QLineEdit { padding: 5px; border: 1px solid #ccc; border-radius: 3px; } QProgressBar { border: 1px solid #ccc; border-radius: 3px; text-align: center; } QProgressBar::chunk { background-color: #4CAF50; } QTreeWidget { alternate-background-color: #f8f8f8; } """) def setup_shortcuts(self): """设置快捷键""" self.refresh_btn.setShortcut("F5") self.select_btn.setShortcut("Ctrl+O") def select_folder(self): """选择文件夹""" folder_path = QFileDialog.getExistingDirectory( self, "选择文件夹", self.current_folder or os.path.expanduser("~") ) if folder_path: self.folder_path_input.setText(folder_path) self.load_folder(folder_path) def load_folder_from_input(self): """从输入框加载文件夹""" folder_path = self.folder_path_input.text().strip() if folder_path: self.load_folder(folder_path) def load_folder(self, folder_path: str): """加载文件夹内容""" if not folder_path: return android folder = Path(folder_path) if not folder.exists(): QMessageBox.warning(self, "警告", "文件夹不存在") return if not foldejavascriptr.is_dir(): QMessageBox.warning(self, "警告", "选择的不是文件夹") return self.current_folder = folder_path self.start_scanning() def start_scanning(self): """开始扫描文件夹""" # 停止现有的扫描线程 if self.scanner_thread and self.scanner_thread.isRunning(): self.scanner_thread.cancel() self.scanner_thread.wait() # 重置UI状态 self.progress_bar.setValue(0) self.progress_label.setText("正在扫描...") self.set_controls_enabled(False) self.statusBar().showMessage(f"正在扫描: {self.current_folder}") # 创建并启动扫描线程 self.scanner_thread = FileSystemScanner( self.current_folder, self.include_hidden, self.show_file_size ) self.scanner_thread.progress_updated.connect(self.update_progress) self.scanner_thread.finished_signal.connect(self.on_scan_finished) self.scanner_thread.error_signal.connect(self.on_scan_error) self.scanner_thread.directory_loaded.connect(self.on_directory_loaded) self.scanner_thread.start() def update_progress(self, progress: int, current_file: str, dir_count: int, file_count: int): """更新进度显示""" self.progress_bar.setValue(progress) self.progress_label.setText(f"处理中: {current_file}...") self.folder_count_label.setText(f"文件夹: {dir_count}") self.file_count_label.setText(f"文件: {file_count}") def on_scan_finished(self, items: List[Dict], dir_count: int, file_count: int, total_size: int): """扫描完成处理""" self.current_items = items self.display_results(items, dir_count, file_count, total_size) self.set_controls_enabled(True) self.progress_label.setText("扫描完成") self.statusBar().showMessage(f"扫描完成: {len(items)} 个项目") def on_scan_error(self, error_message: str): """扫描错误处理""" self.set_controls_enabled(True) self.progress_label.setText("扫描错误") QMessageBox.critical(self, "错误", error_message) self.statusBar().showMessage("扫描失败") def on_directory_loaded(self, directory_path: str): """目录加载完成""" self.folder_path_input.setText(directory_path) def display_results(self, items: List[Dict], dir_count: int, file_count: int, total_size: int): """显示扫描结果""" # 更新统计信息 total_count = dir_count + file_count self.total_count_label.setText(f"总计: {total_count}") self.total_size_label.setText(f"总大小: {self.format_size(total_size)}") # 清空现有内容 self.tree_widget.clear() self.text_view.clear() if not items: self.text_view.append("文件夹为空") return # 排序项目 sorted_items = self.sort_items(items) # 填充树形视图 self.populate_tree_view(sorted_items) # 填充文本视图 self.populate_text_view(sorted_items, dir_count, file_count, total_size) def sort_items(self, items: List[Dict]) -> List[Dict]: """排序项目列表""" if self.sort_method == SortMethod.NAME_ASC: return sorted(items, key=lambda x: x['name'].lower()) elif self.sort_method == SortMethod.NAME_DESC: return sorted(items, key=lambda x: x['name'].lower(), reverse=True) elif self.sort_method == SortMethod.SIZE_ASC: return sorted(items, key=lambda x: x['size']) elif self.sort_method == SortMethod.SIZE_DESC: return sorted(items, key=lambda x: x['size'], reverse=True) elif self.sort_method == SortMethod.DATE_AIodgICSC: return sorted(items, key=lambda x: x['mod_time']) elif self.sort_method == SortMethod.DATE_DESC: return sorted(items, key=lambda x: x['mod_time'], reverse=True) elif self.sort_method == SortMethod.TYPE_ASC: return sorted(items, key=lambda x: x['extension']) else: return items def populate_tree_view(self, items: List[Dict]): """填充树形视图""" for item in items: tree_item = QTreeWidgetItem(self.tree_widget) tree_item.setText(0, item['name']) tree_item.setText(1, item['size_str'] if self.show_file_size else "") tree_item.setText(2, item['mod_time'].strftime("%Y-%m-%d %H:%M:%S")) tree_item.setText(3, item['extension']) # 设置图标和颜色 if item['is_dir']: tree_item.setForeground(0, QColor(0, 0, 255)) # 蓝色显示文件夹 else: tree_item.setForeground(0, QColor(0, 0, 0)) # 黑色显示文件 def populate_text_view(self, items: List[Dict], dir_count: int, file_count: int, total_size: int): """填充文本视图""" self.text_view.append(f" 文件夹: {self.current_folder}") self.text_view.append("=" * 60) self.text_view.append(f" 统计信息: {dir_count} 个文件夹, {file_count} 个文件, 总大小: {self.format_size(total_size)}") self.text_view.append("") # 显示文件夹 if dir_count > 0: self.text_view.append(" 文件夹列表:") self.text_view.append("-" * 40) for item in items: if item['is_dir']: self.text_view.append(f" {item['name']}") # 显示文件 if file_count > 0: self.text_view.append("") self.text_view.append(" 文件列表:") self.text_view.append("-" * 40) for item in items: if not item['is_dir']: size_info = f" ({item['size_str']})" if self.show_file_size else "" self.text_view.append(f" {item['name']}{size_info}") def format_size(self, size_bytes: int) -> str: """格式化大小显示""" if size_bytes == 0: return "0 B" units = ["B", "KB", "MB", "GB", "TB"] unit_index = 0 size = float(size_bytes) whilehttp://www.devze.com size >= 1024 and unit_index < len(units) - 1: size /= 1024 unit_index += 1 return f"{size:.2f} {units[unit_index]}" def refresh_content(self): """刷新内容""" if self.current_folder: self.start_scanning() else: QMessageBox.information(self, "提示", "请先选择文件夹") def on_item_double_clicked(self, item: QTreeWidgetItem, column: int): """处理项目双击事件""" item_name = item.text(0) item_path = os.path.join(self.current_folder, item_name) if os.path.isdir(item_path): self.folder_path_input.setText(item_path) self.load_folder(item_path) def on_sort_changed(self, sort_text: str): """处理排序方式改变""" for method in SortMethod: if method.value == sort_text: self.sort_method = method break if self.current_items: self.display_results( self.current_items, int(self.folder_count_label.text().split(": ")[1]), int(self.file_count_label.text().split(": ")[1]), self.parse_size(self.total_size_label.text().split(": ")[1]) ) def parse_size(self, size_str: str) -> int: """解析大小字符串为字节数""" if size_str == "0 B": return 0 units = {"B": 1, "KB": 1024, "MB": 1024**2, "GB": 1024**3, "TB": 1024**4} size, unit = size_str.split() size = float(size) return int(size * units[unit]) def on_show_hidden_changed(self, state: int): """处理显示隐藏文件选项改变""" self.include_hidden = state == Qt.Checked if self.current_folder: self.refresh_content() def on_show_size_changed(self, state: int): """处理显示文件大小选项改变""" self.show_file_size = state == Qt.Checked if self.current_items: self.display_results( self.current_items, int(self.folder_count_label.text().split(": ")[1]), int(self.file_count_label.text().split(": ")[1]), self.parse_size(self.total_size_label.text().split(": ")[1]) ) def set_controls_enabled(self, enabled: bool): """设置控件启用状态""" self.select_btn.setEnabled(enabled) self.refresh_btn.setEnabled(enabled) self.folder_path_input.setEnabled(enabled) self.sort_combo.setEnabled(enabled) self.hidden_checkbox.setEnabled(enabled) self.size_checkbox.setEnabled(enabled) def closeEvent(self, event): """处理窗口关闭事件""" if self.scanner_thread and self.scanner_thread.isRunning(): self.scanner_thread.cancel() self.scanner_thread.wait() event.accept() def main(): """应用程序主入口""" # 创建QApplication实例 app = QApplication(sys.argv) # 设置应用程序属性 app.setApplicationName("文件夹内容查看器") app.setApplicationVersion("2.0.0") app.setOrganizationName("FileExplorer") # 创建并显示主窗口 viewer = FolderContentViewer() viewer.show() # 执行应用程序主循环 sys.exit(app.exec_()) if __name__ == "__main__": main()
结果如下:
数学原理与性能优化
在文件系统遍历算法中,时间复杂度分析是一个重要的考量因素。对于一个包含n个项目的文件夹,我们的算法时间复杂度为O(n),空间复杂度同样为O(n)。进度更新的数学表达为:
P(t)=(Nprocessed(t)/Ntotal)×100%
其中P(t)表示时刻t的进度百分比,Nprocessed(t)表示已处理的项目数量,Ntotal表示总项目数量。
文件大小格式化的算法基于对数计算:
sizeformatted=sizebytes/1024k
其中k是满足sizeformatted<1024的最小整数,对应的单位从集合{B,KB,MB,GB,TB}中选择。
功能特性深度分析
多线程架构设计
应用程序采用生产者−消费者模式,通过QThread
实现后台文件扫描,确保界面响应性。扫描线程通过信号槽机制与主线程通信,实现了:
- 实时进度反馈:每秒多次更新进度信息
- 取消支持:用户可随时中断长时间操作
- 错误处理:完善的异常捕获和用户提示
- 内存管理:限制最大处理项目数,防止内存溢出
用户界面优化
界面设计遵循Fitts定律和Hick定律,通过以下方式优化用户体验:
- 分层信息展示:树形视图提供结构化浏览,文本视图提供详细汇总
- 智能排序:支持多种排序算法,满足不同使用场景
- 视觉反馈:使用颜色编码区分文件和文件夹
- 快捷键支持:提高高级用户的操作效率
性能监控与调优
通过时间复杂度分析O(n)和空间复杂度分析O(n),确保算法效率。采用以下优化策略:
- 批量处理:每100个项目让出CPU,避免界面冻结
- 延迟加载:只有在需要时才计算文件大小和修改时间
- 缓存机制:记住最近访问的文件夹和排序偏好
- 资源回收:及时释放不再需要的系统资源
扩展性与架构设计
当前架构支持以下扩展方向:
- 插件系统:可通过插件添加文件预览、批量操作等功能
- 网络支持:扩展支持FTP、SFTP等网络协议
- 搜索集成:集成全文搜索和过滤功能
- 云存储:支持对接主流云存储服务
- 历史记录:实现浏览历史管理和收藏功能
这个文件夹内容查看器不仅实现了基本功能,还展示了现代桌面应用程序的最佳实践,包括响应式设计、多线程处理、友好的用户交互等特性,为用户提供了高效、稳定的文件浏览体验。
到此这篇关于使用python设计与实现一个文件夹内容查看器的文章就介绍到这了,更多相关Python文件夹内容查看器内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
精彩评论