PyQt应用与开发(十一):文件浏览器的实现

前言

在PyQt5的GUI开发中,文件浏览器是非常经典的应用场景,它不仅能帮助开发者理解文件系统操作逻辑,还能深入掌握PyQt5中多个核心类的使用。本文将围绕文件浏览器的实现,详细讲解QDir(目录操作)、QListWidget(列表显示)、QProcess(外部进程交互)三个关键类的功能与用法,同时结合界面布局、用户交互逻辑,完整呈现文件浏览器的开发流程,适合有一定PyQt5基础,想要进阶学习文件操作与GUI结合的开发者。

一、文件浏览器整体设计思路

文件浏览器的核心需求是实现目录导航、文件/文件夹展示、文件打开等功能,整体设计需包含界面布局业务逻辑两部分,具体如下:

1.1 界面布局设计

采用PyQt5的布局管理器(QHBoxLayout+QVBoxLayout)实现灵活适配,界面组件包括:

  • 返回按钮:用于切换到上一级目录。
  • 单行文本框(QLineEdit):实时显示当前目录的绝对路径,方便用户查看路径信息。
  • 列表控件(QListWidget):展示当前目录下的所有文件和文件夹,支持双击交互。

布局结构:

  1. 先用QHBoxLayout将“返回按钮”和“单行文本框”横向排列,文本框占比更大以完整显示路径。
  2. 再用QVBoxLayout将上述横向布局与QListWidget纵向排列,形成完整界面。

1.2 核心业务逻辑

  1. 初始化:启动程序时,默认定位到某个初始目录(如当前工作目录),获取目录下的文件/文件夹信息,展示在QListWidget中,并在文本框显示当前路径。
  2. 目录切换
    • 双击QListWidget中的文件夹项:切换到该子目录,重新加载目录内容并更新路径。
    • 点击“返回按钮”:尝试切换到上一级目录,若当前已是根目录则不操作。
  3. 文件打开:双击QListWidget中的文件项:通过QProcess启动外部程序(如记事本)打开该文件。
  4. 目录内容筛选:使用QDir的筛选器,过滤不需要显示的内容(如...条目、隐藏文件等)。

二、核心类详解与用法

文件浏览器的实现依赖QDirQListWidget(含QListWidgetItem)、QProcess三个核心类,以下分别讲解其导入方式、常用方法、典型场景及代码示例。

2.1 QDir:目录操作的核心类

QDir属于PyQt5.QtCore模块,提供跨平台的目录操作能力,可实现目录创建、删除、内容列表、路径解析等功能,是文件浏览器获取目录信息的基础。

2.1.1 导入方式
from PyQt5.QtCore import QDir
2.1.2 常用方法与示例
1. 创建QDir对象

通过指定路径初始化,若路径为空则默认对应当前工作目录:

# 初始化指定目录
dir = QDir("D:/code/PyQt5")
# 初始化当前工作目录
current_dir = QDir()
2. 目录内容列表(关键)

通过entryList()entryInfoList()获取目录内容,前者返回文件名列表,后者返回QFileInfo对象列表(含文件类型、大小等详细信息),支持通过筛选器过滤内容。

常用筛选器(预定义常量):

  • QDir.AllEntries:列出所有条目(文件、子目录、符号链接)。
  • QDir.AllDirs:仅列出目录。
  • QDir.Files:仅列出文件。
  • QDir.NoDotAndDotDot:不列出.(当前目录)和..(父目录),文件浏览器必用。
  • QDir.Hidden:显示隐藏文件/目录。
  • QDir.CaseSensitive:文件名比较区分大小写。

示例:获取目录下所有文件和目录(排除...):

# 设置筛选器:排除.和..,显示所有文件和目录
filters = QDir.NoDotAndDotDot | QDir.Files | QDir.AllDirs
# 获取文件名列表
file_names = dir.entryList(filters)
# 获取QFileInfo列表(含详细信息)
file_infos = dir.entryInfoList(filters)

# 遍历QFileInfo列表,区分文件和目录
for info in file_infos:
    if info.isDir():
        print(f"目录:{info.fileName()}")
    elif info.isFile():
        print(f"文件:{info.fileName()},大小:{info.size()}字节")
3. 目录状态检查

判断目录是否存在、可读、是否为绝对路径等:

