Python实例题:基于 Python 的简单文件管理器

目录

Python实例题

题目

要求:

解题思路:

代码实现:

Python实例题

题目

基于 Python 的简单文件管理器

要求

  • 使用 Python 构建一个简单的文件管理器,支持以下功能:
    • 浏览文件和文件夹
    • 创建、删除和重命名文件 / 文件夹
    • 复制、移动和压缩文件 / 文件夹
    • 搜索文件和文件夹
    • 查看文件属性和预览文本文件
  • 使用 tkinter 构建图形用户界面。
  • 支持多平台(Windows、Linux、macOS)。

解题思路

  • 使用 tkinter 构建多窗口界面。
  • 通过 os 和 shutil 模块进行文件操作。
  • 实现基本的错误处理和进度提示。

代码实现

import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext
import os
import shutil
import time
import threading
import zipfile
from datetime import datetime

class FileManager:
    def __init__(self, root):
        self.root = root
        self.root.title("文件管理器")
        self.root.geometry("900x600")
        
        # 当前路径
        self.current_path = os.getcwd()
        
        # 创建主界面
        self.create_main_window()
    
    def create_main_window(self):
        """创建主窗口"""
        # 创建顶部导航栏
        nav_frame = ttk.Frame(self.root)
        nav_frame.pack(fill=tk.X, padx=10, pady=10)
        
        # 返回上级目录按钮
        ttk.Button(nav_frame, text="返回上级", command=self.go_up).pack(side=tk.LEFT, padx=5)
        
        # 当前路径显示
        self.path_var = tk.StringVar(value=self.current_path)
        path_entry = ttk.Entry(nav_frame, textvariable=self.path_var, width=100)
        path_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
        path_entry.bind("", lambda event: self.navigate_to_path())
        
        # 刷新按钮
        ttk.Button(nav_frame, text="刷新", command=self.refresh).pack(side=tk.LEFT, padx=5)
        
        # 创建工具栏
        toolbar = ttk.Frame(self.root)
        toolbar.pack(fill=tk.X, padx=10, pady=5)
        
        # 文件操作按钮
        ttk.Button(toolbar, text="新建文件夹", command=self.create_folder).pack(side=tk.LEFT, padx=5)
        ttk.Button(toolbar, text="新建文件", command=self.create_file).pack(side=tk.LEFT, padx=5)
        ttk.Button(toolbar, text="删除", command=self.delete_items).pack(side=tk.LEFT, padx=5)
        ttk.Button(toolbar, text="重命名", command=self.rename_item).pack(side=tk.LEFT, padx=5)
        ttk.Button(toolbar, text="复制", command=self.copy_items).pack(side=tk.LEFT, padx=5)
        ttk.Button(toolbar, text="移动", command=self.move_items).pack(side=tk.LEFT, padx=5)
        ttk.Button(toolbar, text="压缩", command=self.zip_items).pack(side=tk.LEFT, padx=5)
        ttk.Button(toolbar, text="解压", command=self.unzip_item).pack(side=tk.LEFT, padx=5)
        
        # 创建搜索框
        search_frame = ttk.Frame(self.root)
        search_frame.pack(fill=tk.X, padx=10, pady=5)
        
        ttk.Label(search_frame, text="搜索:").pack(side=tk.LEFT, padx=5)
        self.search_var = tk.StringVar()
        search_entry = ttk.Entry(search_frame, textvariable=self.search_var, width=50)
        search_entry.pack(side=tk.LEFT, padx=5)
        
        ttk.Button(search_frame, text="搜索", command=self.search_files).pack(side=tk.LEFT, padx=5)
        ttk.Button(search_frame, text="清除搜索", command=self.clear_search).pack(side=tk.LEFT, padx=5)
        
        # 创建主框架
        main_frame = ttk.Frame(self.root)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
        
        # 创建文件列表
        columns = ("name", "type", "size", "modified")
        self.file_tree = ttk.Treeview(main_frame, columns=columns, show="headings")
        self.file_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        # 设置列标题
        self.file_tree.heading("name", text="名称")
        self.file_tree.heading("type", text="类型")
        self.file_tree.heading("size", text="大小")
        self.file_tree.heading("modified", text="修改日期")
        
        # 设置列宽
        self.file_tree.column("name", width=300)
        self.file_tree.column("type", width=100)
        self.file_tree.column("size", width=100, anchor=tk.E)
        self.file_tree.column("modified", width=180)
        
        # 绑定双击事件
        self.file_tree.bind("", self.on_double_click)
        
        # 创建滚动条
        scrollbar = ttk.Scrollbar(main_frame, orient=tk.VERTICAL, command=self.file_tree.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.file_tree.configure(yscroll=scrollbar.set)
        
        # 创建状态栏
        self.status_var = tk.StringVar(value=f"就绪")
        ttk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W).pack(side=tk.BOTTOM, fill=tk.X)
        
        # 加载文件列表
        self.load_files()
    
    def load_files(self, path=None):
        """加载文件列表"""
        if path:
            self.current_path = path
            self.path_var.set(path)
        
        # 清空现有数据
        for item in self.file_tree.get_children():
            self.file_tree.delete(item)
        
        try:
            # 获取文件和文件夹列表
            items = os.listdir(self.current_path)
            
            # 先添加文件夹,再添加文件(按名称排序)
            folders = []
            files = []
            
            for item in items:
                item_path = os.path.join(self.current_path, item)
                if os.path.isdir(item_path):
                    folders.append(item)
                else:
                    files.append(item)
            
            # 排序
            folders.sort(key=str.lower)
            files.sort(key=str.lower)
            
            # 添加到列表
            for item in folders + files:
                item_path = os.path.join(self.current_path, item)
                try:
                    # 获取文件信息
                    stats = os.stat(item_path)
                    modified_time = datetime.fromtimestamp(stats.st_mtime).strftime('%Y-%m-%d %H:%M:%S')
                    
                    if os.path.isdir(item_path):
                        # 文件夹
                        item_type = "文件夹"
                        item_size = ""
                        self.file_tree.insert("", tk.END, values=(item, item_type, item_size, modified_time), tags=('folder',))
                    else:
                        # 文件
                        item_type = self.get_file_type(item)
                        item_size = self.format_size(stats.st_size)
                        self.file_tree.insert("", tk.END, values=(item, item_type, item_size, modified_time))
                except Exception as e:
                    # 无法访问的文件/文件夹
                    self.file_tree.insert("", tk.END, values=(item, "无法访问", "", ""), tags=('error',))
            
            # 设置样式
            self.file_tree.tag_configure('folder', foreground='blue')
            self.file_tree.tag_configure('error', foreground='red')
            
            self.status_var.set(f"已加载 {len(items)} 个项目")
            
        except Exception as e:
            messagebox.showerror("错误", f"无法加载目录: {str(e)}")
            self.status_var.set(f"加载失败: {str(e)}")
    
    def get_file_type(self, filename):
        """获取文件类型"""
        ext = os.path.splitext(filename)[1].lower()
        
        # 常见文件类型
        if ext == '.txt':
            return "文本文件"
        elif ext in ['.jpg', '.jpeg', '.png', '.gif', '.bmp']:
            return "图像文件"
        elif ext in ['.mp3', '.wav', '.ogg', '.flac']:
            return "音频文件"
        elif ext in ['.mp4', '.avi', '.mkv', '.mov']:
            return "视频文件"
        elif ext in ['.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx']:
            return "文档文件"
        elif ext in ['.py', '.java', '.c', '.cpp', '.html', '.css', '.js']:
            return "代码文件"
        elif ext == '.zip':
            return "压缩文件"
        else:
            return "文件"
    
    def format_size(self, size_bytes):
        """格式化文件大小"""
        if size_bytes < 1024:
            return f"{size_bytes} B"
        elif size_bytes < 1024 * 1024:
            return f"{size_bytes/1024:.1f} KB"
        elif size_bytes < 1024 * 1024 * 1024:
            return f"{size_bytes/(1024*1024):.1f} MB"
        else:
            return f"{size_bytes/(1024*1024*1024):.1f} GB"
    
    def go_up(self):
        """返回上级目录"""
        parent_path = os.path.dirname(self.current_path)
        if parent_path != self.current_path:  # 防止在根目录时出错
            self.load_files(parent_path)
    
    def navigate_to_path(self):
        """导航到输入的路径"""
        new_path = self.path_var.get()
        if os.path.exists(new_path) and os.path.isdir(new_path):
            self.load_files(new_path)
        else:
            messagebox.showerror("错误", "无效的路径")
            self.path_var.set(self.current_path)
    
    def refresh(self):
        """刷新当前目录"""
        self.load_files(self.current_path)
    
    def on_double_click(self, event):
        """双击事件处理"""
        item = self.file_tree.identify_row(event.y)
        if item:
            item_name = self.file_tree.item(item, "values")[0]
            item_path = os.path.join(self.current_path, item_name)
            
            if os.path.isdir(item_path):
                # 打开文件夹
                self.load_files(item_path)
            else:
                # 尝试预览文件
                self.preview_file(item_path)
    
    def preview_file(self, file_path):
        """预览文件"""
        try:
            # 获取文件扩展名
            ext = os.path.splitext(file_path)[1].lower()
            
            # 只预览文本文件
            if ext in ['.txt', '.py', '.java', '.c', '.cpp', '.html', '.css', '.js', '.json', '.xml', '.md']:
                # 创建预览窗口
                preview_window = tk.Toplevel(self.root)
                preview_window.title(f"预览: {os.path.basename(file_path)}")
                preview_window.geometry("800x600")
                
                # 创建文本区域
                text_area = scrolledtext.ScrolledText(preview_window, wrap=tk.WORD)
                text_area.pack(fill=tk.BOTH, expand=True)
                
                # 读取文件内容
                try:
                    with open(file_path, 'r', encoding='utf-8') as file:
                        content = file.read()
                        text_area.insert(tk.END, content)
                except UnicodeDecodeError:
                    # 尝试其他编码
                    try:
                        with open(file_path, 'r', encoding='gbk') as file:
                            content = file.read()
                            text_area.insert(tk.END, content)
                    except Exception as e:
                        text_area.insert(tk.END, f"无法解码文件内容: {str(e)}")
                
                # 设置为只读
                text_area.config(state=tk.DISABLED)
            else:
                messagebox.showinfo("提示", f"无法预览 {ext} 类型的文件")
                
        except Exception as e:
            messagebox.showerror("错误", f"预览文件失败: {str(e)}")
    
    def create_folder(self):
        """创建文件夹"""
        folder_name = simpledialog.askstring("新建文件夹", "请输入文件夹名称:", parent=self.root)
        
        if folder_name:
            folder_path = os.path.join(self.current_path, folder_name)
            
            try:
                if not os.path.exists(folder_path):
                    os.makedirs(folder_path)
                    self.refresh()
                    self.status_var.set(f"已创建文件夹: {folder_name}")
                else:
                    messagebox.showerror("错误", "文件夹已存在")
            except Exception as e:
                messagebox.showerror("错误", f"创建文件夹失败: {str(e)}")
    
    def create_file(self):
        """创建文件"""
        file_name = simpledialog.askstring("新建文件", "请输入文件名称:", parent=self.root)
        
        if file_name:
            file_path = os.path.join(self.current_path, file_name)
            
            try:
                if not os.path.exists(file_path):
                    # 创建空文件
                    with open(file_path, 'w') as f:
                        pass
                    self.refresh()
                    self.status_var.set(f"已创建文件: {file_name}")
                else:
                    messagebox.showerror("错误", "文件已存在")
            except Exception as e:
                messagebox.showerror("错误", f"创建文件失败: {str(e)}")
    
    def delete_items(self):
        """删除选中的项目"""
        selected_items = self.file_tree.selection()
        
        if not selected_items:
            messagebox.showinfo("提示", "请选择要删除的项目")
            return
        
        # 获取选中项目的名称和类型
        items_to_delete = []
        for item in selected_items:
            item_name = self.file_tree.item(item, "values")[0]
            item_path = os.path.join(self.current_path, item_name)
            items_to_delete.append((item_name, item_path))
        
        # 确认删除
        if messagebox.askyesno("确认删除", f"确定要删除以下 {len(items_to_delete)} 个项目吗?\n\n" + 
                             "\n".join([name for name, _ in items_to_delete])):
            try:
                # 在单独的线程中执行删除操作
                delete_thread = threading.Thread(target=self._delete_items_thread, args=(items_to_delete,))
                delete_thread.start()
                
                # 显示进度窗口
                self.show_progress_window("正在删除...", delete_thread)
            except Exception as e:
                messagebox.showerror("错误", f"删除项目失败: {str(e)}")
    
    def _delete_items_thread(self, items_to_delete):
        """在单独的线程中执行删除操作"""
        for name, path in items_to_delete:
            try:
                if os.path.isdir(path):
                    shutil.rmtree(path)
                else:
                    os.remove(path)
            except Exception as e:
                # 如果删除失败,继续删除其他项目
                self.status_var.set(f"删除 {name} 失败: {str(e)}")
        
        # 刷新文件列表
        self.root.after(100, self.refresh)
    
    def rename_item(self):
        """重命名选中的项目"""
        selected_items = self.file_tree.selection()
        
        if not selected_items or len(selected_items) > 1:
            messagebox.showinfo("提示", "请选择一个项目进行重命名")
            return
        
        item = selected_items[0]
        old_name = self.file_tree.item(item, "values")[0]
        old_path = os.path.join(self.current_path, old_name)
        
        new_name = simpledialog.askstring("重命名", "请输入新名称:", initialvalue=old_name, parent=self.root)
        
        if new_name and new_name != old_name:
            new_path = os.path.join(self.current_path, new_name)
            
            try:
                if not os.path.exists(new_path):
                    os.rename(old_path, new_path)
                    self.refresh()
                    self.status_var.set(f"已重命名: {old_name} -> {new_name}")
                else:
                    messagebox.showerror("错误", "目标已存在")
            except Exception as e:
                messagebox.showerror("错误", f"重命名失败: {str(e)}")
    
    def copy_items(self):
        """复制选中的项目"""
        selected_items = self.file_tree.selection()
        
        if not selected_items:
            messagebox.showinfo("提示", "请选择要复制的项目")
            return
        
        # 获取选中项目的路径
        self.items_to_copy = []
        for item in selected_items:
            item_name = self.file_tree.item(item, "values")[0]
            item_path = os.path.join(self.current_path, item_name)
            self.items_to_copy.append((item_name, item_path))
        
        # 打开目标目录选择对话框
        target_dir = filedialog.askdirectory(title="选择目标目录", initialdir=self.current_path)
        
        if target_dir:
            try:
                # 在单独的线程中执行复制操作
                copy_thread = threading.Thread(target=self._copy_items_thread, args=(target_dir,))
                copy_thread.start()
                
                # 显示进度窗口
                self.show_progress_window("正在复制...", copy_thread)
            except Exception as e:
                messagebox.showerror("错误", f"复制项目失败: {str(e)}")
    
    def _copy_items_thread(self, target_dir):
        """在单独的线程中执行复制操作"""
        for name, source_path in self.items_to_copy:
            target_path = os.path.join(target_dir, name)
            
            try:
                if os.path.isdir(source_path):
                    shutil.copytree(source_path, target_path)
                else:
                    shutil.copy2(source_path, target_path)
            except Exception as e:
                # 如果复制失败,继续复制其他项目
                self.status_var.set(f"复制 {name} 失败: {str(e)}")
        
        # 刷新目标目录(如果在当前窗口可见)
        if target_dir == self.current_path:
            self.root.after(100, self.refresh)
    
    def move_items(self):
        """移动选中的项目"""
        selected_items = self.file_tree.selection()
        
        if not selected_items:
            messagebox.showinfo("提示", "请选择要移动的项目")
            return
        
        # 获取选中项目的路径
        self.items_to_move = []
        for item in selected_items:
            item_name = self.file_tree.item(item, "values")[0]
            item_path = os.path.join(self.current_path, item_name)
            self.items_to_move.append((item_name, item_path))
        
        # 打开目标目录选择对话框
        target_dir = filedialog.askdirectory(title="选择目标目录", initialdir=self.current_path)
        
        if target_dir:
            try:
                # 在单独的线程中执行移动操作
                move_thread = threading.Thread(target=self._move_items_thread, args=(target_dir,))
                move_thread.start()
                
                # 显示进度窗口
                self.show_progress_window("正在移动...", move_thread)
            except Exception as e:
                messagebox.showerror("错误", f"移动项目失败: {str(e)}")
    
    def _move_items_thread(self, target_dir):
        """在单独的线程中执行移动操作"""
        for name, source_path in self.items_to_move:
            target_path = os.path.join(target_dir, name)
            
            try:
                shutil.move(source_path, target_path)
            except Exception as e:
                # 如果移动失败,继续移动其他项目
                self.status_var.set(f"移动 {name} 失败: {str(e)}")
        
        # 刷新当前目录和目标目录(如果在当前窗口可见)
        self.root.after(100, self.refresh)
        if target_dir == self.current_path:
            self.root.after(100, lambda: self.load_files(target_dir))
    
    def zip_items(self):
        """压缩选中的项目"""
        selected_items = self.file_tree.selection()
        
        if not selected_items:
            messagebox.showinfo("提示", "请选择要压缩的项目")
            return
        
        # 获取选中项目的路径
        items_to_zip = []
        for item in selected_items:
            item_name = self.file_tree.item(item, "values")[0]
            item_path = os.path.join(self.current_path, item_name)
            items_to_zip.append((item_name, item_path))
        
        # 确定压缩文件名
        default_zip_name = "archive.zip"
        if len(items_to_zip) == 1:
            base_name = os.path.splitext(items_to_zip[0][0])[0]
            default_zip_name = f"{base_name}.zip"
        
        # 打开保存对话框
        zip_file_path = filedialog.asksaveasfilename(
            title="保存压缩文件",
            defaultextension=".zip",
            filetypes=[("ZIP 文件", "*.zip"), ("所有文件", "*.*")],
            initialdir=self.current_path,
            initialfile=default_zip_name
        )
        
        if zip_file_path:
            try:
                # 在单独的线程中执行压缩操作
                zip_thread = threading.Thread(target=self._zip_items_thread, args=(items_to_zip, zip_file_path))
                zip_thread.start()
                
                # 显示进度窗口
                self.show_progress_window("正在压缩...", zip_thread)
            except Exception as e:
                messagebox.showerror("错误", f"压缩项目失败: {str(e)}")
    
    def _zip_items_thread(self, items_to_zip, zip_file_path):
        """在单独的线程中执行压缩操作"""
        try:
            with zipfile.ZipFile(zip_file_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
                for name, path in items_to_zip:
                    if os.path.isdir(path):
                        # 压缩文件夹
                        for root, dirs, files in os.walk(path):
                            for file in files:
                                file_path = os.path.join(root, file)
                                arcname = os.path.relpath(file_path, self.current_path)
                                zipf.write(file_path, arcname)
                    else:
                        # 压缩文件
                        arcname = os.path.relpath(path, self.current_path)
                        zipf.write(path, arcname)
            
            self.status_var.set(f"已压缩到: {os.path.basename(zip_file_path)}")
            
            # 刷新当前目录
            self.root.after(100, self.refresh)
            
        except Exception as e:
            self.status_var.set(f"压缩失败: {str(e)}")
    
    def unzip_item(self):
        """解压选中的项目"""
        selected_items = self.file_tree.selection()
        
        if not selected_items or len(selected_items) > 1:
            messagebox.showinfo("提示", "请选择一个压缩文件进行解压")
            return
        
        item = selected_items[0]
        item_name = self.file_tree.item(item, "values")[0]
        item_path = os.path.join(self.current_path, item_name)
        
        # 检查是否为ZIP文件
        if not item_name.lower().endswith('.zip'):
            messagebox.showinfo("提示", "请选择一个ZIP压缩文件")
            return
        
        # 打开目标目录选择对话框
        target_dir = filedialog.askdirectory(title="选择解压目录", initialdir=self.current_path)
        
        if target_dir:
            try:
                # 在单独的线程中执行解压操作
                unzip_thread = threading.Thread(target=self._unzip_item_thread, args=(item_path, target_dir))
                unzip_thread.start()
                
                # 显示进度窗口
                self.show_progress_window("正在解压...", unzip_thread)
            except Exception as e:
                messagebox.showerror("错误", f"解压文件失败: {str(e)}")
    
    def _unzip_item_thread(self, zip_file_path, target_dir):
        """在单独的线程中执行解压操作"""
        try:
            with zipfile.ZipFile(zip_file_path, 'r') as zipf:
                zipf.extractall(target_dir)
            
            self.status_var.set(f"已解压到: {target_dir}")
            
            # 刷新目标目录(如果在当前窗口可见)
            if target_dir == self.current_path:
                self.root.after(100, self.refresh)
            
        except Exception as e:
            self.status_var.set(f"解压失败: {str(e)}")
    
    def show_progress_window(self, message, thread):
        """显示进度窗口"""
        progress_window = tk.Toplevel(self.root)
        progress_window.title("请等待")
        progress_window.geometry("300x120")
        progress_window.transient(self.root)
        progress_window.grab_set()
        
        ttk.Label(progress_window, text=message, padding=20).pack()
        
        progress_bar = ttk.Progressbar(progress_window, mode='indeterminate')
        progress_bar.pack(fill=tk.X, padx=20)
        progress_bar.start()
        
        def check_thread():
            if thread.is_alive():
                progress_window.after(100, check_thread)
            else:
                progress_window.destroy()
        
        progress_window.after(100, check_thread)
    
    def search_files(self):
        """搜索文件"""
        search_text = self.search_var.get().strip()
        
        if not search_text:
            messagebox.showinfo("提示", "请输入搜索关键词")
            return
        
        try:
            # 在单独的线程中执行搜索操作
            search_thread = threading.Thread(target=self._search_files_thread, args=(search_text,))
            search_thread.start()
            
            # 显示进度窗口
            self.show_progress_window("正在搜索...", search_thread)
        except Exception as e:
            messagebox.showerror("错误", f"搜索失败: {str(e)}")
    
    def _search_files_thread(self, search_text):
        """在单独的线程中执行搜索操作"""
        # 清空现有数据
        self.root.after(100, lambda: [self.file_tree.delete(item) for item in self.file_tree.get_children()])
        
        results = []
        
        try:
            # 递归搜索当前目录
            for root, dirs, files in os.walk(self.current_path):
                # 搜索文件夹
                for dir_name in dirs:
                    if search_text.lower() in dir_name.lower():
                        dir_path = os.path.join(root, dir_name)
                        try:
                            stats = os.stat(dir_path)
                            modified_time = datetime.fromtimestamp(stats.st_mtime).strftime('%Y-%m-%d %H:%M:%S')
                            results.append((dir_name, "文件夹", "", modified_time, dir_path))
                        except:
                            results.append((dir_name, "文件夹", "", "", dir_path))
                
                # 搜索文件
                for file_name in files:
                    if search_text.lower() in file_name.lower():
                        file_path = os.path.join(root, file_name)
                        try:
                            stats = os.stat(file_path)
                            modified_time = datetime.fromtimestamp(stats.st_mtime).strftime('%Y-%m-%d %H:%M:%S')
                            file_size = self.format_size(stats.st_size)
                            results.append((file_name, self.get_file_type(file_name), file_size, modified_time, file_path))
                        except:
                            results.append((file_name, "文件", "", "", file_path))
            
            # 在主线程中更新UI
            self.root.after(100, self._update_search_results, results)
            
        except Exception as e:
            self.root.after(100, lambda: messagebox.showerror("错误", f"搜索过程中出错: {str(e)}"))
    
    def _update_search_results(self, results):
        """更新搜索结果"""
        # 清空现有数据
        for item in self.file_tree.get_children():
            self.file_tree.delete(item)
        
        # 添加搜索结果
        for name, item_type, size, modified, path in results:
            if item_type == "文件夹":
                self.file_tree.insert("", tk.END, values=(name, item_type, size, modified), tags=('folder',))
            else:
                self.file_tree.insert("", tk.END, values=(name, item_type, size, modified))
        
        # 设置样式
        self.file_tree.tag_configure('folder', foreground='blue')
        
        self.status_var.set(f"找到 {len(results)} 个匹配项")
    
    def clear_search(self):
        """清除搜索结果,显示原始目录内容"""
        self.search_var.set("")
        self.load_files()

if __name__ == "__main__":
    root = tk.Tk()
    app = FileManager(root)
    root.mainloop()

你可能感兴趣的:(实例,python,开发语言)