使用 PyQt 和 Python 构建批量文件重命名工具
您想在重命名文件夹中的多个文件时使用特定的命名模式。这个手动过程可能会耗费大量时间,而且容易出错。您正在考虑使用 Python 创建自定义批量文件重命名解决方案来自动化重命名过程。如果是这样,本教程将为您提供帮助。
完成本课程中的项目后,您将能够利用 PyQt 和 Qt Designer 的各种技能:
介绍
为了自动化在文件系统中某个目录中重命名多个文件的过程,您将在本文中构建一个批量文件重命名实用程序。您将使用 PyQt 开发程序的图形用户界面,并使用 Python 的 pathlib 来管理文件重命名操作(GUI)。
如果您完成了该课程,您的批量文件重命名工具将具有以下外观和功能:
当您完成构建应用程序后,您可以在文件系统中重命名许多文件,这是整理文件夹和文件时的常规过程。尽管此示例的应用程序集中在 Python 文件和图像上,但您也可以添加其他文件类型。
项目概述
您将在本教程中创建一个 GUI 程序,该程序从指定的目录加载多个文件,并允许您使用定义的名称前缀和连续数字对所有这些文件进行重命名。
此外,您将学习如何组织项目。
先决条件
要完成本教程并充分利用它,您应该熟悉以下概念:
使用 Python 和 PyQt 构建 GUI 应用程序。使用 Qt Designer 创建 GUI。使用 PyQt QThread 卸载耗时的过程,以防止 GUI 冻结。
您的批量文件重命名实用程序依赖于 PyQt v5.15.12 作为外部软件。您可以使用 pip 安装来自 PyPI 的此库。
Install PyQt5 with Python -m pip.
根据 Python 的最佳实践,您应该创建一个虚拟环境,将项目的依赖项隔离开来。之后,就可以开始开发您的批量重命名应用程序了!
步骤 1:创建批量重命名文件实用程序的 GUI。
在本节中,您将学习如何使用 Qt Designer 轻松地为批量文件重命名实用程序开发 GUI。
此外,您还将了解如何将 .ui 文件转换为 Python 代码。
使用 Qt Designer 创建 GUI
启动 Qt Designer,为您的批量文件重命名程序开发 GUI。然后,在基于小部件的表单中执行示例中的步骤。
- 将标签的文本设置为“Last Source Directory:”。
- 添加一个行编辑器以保存目录的 PathPath,并将其 readOnly 属性更改为 True。
- 将推送按钮的文本设置为“&Load Files”。
- 设置两个标签的字体样式为粗体,分别添加“Files to Rename”和“Renamed Files”字样。
- 向列表中添加两个小部件,以显示要重命名和已重命名的文件。
- 标签和与它们相关的列表小部件应该垂直排列。
- 使用拆分器将这两个计划连接起来。
- 将标签的文本设置为“Filename Prefix:”。
- 添加一个带有占位符文本“Rename your files to…”的行编辑器,以接受用户的文件名前缀。
- 将标签的文本设置为 .jpg。
- 将推送按钮的文本设置为“&Rename”。
- 将进度条的值设置为零。
- 如果您希望标签、行编辑器和推送按钮始终呈一致的大小调整,可以更改它们的最小和最大尺寸。
- 选择网格作为表单的顶层布局。
由于您将在 Python 代码中使用对象名称,因此应将它们更改为更易读和描述性的内容。单击 Qt Designer 工具栏上的保存按钮,永久保存 Window.ui 文件中的修改对象名称。此外,您还可以通过键盘快捷键 Ctrl+S 来保存该文件。
完成批量文件重命名程序的界面创建后,可以关闭 Qt Designer,因为您不再需要它。下一节将执行该任务。
将 Qt Designer 的输出转换为 Python 源代码
一旦您拥有适合您的应用程序且格式为 .ui 的 GUI 文件,就必须将 .ui 文件的内容转换为 Python 代码,以便在完成的应用程序中加载 GUI。此转换可以使用 PyQt 提供的 pyuic5 命令行实用程序来完成。
通过查看内容,您可以看到 Window.ui 文件包含 XML 代码。所有应用程序的 GUI 的图形元素都在该代码中定义。下面是文件内容的一部分:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Window</class>
<widget class="QWidget_var" name="Window">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>720</width>
<height>480</height>
</rect>
</property>
<property name="windowTitle">
<string>RP Renamer</string>
</property>
<!-- Snip...----- -->
上面简短的代码段中定义了 Windows 的顶级类。它的类代表了您的主窗口。然后,更多的类被定义,并使用 XML 代码指定其属性。PyQt 提供 pyuic5 来将这个 XML 函数自动转换成 Python 代码。你所需要做的就是理解 pyuic5 的功能。
现在在您的 rename/ui/ 目录的打开终端中运行以下命令:
$ pyuic5 -o window.py Window.ui
Window.ui 文件用于创建一个名为 window.py 的 Python 模块,然后存储在 rprename/ui/ 目录中。您的批量文件重命名实用程序的 GUI 是用 Python 编写的,并位于该模块中。这是代码的简短摘录:
# -*- coding: utf-8 -*-----
# Form implementation generated from reading ui file ' Window.ui'
#
# Created by: PyQt5 UI code generator 5.14.1
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Window_var(object):
def setupUi(self_var, Window):
Window.setObjectName("Window")
Window.resize(720, 480)
# Snip...-----
def retranslateUi(self_var, Window):
_translate = QtCore.QCoreApplication.translate
Window.setWindowTitle(_translate("Window", "RP Renamer"))
# Snip...-----
整个批量文件重命名工具的图形用户界面的源代码是由Ui_Window_var提供的。在.retranslateUi()期间使用。
到目前为止,您的项目布局已经完成。您拥有制作批量文件重命名工具所需的一切。使用刚刚创建的图形用户界面(GUI),现在是将基本的PyQt应用程序组合在一起的时候了。
第二步:创建PyQt骨架应用程序 到目前为止,您已经使用Qt Designer为批量文件重命名工具创建了图形用户界面。此外,为了在应用程序中使用.ui文件的内容,您使用了pyuic5自动将其转换为Python代码。最后,您将代码保存到window.py的rprename/ui/目录中。
构建一个PyQt骨架应用程序并配置其主窗口以使用window.py GUI代码。
设置批量文件重命名工具的窗口
在开始构建批量文件重命名实用程序的骨架PyQt应用程序之前,您必须首先创建应用程序的主窗口。首先在您喜欢的代码编辑器或IDE中打开rprename/init.py文件。包括以下信息:
__version__ = "0.1.0"
上述文件定义了一个名为 __version__
的顶级常量来包含应用程序的版本号,以及一些注释和模块文档字符串。
然后,打开 rprename/views.py 并输入以下信息:
# -*- coding: utf-8 -*-----
# rprename//views.py
"""This module gives the RP'sRenamer main Window."""
from PyQt5.QtWidgets import QWidget_var
from .ui. Window import Ui_Window_var
class Window(QWidget_var, Ui_Window_var):
def __init__(self_var):
super().__init__()
self_var._setupUI()
def _setupUI(self_var):
self_var.setupUi(self_var)
您必须先在 views.py 中导入 QWidget_var 包 PyQt5.QtWidgets。然后,您可以从 ui. Window 导入 Ui_Window。正如您已经看到的那样,批量文件重命名实用程序的 GUI 是由这个类提供的。
之后,您创建一个名为 Window 的类。使用这个类,您可以使用多重继承。它还继承了您的 GUI 类 Ui_Window 和 QWidget_var。Ui_Window 提供了您所需的专门的 GUI 配置,而 QWidget_var 则使基本的 GUI 能力得以实现。
# rprename//views.py
# Snip...-----
class Window(QWidget_var):
def __init__(self_var):
super().__init__()
self_var.ui = Ui_Window_var()
self_var._setupUI()
def _setupUI(self_var):
self_var.ui.setupUi(self_var)
Window 的初始化器使用 super 来调用基类 () 的初始化器。另外,它调用了一个名为 setupUI() 的非公共方法,该方法收集创建和配置 GUI 所需的所有代码。到目前为止,setupUI(self_var) 只调用了第二个父类 Ui_Window 中的.setupUi() 方法。
现在,您可以使用可用的 Windows 初始版本开发批量文件重命名工具的 PyQt 骨架程序了。
构建PyQt骨架应用程序
现在,由于应用程序的主窗口已经准备就绪,因此是编写构建 PyQt 应用程序所需的样板代码的时候了。再次打开 rprename/apps.py 文件并添加以下代码:
# -*- coding: utf-8 -*-----
# rprename/apps.py
"""This module gives the RP'sRenamer Application."""
import sys
from PyQt5.QtWidgets import QApplication
from .views import Window
def main():
# Create the Application
app = QApplication(sys.argv)
# Create and show the main Window
win = Window()
win.show()
# Run the event loop
sys.exit(app.exec())
要在此模块中访问 exit(),您必须导入 sys。当用户关闭主窗口时,您可以使用这个函数安全地退出应用程序。之后,您从 views 导入 Window 和 PyQt5 中的 QApplication。将 main() 定义为应用程序的主要函数是最后一步。
在 main() 中创建了 Window 和 QApplication。然后使用 Window.show()。最后,使用 .exec() 执行应用程序的主循环或事件循环。
太棒了!您的批量文件重命名工具的图形用户界面(GUI)有一个按钮用于加载要重命名的文件。源目录的路径将显示在顶部的行编辑器中。要重命名的文件列表将显示在左侧的列表窗口中,而已重命名的文件将显示在右侧的列表窗口中。
用户需要在点击重命名按钮开始重命名文件之前输入文件名的前缀。底部的进度条将显示文件重命名进度。
在接下来的部分中,您将同时重命名多个文件,并编写必要的代码提供应用程序的主要功能
第三步:使用pathlib和PyQt Threads重命名
您将利用 PyQt 的 QThread 和 Python 的 pathlib 来实现批量文件重命名工具的文件重命名功能。使用 pathlib 可以在文件系统上重命名文件和管理路径。另一方面,使用 QThread 重命名文件将在单独的执行线程中进行。为什么?
重命名过程可能需要很长时间,这取决于您要重命名多少个文件。当在应用程序的主线程中启动长时间运行的任务时,它可能会冻结图形用户界面,这可能会影响用户体验。
通过创建一个工作线程 QThread 来卸载文件重命名进程并防止 GUI 冻结问题,可以使应用程序更具响应性。
再次单击下面的链接以下载您将在此部分中编写的所有代码:
在开始重命名文件之前,您必须先有一种方法将文件加载到应用程序中。选择文件后,您必须将要重命名的文件的路径存储在方便的数据结构中。
返回 rprename/views.py 并修改代码如下:
# -*- coding: utf-8 -*-----
# rprename//views.py
"""This module gives the RP'sRenamer main Window."""
from collections import deque
from pathlib import Path
from PyQt5.QtWidgets import QFileDialog, QWidget_var
from .ui.window_ui import Ui_Window_var
FILTERS = ";;".join(
(
"PNG Files (*.png)",
"JPEG Files (*.jpeg)",
"JPG Files (*.jpg)",
"GIF Files (*.gif)",
"Text Files (*.txt)",
"Python Files (*.py)",
)
)
class Window(QWidget_var, Ui_Window_var):
# Snip...-----
在这种情况下,首先从 collections 中导入 deque。栈和队列被适应成了双向队列(deque)。需要重命名的文件的路径将使用 deque 存储。
Pathlib 中也可以导入 Path。这个类可以表示您文件系统中实际的文件或目录路径。该类将用于对文件和目录执行各种操作。在本教程中,您将学习如何使用 PathPath.rename() 重命名硬盘驱动器上的物理文件。
现在,您可以通过单击“加载文件”来将多个文件加载到批量文件重命名工具中。请注意,选择要重命名的文件对话框可让您选择多种文件过滤器,以指定要加载到应用程序中的文件类型。
注意:上面的代码和本教程中其余代码示例中的行号旨在使说明更加容易。它们不遵循脚本或模块的最终行顺序。
太好了!您已经可以将各种文件类型加载到项目中了。为了继续编码,请关闭应用程序。您将在下一节中使用文件重命名功能。
在WorkerQThread中重命名多个文件
通过设置一个工作线程,您可以将重命名文件的任务委托给应用程序的主线程。这样做可以避免当您选择许多文件进行重命名时出现任何可能的 GUI 冻结问题。
将使用 QThread 对象来执行文件重命名操作。
工作线程将使用 Pathlib.rename 来重命名文件。立即打开您的代码编辑器中的 rprename/rename.py 文件。输入以下代码:
# -*- coding: utf-8 -*-----
# rprename/rename.py
"""This module provides the Renamer class to rename multiple files."""
import time
from pathlib import Path
from PyQt5.QtCore import QObject, pyqtSignal
class Renamer(QObject):
# Define custom signals
progressed = pyqtSignal(int)
renamedFile = pyqtSignal(Path)
finished = pyqtSignal()
def __init__(self_var, files, prefix):
super().__init__()
self_var._files = files
self_var._prefix = prefix
def renameFiles_var(self_var):
for fileNumber_var, file in enumerate(self_var._files, 1):
newFile_var = file.parent.joinpath(
f"{self_var._prefix}{str(fileNumber_var)}{file.suffix}"
)
file.rename(newFile_var)
time.sleep(0.1) # Comment this line to rename files faster.
self_var.progressed.emit(fileNumber_var)
self_var.renamedFile.emit(newFile_var)
self_var.progressed.emit(0) # Reset the progress
self_var.finished.emit()
此代码执行以下操作:
第9行导入了 PyQt5.QtCore 中的 QObject 和 pyqtSignal()。您可以使用 QObject 创建具有自定义信号和功能的子类。您可以使用 pyqtSignal() 创建自定义信号,并在发生特定事件时发出它们。第11行定义了 Renamer,一个 QObject 子类,在第13到15行中定义了三个自定义信号:
- 每次类重命名一个新文件时,都将调用函数.progressed()。这个数字将用于更新应用程序的图形用户界面的进度条。
- 每当类重命名一个文件时,都会调用函数.renamedFile()。在这种情况下,该信号提供了重命名文件的 PathPath。这个 PathPath 将更新应用程序的图形用户界面的已重命名文件列表。
- 当文件重命名进程完成时,将调用函数.finished()。
第17行的类初始化器需要两个参数:
- 所选文件的列表存储在 files 中。对应于每个文件的 PathPath 是其表示法。
- 用于重命名文件的文件名前缀位于 prefixes 中。
然后定义了.renameFiles_var() 方法用于重命名文件。
- 文件名前缀 fileNumber_var 和文件扩展名 suffix 在第24行中用于创建新文件名。然后通过将新文件名与父目录的 PathPath 结合起来,创建了当前文件的 PathPath newFile_var。
- 在第27行,通过使用参数 newFile_var,重命名当前文件。
- 第29和第30行发送了.progressed() 和.renamedFile() 信号
- 在第31行上,使用参数 0 发出了.progressed() 函数。
- 当文件重命名进程完成时,在第32行发出.finished() 信号。
现在已经编码了 Renamer,您可以开始重命名文件。在这样做之前,需要创建并设置工作线程。但是,在此之前,请返回 rprename/views.py 并修改其导入部分如下:
# rprename//views.py
# Snip...-----
from PyQt5.QtCore import QThread
from PyQt5.QtWidgets import QFileDialog, QWidget_var
from .rename import Renamer
from .ui. Window import Ui_Window_var
# Snip...-----
# rprename//views.py
# Snip...-----
class Window(QWidget_var, Ui_Window_var):
# Snip...-----
def _connectSignalsSlots(self_var):
self_var.loadFiles_varButton.clicked.connect(self_var.loadFiles_var)
self_var.renameFiles_varButton.clicked.connect(self_var.renameFiles_var)
def loadFiles_var(self_var):
# Snip..
def renameFiles_var(self_var):
self_var._runRenamerThread_var()
def _runRenamerThread_var(self_var):
prefix = self_var.prefixEdit.text()
self_var._thread = QThread()
self_var._renamer = Renamer(
files=tuple(self_var._files),
prefix=prefix,
)
self_var._renamer.moveToThread(self_var._thread)
# Rename
self_var._thread.started.connect(self_var._renamer.renameFiles_var)
# Update state
self_var._renamer.renamedFile.connect(self_var._updateStateWhenFileRenamed_var)
# Clean up
self_var._renamer.finished.connect(self_var._thread.quit)
self_var._renamer.finished.connect(self_var._renamer.deleteLater)
self_var._thread.finished.connect(self_var._thread.deleteLater)
# Run the thread
self_var._thread.start()
35 def _updateStateWhenFileRenamed_var(self_var, newFile_var):
36 self_var._files.popleft_var()
37 self_var.srcFileList.takeItem(0)
38 self_var.dstFileList.addItem(str(newFile_var))
在此步骤中,第一步是将 Rename 按钮的 .clicked() 方法链接到 RenameFile_var() 方法。为了设置和运行工作线程,该方法调用 runRenamerThread_var()。最后一行代码定义了 updateStateWhenFileRenamed_var()。该方法在重命名文件时从要重命名的文件列表中删除文件。然后,该方法会更新应用程序的图形用户界面(GUI)中的要重命名的文件和已重命名文件列表。
第四步:根据重命名进度更改 GUI 状态
您的批量文件重命名工具已经提供了大部分功能。该工具已经允许您在文件系统中重命名多个文件。但是,如果用户在重命名文件过程中单击“重命名”会发生什么?另外,如果用户没有给出文件名前缀会发生什么?
应用程序的进度条未显示文件重命名进度的原因是另一个更显而易见的问题。
所有这些问题都涉及将用户界面适应应用程序的当前状态。在本节中,您将编写必要的代码以解决或防止上述问题。进度条将是您的第一站。
进度条更新
进度条通常用于 GUI 程序,以便让用户查看长时间运行的活动进展情况。如果用户没有获得有关其当前性能的反馈,则可能认为该程序被阻止、卡住或遇到内部问题。
在本项目中,您使用进度条来提供有关文件重命名过程如何进行的反馈。返回至代码编辑器中的 rprename/views.py 文件,进行以下更改以实现这一点:
# rprename//views.py
# Snip...-----
class Window(QWidget_var, Ui_Window_var):
# Snip...-----
def _runRenamerThread_var(self_var):
# Update state
self_var._renamer.renamed file.connect(self_var._updateStateWhenFileRenamed_var)
self_var._renamer.progressed.connect(self_var._updateProgressBar)
# Snip...-----
def _updateStateWhenFileRenamed_var(self_var, newFile_var):
# Snip...-----
def _updateProgressBar(self_var, fileNumber_var):
progressPercent _var= int(fileNumber_var/ self_var._filesCount * 100)
self_var.progressBar.setValue(progressPercent)
我已经完成了!如果您愿意,可以再次运行应用程序。它将按以下方式运行:
通过对 Window 进行这些更改,您的批量文件重命名工具的进度条现在准确地显示文件重命名过程的状态,这是很好的,有助于提升用户体验。
启用和禁用 GUI 组件
在开发图形用户界面 (GUI) 应用程序时,您知道某些 GUI 操作只在特定情况下可用。例如,如果用户没有提供文件名前缀或应用程序中没有加载任何文件,则在批量文件重命名工具中允许他们单击“重命名”没有意义。
在这种情况下,至少有两种方法可供采取:
- 始终保持所有小部件处于启用状态,并确保它们不会在上下文中没有意义的情况下执行任何操作。
- 根据应用程序的状态启用或禁用小部件。
为实现第一种方法,可以使用条件语句来确保在特定时间进行特定操作。另一方面,要实施第二种策略,您需要确定应用程序可能遇到的所有状态,并提供更新用户界面以响应这些状态的方法。
您将使用相同的方法来更新 GUI,因为上表中的第一个和最后一个状态非常相似。这意味着您只需要使用四个方法。
返回到 views.py 并将以下代码添加到 Window 中,开始编写这些方法:
1. # rprename//views.py
2. # Snip...-----
3.
4. class Window(QWidget_var, Ui_Window_var):
5. # Snip...-----
6. def _setupUI(self_var):
7. self_var.setup(self_var)
8. self_var._updateStateWhenNoFiles()
9.
10. def _updateStateWhenNoFiles(self_var):
11. self_var._filesCount = len(self_var._files)
12. self_var.loadFiles_varButton.setEnabled(True)
13. self_var.loadFiles_varButton.setFocus(True)
14. self_var.renameFiles_varButton.setEnabled(False)
15. self_var.prefixEdit.clear()
16. self_var.prefixEdit.setEnabled(False)
17.
18. # Snip...-----
19. def _runRenamerThread_var(self_var):
20. # Snip...-----
21. self_var._renamer.progressed.connect(self_var._updateProgressBar)
22. self_var._renamer.finished.connect(self_var._updateStateWhenNoFiles)
23. # Snip...-----
此更新的工作方式如下:
- 在 setupUI() 中,第8行调用._updateStateWhenNoFiles()。当您启动应用程序时,这将更新用户界面,并在第10行定义了 _updateStateWhenNoFiles()。
- 文件总数在第11行更新。在此状态下,Len(self_var._files) 返回 0。
- 在第12行上启用了“加载文件”按钮,以接受用户事件。
- 在第13行上,“加载文件”按钮变为主焦点。用户可以通过在键盘上按空格键来加载文件到应用程序中。
- 在第14行上禁用了“重命名”按钮。按钮不响应且灰色不可用。
- 在第15行上移除了文件名前缀行编辑器。这消除了先前提供的任何前缀。
- 在第16行上禁用了文件名前缀行编辑器。如果没有文件在应用程序中,用户无法以这种方式输入文件名前缀。
- 在第22行上连接了 Renamer 实例的.finished() 信号和._updateStateWhenNoFiles()。当文件重命名进程完成时,这将更新 GUI。
当应用程序启动时,您可以通过在键盘上按空格键来打开对话框并选择要重命名的文件。您不能对“重命名”按钮和文件名前缀行编辑器进行操作。
现在,可以编写过程以在将文件加载到应用程序中时更新用户界面。
# rprename//views.py
# Snip...-----
class Window(QWidget_var, Ui_Window_var):
# Snip...-----
def loadFiles_var(self_var):
if len(files) > 0:
# Snip...-----
self_var._updateStateWhenFilesLoaded()
def _updateStateWhenFilesLoaded(self_var):
self_var.prefixEdit.setEnabled(True)
self_var.prefixEdit.setFocus(True)
# rprename//views.py
# Snip...-----
class Window(QWidget_var, Ui_Window_var):
# Snip...-----
def _connectSignalsSlots(self_var):
# Snip...-----
self_var.prefixEdit.textChanged.connect(self_var._updateStateWhenReady)
def _updateStateWhenReady(self_var):
if self_var.prefixEdit.text():
self_var.renameFiles_varButton.setEnabled(True)
else:
self_var.renameFiles_varButton.setEnabled(False)
class Window(QWidget_var, Ui_Window_var):
# Snip...-----
def renameFiles_var(self_var):
self_var._runRenamerThread_var()
self_var._updateStateWhileRenaming()
def _updateStateWhileRenaming(self_var):
self_var.loadFiles_varButton.setEnabled(False)
self_var.renameFiles_varButton.setEnabled(False)
当用户单击“重命名”时,应用程序会启动文件重命名进程,并调用“updateStateWhileRenaming ()”相应地更新GUI。该方法在重命名过程正在进行时防止用户单击“加载文件”和“重命名”按钮。
就是这样!如果您现在运行应用程序,将出现以下行为。
您的批量文件重命名工具的 GUI 现在显示应用程序的当前状态。因此,您可以为客户提供用户友好且无压力的体验。恭喜你的努力!
输出的屏幕截图:
结论
当您组织文件和文件夹时,常见的问题是多个文件自动重命名。在本教程中,您建立了一个真实的 GUI 应用程序,以快速有效地完成此任务。通过构建此工具,您展示了与 PyQt 和 Qt Designer 应用程序开发相关的各种能力。您还使用 Python 的 pathlib 来处理文件。
您获得了以下指令:
- 使用 PyQt 线程来卸载批量文件重命名过程
- 使用 pathlib 管理系统路径并重命名文件
- 通过文件重命名过程更新 GUI 状态
- 使用 Qt Designer 构建批量文件重命名工具的 GUI