# 检查目录是否存在
if dir.exists():
    print("目录存在")
else:
    print("目录不存在")

# 检查目录是否可读
if dir.isReadable():
    print("目录可读")

# 检查路径是否为绝对路径
if dir.isAbsolute():
    print("当前路径是绝对路径")
else:
    print("当前路径是相对路径")
4. 目录创建与删除
  • mkdir():创建子目录(仅创建一级)。
  • mkpath():创建多级子目录(如a/b/c)。
  • rmdir():删除空目录(非空目录无法删除)。

示例:

# 创建一级子目录
if dir.mkdir("new_dir"):
    print("子目录创建成功")
else:
    print("子目录创建失败(可能已存在)")

# 创建多级子目录
if dir.mkpath("a/b/c"):
    print("多级子目录创建成功")

# 删除空目录
if dir.rmdir("new_dir"):
    print("子目录删除成功")
5. 路径操作
  • absolutePath():获取目录的绝对路径(文件浏览器中用于更新文本框显示)。
  • cd():切换到指定子目录。
  • cdUp():切换到上一级目录(文件浏览器“返回”功能核心)。
  • relativeFilePath():将绝对路径转换为相对于当前目录的相对路径。

示例:

# 获取绝对路径
abs_path = dir.absolutePath()
print(f"绝对路径:{abs_path}")

# 切换到子目录
if dir.cd("new_dir"):
    print(f"切换到子目录:{dir.absolutePath()}")
else:
    print("子目录不存在,切换失败")

# 切换到上一级目录
if dir.cdUp():
    print(f"返回上一级:{dir.absolutePath()}")

# 转换为相对路径
abs_file_path = "D:/code/PyQt5/test.py"
rel_path = dir.relativeFilePath(abs_file_path)
print(f"相对路径:{rel_path}")
6. 文件过滤(按后缀)

通过setNameFilters()设置文件名过滤器,仅显示指定后缀的文件:

# 仅显示.txt和.py文件
dir.setNameFilters(["*.txt", "*.py"])
filtered_files = dir.entryList(QDir.Files | QDir.NoDotAndDotDot)
print("筛选后的文件:", filtered_files)
2.1.3 典型应用场景
  • 文件浏览器中获取目录内容并区分文件/目录。
  • 动态创建/删除目录(如程序日志目录)。
  • 路径解析与转换(绝对路径→相对路径)。
  • 跨平台路径适配(自动处理Windows的\和Linux的/)。

2.2 QListWidget与QListWidgetItem:列表显示与交互

QListWidgetPyQt5.QtWidgets模块中的列表控件,用于展示条目列表;QListWidgetItem则表示列表中的单个条目,支持文本、图标、复选框等功能,二者结合实现文件浏览器的目录内容显示与交互。

2.2.1 导入方式
from PyQt5.QtWidgets import QListWidget, QListWidgetItem
from PyQt5.QtGui import QIcon  # 用于设置条目图标
from PyQt5.QtCore import Qt   # 用于设置条目状态(如复选框)
2.2.2 QListWidget常用方法
  • addItem():添加QListWidgetItem条目。
  • clear():清空所有条目(目录切换时必用)。
  • itemDoubleClicked:信号,当条目被双击时触发(文件打开/目录切换的核心信号)。
  • currentItem():获取当前选中的条目。

示例:初始化QListWidget并添加条目:

