Python 編程實戰:打造高效便捷的目錄結構生成器
相關資源文件已經打包成EXE文件,可雙擊直接運行程序,且文章末尾已附上相關源碼,以供大家學習交流,博主主頁還有更多Python相關程序案例,秉著開源精神的想法,望大家喜歡,點個關注不迷路!!!
1.概述
隨著文件和目錄管理的復雜性不斷增加,尤其是在處理大量項目文件和文件夾時,手動管理和查看目錄結構變得越來越繁瑣。在這個背景下,我們開發了一款 目錄結構生成器,它不僅能夠幫助用戶高效地生成文件夾目錄結構,還具備了很多智能化的功能,如文件大小格式化、Excel 導出、拖拽操作以及動態進度條等,極大提升了文件管理的效率。
本文將深入介紹該工具的功能,幫助大家更好地理解并利用它來優化自己的工作流程。
2.功能亮點
1. 目錄結構生成
該工具能夠遍歷指定的文件夾,自動生成該文件夾下所有子文件夾和文件的目錄結構。對于每個文件和文件夾,系統會自動獲取其名稱、大小以及其他必要的屬性,并以表格的形式進行展示。
2. 文件大小格式化
通過對文件大小的自動轉換,工具能夠將字節數(Bytes)轉換為更直觀的單位,如 KB、MB、GB 等,極大提升了可讀性。
3. 支持 Excel 導出
生成的目錄結構可以輕松導出為 Excel 文件,支持動態設置 Excel 文件中工作表的名稱,并且可以一鍵點擊打開文件所在目錄,便于快速訪問。
4. 動態進度條與反饋提示
在生成目錄結構的過程中,工具會提供進度條,顯示當前操作的完成情況。用戶能夠清晰地看到操作進度,而在操作完成時,工具還會彈出“導出成功”提示,提升了用戶體驗。
5. 拖拽操作與美化效果
新增的拖拽區域高亮效果,讓用戶在拖拽目錄時能夠更加直觀地看到拖拽的目標區域,從而提升操作的準確性和流暢性。
6. 文件圖標預覽
工具還增加了對 Windows 系統的支持,能夠展示文件的圖標預覽。通過 PyQt6 中的 QIconProvider
,讓目錄中的每個文件都能展現出圖標樣式,進一步優化了可視化效果。
3. 功能使用
1. 界面設計與操作
該工具的核心界面設計非常簡潔,所有操作都集成在一個窗口中,用戶可以非常輕松地完成以下任務:
- 選擇目標文件夾:點擊“選擇文件夾”按鈕,選擇需要生成目錄結構的文件夾路徑。
- 拖拽操作:在界面的拖拽區域內,用戶可以通過拖拽文件夾來快速選擇文件夾并自動加載目錄結構。
- 查看目錄結構:生成的目錄結構會以樹形結構展示,左側為文件夾樹狀圖,右側則展示文件的詳細信息(如文件大小、文件類型等)。
- 文件大小顯示:文件大小將自動格式化,用戶能夠清晰地看到文件的大小(如:1.5MB、2GB等)。
- 導出 Excel 文件:點擊“導出 Excel”按鈕,工具會將當前目錄結構生成 Excel 文件。文件生成后,用戶可以選擇打開文件所在的文件夾。
- 進度反饋:每當操作開始時,工具會顯示進度條,完成時會彈出“導出成功”的提示。
2. 高級功能操作
拖拽目錄與文件結構生成
為了讓用戶操作更加便捷,我們特別加入了拖拽操作功能。用戶只需要將文件夾拖拽到工具界面的指定區域,工具就會自動加載該文件夾內的目錄結構,并以樹形方式展示出來。這種方式對于需要快速查看文件夾內容的用戶來說,無疑是極大的提升。
導出 Excel 文件
生成目錄結構后,用戶可以選擇將當前目錄結構導出為 Excel 文件。導出的文件將包含所有文件夾和文件的相關信息,如文件名、文件路徑、文件大小等,并且工具會自動將工作表命名為“目錄結構”,方便用戶區分。
文件圖標預覽
對于 Windows 系統用戶,工具可以根據每個文件的類型,顯示文件的圖標。比如,文本文件會顯示一個 TXT 圖標,圖片文件會顯示圖片縮略圖。通過 QIconProvider
,文件圖標得以自動提取,并顯示在界面上,幫助用戶快速識別文件類型。
動態進度條與操作提示
在整個操作過程中,用戶可以實時查看進度條,工具會根據文件夾大小和文件數量動態更新進度。當生成目錄結構或導出 Excel 完成時,用戶會收到一個“導出成功”的提示框,極大地增強了用戶體驗。
3.運行效果:
4. 總結
1. 整體優化
目錄結構生成器 v3.0 在原有版本的基礎上,進行了多項優化和功能擴展。尤其是在界面美化和操作便捷性方面,加入了拖拽操作、文件圖標預覽等細節,使得工具的使用體驗更加流暢和直觀。
2. 性能提升
除了界面和功能的提升,工具在處理大量文件和目錄時的性能也得到了優化。通過合理的進度反饋和動態顏色變化的進度條,用戶能夠清晰地感受到操作的進展,不會在等待過程中產生焦慮。
3. 實際應用
該工具非常適合需要管理大量文件或處理復雜目錄結構的用戶,尤其對于開發人員、IT運維人員、文檔管理員等群體而言,它可以顯著提高工作效率。通過自動生成目錄結構和文件大小顯示,再結合 Excel 導出功能,用戶能夠輕松導出目錄數據并進行后續處理或備份。
4. 相關源碼:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-import sys
import os
import pandas as pd
import time
import logging
from datetime import datetime
from PyQt6.QtWidgets import (QApplication, QWidget, QPushButton, QVBoxLayout, QLabel, QFileDialog, QLineEdit, QTextEdit, QProgressBar, QMessageBox,QCheckBox, QToolTip, QMenu, QHBoxLayout
)
from PyQt6.QtCore import Qt, QThread, pyqtSignal, QPropertyAnimation, QEasingCurve
from PyQt6.QtGui import QIcon, QPixmaplogging.basicConfig(filename='scanner.log', level=logging.INFO)class PathTextEdit(QTextEdit):"""支持拖放文件夾并顯示純路徑的自定義文本框"""def __init__(self, parent=None):super().__init__(parent)self.setAcceptDrops(True)self.setPlaceholderText("拖拽文件夾到此處或點擊瀏覽按鈕")self.setMinimumHeight(80)def dragEnterEvent(self, event):if any(url.isLocalFile() and os.path.isdir(url.toLocalFile())for url in event.mimeData().urls()):self.setStyleSheet("border: 2px dashed #0078D7; background-color: #F0F7FF;")event.acceptProposedAction()else:event.ignore()def dragLeaveEvent(self, event):self.setStyleSheet("border: 2px dashed #9E9E9E; background-color: #FAFAFA;")event.accept()def dropEvent(self, event):self.setStyleSheet("border: 1px solid #CCCCCC; background-color: white;")valid_paths = []for url in event.mimeData().urls():if url.isLocalFile():path = url.toLocalFile()if os.path.isdir(path):valid_paths.append(path)if valid_paths:self.setText("\n".join(valid_paths))event.accept()else:QToolTip.showText(event.pos(), "請拖入有效的文件夾", self)event.ignore()def contextMenuEvent(self, event):menu = QMenu(self)clear_action = menu.addAction(QIcon("icons/clear.png"), "清空") if os.path.exists("icons/clear.png") else menu.addAction("清空")paste_action = menu.addAction(QIcon("icons/paste.png"), "粘貼路徑") if os.path.exists("icons/paste.png") else menu.addAction("粘貼路徑")action = menu.exec(event.globalPos())if action == clear_action:self.clear()elif action == paste_action:self.paste()class DirectoryScanner(QThread):progress = pyqtSignal(int, int, float)completed = pyqtSignal(list)error = pyqtSignal(str)def __init__(self, directory, file_types):super().__init__()self.directory = directoryself.file_types = file_typesself._is_running = Truedef stop(self):self._is_running = Falsedef run(self):try:file_data = []total_files = sum(len(files) for _, _, files in os.walk(self.directory))processed_files = 0start_time = time.time()for root, _, files in os.walk(self.directory):if not self._is_running:breakfor file in files:if self.file_types and not any(file.lower().endswith(ft.lower()) for ft in self.file_types if ft):continuetry:file_path = os.path.join(root, file)file_stat = os.stat(file_path)file_data.append([root, file, os.path.splitext(file)[1], os.path.getsize(file_path),time.ctime(file_stat.st_mtime), time.ctime(file_stat.st_ctime), file_path])except Exception as e:logging.error(f"Error processing {file_path}: {str(e)}")continueprocessed_files += 1elapsed_time = time.time() - start_timeestimated_total_time = (elapsed_time / processed_files) * total_files if processed_files else 0remaining_time = max(0, estimated_total_time - elapsed_time)self.progress.emit(processed_files, total_files, remaining_time)if self._is_running:self.completed.emit(file_data)except Exception as e:self.error.emit(f"掃描過程中出錯: {str(e)}")class DirectoryScannerApp(QWidget):def __init__(self):super().__init__()self.initUI()self.setup_animations()def initUI(self):self.setWindowTitle("目錄結構生成器")self.setGeometry(100, 100, 650, 550)# 主布局self.main_layout = QVBoxLayout()self.main_layout.setSpacing(15)self.main_layout.setContentsMargins(20, 20, 20, 20)# 標題區域title_layout = QHBoxLayout()self.title_icon = QLabel()if os.path.exists("icons/folder.png"):self.title_icon.setPixmap(QPixmap("icons/folder.png").scaled(32, 32))title_layout.addWidget(self.title_icon)self.title_label = QLabel("目錄結構生成器")self.title_label.setStyleSheet("font-size: 18pt; font-weight: bold; color: #0078D7;")title_layout.addWidget(self.title_label)title_layout.addStretch()self.main_layout.addLayout(title_layout)# 目錄選擇部分self.dir_group = QWidget()dir_layout = QVBoxLayout()self.dir_label = QLabel("選擇掃描目錄:")dir_layout.addWidget(self.dir_label)self.directory_input = PathTextEdit()dir_layout.addWidget(self.directory_input)self.browse_button = QPushButton(" 瀏覽目錄")if os.path.exists("icons/folder_open.png"):self.browse_button.setIcon(QIcon("icons/folder_open.png"))self.browse_button.clicked.connect(self.browse_directory)dir_layout.addWidget(self.browse_button)self.dir_group.setLayout(dir_layout)self.main_layout.addWidget(self.dir_group)# 過濾選項部分self.filter_group = QWidget()filter_layout = QVBoxLayout()self.filter_label = QLabel("文件類型過濾:")filter_layout.addWidget(self.filter_label)self.filter_input = QLineEdit()self.filter_input.setPlaceholderText("例如: .txt,.docx,.xlsx")filter_layout.addWidget(self.filter_input)self.filter_group.setLayout(filter_layout)self.main_layout.addWidget(self.filter_group)# 選項設置self.options_group = QWidget()options_layout = QVBoxLayout()self.checkbox_size = QCheckBox("顯示友好文件大小(KB/MB)")self.checkbox_open = QCheckBox("導出后自動打開文件")options_layout.addWidget(self.checkbox_size)options_layout.addWidget(self.checkbox_open)self.options_group.setLayout(options_layout)self.main_layout.addWidget(self.options_group)# 操作按鈕self.generate_button = QPushButton(" 生成Excel文件")if os.path.exists("icons/excel.png"):self.generate_button.setIcon(QIcon("icons/excel.png"))self.generate_button.clicked.connect(self.generate_excel)self.main_layout.addWidget(self.generate_button)# 進度顯示self.progress_bar = QProgressBar()self.progress_bar.setTextVisible(True)self.main_layout.addWidget(self.progress_bar)# 狀態欄self.status_bar = QHBoxLayout()self.status_icon = QLabel()if os.path.exists("icons/info.png"):self.status_icon.setPixmap(QPixmap("icons/info.png").scaled(16, 16))self.status_bar.addWidget(self.status_icon)self.status_label = QLabel("就緒")self.status_bar.addWidget(self.status_label)self.status_bar.addStretch()self.version_label = QLabel(f"探客白澤 ? {datetime.now().year}")self.status_bar.addWidget(self.version_label)self.main_layout.addLayout(self.status_bar)self.setLayout(self.main_layout)self.apply_stylesheet()def setup_animations(self):self.animations = {}for btn in [self.browse_button, self.generate_button]:animation = QPropertyAnimation(btn, b"geometry")animation.setDuration(200)animation.setEasingCurve(QEasingCurve.Type.OutQuad)self.animations[btn] = animationdef make_animator(button):return lambda: self.animate_button(button)btn.clicked.connect(make_animator(btn))def animate_button(self, button):animation = self.animations[button]orig_rect = button.geometry()animation.setStartValue(orig_rect.adjusted(0, 5, 0, 5))animation.setEndValue(orig_rect)animation.start()def apply_stylesheet(self):self.setStyleSheet("""/* 主窗口樣式 */QWidget {font-family: 'Microsoft YaHei', 'Segoe UI';font-size: 10pt;background-color: #F5F5F5;}/* 分組框樣式 */QWidget#dir_group, QWidget#filter_group, QWidget#options_group {background-color: white;border-radius: 6px;padding: 12px;}/* 標簽樣式 */QLabel {color: #333333;font-weight: 500;}/* 輸入框樣式 */QTextEdit, QLineEdit {background-color: white;border: 1px solid #CCCCCC;border-radius: 4px;padding: 8px;selection-background-color: #0078D7;}QTextEdit:hover, QLineEdit:hover {border: 1px solid #0078D7;}/* 按鈕基礎樣式 */QPushButton {color: white;border: none;border-radius: 6px;padding: 10px 18px;min-width: 100px;font-weight: 500;}/* 瀏覽按鈕樣式 */QPushButton#browse_button {background-color: #4CAF50;}QPushButton#browse_button:hover {background-color: #3E8E41;}QPushButton#browse_button:pressed {background-color: #2E7D32;}/* 生成按鈕樣式 */QPushButton#generate_button {background-color: #FF5722;font-weight: bold;}QPushButton#generate_button:hover {background-color: #E64A19;}QPushButton#generate_button:pressed {background-color: #BF360C;}/* 進度條樣式 */QProgressBar {border: 1px solid #CCCCCC;border-radius: 4px;text-align: center;height: 24px;}QProgressBar::chunk {background-color: #4CAF50;border-radius: 3px;}/* 復選框樣式 */QCheckBox {spacing: 8px;color: #333333;}QCheckBox::indicator {width: 18px;height: 18px;}QCheckBox::indicator:unchecked {border: 1px solid #CCCCCC;background: white;border-radius: 3px;}QCheckBox::indicator:checked {border: 1px solid #0078D7;background: #0078D7;border-radius: 3px;}/* 狀態欄樣式 */QLabel#version_label {color: #666666;font-size: 9pt;}""")# 設置對象名用于樣式選擇self.browse_button.setObjectName("browse_button")self.generate_button.setObjectName("generate_button")self.version_label.setObjectName("version_label")self.dir_group.setObjectName("dir_group")self.filter_group.setObjectName("filter_group")self.options_group.setObjectName("options_group")def browse_directory(self):directory = QFileDialog.getExistingDirectory(self, "選擇目錄")if directory:self.directory_input.setText(directory)self.status_label.setText(f"已選擇目錄: {directory[:50]}..." if len(directory) > 50 else directory)def generate_excel(self):directory = self.directory_input.toPlainText().strip()file_types = [ft.strip() for ft in self.filter_input.text().split(',') if ft.strip()]if not directory or not os.path.isdir(directory):QMessageBox.warning(self, "錯誤", "請選擇有效的目錄!")returndefault_name = f"目錄結構_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"save_path, _ = QFileDialog.getSaveFileName(self, "保存 Excel 文件", default_name, "Excel Files (*.xlsx)")if not save_path:returnself.scanner = DirectoryScanner(directory, file_types)self.scanner.progress.connect(self.update_progress)self.scanner.completed.connect(lambda data: self.save_to_excel(data, save_path))self.scanner.error.connect(self.show_error)self.generate_button.setEnabled(False)self.status_label.setText("正在掃描目錄...")self.status_icon.setPixmap(QPixmap("icons/loading.png").scaled(16, 16)) if os.path.exists("icons/loading.png") else Noneself.scanner.start()def update_progress(self, processed, total, remaining_time):self.progress_bar.setMaximum(total)self.progress_bar.setValue(processed)progress_percent = processed / total * 100status_text = (f"掃描進度: {progress_percent:.1f}% | "f"已處理: {processed}/{total} | "f"剩余時間: {remaining_time:.0f}秒")self.status_label.setText(status_text)def save_to_excel(self, file_data, save_path):try:df = pd.DataFrame(file_data, columns=["目錄路徑", "文件名", "類型", "大小(B)", "修改日期", "創建時間", "文件位置"])# 添加人性化大小顯示if self.checkbox_size.isChecked():df["大小"] = df["大小(B)"].apply(lambda x: f"{x/1024:.2f}KB" if x < 1024*1024 else f"{x/1024/1024:.2f}MB")# 添加超鏈接df["文件位置"] = df["文件位置"].apply(lambda x: f'=HYPERLINK("{x}", "📂 打開")')with pd.ExcelWriter(save_path, engine='xlsxwriter') as writer:df.to_excel(writer, index=False, sheet_name='目錄結構')workbook = writer.bookworksheet = writer.sheets['目錄結構']# 設置列寬col_widths = {"目錄路徑": 40, "文件名": 30, "類型": 10, "大小(B)": 15, "大小": 15, "修改日期": 20,"創建時間": 20, "文件位置": 15}for idx, col in enumerate(df.columns):worksheet.set_column(idx, idx, col_widths.get(col, 15))# 添加凍結窗格和自動篩選worksheet.autofilter(0, 0, 0, len(df.columns)-1)worksheet.freeze_panes(1, 0)self.status_label.setText(f"導出成功: {os.path.basename(save_path)}")self.status_icon.setPixmap(QPixmap("icons/success.png").scaled(16, 16)) if os.path.exists("icons/success.png") else Noneif self.checkbox_open.isChecked():try:if sys.platform == "win32":os.startfile(save_path)elif sys.platform == "darwin":os.system(f'open "{save_path}"')else:os.system(f'xdg-open "{save_path}"')except Exception as e:logging.error(f"無法打開文件: {str(e)}")except Exception as e:self.show_error(f"生成Excel時出錯:\n{str(e)}")logging.error(f"Excel導出錯誤: {str(e)}")finally:self.generate_button.setEnabled(True)self.progress_bar.reset()def show_error(self, message):QMessageBox.critical(self, "錯誤", message)self.status_label.setText("操作失敗")self.status_icon.setPixmap(QPixmap("icons/error.png").scaled(16, 16)) if os.path.exists("icons/error.png") else Noneself.generate_button.setEnabled(True)def closeEvent(self, event):if hasattr(self, 'scanner') and self.scanner.isRunning():self.scanner.stop()self.scanner.wait()event.accept()if __name__ == "__main__":app = QApplication(sys.argv)app.setStyle('Fusion')# 設置應用程序圖標if os.path.exists("icons/app_icon.png"):app.setWindowIcon(QIcon("icons/app_icon.png"))window = DirectoryScannerApp()window.show()sys.exit(app.exec())
結語
通過本篇文章,您已經對 目錄結構生成器 v3.0 的功能和使用方法有了全面了解。它不僅提升了目錄管理效率,還優化了用戶體驗,使得復雜的文件夾和文件操作變得輕松愉快。希望這款工具能為您的工作帶來便利,并期待您的寶貴反饋!