from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, QListWidget, QWidget, ![请添加图片描述](https://i-blog.csdnimg.cn/direct/9ec2637b53774d0b8ccbb7a6524c4f7f.png)
QVBoxLayout,QListWidgetItem
import sys

app = QApplication(sys.argv)
window = QWidget()
window.setWindowTitle("QListWidget示例")

# 创建QListWidget
list_widget = QListWidget()

# 添加条目(文本+图标)
# 目录图标(可替换为自定义图标路径)
dir_icon = QIcon.fromTheme("folder")
# 文件图标
file_icon = QIcon.fromTheme("file")

# 添加目录条目
dir_item = QListWidgetItem(dir_icon, "文档目录")
list_widget.addItem(dir_item)

# 添加文件条目
file_item = QListWidgetItem(file_icon, "笔记.txt")
list_widget.addItem(file_item)

# 布局
layout = QVBoxLayout(window)
layout.addWidget(list_widget)

# 双击信号绑定(后续讲解)
def on_item_double_clicked(item):
    print(f"双击条目:{item.text()}")

list_widget.itemDoubleClicked.connect(on_item_double_clicked)

window.show()
sys.exit(app.exec_())

输出结果为

请添加图片描述

2.2.3 QListWidgetItem常用方法
1. 创建条目

支持仅文本、文本+图标两种方式:

# 仅文本
item1 = QListWidgetItem("test.py")
# 文本+图标
item2 = QListWidgetItem(QIcon("file.png"), "report.txt")
2. 文本与图标设置
  • setText():修改条目文本。
  • text():获取条目文本。
  • setIcon():设置条目图标(文件浏览器中用于区分文件/目录)。
  • icon():获取条目图标。

示例:

item = QListWidgetItem("old_name.txt")
# 修改文本
item.setText("new_name.txt")
# 设置图标
item.setIcon(QIcon("file.png"))
print(f"条目文本:{item.text()}")
3. 复选框功能

通过setCheckState()设置复选状态,支持多选场景(如文件批量操作):

# 设置复选框为选中状态
item.setCheckState(Qt.Checked)
# 设置复选框为未选中状态
item.setCheckState(Qt.Unchecked)

# 获取复选状态
if item.checkState() == Qt.Checked:
    print(f"条目 {item.text()} 已选中")
4. 附加自定义数据

通过setData()data()为条目附加自定义数据(如文件绝对路径),解决“条目文本≠完整路径”的问题:

# 附加文件绝对路径(使用Qt.UserRole作为角色标识)
item.setData(Qt.UserRole, "D:/code/test.py")
# 获取附加数据
file_path = item.data(Qt.UserRole)
print(f"条目对应的文件路径:{file_path}")
5. 工具提示与可用性
  • setToolTip():鼠标悬停时显示提示信息(如文件大小、修改时间)。
  • setFlags():设置条目是否可选、可编辑(文件浏览器中通常设为“可选+可用”)。

示例:

# 设置工具提示
item.setToolTip("这是一个Python脚本文件")
# 设置条目为可选且可用
item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
2.2.4 典型应用场景
  • 文件浏览器中显示目录下的文件/目录条目(区分图标)。
  • 双击条目触发文件打开或目录切换。
  • 批量操作(如复选框选择多个文件删除)。
  • 条目附加自定义数据(如文件路径、类型)。

2.3 QProcess:外部进程交互

QProcess属于PyQt5.QtCore模块,用于启动外部程序(如记事本、Python脚本)并与之交互,是文件浏览器“打开文件”功能的核心。

2.3.1 导入方式
from PyQt5.QtCore import QProcess
2.3.2 常用方法与示例
1. 启动进程(关键)
  • start(program, arguments):异步启动外部程序,program为程序路径/命令,arguments为参数列表(可选)。
  • execute(program, arguments):同步启动外部程序,阻塞当前线程直到程序结束,返回退出码。

示例1:用记事本打开文本文件(文件浏览器核心功能):

def open_file(file_path):
    process = QProcess()
    # Windows:notepad.exe打开文件;Linux:gedit;macOS:open
    # 跨平台适配可通过QDir判断系统
    if QDir.separator() == "\\":  # Windows系统
        program = "notepad.exe"
    else:  # Linux/macOS
        program = "gedit" if QDir("/usr/bin/gedit").exists() else "open"
    # 启动进程:打开指定文件
    process.start(program, [file_path])
    # 检查进程是否启动成功
    if not process.waitForStarted(3000):  # 等待3秒
        print(f"启动程序失败:{program}")

示例2:同步执行系统命令(如ls -l):

# 执行ls -l命令(Linux/macOS)
exit_code = QProcess.execute("ls", ["-l"])
print(f"命令退出码:{exit_code}")
2. 读取进程输出

通过readAllStandardOutput()readAllStandardError()获取外部程序的标准输出和错误输出,通常结合信号readyReadStandardOutput实时读取:

process = QProcess()

# 实时读取标准输出
def read_output():
    output = process.readAllStandardOutput().data().decode("utf-8")
    if output:
        print(f"程序输出:{output}")

# 实时读取标准错误
def read_error():
    error = process.readAllStandardError().data().decode("utf-8")
    if error:
        print(f"程序错误:{error}")

# 绑定信号
process.readyReadStandardOutput.connect(read_output)
process.readyReadStandardError.connect(read_error)

# 启动进程(示例:执行Python脚本并查看输出)
process.start("python", ["test.py"])
3. 监控进程状态
  • waitForFinished(msecs):等待进程结束(超时时间,单位毫秒)。
  • state():获取进程状态(QProcess.NotRunning/QProcess.Starting/QProcess.Running)。
  • exitCode():获取进程退出码(0通常表示成功)。

示例:

process = QProcess()
process.start("notepad.exe", ["test.txt"])

# 等待进程结束(超时10秒)
if process.waitForFinished(10000):
    print(f"进程正常结束,退出码:{process.exitCode()}")
else:
    print("进程超时未结束,强制终止")
    process.kill()  # 强制终止进程
4. 信号与槽(常用)
  • started:进程启动时触发。
  • finished(exit_code):进程结束时触发,返回退出码。
  • readyReadStandardOutput:有标准输出可读时触发。

示例:绑定进程结束信号:

def on_process_finished(exit_code):
    print(f"进程结束,退出码:{exit_code}")

process = QProcess()
process.finished.connect(on_process_finished)
process.start("notepad.exe", ["test.txt"])
2.3.3 典型应用场景
  • 文件浏览器中打开文件(调用系统默认程序或指定程序)。
  • 执行外部脚本(如Python、Shell脚本)并获取结果。
  • 实时监控外部程序输出(如日志打印)。
  • 跨平台执行系统命令(如Windows的ping、Linux的ifconfig)。

三、文件浏览器完整代码实现

from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout,
                             QPushButton, QLineEdit, QListWidget, QListWidgetItem)
from PyQt5.QtCore import QDir, QProcess, Qt
from PyQt5.QtGui import QIcon
import sys


class FileBrowser(QWidget):
    def __init__(self):
        super().__init__()
        # 初始化界面(按文档要求采用水平+垂直布局)
        self.init_ui()
        # 初始化当前目录(文档逻辑:默认加载当前目录)
        self.current_dir = QDir()
        # 加载当前目录下的文件与文件夹列表
        self.load_dir_content()

    def init_ui(self):
        # 窗口基础设置
        self.setWindowTitle("PyQt5文件浏览器")
        self.resize(800, 600)

        # 1. 水平布局(文档要求:按钮和文本框放在QHBoxLayout)
        top_h_layout = QHBoxLayout()
        # 返回上一级按钮(文档核心界面组件)
        self.back_btn = QPushButton("返回上一级目录")
        self.back_btn.clicked.connect(self.switch_to_parent_dir)
        # 显示当前目录路径的单行文本框(文档核心界面组件)
        self.path_edit = QLineEdit()
        self.path_edit.setReadOnly(True)  # 路径仅显示,不允许编辑

        # 将按钮和文本框添加到水平布局,文本框占满剩余空间
        top_h_layout.addWidget(self.back_btn)
        top_h_layout.addWidget(self.path_edit)
        top_h_layout.setStretchFactor(self.path_edit, 1)

        # 2. QListWidget(文档要求:显示当前目录下所有文件夹及文件列表)
        self.file_list_widget = QListWidget()
        # 绑定双击信号(文档逻辑:双击处理文件打开/目录切换)
        self.file_list_widget.itemDoubleClicked.connect(self.handle_item_double_click)

        # 3. 垂直布局(文档要求:水平布局和QListWidget放在QVBoxLayout)
        main_v_layout = QVBoxLayout(self)
        main_v_layout.addLayout(top_h_layout)
        main_v_layout.addWidget(self.file_list_widget)

    def load_dir_content(self):
        """按文档逻辑:读取当前目录内容,添加到QListWidget"""
        # 清空列表(避免目录切换时内容叠加)
        self.file_list_widget.clear()
        # 更新文本框显示当前目录绝对路径(文档QDir的absolutePath()方法)
        self.path_edit.setText(self.current_dir.absolutePath())

        # 文档预定义筛选器:排除"."(当前目录)和".."(父目录),显示所有文件和文件夹
        filters = QDir.NoDotAndDotDot | QDir.Files | QDir.AllDirs
        # 读取目录下文件/文件夹的详细信息(文档QDir的entryInfoList()方法)
        file_info_list = self.current_dir.entryInfoList(filters)

        # 区分文件与文件夹图标(提升视觉识别性)
        dir_icon = QIcon.fromTheme("folder")  # 文件夹图标
        file_icon = QIcon.fromTheme("file")  # 文件图标

        # 循环添加条目到QListWidget(文档步骤:挨个添加文件/文件夹信息)
        for info in file_info_list:
            if info.isDir():
                # 文件夹条目:图标+名称,附加绝对路径(便于后续切换)
                item = QListWidgetItem(dir_icon, info.fileName())
            else:
                # 文件条目:图标+名称,附加绝对路径(便于后续打开)
                item = QListWidgetItem(file_icon, info.fileName())
                # 附加文件大小作为工具提示(优化用户体验)
                item.setToolTip(f"文件大小:{info.size()} 字节")

            # 用Qt.UserRole存储文件/文件夹绝对路径(文档QListWidgetItem的setData()思路)
            item.setData(Qt.UserRole, info.absoluteFilePath())
            self.file_list_widget.addItem(item)

    def switch_to_parent_dir(self):
        """按文档逻辑:点击返回按钮,切换到上一级目录"""
        # 文档QDir的cdUp()方法:返回True表示切换成功
        if self.current_dir.cdUp():
            # 切换成功后重新加载目录内容(文档步骤:重复3.4.5步骤)
            self.load_dir_content()
        # 切换失败(如当前已是根目录)则不操作(文档逻辑)

    def handle_item_double_click(self, item):
        """按文档逻辑:双击条目,区分文件/文件夹处理"""
        # 获取条目附加的绝对路径(文档QListWidgetItem的data()思路)
        target_path = item.data(Qt.UserRole)
        target_dir = QDir(target_path)

        if target_dir.isDir():
            # 双击的是文件夹:切换目录并重新加载内容(文档步骤:切换目录,重复3.4.5)
            self.current_dir = target_dir
            self.load_dir_content()
        else:
            # 双击的是文件:用外部程序打开(文档逻辑:调用记事本打开文件)
            self.open_file_with_process(target_path)

    def open_file_with_process(self, file_path):
        """按文档逻辑:用QProcess启动外部程序打开文件"""
        # 初始化QProcess(文档核心类:用于启动外部进程)
        process = QProcess()
        # 文档格式:Windows下用记事本打开,其他系统适配基础打开方式
        if QDir.separator() == "\\":
            # Windows系统:调用notepad.exe打开文件(文档推荐程序)
            program = "notepad.exe"
            arguments = [file_path]
        else:
            # Linux/macOS:用系统默认文本编辑器打开
            program = "gedit" if QDir("/usr/bin/gedit").exists() else "open"
            arguments = [file_path] if program == "gedit" else ["-a", "TextEdit", file_path]

        # 文档QProcess的start()方法:启动外部程序
        process.start(program, arguments)
        # 检查程序是否启动成功(超时3秒,避免阻塞)
        if not process.waitForStarted(3000):
            print(f"启动程序失败:{program}")


if __name__ == "__main__":
    # 程序入口:初始化应用并显示窗口
    app = QApplication(sys.argv)
    browser = FileBrowser()
    browser.show()
    sys.exit(app.exec_())

输出结果可以自己试一下,生成了一个文件浏览器窗口

总结

本文通过实现一个简单的文件浏览器,深入讲解了PyQt5中三个核心类:QDir、QListWidget和QProcess的用法。以下是关键点回顾:

  1. 设计思路
    界面布局:采用水平布局(返回按钮和路径显示)和垂直布局(整体结构)相结合,确保界面整洁且自适应。

业务逻辑:包括初始化当前目录、双击目录切换、返回上一级、双击文件用默认程序打开等。

  1. 核心类详解
    QDir:用于目录操作,如获取目录内容、切换目录、判断目录是否存在等。我们使用entryInfoList方法获取目录下的文件和文件夹信息,并利用过滤器排除不必要的条目(如.和…)。

QListWidget和QListWidgetItem:用于显示目录内容。每个条目都是一个QListWidgetItem,可以设置图标、文本和附加数据(如绝对路径)。通过双击条目触发相应的操作(切换目录或打开文件)。

QProcess:用于启动外部程序来打开文件。我们根据操作系统选择适当的程序(如Windows的记事本,Linux的gedit或macOS的open命令)。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mrliu__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值