├── README.md ├── res └── icon │ ├── add.png │ ├── app.ico │ ├── top.png │ ├── up.png │ ├── audio.png │ ├── bottom.png │ ├── cancel.png │ ├── clear.png │ ├── down.png │ ├── error.png │ ├── folder.png │ ├── image.png │ ├── list.png │ ├── office.png │ ├── quit.png │ ├── remove.png │ ├── rename.png │ ├── right.png │ ├── skip.png │ ├── video.png │ ├── archive.png │ ├── occupied.png │ ├── unknown.png │ ├── warning.png │ ├── cancelled.png │ └── not_exist.png ├── nuitka.txt ├── module ├── function_class.py ├── function_file_item.py ├── class_ │ ├── class_rename_rule.py │ ├── class_rename_info.py │ ├── class_rename_method.py │ └── class_rename_type.py ├── WindowsSorted.py └── function_normal.py ├── main.py └── ui ├── widget_replace.py ├── src ├── ui_widget_exec.ui ├── ui_widget_exec.py ├── ui_widget_file_extension.ui ├── ui_widget_replace.ui ├── ui_widget_replace.py ├── ui_widget_file_extension.py ├── ui_widget_filename_standard.ui ├── ui_widget_file_list.ui ├── ui_widget_insert.ui ├── ui_main.py ├── ui_widget_file_list.py ├── ui_main.ui ├── ui_widget_filename_standard.py ├── ui_widget_insert.py ├── ui_widget_delete.ui ├── ui_widget_delete.py ├── ui_widget_filename_pattern.py └── ui_widget_filename_pattern.ui ├── widget_exec.py ├── widget_filename_standard.py ├── main_window.py ├── widget_insert.py ├── widget_file_list.py ├── widget_file_extension.py ├── widget_delete.py ├── widget_filename_pattern.py └── tableWidget_file_list.py /README.md: -------------------------------------------------------------------------------- 1 | # Easy-ReNamer 2 | 文件重命名 3 | -------------------------------------------------------------------------------- /res/icon/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPJUST/Easy-ReNamer/HEAD/res/icon/add.png -------------------------------------------------------------------------------- /res/icon/app.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPJUST/Easy-ReNamer/HEAD/res/icon/app.ico -------------------------------------------------------------------------------- /res/icon/top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPJUST/Easy-ReNamer/HEAD/res/icon/top.png -------------------------------------------------------------------------------- /res/icon/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPJUST/Easy-ReNamer/HEAD/res/icon/up.png -------------------------------------------------------------------------------- /res/icon/audio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPJUST/Easy-ReNamer/HEAD/res/icon/audio.png -------------------------------------------------------------------------------- /res/icon/bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPJUST/Easy-ReNamer/HEAD/res/icon/bottom.png -------------------------------------------------------------------------------- /res/icon/cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPJUST/Easy-ReNamer/HEAD/res/icon/cancel.png -------------------------------------------------------------------------------- /res/icon/clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPJUST/Easy-ReNamer/HEAD/res/icon/clear.png -------------------------------------------------------------------------------- /res/icon/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPJUST/Easy-ReNamer/HEAD/res/icon/down.png -------------------------------------------------------------------------------- /res/icon/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPJUST/Easy-ReNamer/HEAD/res/icon/error.png -------------------------------------------------------------------------------- /res/icon/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPJUST/Easy-ReNamer/HEAD/res/icon/folder.png -------------------------------------------------------------------------------- /res/icon/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPJUST/Easy-ReNamer/HEAD/res/icon/image.png -------------------------------------------------------------------------------- /res/icon/list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPJUST/Easy-ReNamer/HEAD/res/icon/list.png -------------------------------------------------------------------------------- /res/icon/office.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPJUST/Easy-ReNamer/HEAD/res/icon/office.png -------------------------------------------------------------------------------- /res/icon/quit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPJUST/Easy-ReNamer/HEAD/res/icon/quit.png -------------------------------------------------------------------------------- /res/icon/remove.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPJUST/Easy-ReNamer/HEAD/res/icon/remove.png -------------------------------------------------------------------------------- /res/icon/rename.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPJUST/Easy-ReNamer/HEAD/res/icon/rename.png -------------------------------------------------------------------------------- /res/icon/right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPJUST/Easy-ReNamer/HEAD/res/icon/right.png -------------------------------------------------------------------------------- /res/icon/skip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPJUST/Easy-ReNamer/HEAD/res/icon/skip.png -------------------------------------------------------------------------------- /res/icon/video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPJUST/Easy-ReNamer/HEAD/res/icon/video.png -------------------------------------------------------------------------------- /res/icon/archive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPJUST/Easy-ReNamer/HEAD/res/icon/archive.png -------------------------------------------------------------------------------- /res/icon/occupied.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPJUST/Easy-ReNamer/HEAD/res/icon/occupied.png -------------------------------------------------------------------------------- /res/icon/unknown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPJUST/Easy-ReNamer/HEAD/res/icon/unknown.png -------------------------------------------------------------------------------- /res/icon/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPJUST/Easy-ReNamer/HEAD/res/icon/warning.png -------------------------------------------------------------------------------- /res/icon/cancelled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPJUST/Easy-ReNamer/HEAD/res/icon/cancelled.png -------------------------------------------------------------------------------- /res/icon/not_exist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPJUST/Easy-ReNamer/HEAD/res/icon/not_exist.png -------------------------------------------------------------------------------- /nuitka.txt: -------------------------------------------------------------------------------- 1 | python -m nuitka --standalone --onefile --remove-output --mingw64 --windows-console-mode=disable --windows-icon-from-ico=res\icon\app.ico --enable-plugin=pyside6 main.py -------------------------------------------------------------------------------- /module/function_class.py: -------------------------------------------------------------------------------- 1 | 2 | def get_subclasses(cls): 3 | """获取所有子类对象""" 4 | subclasses = [] 5 | for subclass in cls.__subclasses__(): 6 | subclasses.append(subclass) 7 | subclasses.extend(get_subclasses(subclass)) 8 | return subclasses 9 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from PySide6.QtGui import QPalette, QColor 2 | from PySide6.QtWidgets import QApplication 3 | 4 | from ui.main_window import MainWindow 5 | 6 | if __name__ == '__main__': 7 | app = QApplication() 8 | app.setStyle('Fusion') 9 | palette = QPalette() 10 | palette.setColor(QPalette.Window, QColor(255, 255, 255)) 11 | app.setPalette(palette) 12 | show_ui = MainWindow() 13 | show_ui.setMinimumSize(800, 530) 14 | show_ui.setWindowTitle('Easy ReNamer v1.0.6') 15 | show_ui.show() 16 | app.exec() 17 | -------------------------------------------------------------------------------- /module/function_file_item.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import os 3 | 4 | import filetype 5 | 6 | from res.icon import * 7 | 8 | 9 | def get_icon_filetype(path: str): 10 | """获取路径对应的文件类型图标""" 11 | if not os.path.exists(path): 12 | return base64.b64decode(warning_base64) 13 | elif os.path.isdir(path): 14 | return base64.b64decode(folder_base64) 15 | elif filetype.is_image(path): 16 | return base64.b64decode(image_base64) 17 | elif filetype.is_document(path): 18 | return base64.b64decode(office_base64) 19 | elif filetype.is_archive(path): 20 | return base64.b64decode(archive_base64) 21 | elif filetype.is_video(path): 22 | return base64.b64decode(video_base64) 23 | elif filetype.is_audio(path): 24 | return base64.b64decode(audio_base64) 25 | else: 26 | return base64.b64decode(unknown_base64) 27 | 28 | 29 | def get_filename(path: str): 30 | """获取路径对应的文件名""" 31 | return os.path.basename(path) 32 | -------------------------------------------------------------------------------- /ui/widget_replace.py: -------------------------------------------------------------------------------- 1 | from PySide6.QtCore import Signal 2 | from PySide6.QtWidgets import QWidget, QApplication 3 | 4 | from module.class_.class_rename_type import TypeNormal 5 | from ui.src.ui_widget_replace import Ui_Form 6 | 7 | 8 | class WidgetReplace(QWidget): 9 | signal_replace = Signal(TypeNormal) 10 | 11 | def __init__(self, parent=None): 12 | super().__init__(parent) 13 | self.ui = Ui_Form() 14 | self.ui.setupUi(self) 15 | 16 | self.ui.checkBox_replace.stateChanged.connect(self.emit_signal) 17 | self.ui.lineEdit_old_character.textChanged.connect(self.prefix_changed) 18 | self.ui.lineEdit_new_character.textChanged.connect(self.prefix_changed) 19 | 20 | def emit_signal(self): 21 | """发送信号""" 22 | old_char = self.ui.lineEdit_old_character.text() 23 | new_char = self.ui.lineEdit_new_character.text() 24 | if len(old_char) and len(new_char): 25 | is_enable = self.ui.checkBox_replace.isChecked() 26 | else: 27 | is_enable = False 28 | 29 | rule_class = TypeNormal.Replace(is_enable, old_char, new_char) 30 | self.signal_replace.emit(rule_class) 31 | 32 | def prefix_changed(self): 33 | """选项变动""" 34 | text_old = self.ui.lineEdit_old_character.text() 35 | text_new = self.ui.lineEdit_new_character.text() 36 | if len(text_old) and len(text_new): 37 | self.ui.checkBox_replace.setChecked(True) 38 | else: 39 | self.ui.checkBox_replace.setChecked(False) 40 | 41 | self.emit_signal() 42 | 43 | 44 | if __name__ == '__main__': 45 | app = QApplication() 46 | app.setStyle('Fusion') # 设置风格 47 | show_ui = WidgetReplace() 48 | show_ui.show() 49 | app.exec() 50 | -------------------------------------------------------------------------------- /ui/src/ui_widget_exec.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 257 10 | 55 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 3 19 | 20 | 21 | 3 22 | 23 | 24 | 3 25 | 26 | 27 | 3 28 | 29 | 30 | 3 31 | 32 | 33 | 34 | 35 | 自动处理命名冲突 36 | 37 | 38 | 39 | 40 | 41 | 42 | 12 43 | 44 | 45 | 46 | 47 | 重命名 48 | 49 | 50 | 51 | 52 | 53 | 54 | 撤销 55 | 56 | 57 | 58 | 59 | 60 | 61 | 退出 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /ui/widget_exec.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import sys 3 | 4 | from PySide6.QtCore import Signal 5 | from PySide6.QtGui import QPixmap, QIcon 6 | from PySide6.QtWidgets import QWidget, QApplication 7 | 8 | from res.icon import * 9 | from ui.src.ui_widget_exec import Ui_Form 10 | 11 | 12 | class WidgetExec(QWidget): 13 | signal_rename = Signal(bool) 14 | signal_cancel = Signal() 15 | 16 | def __init__(self, parent=None): 17 | super().__init__(parent) 18 | self.ui = Ui_Form() 19 | self.ui.setupUi(self) 20 | 21 | self.set_tips() 22 | self._set_icon() 23 | 24 | # 设置槽函数 25 | self.ui.pushButton_quit.clicked.connect(lambda: sys.exit()) 26 | self.ui.pushButton_rename.clicked.connect(self.emit_signal_rename) 27 | self.ui.pushButton_cancel.clicked.connect(self.emit_signal_cancel) 28 | 29 | def set_tips(self): 30 | """设置说明文本""" 31 | tips_auto_dup = '不再弹出重复文件名提示,在文件名后自动添加后缀\n(参照Windows规则,在文件名后添加" (2)“等后缀' 32 | self.ui.checkBox_auto_deal_dup.setToolTip(tips_auto_dup) 33 | 34 | tips_cancel = '撤销对应文件的所有重命名操作,恢复到最开始的文件名' 35 | self.ui.pushButton_cancel.setToolTip(tips_cancel) 36 | 37 | def _set_icon(self): 38 | """设置图标""" 39 | pixmap = QPixmap() 40 | pixmap.loadFromData(base64.b64decode(rename_base64)) 41 | self.ui.pushButton_rename.setIcon(QIcon(pixmap)) 42 | 43 | pixmap = QPixmap() 44 | pixmap.loadFromData(base64.b64decode(cancel_base64)) 45 | self.ui.pushButton_cancel.setIcon(QIcon(pixmap)) 46 | 47 | pixmap = QPixmap() 48 | pixmap.loadFromData(base64.b64decode(quit_base64)) 49 | self.ui.pushButton_quit.setIcon(QIcon(pixmap)) 50 | 51 | def emit_signal_rename(self): 52 | """发送信号""" 53 | self.signal_rename.emit(self.ui.checkBox_auto_deal_dup.isChecked()) 54 | 55 | def emit_signal_cancel(self): 56 | """发送信号""" 57 | self.signal_cancel.emit() 58 | 59 | 60 | if __name__ == '__main__': 61 | app = QApplication() 62 | app.setStyle('Fusion') # 设置风格 63 | show_ui = WidgetExec() 64 | show_ui.show() 65 | app.exec() 66 | -------------------------------------------------------------------------------- /ui/src/ui_widget_exec.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ################################################################################ 4 | ## Form generated from reading UI file 'ui_widget_execyuUrNb.ui' 5 | ## 6 | ## Created by: Qt User Interface Compiler version 6.1.3 7 | ## 8 | ## WARNING! All changes made in this file will be lost when recompiling UI file! 9 | ################################################################################ 10 | 11 | from PySide6.QtCore import * # type: ignore 12 | from PySide6.QtGui import * # type: ignore 13 | from PySide6.QtWidgets import * # type: ignore 14 | 15 | 16 | class Ui_Form(object): 17 | def setupUi(self, Form): 18 | if not Form.objectName(): 19 | Form.setObjectName(u"Form") 20 | Form.resize(257, 55) 21 | self.verticalLayout = QVBoxLayout(Form) 22 | self.verticalLayout.setSpacing(3) 23 | self.verticalLayout.setObjectName(u"verticalLayout") 24 | self.verticalLayout.setContentsMargins(3, 3, 3, 3) 25 | self.checkBox_auto_deal_dup = QCheckBox(Form) 26 | self.checkBox_auto_deal_dup.setObjectName(u"checkBox_auto_deal_dup") 27 | 28 | self.verticalLayout.addWidget(self.checkBox_auto_deal_dup) 29 | 30 | self.horizontalLayout = QHBoxLayout() 31 | self.horizontalLayout.setSpacing(12) 32 | self.horizontalLayout.setObjectName(u"horizontalLayout") 33 | self.pushButton_rename = QPushButton(Form) 34 | self.pushButton_rename.setObjectName(u"pushButton_rename") 35 | 36 | self.horizontalLayout.addWidget(self.pushButton_rename) 37 | 38 | self.pushButton_cancel = QPushButton(Form) 39 | self.pushButton_cancel.setObjectName(u"pushButton_cancel") 40 | 41 | self.horizontalLayout.addWidget(self.pushButton_cancel) 42 | 43 | self.pushButton_quit = QPushButton(Form) 44 | self.pushButton_quit.setObjectName(u"pushButton_quit") 45 | 46 | self.horizontalLayout.addWidget(self.pushButton_quit) 47 | 48 | 49 | self.verticalLayout.addLayout(self.horizontalLayout) 50 | 51 | 52 | self.retranslateUi(Form) 53 | 54 | QMetaObject.connectSlotsByName(Form) 55 | # setupUi 56 | 57 | def retranslateUi(self, Form): 58 | Form.setWindowTitle(QCoreApplication.translate("Form", u"Form", None)) 59 | self.checkBox_auto_deal_dup.setText(QCoreApplication.translate("Form", u"\u81ea\u52a8\u5904\u7406\u547d\u540d\u51b2\u7a81", None)) 60 | self.pushButton_rename.setText(QCoreApplication.translate("Form", u"\u91cd\u547d\u540d", None)) 61 | self.pushButton_cancel.setText(QCoreApplication.translate("Form", u"\u64a4\u9500", None)) 62 | self.pushButton_quit.setText(QCoreApplication.translate("Form", u"\u9000\u51fa", None)) 63 | # retranslateUi 64 | 65 | -------------------------------------------------------------------------------- /ui/src/ui_widget_file_extension.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 174 10 | 123 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 3 19 | 20 | 21 | 3 22 | 23 | 24 | 3 25 | 26 | 27 | 3 28 | 29 | 30 | 3 31 | 32 | 33 | 34 | 35 | 文件扩展名 36 | 37 | 38 | 39 | 3 40 | 41 | 42 | 3 43 | 44 | 45 | 3 46 | 47 | 48 | 3 49 | 50 | 51 | 3 52 | 53 | 54 | 55 | 56 | 0 57 | 58 | 59 | 60 | 61 | 扩展名改为 62 | 63 | 64 | 65 | 66 | 67 | 68 | 新的扩展名 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 扩展名改为真实文件类型* 78 | 79 | 80 | 81 | 82 | 83 | 84 | 扩展名改为小写 85 | 86 | 87 | 88 | 89 | 90 | 91 | 扩展名改为大写 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /ui/widget_filename_standard.py: -------------------------------------------------------------------------------- 1 | from PySide6.QtCore import Signal 2 | from PySide6.QtWidgets import QWidget, QApplication 3 | 4 | from module.class_.class_rename_type import TypeConvert 5 | from ui.src.ui_widget_filename_standard import Ui_Form 6 | 7 | 8 | class WidgetFilenameStandard(QWidget): 9 | signal_standard_letter = Signal(TypeConvert) 10 | signal_standard_chinese = Signal(TypeConvert) 11 | signal_standard_character = Signal(TypeConvert) 12 | signal_excess_spaces = Signal(TypeConvert) 13 | 14 | def __init__(self, parent=None): 15 | super().__init__(parent) 16 | self.ui = Ui_Form() 17 | self.ui.setupUi(self) 18 | 19 | self.set_tips() 20 | 21 | self.ui.checkBox_letter.stateChanged.connect(self.emit_signal_letter) 22 | self.ui.comboBox_letter.currentTextChanged.connect(self.signal_letter_changed) 23 | self.ui.checkBox_chinese.stateChanged.connect(self.emit_signal_chinese) 24 | self.ui.comboBox_chinese.currentTextChanged.connect(self.chinese_changed) 25 | self.ui.checkBox_character.stateChanged.connect(self.emit_signal_character) 26 | self.ui.comboBox_character.currentTextChanged.connect(self.character_changed) 27 | self.ui.checkBox_excess_spaces.stateChanged.connect(self.emit_signal_excess_spaces) 28 | 29 | def set_tips(self): 30 | """设置说明文本""" 31 | tips_auto_dig = '删除两端的空格、连续的空格' 32 | self.ui.checkBox_excess_spaces.setToolTip(tips_auto_dig) 33 | 34 | def emit_signal_letter(self): 35 | """发送信号""" 36 | is_enable = self.ui.checkBox_letter.isChecked() 37 | type_ = self.ui.comboBox_letter.currentText() 38 | 39 | rule_class = TypeConvert.ConvertLetter(is_enable, type_) 40 | self.signal_standard_letter.emit(rule_class) 41 | 42 | def signal_letter_changed(self): 43 | """选项变动""" 44 | # 变动后直接勾选即可 45 | self.ui.checkBox_letter.setChecked(True) 46 | self.emit_signal_letter() 47 | 48 | def emit_signal_chinese(self): 49 | """发送信号""" 50 | is_enable = self.ui.checkBox_chinese.isChecked() 51 | type_ = self.ui.comboBox_chinese.currentText() 52 | 53 | rule_class = TypeConvert.ConvertChinese(is_enable, type_) 54 | self.signal_standard_chinese.emit(rule_class) 55 | 56 | def chinese_changed(self): 57 | """选项变动""" 58 | # 变动后直接勾选即可 59 | self.ui.checkBox_chinese.setChecked(True) 60 | self.emit_signal_chinese() 61 | 62 | def emit_signal_character(self): 63 | """发送信号""" 64 | is_enable = self.ui.checkBox_character.isChecked() 65 | type_ = self.ui.comboBox_character.currentText() 66 | 67 | rule_class = TypeConvert.ConvertCharacter(is_enable, type_) 68 | self.signal_standard_character.emit(rule_class) 69 | 70 | def character_changed(self): 71 | """选项变动""" 72 | # 变动后直接勾选即可 73 | self.ui.checkBox_character.setChecked(True) 74 | self.emit_signal_character() 75 | 76 | def emit_signal_excess_spaces(self): 77 | """发送信号""" 78 | is_enable = self.ui.checkBox_excess_spaces.isChecked() 79 | 80 | rule_class = TypeConvert.ClearExcessSpaces(is_enable) 81 | self.signal_excess_spaces.emit(rule_class) 82 | 83 | 84 | if __name__ == '__main__': 85 | app = QApplication() 86 | app.setStyle('Fusion') # 设置风格 87 | show_ui = WidgetFilenameStandard() 88 | show_ui.show() 89 | app.exec() 90 | -------------------------------------------------------------------------------- /ui/src/ui_widget_replace.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 166 10 | 77 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 0 19 | 20 | 21 | 3 22 | 23 | 24 | 3 25 | 26 | 27 | 3 28 | 29 | 30 | 3 31 | 32 | 33 | 34 | 35 | 替换 36 | 37 | 38 | false 39 | 40 | 41 | false 42 | 43 | 44 | 45 | 3 46 | 47 | 48 | 3 49 | 50 | 51 | 3 52 | 53 | 54 | 3 55 | 56 | 57 | 3 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 被替换的字符 66 | 67 | 68 | 69 | 70 | 71 | 72 | Qt::ContextMenuPolicy::DefaultContextMenu 73 | 74 | 75 | Qt::LayoutDirection::LeftToRight 76 | 77 | 78 | 将文件名中的 79 | 80 | 81 | 82 | 83 | 84 | 85 | Qt::LayoutDirection::LeftToRight 86 | 87 | 88 | false 89 | 90 | 91 | 替换为 92 | 93 | 94 | Qt::AlignmentFlag::AlignCenter 95 | 96 | 97 | 98 | 99 | 100 | 101 | 新的字符 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /ui/src/ui_widget_replace.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ################################################################################ 4 | ## Form generated from reading UI file 'ui_widget_replaceibnmra.ui' 5 | ## 6 | ## Created by: Qt User Interface Compiler version 6.8.1 7 | ## 8 | ## WARNING! All changes made in this file will be lost when recompiling UI file! 9 | ################################################################################ 10 | 11 | from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, 12 | QMetaObject, QObject, QPoint, QRect, 13 | QSize, QTime, QUrl, Qt) 14 | from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, 15 | QFont, QFontDatabase, QGradient, QIcon, 16 | QImage, QKeySequence, QLinearGradient, QPainter, 17 | QPalette, QPixmap, QRadialGradient, QTransform) 18 | from PySide6.QtWidgets import (QApplication, QCheckBox, QGridLayout, QGroupBox, 19 | QLabel, QLineEdit, QSizePolicy, QVBoxLayout, 20 | QWidget) 21 | 22 | class Ui_Form(object): 23 | def setupUi(self, Form): 24 | if not Form.objectName(): 25 | Form.setObjectName(u"Form") 26 | Form.resize(166, 77) 27 | self.verticalLayout = QVBoxLayout(Form) 28 | self.verticalLayout.setSpacing(0) 29 | self.verticalLayout.setObjectName(u"verticalLayout") 30 | self.verticalLayout.setContentsMargins(3, 3, 3, 3) 31 | self.groupBox = QGroupBox(Form) 32 | self.groupBox.setObjectName(u"groupBox") 33 | self.groupBox.setFlat(False) 34 | self.groupBox.setCheckable(False) 35 | self.gridLayout_2 = QGridLayout(self.groupBox) 36 | self.gridLayout_2.setSpacing(3) 37 | self.gridLayout_2.setObjectName(u"gridLayout_2") 38 | self.gridLayout_2.setContentsMargins(3, 3, 3, 3) 39 | self.lineEdit_old_character = QLineEdit(self.groupBox) 40 | self.lineEdit_old_character.setObjectName(u"lineEdit_old_character") 41 | 42 | self.gridLayout_2.addWidget(self.lineEdit_old_character, 0, 1, 1, 1) 43 | 44 | self.checkBox_replace = QCheckBox(self.groupBox) 45 | self.checkBox_replace.setObjectName(u"checkBox_replace") 46 | self.checkBox_replace.setContextMenuPolicy(Qt.ContextMenuPolicy.DefaultContextMenu) 47 | self.checkBox_replace.setLayoutDirection(Qt.LayoutDirection.LeftToRight) 48 | 49 | self.gridLayout_2.addWidget(self.checkBox_replace, 0, 0, 1, 1) 50 | 51 | self.label_2 = QLabel(self.groupBox) 52 | self.label_2.setObjectName(u"label_2") 53 | self.label_2.setLayoutDirection(Qt.LayoutDirection.LeftToRight) 54 | self.label_2.setAutoFillBackground(False) 55 | self.label_2.setAlignment(Qt.AlignmentFlag.AlignCenter) 56 | 57 | self.gridLayout_2.addWidget(self.label_2, 1, 0, 1, 1) 58 | 59 | self.lineEdit_new_character = QLineEdit(self.groupBox) 60 | self.lineEdit_new_character.setObjectName(u"lineEdit_new_character") 61 | 62 | self.gridLayout_2.addWidget(self.lineEdit_new_character, 1, 1, 1, 1) 63 | 64 | 65 | self.verticalLayout.addWidget(self.groupBox) 66 | 67 | 68 | self.retranslateUi(Form) 69 | 70 | QMetaObject.connectSlotsByName(Form) 71 | # setupUi 72 | 73 | def retranslateUi(self, Form): 74 | Form.setWindowTitle(QCoreApplication.translate("Form", u"Form", None)) 75 | self.groupBox.setTitle(QCoreApplication.translate("Form", u"\u66ff\u6362", None)) 76 | self.lineEdit_old_character.setText("") 77 | self.lineEdit_old_character.setPlaceholderText(QCoreApplication.translate("Form", u"\u88ab\u66ff\u6362\u7684\u5b57\u7b26", None)) 78 | self.checkBox_replace.setText(QCoreApplication.translate("Form", u"\u5c06\u6587\u4ef6\u540d\u4e2d\u7684", None)) 79 | self.label_2.setText(QCoreApplication.translate("Form", u"\u66ff\u6362\u4e3a", None)) 80 | self.lineEdit_new_character.setPlaceholderText(QCoreApplication.translate("Form", u"\u65b0\u7684\u5b57\u7b26", None)) 81 | # retranslateUi 82 | 83 | -------------------------------------------------------------------------------- /ui/src/ui_widget_file_extension.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ################################################################################ 4 | ## Form generated from reading UI file 'ui_widget_file_extensionxHeoaj.ui' 5 | ## 6 | ## Created by: Qt User Interface Compiler version 6.8.1 7 | ## 8 | ## WARNING! All changes made in this file will be lost when recompiling UI file! 9 | ################################################################################ 10 | 11 | from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, 12 | QMetaObject, QObject, QPoint, QRect, 13 | QSize, QTime, QUrl, Qt) 14 | from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, 15 | QFont, QFontDatabase, QGradient, QIcon, 16 | QImage, QKeySequence, QLinearGradient, QPainter, 17 | QPalette, QPixmap, QRadialGradient, QTransform) 18 | from PySide6.QtWidgets import (QApplication, QCheckBox, QGroupBox, QHBoxLayout, 19 | QLineEdit, QSizePolicy, QVBoxLayout, QWidget) 20 | 21 | class Ui_Form(object): 22 | def setupUi(self, Form): 23 | if not Form.objectName(): 24 | Form.setObjectName(u"Form") 25 | Form.resize(174, 123) 26 | self.verticalLayout = QVBoxLayout(Form) 27 | self.verticalLayout.setSpacing(3) 28 | self.verticalLayout.setObjectName(u"verticalLayout") 29 | self.verticalLayout.setContentsMargins(3, 3, 3, 3) 30 | self.groupBox = QGroupBox(Form) 31 | self.groupBox.setObjectName(u"groupBox") 32 | self.verticalLayout_2 = QVBoxLayout(self.groupBox) 33 | self.verticalLayout_2.setSpacing(3) 34 | self.verticalLayout_2.setObjectName(u"verticalLayout_2") 35 | self.verticalLayout_2.setContentsMargins(3, 3, 3, 3) 36 | self.horizontalLayout = QHBoxLayout() 37 | self.horizontalLayout.setSpacing(0) 38 | self.horizontalLayout.setObjectName(u"horizontalLayout") 39 | self.checkBox_change_to = QCheckBox(self.groupBox) 40 | self.checkBox_change_to.setObjectName(u"checkBox_change_to") 41 | 42 | self.horizontalLayout.addWidget(self.checkBox_change_to) 43 | 44 | self.lineEdit_file_extension = QLineEdit(self.groupBox) 45 | self.lineEdit_file_extension.setObjectName(u"lineEdit_file_extension") 46 | 47 | self.horizontalLayout.addWidget(self.lineEdit_file_extension) 48 | 49 | self.horizontalLayout.setStretch(1, 1) 50 | 51 | self.verticalLayout_2.addLayout(self.horizontalLayout) 52 | 53 | self.checkBox_real_type = QCheckBox(self.groupBox) 54 | self.checkBox_real_type.setObjectName(u"checkBox_real_type") 55 | 56 | self.verticalLayout_2.addWidget(self.checkBox_real_type) 57 | 58 | self.checkBox_lowercase = QCheckBox(self.groupBox) 59 | self.checkBox_lowercase.setObjectName(u"checkBox_lowercase") 60 | 61 | self.verticalLayout_2.addWidget(self.checkBox_lowercase) 62 | 63 | self.checkBox_capital = QCheckBox(self.groupBox) 64 | self.checkBox_capital.setObjectName(u"checkBox_capital") 65 | 66 | self.verticalLayout_2.addWidget(self.checkBox_capital) 67 | 68 | 69 | self.verticalLayout.addWidget(self.groupBox) 70 | 71 | 72 | self.retranslateUi(Form) 73 | 74 | QMetaObject.connectSlotsByName(Form) 75 | # setupUi 76 | 77 | def retranslateUi(self, Form): 78 | Form.setWindowTitle(QCoreApplication.translate("Form", u"Form", None)) 79 | self.groupBox.setTitle(QCoreApplication.translate("Form", u"\u6587\u4ef6\u6269\u5c55\u540d", None)) 80 | self.checkBox_change_to.setText(QCoreApplication.translate("Form", u"\u6269\u5c55\u540d\u6539\u4e3a", None)) 81 | self.lineEdit_file_extension.setPlaceholderText(QCoreApplication.translate("Form", u"\u65b0\u7684\u6269\u5c55\u540d", None)) 82 | self.checkBox_real_type.setText(QCoreApplication.translate("Form", u"\u6269\u5c55\u540d\u6539\u4e3a\u771f\u5b9e\u6587\u4ef6\u7c7b\u578b*", None)) 83 | self.checkBox_lowercase.setText(QCoreApplication.translate("Form", u"\u6269\u5c55\u540d\u6539\u4e3a\u5c0f\u5199", None)) 84 | self.checkBox_capital.setText(QCoreApplication.translate("Form", u"\u6269\u5c55\u540d\u6539\u4e3a\u5927\u5199", None)) 85 | # retranslateUi 86 | 87 | -------------------------------------------------------------------------------- /ui/src/ui_widget_filename_standard.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 141 10 | 131 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 3 19 | 20 | 21 | 3 22 | 23 | 24 | 3 25 | 26 | 27 | 3 28 | 29 | 30 | 3 31 | 32 | 33 | 34 | 35 | 标准化 36 | 37 | 38 | 39 | 3 40 | 41 | 42 | 3 43 | 44 | 45 | 3 46 | 47 | 48 | 3 49 | 50 | 51 | 3 52 | 53 | 54 | 55 | 56 | 3 57 | 58 | 59 | 60 | 61 | 字母改为 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 小写 70 | 71 | 72 | 73 | 74 | 大写 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 3 85 | 86 | 87 | 88 | 89 | 汉字改为 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 简体 98 | 99 | 100 | 101 | 102 | 繁体 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 3 113 | 114 | 115 | 116 | 117 | 字符改为 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 半角 126 | 127 | 128 | 129 | 130 | 全角 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 清除多余空格 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /ui/main_window.py: -------------------------------------------------------------------------------- 1 | import base64 2 | 3 | from PySide6.QtGui import QPixmap, QIcon 4 | from PySide6.QtWidgets import QMainWindow 5 | 6 | from module.class_.class_rename_rule import RenameRule 7 | from res.icon import app_base64 8 | from ui.src.ui_main import Ui_MainWindow 9 | from ui.widget_delete import WidgetDelete 10 | from ui.widget_exec import WidgetExec 11 | from ui.widget_file_extension import WidgetFileExtension 12 | from ui.widget_file_list import WidgetFileList 13 | from ui.widget_filename_pattern import WidgetFilenamePattern 14 | from ui.widget_filename_standard import WidgetFilenameStandard 15 | from ui.widget_insert import WidgetInsert 16 | from ui.widget_replace import WidgetReplace 17 | 18 | 19 | class MainWindow(QMainWindow): 20 | 21 | def __init__(self, parent=None): 22 | super().__init__(parent) 23 | self.ui = Ui_MainWindow() 24 | self.ui.setupUi(self) 25 | 26 | # 设置图标 27 | pixmap = QPixmap() 28 | pixmap.loadFromData(base64.b64decode(app_base64)) 29 | self.setWindowIcon(QIcon(pixmap)) 30 | 31 | self.class_collect = RenameRule() 32 | 33 | # 添加控件组1 34 | # 模板控件 35 | self.widget_filename_pattern = WidgetFilenamePattern() 36 | self.ui.tab_pattern.layout().addWidget(self.widget_filename_pattern) 37 | self.widget_filename_pattern.signal_pattern.connect(self.collect_rename_rule) 38 | # 标准文件名控件 39 | self.widget_filename_standard = WidgetFilenameStandard() 40 | self.widget_filename_pattern.ui.verticalLayout_standard.addWidget(self.widget_filename_standard) 41 | self.widget_filename_standard.signal_standard_letter.connect(self.collect_rename_rule) 42 | self.widget_filename_standard.signal_standard_chinese.connect(self.collect_rename_rule) 43 | self.widget_filename_standard.signal_standard_character.connect(self.collect_rename_rule) 44 | self.widget_filename_standard.signal_excess_spaces.connect(self.collect_rename_rule) 45 | # 后缀控件 46 | self.widget_file_extension = WidgetFileExtension() 47 | self.widget_filename_pattern.ui.verticalLayout_file_extension.addWidget(self.widget_file_extension) 48 | self.widget_file_extension.signal_file_extension_change_to.connect(self.collect_rename_rule) 49 | self.widget_file_extension.signal_convert_to_capital.connect(self.collect_rename_rule) 50 | self.widget_file_extension.signal_convert_to_lowercase.connect(self.collect_rename_rule) 51 | self.widget_file_extension.signal_change_to_real_type.connect(self.collect_rename_rule) 52 | 53 | # 添加控件组2 54 | # 增 55 | self.widget_insert = WidgetInsert() 56 | self.ui.verticalLayout_place.addWidget(self.widget_insert) 57 | self.widget_insert.signal_insert.connect(self.collect_rename_rule) 58 | self.widget_insert.signal_insert_back.connect(self.collect_rename_rule) 59 | self.widget_insert.signal_add_prefix.connect(self.collect_rename_rule) 60 | self.widget_insert.signal_add_suffix.connect(self.collect_rename_rule) 61 | # 删 62 | self.widget_delete = WidgetDelete() 63 | self.ui.verticalLayout_place.addWidget(self.widget_delete) 64 | self.widget_delete.signal_delete_character.connect(self.collect_rename_rule) 65 | self.widget_delete.signal_delete_index.connect(self.collect_rename_rule) 66 | self.widget_delete.signal_delete_index_back.connect(self.collect_rename_rule) 67 | self.widget_delete.signal_delete_index_after.connect(self.collect_rename_rule) 68 | self.widget_delete.signal_delete_index_after_back.connect(self.collect_rename_rule) 69 | # 改 70 | self.widget_replace = WidgetReplace() 71 | self.ui.verticalLayout_place.addWidget(self.widget_replace) 72 | self.widget_replace.signal_replace.connect(self.collect_rename_rule) 73 | 74 | # 添加控件组3 75 | # 文件列表 76 | self.widget_file_list = WidgetFileList(self) 77 | self.ui.widget_file_list.layout().addWidget(self.widget_file_list) 78 | # 执行功能区 79 | self.widget_exec = WidgetExec() 80 | self.ui.widget_exec.layout().addWidget(self.widget_exec) 81 | self.widget_exec.signal_rename.connect(self.run_rename) 82 | self.widget_exec.signal_cancel.connect(self.cancel_rename) 83 | 84 | def collect_rename_rule(self, rule_class): 85 | """收集重命名规则""" 86 | self.class_collect.update(rule_class) 87 | self.widget_file_list.calc_filename() 88 | 89 | def run_rename(self, is_auto_dup): 90 | """执行重命名操作""" 91 | self.widget_file_list.run_rename(is_auto_dup) 92 | 93 | def cancel_rename(self): 94 | """撤销重命名操作""" 95 | self.widget_file_list.cancel_rename() 96 | -------------------------------------------------------------------------------- /ui/widget_insert.py: -------------------------------------------------------------------------------- 1 | from PySide6.QtCore import Signal 2 | from PySide6.QtWidgets import QWidget, QApplication 3 | 4 | from module.class_.class_rename_type import TypeNormal 5 | from ui.src.ui_widget_insert import Ui_Form 6 | 7 | 8 | class WidgetInsert(QWidget): 9 | signal_add_prefix = Signal(TypeNormal) 10 | signal_add_suffix = Signal(TypeNormal) 11 | signal_insert = Signal(TypeNormal) 12 | signal_insert_back = Signal(TypeNormal) 13 | 14 | def __init__(self, parent=None): 15 | super().__init__(parent) 16 | self.ui = Ui_Form() 17 | self.ui.setupUi(self) 18 | 19 | self.insert_changed() 20 | 21 | self.ui.checkBox_add_prefix.stateChanged.connect(self.emit_signal_prefix) 22 | self.ui.lineEdit_add_prefix.textChanged.connect(self.prefix_changed) 23 | self.ui.checkBox_add_suffix.stateChanged.connect(self.emit_signal_suffix) 24 | self.ui.lineEdit_add_suffix.textChanged.connect(self.suffix_changed) 25 | self.ui.checkBox_insert_index.stateChanged.connect(self.emit_signal_insert) 26 | self.ui.spinBox_index.valueChanged.connect(self.insert_changed) 27 | self.ui.lineEdit_insert_character.textChanged.connect(self.insert_changed) 28 | self.ui.checkBox_insert_index_back.stateChanged.connect(self.emit_signal_insert_back) 29 | self.ui.spinBox_index_back.valueChanged.connect(self.insert_back_changed) 30 | self.ui.lineEdit_insert_character_back.textChanged.connect(self.insert_back_changed) 31 | 32 | def emit_signal_prefix(self): 33 | """发送信号""" 34 | char = self.ui.lineEdit_add_prefix.text() 35 | if len(char): 36 | is_enable = self.ui.checkBox_add_prefix.isChecked() 37 | else: 38 | is_enable = False 39 | 40 | rule_class = TypeNormal.AddPrefix(is_enable, char) 41 | self.signal_add_prefix.emit(rule_class) 42 | 43 | def prefix_changed(self): 44 | """选项变动""" 45 | text = self.ui.lineEdit_add_prefix.text() 46 | if len(text): 47 | self.ui.checkBox_add_prefix.setChecked(True) 48 | else: 49 | self.ui.checkBox_add_prefix.setChecked(False) 50 | 51 | self.emit_signal_prefix() 52 | 53 | def emit_signal_suffix(self): 54 | """发送信号""" 55 | char = self.ui.lineEdit_add_suffix.text() 56 | if len(char): 57 | is_enable = self.ui.checkBox_add_suffix.isChecked() 58 | else: 59 | is_enable = False 60 | 61 | rule_class = TypeNormal.AddSuffix(is_enable, char) 62 | self.signal_add_suffix.emit(rule_class) 63 | 64 | def suffix_changed(self): 65 | """选项变动""" 66 | text = self.ui.lineEdit_add_suffix.text() 67 | if len(text): 68 | self.ui.checkBox_add_suffix.setChecked(True) 69 | else: 70 | self.ui.checkBox_add_suffix.setChecked(False) 71 | 72 | self.emit_signal_suffix() 73 | 74 | def emit_signal_insert(self): 75 | """发送信号""" 76 | index = self.ui.spinBox_index.value() 77 | char = self.ui.lineEdit_insert_character.text() 78 | if len(char): 79 | is_enable = self.ui.checkBox_insert_index.isChecked() 80 | else: 81 | is_enable = False 82 | 83 | rule_class = TypeNormal.Insert(is_enable, index, char) 84 | self.signal_insert.emit(rule_class) 85 | 86 | def insert_changed(self): 87 | """选项变动""" 88 | text = self.ui.lineEdit_insert_character.text() 89 | if len(text): 90 | self.ui.checkBox_insert_index.setChecked(True) 91 | else: 92 | self.ui.checkBox_insert_index.setChecked(False) 93 | 94 | self.emit_signal_insert() 95 | 96 | def emit_signal_insert_back(self): 97 | """发送信号""" 98 | index = self.ui.spinBox_index_back.value() 99 | char = self.ui.lineEdit_insert_character_back.text() 100 | if len(char): 101 | is_enable = self.ui.checkBox_insert_index_back.isChecked() 102 | else: 103 | is_enable = False 104 | 105 | rule_class = TypeNormal.InsertBack(is_enable, index, char) 106 | self.signal_insert_back.emit(rule_class) 107 | 108 | def insert_back_changed(self): 109 | """选项变动""" 110 | text = self.ui.lineEdit_insert_character_back.text() 111 | if len(text): 112 | self.ui.checkBox_insert_index_back.setChecked(True) 113 | else: 114 | self.ui.checkBox_insert_index_back.setChecked(False) 115 | 116 | self.emit_signal_insert_back() 117 | 118 | 119 | if __name__ == '__main__': 120 | app = QApplication() 121 | app.setStyle('Fusion') # 设置风格 122 | show_ui = WidgetInsert() 123 | show_ui.show() 124 | app.exec() 125 | -------------------------------------------------------------------------------- /ui/widget_file_list.py: -------------------------------------------------------------------------------- 1 | import base64 2 | 3 | from PySide6.QtCore import Signal 4 | from PySide6.QtGui import QPixmap, QIcon 5 | from PySide6.QtWidgets import QWidget, QApplication, QFileDialog 6 | 7 | from res.icon import * 8 | from ui.src.ui_widget_file_list import Ui_Form 9 | from ui.tableWidget_file_list import TabWidgetFileList 10 | 11 | 12 | class WidgetFileList(QWidget): 13 | signal_dropped = Signal() 14 | 15 | def __init__(self, parent=None): 16 | super().__init__(parent) 17 | self.ui = Ui_Form() 18 | self.ui.setupUi(self) 19 | 20 | # 添加列表控件 21 | self.tableWidget_file_list = TabWidgetFileList(self) 22 | self.ui.verticalLayout__place.addWidget(self.tableWidget_file_list) 23 | # 设置图标 24 | self.set_tips() 25 | self._set_icon() 26 | 27 | # 绑定槽函数 28 | self.ui.pushButton_add_file.clicked.connect(self.select_file) 29 | self.ui.pushButton_add_folder.clicked.connect(self.select_folder) 30 | self.ui.pushButton_remove.clicked.connect(self.remove_item) 31 | self.ui.pushButton_clear.clicked.connect(self.clear_items) 32 | self.ui.toolButton_move_top.clicked.connect(self.move_top) 33 | self.ui.toolButton_move_bottom.clicked.connect(self.move_bottom) 34 | self.ui.toolButton_move_up.clicked.connect(self.move_up) 35 | self.ui.toolButton_move_down.clicked.connect(self.move_down) 36 | 37 | # 备忘录 - 屏蔽未完成的按钮 38 | self.ui.pushButton_add_folder.setVisible(False) 39 | 40 | def set_tips(self): 41 | """设置说明文本""" 42 | tips_top = '移动至顶部' 43 | self.ui.toolButton_move_top.setToolTip(tips_top) 44 | 45 | tips_bottom = '移动至底部' 46 | self.ui.toolButton_move_bottom.setToolTip(tips_bottom) 47 | 48 | tips_up = '上移一位' 49 | self.ui.toolButton_move_up.setToolTip(tips_up) 50 | 51 | tips_down = '下移一位' 52 | self.ui.toolButton_move_down.setToolTip(tips_down) 53 | 54 | def calc_filename(self): 55 | """实时计算新的文件名""" 56 | self.tableWidget_file_list.calc_new_filename() 57 | 58 | def select_file(self): 59 | """弹出文件选择框""" 60 | options = QFileDialog.Options() 61 | file_path, _ = QFileDialog.getOpenFileNames(self, "选择文件", "", "所有文件 (*);", 62 | options=options) 63 | if file_path: 64 | self.tableWidget_file_list.insert_path_item(file_path) 65 | 66 | def select_folder(self): 67 | """弹出文件夹选择框""" 68 | pass 69 | 70 | def run_rename(self, is_auto_dup): 71 | self.tableWidget_file_list.rename(is_auto_dup) 72 | 73 | def cancel_rename(self): 74 | self.tableWidget_file_list.cancel_rename() 75 | 76 | def remove_item(self): 77 | """删除当前项目""" 78 | self.tableWidget_file_list.remove_item() 79 | 80 | def clear_items(self): 81 | """移除全部项目""" 82 | self.tableWidget_file_list.clear_items() 83 | 84 | def move_up(self): 85 | """将当前项目向上移动一位""" 86 | self.tableWidget_file_list.move_item_up() 87 | 88 | def move_down(self): 89 | """将当前项目向下移动一位""" 90 | self.tableWidget_file_list.move_item_down() 91 | 92 | def move_top(self): 93 | """将当前项目移动到顶部""" 94 | self.tableWidget_file_list.move_item_top() 95 | 96 | def move_bottom(self): 97 | """将当前项目移动到底部""" 98 | self.tableWidget_file_list.move_item_bottom() 99 | 100 | def _set_icon(self): 101 | """设置图标""" 102 | pixmap = QPixmap() 103 | pixmap.loadFromData(base64.b64decode(add_base64)) 104 | self.ui.pushButton_add_file.setIcon(QIcon(pixmap)) 105 | self.ui.pushButton_add_folder.setIcon(QIcon(pixmap)) 106 | 107 | pixmap = QPixmap() 108 | pixmap.loadFromData(base64.b64decode(remove_base64)) 109 | self.ui.pushButton_remove.setIcon(QIcon(pixmap)) 110 | 111 | pixmap = QPixmap() 112 | pixmap.loadFromData(base64.b64decode(clear_base64)) 113 | self.ui.pushButton_clear.setIcon(QIcon(pixmap)) 114 | 115 | pixmap = QPixmap() 116 | pixmap.loadFromData(base64.b64decode(top_base64)) 117 | self.ui.toolButton_move_top.setIcon(QIcon(pixmap)) 118 | 119 | pixmap = QPixmap() 120 | pixmap.loadFromData(base64.b64decode(up_base64)) 121 | self.ui.toolButton_move_up.setIcon(QIcon(pixmap)) 122 | 123 | pixmap = QPixmap() 124 | pixmap.loadFromData(base64.b64decode(down_base64)) 125 | self.ui.toolButton_move_down.setIcon(QIcon(pixmap)) 126 | 127 | pixmap = QPixmap() 128 | pixmap.loadFromData(base64.b64decode(bottom_base64)) 129 | self.ui.toolButton_move_bottom.setIcon(QIcon(pixmap)) 130 | 131 | 132 | if __name__ == '__main__': 133 | app = QApplication() 134 | app.setStyle('Fusion') # 设置风格 135 | show_ui = WidgetFileList() 136 | show_ui.show() 137 | app.exec() 138 | -------------------------------------------------------------------------------- /ui/widget_file_extension.py: -------------------------------------------------------------------------------- 1 | from PySide6.QtCore import Signal 2 | from PySide6.QtWidgets import QWidget, QApplication 3 | 4 | from module.class_.class_rename_type import TypeFileExtension 5 | from ui.src.ui_widget_file_extension import Ui_Form 6 | 7 | 8 | class WidgetFileExtension(QWidget): 9 | signal_file_extension_change_to = Signal(TypeFileExtension) 10 | signal_convert_to_lowercase = Signal(TypeFileExtension) 11 | signal_convert_to_capital = Signal(TypeFileExtension) 12 | signal_change_to_real_type = Signal(TypeFileExtension) 13 | 14 | def __init__(self, parent=None): 15 | super().__init__(parent) 16 | self.ui = Ui_Form() 17 | self.ui.setupUi(self) 18 | 19 | self.set_tips() 20 | 21 | self.ui.lineEdit_file_extension.textChanged.connect(self.change_to_changed) 22 | self.ui.checkBox_change_to.stateChanged.connect(self.emit_signal_change_to) 23 | self.ui.checkBox_change_to.stateChanged.connect(self.set_exclusive_group_change) 24 | self.ui.checkBox_real_type.stateChanged.connect(self.emit_signal_real_type) 25 | self.ui.checkBox_real_type.stateChanged.connect(self.set_exclusive_group_real) 26 | self.ui.checkBox_lowercase.stateChanged.connect(self.emit_signal_lowercase) 27 | self.ui.checkBox_lowercase.stateChanged.connect(self.set_exclusive_group_lowercase) 28 | self.ui.checkBox_capital.stateChanged.connect(self.emit_signal_capital) 29 | self.ui.checkBox_capital.stateChanged.connect(self.set_exclusive_group_capital) 30 | 31 | def set_tips(self): 32 | """设置说明文本""" 33 | tips_change_to = '修改后缀名,请勿将文件的后缀名修改为空' 34 | self.ui.checkBox_change_to.setToolTip(tips_change_to) 35 | 36 | tips_real_type = ('修改后缀名为文件的真实类型\n' 37 | '注意:\n' 38 | ' 1. 仅支持部分常见的文件类型\n' 39 | ' 2.结果可能存在偏差,请手工检查(例:分卷压缩包的分卷包会被识别为压缩包)') 40 | self.ui.checkBox_real_type.setToolTip(tips_real_type) 41 | 42 | def emit_signal_change_to(self): 43 | """发送信号""" 44 | file_extension = self.ui.lineEdit_file_extension.text() 45 | if len(file_extension.strip()): 46 | is_enable = self.ui.checkBox_change_to.isChecked() 47 | else: 48 | is_enable = False 49 | 50 | rule_class = TypeFileExtension.ChangeTo(is_enable, file_extension) 51 | self.signal_file_extension_change_to.emit(rule_class) 52 | 53 | def change_to_changed(self): 54 | """选项变动""" 55 | text = self.ui.lineEdit_file_extension.text() 56 | if len(text.strip()): 57 | self.ui.checkBox_change_to.setChecked(True) 58 | else: 59 | self.ui.checkBox_change_to.setChecked(False) 60 | 61 | self.emit_signal_change_to() 62 | 63 | def emit_signal_lowercase(self): 64 | """发送信号""" 65 | is_enable = self.ui.checkBox_lowercase.isChecked() 66 | 67 | rule_class = TypeFileExtension.ConvertToLowercase(is_enable) 68 | self.signal_convert_to_lowercase.emit(rule_class) 69 | 70 | def emit_signal_capital(self): 71 | """发送信号""" 72 | is_enable = self.ui.checkBox_capital.isChecked() 73 | 74 | rule_class = TypeFileExtension.ConvertToCapital(is_enable) 75 | self.signal_convert_to_capital.emit(rule_class) 76 | 77 | def emit_signal_real_type(self): 78 | """发送信号""" 79 | is_enable = self.ui.checkBox_real_type.isChecked() 80 | 81 | rule_class = TypeFileExtension.ChangeToRealType(is_enable) 82 | self.signal_change_to_real_type.emit(rule_class) 83 | 84 | def set_exclusive_group_lowercase(self): 85 | """大小写互斥勾选""" 86 | group_low = self.ui.checkBox_lowercase 87 | group_cap = self.ui.checkBox_capital 88 | 89 | if group_low.isChecked() and group_cap.isChecked(): 90 | group_cap.setChecked(False) 91 | 92 | def set_exclusive_group_capital(self): 93 | """大小写互斥勾选""" 94 | group_low = self.ui.checkBox_lowercase 95 | group_cap = self.ui.checkBox_capital 96 | 97 | if group_low.isChecked() and group_cap.isChecked(): 98 | group_low.setChecked(False) 99 | 100 | def set_exclusive_group_change(self): 101 | """手工修改与自动修改互斥""" 102 | group_change = self.ui.checkBox_change_to 103 | group_real = self.ui.checkBox_real_type 104 | 105 | if group_change.isChecked() and group_real.isChecked(): 106 | group_real.setChecked(False) 107 | 108 | def set_exclusive_group_real(self): 109 | """手工修改与自动修改互斥""" 110 | group_change = self.ui.checkBox_change_to 111 | group_real = self.ui.checkBox_real_type 112 | 113 | if group_change.isChecked() and group_real.isChecked(): 114 | group_change.setChecked(False) 115 | 116 | 117 | if __name__ == '__main__': 118 | app = QApplication() 119 | app.setStyle('Fusion') # 设置风格 120 | show_ui = WidgetFileExtension() 121 | show_ui.show() 122 | app.exec() 123 | -------------------------------------------------------------------------------- /module/class_/class_rename_rule.py: -------------------------------------------------------------------------------- 1 | # 汇总的重命名规则类 2 | # 重命名各个模块优先关系:替换>删除>添加>标准化>模版>文件扩展名 3 | import os.path 4 | from typing import Union 5 | 6 | from module import function_class 7 | from module.class_.class_rename_info import RenameInfo 8 | from module.class_.class_rename_method import MethodFileExtension 9 | from module.class_.class_rename_type import TypePattern, TypeConvert, TypeFileExtension, TypeNormal 10 | 11 | 12 | class RenameRule: 13 | """汇总的重命名规则类""" 14 | _instance = None 15 | _is_init = False 16 | 17 | def __new__(cls, *args, **kwargs): 18 | if not cls._instance: 19 | cls._instance = super().__new__(cls, *args, **kwargs) 20 | return cls._instance 21 | 22 | def __init__(self): 23 | if not self._is_init: 24 | super().__init__() 25 | self._is_init = True 26 | 27 | self._rule_pattern_dict = dict() # 模板类 28 | self._rule_filename_dict = dict() # 文件名类 29 | self._rule_file_extension_dict = dict() # 文件扩展名类 30 | self._set_default_class() # 设置初始键值对 31 | 32 | def update(self, rename_class): 33 | """添加""" 34 | class_ = type(rename_class) 35 | if class_ in self._rule_pattern_dict: 36 | self._rule_pattern_dict[class_] = rename_class 37 | elif class_ in self._rule_filename_dict: 38 | self._rule_filename_dict[class_] = rename_class 39 | elif class_ in self._rule_file_extension_dict: 40 | self._rule_file_extension_dict[class_] = rename_class 41 | 42 | def calc_new_name(self, path: Union[str, RenameInfo], index) -> RenameInfo: 43 | """计算新的文件名""" 44 | if isinstance(path, RenameInfo): 45 | path_need = path.path_need 46 | else: 47 | path_need = path 48 | 49 | if os.path.isdir(path_need): 50 | parent_dirpath, filetitle = os.path.split(path_need) 51 | file_extension = '' 52 | else: 53 | _temp, file_extension = os.path.splitext(path_need) 54 | parent_dirpath, filetitle = os.path.split(_temp) 55 | 56 | # 重命名各个模块优先关系:替换>删除>添加>标准化>模版>文件扩展名 57 | # 处理文件名 替换>删除>添加>标准化 58 | new_filetitle = filetitle 59 | for rule_type in self._rule_filename_dict.values(): 60 | if rule_type and rule_type.is_enable: 61 | rule = rule_type.rule 62 | new_filetitle = rule.rename_method(new_filetitle) 63 | 64 | # 套用模板 65 | for rule_type in self._rule_pattern_dict.values(): 66 | if rule_type and rule_type.is_enable: 67 | new_filetitle = rule_type.rename_method(new_filetitle, path_need, index) # 单独适用模板重命名模块 68 | 69 | # 处理文件扩展名 70 | new_file_extension = file_extension 71 | if os.path.isfile(path_need): 72 | for rule_type in self._rule_file_extension_dict.values(): 73 | if rule_type and rule_type.is_enable: 74 | rule = rule_type.rule 75 | # 单独处理“真实文件后缀名“选项 76 | if isinstance(rule, MethodFileExtension.ChangeToRealType): 77 | guess_filetype = rule.rename_method(path_need) 78 | else: 79 | guess_filetype = rule.rename_method(new_file_extension) 80 | 81 | if guess_filetype: 82 | new_file_extension = guess_filetype 83 | 84 | # 返回信息类 85 | if isinstance(path, RenameInfo): 86 | info = path 87 | info.update_new(new_filetitle, new_file_extension) 88 | else: 89 | info = RenameInfo(path) 90 | info.update_old(filetitle, file_extension) 91 | info.update_new(new_filetitle, new_file_extension) 92 | return info 93 | 94 | def _set_default_class(self): 95 | """设置初始类""" 96 | # 添加增删查改类,替换>删除>添加 97 | self._rule_filename_dict[TypeNormal.Replace] = None 98 | self._rule_filename_dict[TypeNormal.DeleteIndex] = None 99 | self._rule_filename_dict[TypeNormal.DeleteIndexBack] = None 100 | self._rule_filename_dict[TypeNormal.DeleteIndexAfter] = None 101 | self._rule_filename_dict[TypeNormal.DeleteIndexAfterBack] = None 102 | self._rule_filename_dict[TypeNormal.DeleteChar] = None 103 | self._rule_filename_dict[TypeNormal.Insert] = None 104 | self._rule_filename_dict[TypeNormal.InsertBack] = None 105 | self._rule_filename_dict[TypeNormal.AddPrefix] = None 106 | self._rule_filename_dict[TypeNormal.AddSuffix] = None 107 | 108 | # 添加文件名转换类 109 | child_class = function_class.get_subclasses(TypeConvert._Model) 110 | for class_ in child_class: 111 | self._rule_filename_dict[class_] = None 112 | 113 | # 添加模版类 114 | self._rule_pattern_dict[TypePattern] = None 115 | 116 | # 添加文件扩展名类 117 | child_class = function_class.get_subclasses(TypeFileExtension._Model) 118 | for class_ in child_class: 119 | self._rule_file_extension_dict[class_] = None 120 | -------------------------------------------------------------------------------- /ui/src/ui_widget_file_list.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 365 10 | 309 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 3 19 | 20 | 21 | 3 22 | 23 | 24 | 3 25 | 26 | 27 | 3 28 | 29 | 30 | 3 31 | 32 | 33 | 34 | 35 | 3 36 | 37 | 38 | 39 | 40 | 0 41 | 42 | 43 | 44 | 45 | 46 | 47 | 16 48 | 49 | 50 | 51 | 52 | Qt::Vertical 53 | 54 | 55 | 56 | 20 57 | 40 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | ... 66 | 67 | 68 | 69 | 70 | 71 | 72 | ... 73 | 74 | 75 | 76 | 77 | 78 | 79 | ... 80 | 81 | 82 | 83 | 84 | 85 | 86 | ... 87 | 88 | 89 | 90 | 91 | 92 | 93 | Qt::Vertical 94 | 95 | 96 | 97 | 20 98 | 40 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 12 111 | 112 | 113 | 114 | 115 | Qt::Horizontal 116 | 117 | 118 | 119 | 40 120 | 20 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 添加文件 129 | 130 | 131 | 132 | 133 | 134 | 135 | 添加文件夹 136 | 137 | 138 | 139 | 140 | 141 | 142 | 移除 143 | 144 | 145 | 146 | 147 | 148 | 149 | 清空 150 | 151 | 152 | 153 | 154 | 155 | 156 | Qt::Horizontal 157 | 158 | 159 | 160 | 40 161 | 20 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /ui/src/ui_widget_insert.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 274 10 | 130 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 3 19 | 20 | 21 | 3 22 | 23 | 24 | 3 25 | 26 | 27 | 3 28 | 29 | 30 | 3 31 | 32 | 33 | 34 | 35 | 添加 36 | 37 | 38 | 39 | 3 40 | 41 | 42 | 3 43 | 44 | 45 | 3 46 | 47 | 48 | 3 49 | 50 | 51 | 3 52 | 53 | 54 | 55 | 56 | 57 | 58 | 文件名前插入 59 | 60 | 61 | 62 | 63 | 64 | 65 | 需要插入的字符 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 文件名后添加 77 | 78 | 79 | 80 | 81 | 82 | 83 | 需要插入的字符 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 在第 95 | 96 | 97 | 98 | 99 | 100 | 101 | 1 102 | 103 | 104 | 999 105 | 106 | 107 | 108 | 109 | 110 | 111 | 个字符后插入 112 | 113 | 114 | 115 | 116 | 117 | 118 | 需要插入的字符 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 在倒数第 130 | 131 | 132 | 133 | 134 | 135 | 136 | 1 137 | 138 | 139 | 999 140 | 141 | 142 | 143 | 144 | 145 | 146 | 个字符后插入 147 | 148 | 149 | 150 | 151 | 152 | 153 | 需要插入的字符 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /module/class_/class_rename_info.py: -------------------------------------------------------------------------------- 1 | # 重命名的信息类(仅包含信息,不包含相关执行方法) 2 | import os 3 | 4 | 5 | class RenameInfo: 6 | """重命名的信息类""" 7 | 8 | def __init__(self, path): 9 | # 原始路径的信息 10 | if os.path.isdir(path): 11 | self.filetype = 'folder' 12 | else: 13 | self.filetype = 'file' 14 | self.original_path = os.path.normpath(path) 15 | self.parent_dirpath = os.path.dirname(path) 16 | self.original_filetitle = None 17 | self.original_file_extension = None 18 | self.original_filename = None 19 | # 根据重命名规则计算的新路径和文件名信息(未检查重复项) 20 | self.calc_filetitle = None 21 | self.calc_file_extension = None 22 | self.calc_filename = None 23 | self.calc_path = None 24 | # 执行重命名后的路径和文件名信息(重命名结果) 25 | self.rename_result = None 26 | self.is_dup = None 27 | self.filetitle_renamed = None 28 | self.file_extension_renamed = None 29 | self.filename_renamed = None 30 | self.path_renamed = None 31 | # 需要进行改名的文件路径和文件名信息(单独用于外部调用,在重命名后进行更新,使得原始路径信息不被修改) 32 | self.filetitle_need = None 33 | self.file_extension_need = None 34 | self.filename_need = None 35 | self.path_need = self.original_path 36 | # 撤销重命名时使用的文件路径和文件名信息 37 | self.is_dup_cancel = None 38 | self.filetitle_cancel = None 39 | self.file_extension_cancel = None 40 | self.filename_cancel = None 41 | self.path_cancel = self.original_path 42 | 43 | def update_old(self, filetitle, file_extension): 44 | self.original_filetitle = filetitle 45 | self.original_file_extension = file_extension 46 | self.original_filename = filetitle + file_extension 47 | 48 | self.filetitle_need = self.original_filetitle 49 | self.file_extension_need = self.original_file_extension 50 | self.filename_need = self.original_filename 51 | 52 | def update_new(self, filetitle, file_extension): 53 | self.calc_filetitle = filetitle 54 | self.calc_file_extension = file_extension 55 | self.calc_filename = filetitle + file_extension 56 | self.calc_path = os.path.normpath(os.path.join(self.parent_dirpath, self.calc_filename)) 57 | 58 | def update_rename_no_dup(self): 59 | """更新重命名信息(无重复文件名的情况)""" 60 | self.is_dup = False 61 | self.filetitle_renamed = self.calc_filetitle 62 | self.file_extension_renamed = self.calc_file_extension 63 | self.filename_renamed = self.calc_filename 64 | self.path_renamed = self.calc_path 65 | 66 | def update_rename(self, filetitle): 67 | """更新重命名信息(存在重复文件名的情况)""" 68 | self.is_dup = True 69 | self.filetitle_renamed = filetitle 70 | self.file_extension_renamed = self.calc_file_extension 71 | self.filename_renamed = filetitle + self.file_extension_renamed 72 | self.path_renamed = os.path.normpath(os.path.join(self.parent_dirpath, self.filename_renamed)) 73 | 74 | def update_cancel_rename_no_dup(self): 75 | """更新撤销重命名信息(无重复文件名的情况)""" 76 | self.is_dup_cancel = False 77 | self.filetitle_cancel = self.original_filetitle 78 | self.file_extension_cancel = self.original_file_extension 79 | self.filename_cancel = self.original_filename 80 | self.path_cancel = self.original_path 81 | 82 | def update_cancel_rename(self, filetitle): 83 | """更新撤销重命名信息(存在重复文件名的情况)""" 84 | self.is_dup = True 85 | self.filetitle_cancel = filetitle 86 | self.file_extension_cancel = self.original_file_extension 87 | self.filename_cancel = filetitle + self.file_extension_cancel 88 | self.path_cancel = os.path.normpath(os.path.join(self.parent_dirpath, self.filename_cancel)) 89 | 90 | def set_result_renamed(self): 91 | """设置重命名结果""" 92 | self.rename_result = 'renamed' 93 | 94 | self.filetitle_need = self.filetitle_renamed 95 | self.file_extension_need = self.file_extension_renamed 96 | self.filename_need = self.filename_renamed 97 | self.path_need = self.path_renamed 98 | 99 | def set_result_unknown_error(self): 100 | """设置重命名结果""" 101 | self.rename_result = 'unknown_error' 102 | 103 | def set_result_skipped(self): 104 | """设置重命名结果""" 105 | self.rename_result = 'skipped' 106 | 107 | def set_result_not_exist(self): 108 | """设置重命名结果""" 109 | self.rename_result = 'not_exist' 110 | 111 | def set_result_occupied(self): 112 | """设置重命名结果""" 113 | self.rename_result = 'occupied' 114 | 115 | def set_result_cancelled(self): 116 | """设置重命名结果""" 117 | self.rename_result = 'cancelled' 118 | 119 | # 原始文件信息不做修改 120 | # self.original_path = self.path_cancel 121 | # self.original_filetitle = self.filetitle_cancel 122 | # self.original_file_extension = self.file_extension_cancel 123 | # self.original_filename = self.filename_cancel 124 | 125 | self.filetitle_need = self.filetitle_cancel 126 | self.file_extension_need = self.file_extension_cancel 127 | self.filename_need = self.filename_cancel 128 | self.path_need = self.path_cancel 129 | -------------------------------------------------------------------------------- /ui/src/ui_main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ################################################################################ 4 | ## Form generated from reading UI file 'ui_mainzfkvkX.ui' 5 | ## 6 | ## Created by: Qt User Interface Compiler version 6.1.3 7 | ## 8 | ## WARNING! All changes made in this file will be lost when recompiling UI file! 9 | ################################################################################ 10 | 11 | from PySide6.QtCore import * # type: ignore 12 | from PySide6.QtGui import * # type: ignore 13 | from PySide6.QtWidgets import * # type: ignore 14 | 15 | 16 | class Ui_MainWindow(object): 17 | def setupUi(self, MainWindow): 18 | if not MainWindow.objectName(): 19 | MainWindow.setObjectName(u"MainWindow") 20 | MainWindow.resize(800, 530) 21 | self.centralwidget = QWidget(MainWindow) 22 | self.centralwidget.setObjectName(u"centralwidget") 23 | self.horizontalLayout_3 = QHBoxLayout(self.centralwidget) 24 | self.horizontalLayout_3.setSpacing(3) 25 | self.horizontalLayout_3.setObjectName(u"horizontalLayout_3") 26 | self.horizontalLayout_3.setContentsMargins(3, 3, 3, 3) 27 | self.tabWidget = QTabWidget(self.centralwidget) 28 | self.tabWidget.setObjectName(u"tabWidget") 29 | self.tab_pattern = QWidget() 30 | self.tab_pattern.setObjectName(u"tab_pattern") 31 | self.verticalLayout_2 = QVBoxLayout(self.tab_pattern) 32 | self.verticalLayout_2.setSpacing(0) 33 | self.verticalLayout_2.setObjectName(u"verticalLayout_2") 34 | self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) 35 | self.tabWidget.addTab(self.tab_pattern, "") 36 | self.tab_other = QWidget() 37 | self.tab_other.setObjectName(u"tab_other") 38 | self.verticalLayout_3 = QVBoxLayout(self.tab_other) 39 | self.verticalLayout_3.setSpacing(0) 40 | self.verticalLayout_3.setObjectName(u"verticalLayout_3") 41 | self.verticalLayout_3.setContentsMargins(0, 0, 0, 0) 42 | self.verticalLayout_place = QVBoxLayout() 43 | self.verticalLayout_place.setSpacing(3) 44 | self.verticalLayout_place.setObjectName(u"verticalLayout_place") 45 | 46 | self.verticalLayout_3.addLayout(self.verticalLayout_place) 47 | 48 | self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) 49 | 50 | self.verticalLayout_3.addItem(self.verticalSpacer) 51 | 52 | self.tabWidget.addTab(self.tab_other, "") 53 | 54 | self.horizontalLayout_3.addWidget(self.tabWidget) 55 | 56 | self.line_2 = QFrame(self.centralwidget) 57 | self.line_2.setObjectName(u"line_2") 58 | self.line_2.setFrameShape(QFrame.VLine) 59 | self.line_2.setFrameShadow(QFrame.Sunken) 60 | 61 | self.horizontalLayout_3.addWidget(self.line_2) 62 | 63 | self.verticalLayout = QVBoxLayout() 64 | self.verticalLayout.setSpacing(3) 65 | self.verticalLayout.setObjectName(u"verticalLayout") 66 | self.widget_file_list = QWidget(self.centralwidget) 67 | self.widget_file_list.setObjectName(u"widget_file_list") 68 | self.horizontalLayout = QHBoxLayout(self.widget_file_list) 69 | self.horizontalLayout.setSpacing(0) 70 | self.horizontalLayout.setObjectName(u"horizontalLayout") 71 | self.horizontalLayout.setContentsMargins(0, 0, 0, 0) 72 | 73 | self.verticalLayout.addWidget(self.widget_file_list) 74 | 75 | self.line = QFrame(self.centralwidget) 76 | self.line.setObjectName(u"line") 77 | self.line.setFrameShape(QFrame.HLine) 78 | self.line.setFrameShadow(QFrame.Sunken) 79 | 80 | self.verticalLayout.addWidget(self.line) 81 | 82 | self.widget_exec = QWidget(self.centralwidget) 83 | self.widget_exec.setObjectName(u"widget_exec") 84 | self.horizontalLayout_2 = QHBoxLayout(self.widget_exec) 85 | self.horizontalLayout_2.setSpacing(0) 86 | self.horizontalLayout_2.setObjectName(u"horizontalLayout_2") 87 | self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) 88 | 89 | self.verticalLayout.addWidget(self.widget_exec) 90 | 91 | self.verticalLayout.setStretch(0, 1) 92 | 93 | self.horizontalLayout_3.addLayout(self.verticalLayout) 94 | 95 | self.horizontalLayout_3.setStretch(0, 2) 96 | self.horizontalLayout_3.setStretch(2, 3) 97 | MainWindow.setCentralWidget(self.centralwidget) 98 | 99 | self.retranslateUi(MainWindow) 100 | 101 | self.tabWidget.setCurrentIndex(0) 102 | 103 | 104 | QMetaObject.connectSlotsByName(MainWindow) 105 | # setupUi 106 | 107 | def retranslateUi(self, MainWindow): 108 | MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"Easy ReNamer", None)) 109 | #if QT_CONFIG(accessibility) 110 | self.tab_pattern.setAccessibleName("") 111 | #endif // QT_CONFIG(accessibility) 112 | self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_pattern), QCoreApplication.translate("MainWindow", u"\u901a\u7528", None)) 113 | self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_other), QCoreApplication.translate("MainWindow", u"\u589e\u5220\u67e5\u6539", None)) 114 | # retranslateUi 115 | 116 | -------------------------------------------------------------------------------- /module/WindowsSorted.py: -------------------------------------------------------------------------------- 1 | """ 2 | 更新日期: 3 | 2024.04.17 4 | 5 | 功能: 6 | 以Windows本地环境的排序规则对传入列表/传入路径的内部文件列表进行排序 7 | 8 | 列表排序实现方法: 9 | 1. 初始化本地环境 10 | 2. 使用冒泡排序,对比两个键的顺序,将其每个字符拆分,逐级对比 11 | 3. 返回排序后的列表 12 | 13 | 路径排序实现方法: 14 | 1. 遍历传入路径,按需提取文件或文件夹 15 | 2. 对路径中的每一个文件夹及其自身进行列表排序 16 | 3. 返回排序后的列表 17 | """ 18 | 19 | import locale 20 | import os 21 | import re 22 | from typing import Union 23 | 24 | 25 | def sort_list(keys_list: list, order: str = 'ASC') -> Union[list, SystemExit]: 26 | """ 27 | 排序列表list 28 | :param keys_list: 需要排序的list 29 | :param order: 排序类型,'ASC' 升序或 'DESC' 降序 30 | :return: 排序后的list 31 | """ 32 | # 初始化本地环境 33 | locale.setlocale(locale.LC_ALL, '') 34 | 35 | # 冒泡排序 36 | keys_list = keys_list.copy() 37 | n = len(keys_list) 38 | for i in range(n): 39 | for j in range(n - i - 1): 40 | if _is_reversal_two_key(keys_list[j], keys_list[j + 1]): 41 | keys_list[j], keys_list[j + 1] = keys_list[j + 1], keys_list[j] 42 | 43 | # 按升降序参数返回对应list 44 | if order.upper() == 'ASC': 45 | return keys_list 46 | elif order.upper() == 'DESC': 47 | return keys_list[::-1] 48 | else: 49 | return SystemExit('传入参数错误,请检查!') 50 | 51 | 52 | def sort_path(dirpath: str, order: str = 'ASC', filetype: str = 'both', depth: int = 0) -> list: 53 | """ 54 | 排序指定路径中的文件/文件夹 55 | :param dirpath: 文件夹路径 56 | :param order: 排序类型,'ASC' 升序或 'DESC' 降序,默认为 'ASC' 57 | :param filetype: 排序的文件类型,'file' 文件或 'folder' 文件夹或 'both' 两者,默认为 'both' 58 | :param depth: 遍历的层级深度,默认为0(最大层) 59 | :return: 根据参数返回不同的完整路径list 60 | """ 61 | path_sorted = [dirpath] # 存放最终结果 62 | current_listdir_sorted_folder = [dirpath] # 当前层级中的文件夹 63 | current_listdir_sorted_folder_copy = current_listdir_sorted_folder.copy() # 用于递归 64 | if depth == 0: 65 | depth = 100 66 | 67 | current_depth = 0 68 | while current_depth < depth and current_listdir_sorted_folder: 69 | current_depth += 1 70 | for path in current_listdir_sorted_folder_copy: 71 | current_listdir_sorted = _sort_path_listdir(path, order=order, filetype=filetype) 72 | current_index = path_sorted.index(path) 73 | path_sorted[current_index + 1:current_index + 1] = current_listdir_sorted # 利用切片插入列表元素(不能用insert) 74 | current_listdir_sorted_folder.remove(path) 75 | current_listdir_sorted_folder += [i for i in current_listdir_sorted if os.path.isdir(i)] 76 | current_listdir_sorted_folder_copy = current_listdir_sorted_folder.copy() 77 | 78 | # 删除一开始赋值的多余的项目 79 | path_sorted.remove(dirpath) 80 | # 额外处理文件类型为file时的情况 81 | if filetype == 'file': 82 | path_sorted = [i for i in path_sorted if os.path.isfile(i)] 83 | 84 | return path_sorted 85 | 86 | 87 | def _sort_path_listdir(dirpath: str, order: str = 'ASC', filetype: str = 'both') -> Union[SystemExit, list]: 88 | """ 89 | 排序指定路径中的文件/文件夹(仅第1层下级目录),并返回完整路径list 90 | :param dirpath: 文件夹路径 91 | :param order: 排序类型,'ASC' 升序或 'DESC' 降序 92 | :param filetype: 排序的文件类型,'file' 文件或 'folder' 文件夹或 'both' 两者 93 | :return: 排序后的完整路径list 94 | """ 95 | listdir = os.listdir(dirpath) 96 | listdir_fullpath = [os.path.normpath(os.path.join(dirpath, i)) for i in listdir] 97 | listdir_fullpath_folder = [i for i in listdir_fullpath if os.path.isdir(i)] 98 | listdir_fullpath_file = [i for i in listdir_fullpath if os.path.isfile(i)] 99 | 100 | list_sorted_fullpath_folder = sort_list(listdir_fullpath_folder, order=order) 101 | list_sorted_fullpath_file = sort_list(listdir_fullpath_file, order=order) 102 | 103 | if filetype.lower() in ['both', 'file']: # 如果排序类型是file也要返回整个list,但之后要删除folder项 104 | return list_sorted_fullpath_folder + list_sorted_fullpath_file 105 | elif filetype.lower() == 'folder': 106 | return list_sorted_fullpath_folder 107 | else: 108 | return SystemExit('传入参数错误,请检查!') 109 | 110 | 111 | def _is_reversal_two_key(key_1: str, key_2: str): 112 | """ 113 | 排序两个键,决定是否在冒泡排序中掉转位置 114 | """ 115 | key_1_split = _split_key(key_1) 116 | key_2_split = _split_key(key_2) 117 | check_step = min(len(key_1_split), len(key_2_split)) # 检查步数,按最短的键的长度 118 | 119 | # 逐个对比两个键的每个字符,直到对比不同的字符,并返回对比结果 120 | for _ in range(check_step): 121 | split_1 = key_1_split.pop(0) 122 | split_2 = key_2_split.pop(0) 123 | if split_1 != split_2: 124 | splits = [split_1, split_2] 125 | if split_1.isdigit() and split_2.isdigit(): # 如果两个字符都是字符型数字,则按数值大小排序 126 | if int(split_1) == int(split_2): # 处理特殊情况,如果两个字符转数值后相等,则需要按长度倒序排序,在Windows中00会排在0前 127 | splits_sorted = sorted(splits, key=lambda x: len(x))[::-1] 128 | else: 129 | splits_sorted = sorted(splits, key=lambda x: int(x)) 130 | else: # 否则按文本排序 131 | splits_sorted = sorted(splits, key=locale.strxfrm) 132 | if splits_sorted == splits: 133 | return False 134 | else: 135 | return True 136 | 137 | return False 138 | 139 | 140 | def _split_key(key: str) -> list: 141 | """拆分key中的每一个字符(连续数字字符除外),返回拆分后元素组成的list""" 142 | pattern = r'(\d+)' # 不需要拆分. ,在排序时可以直接视其为字符 143 | split_pattern = re.split(pattern, key) 144 | 145 | key_split = [] 146 | for part in split_pattern: 147 | if part.isdigit(): 148 | key_split.append(part) 149 | else: 150 | key_split += list(part) 151 | 152 | return key_split 153 | -------------------------------------------------------------------------------- /ui/src/ui_widget_file_list.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ################################################################################ 4 | ## Form generated from reading UI file 'ui_widget_file_listTqyBOE.ui' 5 | ## 6 | ## Created by: Qt User Interface Compiler version 6.1.3 7 | ## 8 | ## WARNING! All changes made in this file will be lost when recompiling UI file! 9 | ################################################################################ 10 | 11 | from PySide6.QtCore import * # type: ignore 12 | from PySide6.QtGui import * # type: ignore 13 | from PySide6.QtWidgets import * # type: ignore 14 | 15 | 16 | class Ui_Form(object): 17 | def setupUi(self, Form): 18 | if not Form.objectName(): 19 | Form.setObjectName(u"Form") 20 | Form.resize(368, 309) 21 | self.verticalLayout_2 = QVBoxLayout(Form) 22 | self.verticalLayout_2.setSpacing(3) 23 | self.verticalLayout_2.setObjectName(u"verticalLayout_2") 24 | self.verticalLayout_2.setContentsMargins(3, 3, 3, 3) 25 | self.horizontalLayout_2 = QHBoxLayout() 26 | self.horizontalLayout_2.setSpacing(3) 27 | self.horizontalLayout_2.setObjectName(u"horizontalLayout_2") 28 | self.verticalLayout__place = QVBoxLayout() 29 | self.verticalLayout__place.setSpacing(0) 30 | self.verticalLayout__place.setObjectName(u"verticalLayout__place") 31 | 32 | self.horizontalLayout_2.addLayout(self.verticalLayout__place) 33 | 34 | self.verticalLayout = QVBoxLayout() 35 | self.verticalLayout.setSpacing(16) 36 | self.verticalLayout.setObjectName(u"verticalLayout") 37 | self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) 38 | 39 | self.verticalLayout.addItem(self.verticalSpacer) 40 | 41 | self.toolButton_move_top = QToolButton(Form) 42 | self.toolButton_move_top.setObjectName(u"toolButton_move_top") 43 | 44 | self.verticalLayout.addWidget(self.toolButton_move_top) 45 | 46 | self.toolButton_move_up = QToolButton(Form) 47 | self.toolButton_move_up.setObjectName(u"toolButton_move_up") 48 | 49 | self.verticalLayout.addWidget(self.toolButton_move_up) 50 | 51 | self.toolButton_move_down = QToolButton(Form) 52 | self.toolButton_move_down.setObjectName(u"toolButton_move_down") 53 | 54 | self.verticalLayout.addWidget(self.toolButton_move_down) 55 | 56 | self.toolButton_move_bottom = QToolButton(Form) 57 | self.toolButton_move_bottom.setObjectName(u"toolButton_move_bottom") 58 | 59 | self.verticalLayout.addWidget(self.toolButton_move_bottom) 60 | 61 | self.verticalSpacer_2 = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) 62 | 63 | self.verticalLayout.addItem(self.verticalSpacer_2) 64 | 65 | 66 | self.horizontalLayout_2.addLayout(self.verticalLayout) 67 | 68 | self.horizontalLayout_2.setStretch(0, 1) 69 | 70 | self.verticalLayout_2.addLayout(self.horizontalLayout_2) 71 | 72 | self.horizontalLayout = QHBoxLayout() 73 | self.horizontalLayout.setSpacing(12) 74 | self.horizontalLayout.setObjectName(u"horizontalLayout") 75 | self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) 76 | 77 | self.horizontalLayout.addItem(self.horizontalSpacer_2) 78 | 79 | self.pushButton_add_file = QPushButton(Form) 80 | self.pushButton_add_file.setObjectName(u"pushButton_add_file") 81 | 82 | self.horizontalLayout.addWidget(self.pushButton_add_file) 83 | 84 | self.pushButton_add_folder = QPushButton(Form) 85 | self.pushButton_add_folder.setObjectName(u"pushButton_add_folder") 86 | 87 | self.horizontalLayout.addWidget(self.pushButton_add_folder) 88 | 89 | self.pushButton_remove = QPushButton(Form) 90 | self.pushButton_remove.setObjectName(u"pushButton_remove") 91 | 92 | self.horizontalLayout.addWidget(self.pushButton_remove) 93 | 94 | self.pushButton_clear = QPushButton(Form) 95 | self.pushButton_clear.setObjectName(u"pushButton_clear") 96 | 97 | self.horizontalLayout.addWidget(self.pushButton_clear) 98 | 99 | self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) 100 | 101 | self.horizontalLayout.addItem(self.horizontalSpacer) 102 | 103 | 104 | self.verticalLayout_2.addLayout(self.horizontalLayout) 105 | 106 | 107 | self.retranslateUi(Form) 108 | 109 | QMetaObject.connectSlotsByName(Form) 110 | # setupUi 111 | 112 | def retranslateUi(self, Form): 113 | Form.setWindowTitle(QCoreApplication.translate("Form", u"Form", None)) 114 | self.toolButton_move_top.setText(QCoreApplication.translate("Form", u"...", None)) 115 | self.toolButton_move_up.setText(QCoreApplication.translate("Form", u"...", None)) 116 | self.toolButton_move_down.setText(QCoreApplication.translate("Form", u"...", None)) 117 | self.toolButton_move_bottom.setText(QCoreApplication.translate("Form", u"...", None)) 118 | self.pushButton_add_file.setText(QCoreApplication.translate("Form", u"\u6dfb\u52a0\u6587\u4ef6", None)) 119 | self.pushButton_add_folder.setText(QCoreApplication.translate("Form", u"\u6dfb\u52a0\u6587\u4ef6\u5939", None)) 120 | self.pushButton_remove.setText(QCoreApplication.translate("Form", u"\u79fb\u9664", None)) 121 | self.pushButton_clear.setText(QCoreApplication.translate("Form", u"\u6e05\u7a7a", None)) 122 | # retranslateUi 123 | 124 | -------------------------------------------------------------------------------- /ui/src/ui_main.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 800 10 | 530 11 | 12 | 13 | 14 | Easy ReNamer 15 | 16 | 17 | 18 | 19 | 3 20 | 21 | 22 | 3 23 | 24 | 25 | 3 26 | 27 | 28 | 3 29 | 30 | 31 | 3 32 | 33 | 34 | 35 | 36 | 0 37 | 38 | 39 | 40 | 41 | 42 | 43 | 通用 44 | 45 | 46 | 47 | 0 48 | 49 | 50 | 0 51 | 52 | 53 | 0 54 | 55 | 56 | 0 57 | 58 | 59 | 0 60 | 61 | 62 | 63 | 64 | 65 | 增删查改 66 | 67 | 68 | 69 | 0 70 | 71 | 72 | 0 73 | 74 | 75 | 0 76 | 77 | 78 | 0 79 | 80 | 81 | 0 82 | 83 | 84 | 85 | 86 | 3 87 | 88 | 89 | 90 | 91 | 92 | 93 | Qt::Vertical 94 | 95 | 96 | 97 | 20 98 | 40 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | Qt::Vertical 111 | 112 | 113 | 114 | 115 | 116 | 117 | 3 118 | 119 | 120 | 121 | 122 | 123 | 0 124 | 125 | 126 | 0 127 | 128 | 129 | 0 130 | 131 | 132 | 0 133 | 134 | 135 | 0 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | Qt::Horizontal 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 0 152 | 153 | 154 | 0 155 | 156 | 157 | 0 158 | 159 | 160 | 0 161 | 162 | 163 | 0 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | -------------------------------------------------------------------------------- /ui/src/ui_widget_filename_standard.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ################################################################################ 4 | ## Form generated from reading UI file 'ui_widget_filename_standardFqJLqI.ui' 5 | ## 6 | ## Created by: Qt User Interface Compiler version 6.1.3 7 | ## 8 | ## WARNING! All changes made in this file will be lost when recompiling UI file! 9 | ################################################################################ 10 | 11 | from PySide6.QtCore import * # type: ignore 12 | from PySide6.QtGui import * # type: ignore 13 | from PySide6.QtWidgets import * # type: ignore 14 | 15 | 16 | class Ui_Form(object): 17 | def setupUi(self, Form): 18 | if not Form.objectName(): 19 | Form.setObjectName(u"Form") 20 | Form.resize(141, 131) 21 | self.verticalLayout = QVBoxLayout(Form) 22 | self.verticalLayout.setSpacing(3) 23 | self.verticalLayout.setObjectName(u"verticalLayout") 24 | self.verticalLayout.setContentsMargins(3, 3, 3, 3) 25 | self.groupBox = QGroupBox(Form) 26 | self.groupBox.setObjectName(u"groupBox") 27 | self.verticalLayout_2 = QVBoxLayout(self.groupBox) 28 | self.verticalLayout_2.setSpacing(3) 29 | self.verticalLayout_2.setObjectName(u"verticalLayout_2") 30 | self.verticalLayout_2.setContentsMargins(3, 3, 3, 3) 31 | self.horizontalLayout = QHBoxLayout() 32 | self.horizontalLayout.setSpacing(3) 33 | self.horizontalLayout.setObjectName(u"horizontalLayout") 34 | self.checkBox_letter = QCheckBox(self.groupBox) 35 | self.checkBox_letter.setObjectName(u"checkBox_letter") 36 | 37 | self.horizontalLayout.addWidget(self.checkBox_letter) 38 | 39 | self.comboBox_letter = QComboBox(self.groupBox) 40 | self.comboBox_letter.addItem("") 41 | self.comboBox_letter.addItem("") 42 | self.comboBox_letter.setObjectName(u"comboBox_letter") 43 | 44 | self.horizontalLayout.addWidget(self.comboBox_letter) 45 | 46 | self.horizontalLayout.setStretch(1, 1) 47 | 48 | self.verticalLayout_2.addLayout(self.horizontalLayout) 49 | 50 | self.horizontalLayout_2 = QHBoxLayout() 51 | self.horizontalLayout_2.setSpacing(3) 52 | self.horizontalLayout_2.setObjectName(u"horizontalLayout_2") 53 | self.checkBox_chinese = QCheckBox(self.groupBox) 54 | self.checkBox_chinese.setObjectName(u"checkBox_chinese") 55 | 56 | self.horizontalLayout_2.addWidget(self.checkBox_chinese) 57 | 58 | self.comboBox_chinese = QComboBox(self.groupBox) 59 | self.comboBox_chinese.addItem("") 60 | self.comboBox_chinese.addItem("") 61 | self.comboBox_chinese.setObjectName(u"comboBox_chinese") 62 | 63 | self.horizontalLayout_2.addWidget(self.comboBox_chinese) 64 | 65 | self.horizontalLayout_2.setStretch(1, 1) 66 | 67 | self.verticalLayout_2.addLayout(self.horizontalLayout_2) 68 | 69 | self.horizontalLayout_3 = QHBoxLayout() 70 | self.horizontalLayout_3.setSpacing(3) 71 | self.horizontalLayout_3.setObjectName(u"horizontalLayout_3") 72 | self.checkBox_character = QCheckBox(self.groupBox) 73 | self.checkBox_character.setObjectName(u"checkBox_character") 74 | 75 | self.horizontalLayout_3.addWidget(self.checkBox_character) 76 | 77 | self.comboBox_character = QComboBox(self.groupBox) 78 | self.comboBox_character.addItem("") 79 | self.comboBox_character.addItem("") 80 | self.comboBox_character.setObjectName(u"comboBox_character") 81 | 82 | self.horizontalLayout_3.addWidget(self.comboBox_character) 83 | 84 | self.horizontalLayout_3.setStretch(1, 1) 85 | 86 | self.verticalLayout_2.addLayout(self.horizontalLayout_3) 87 | 88 | self.checkBox_excess_spaces = QCheckBox(self.groupBox) 89 | self.checkBox_excess_spaces.setObjectName(u"checkBox_excess_spaces") 90 | 91 | self.verticalLayout_2.addWidget(self.checkBox_excess_spaces) 92 | 93 | 94 | self.verticalLayout.addWidget(self.groupBox) 95 | 96 | 97 | self.retranslateUi(Form) 98 | 99 | QMetaObject.connectSlotsByName(Form) 100 | # setupUi 101 | 102 | def retranslateUi(self, Form): 103 | Form.setWindowTitle(QCoreApplication.translate("Form", u"Form", None)) 104 | self.groupBox.setTitle(QCoreApplication.translate("Form", u"\u6807\u51c6\u5316", None)) 105 | self.checkBox_letter.setText(QCoreApplication.translate("Form", u"\u5b57\u6bcd\u6539\u4e3a", None)) 106 | self.comboBox_letter.setItemText(0, QCoreApplication.translate("Form", u"\u5c0f\u5199", None)) 107 | self.comboBox_letter.setItemText(1, QCoreApplication.translate("Form", u"\u5927\u5199", None)) 108 | 109 | self.checkBox_chinese.setText(QCoreApplication.translate("Form", u"\u6c49\u5b57\u6539\u4e3a", None)) 110 | self.comboBox_chinese.setItemText(0, QCoreApplication.translate("Form", u"\u7b80\u4f53", None)) 111 | self.comboBox_chinese.setItemText(1, QCoreApplication.translate("Form", u"\u7e41\u4f53", None)) 112 | 113 | self.checkBox_character.setText(QCoreApplication.translate("Form", u"\u5b57\u7b26\u6539\u4e3a", None)) 114 | self.comboBox_character.setItemText(0, QCoreApplication.translate("Form", u"\u534a\u89d2", None)) 115 | self.comboBox_character.setItemText(1, QCoreApplication.translate("Form", u"\u5168\u89d2", None)) 116 | 117 | self.checkBox_excess_spaces.setText(QCoreApplication.translate("Form", u"\u6e05\u9664\u591a\u4f59\u7a7a\u683c", None)) 118 | # retranslateUi 119 | 120 | -------------------------------------------------------------------------------- /ui/widget_delete.py: -------------------------------------------------------------------------------- 1 | from PySide6.QtCore import Signal 2 | from PySide6.QtWidgets import QWidget, QApplication 3 | 4 | from module.class_.class_rename_type import TypeNormal 5 | from ui.src.ui_widget_delete import Ui_Form 6 | 7 | 8 | class WidgetDelete(QWidget): 9 | signal_delete_character = Signal(TypeNormal) 10 | signal_delete_index = Signal(TypeNormal) 11 | signal_delete_index_back = Signal(TypeNormal) 12 | signal_delete_index_after = Signal(TypeNormal) 13 | signal_delete_index_after_back = Signal(TypeNormal) 14 | 15 | def __init__(self, parent=None): 16 | super().__init__(parent) 17 | self.ui = Ui_Form() 18 | self.ui.setupUi(self) 19 | 20 | self.set_tips() 21 | 22 | self.ui.checkBox_delete_character.stateChanged.connect(self.emit_signal_delete_character) 23 | self.ui.lineEdit_delete_character.textChanged.connect(self.delete_character_changed) 24 | 25 | self.ui.checkBox_delete_index.stateChanged.connect(self.emit_signal_delete_index) 26 | self.ui.spinBox_index.valueChanged.connect(self.delete_index_changed) 27 | self.ui.spinBox_delete_count.valueChanged.connect(self.delete_index_changed) 28 | 29 | self.ui.checkBox_delete_index_back.stateChanged.connect(self.emit_signal_delete_index_back) 30 | self.ui.spinBox_index_back.valueChanged.connect(self.delete_index_back_changed) 31 | self.ui.spinBox_delete_count_back.valueChanged.connect(self.delete_index_back_changed) 32 | 33 | self.ui.checkBox_delete_index_after.stateChanged.connect(self.emit_signal_delete_after) 34 | self.ui.spinBox_index_del_after.valueChanged.connect(self.delete_index_after_changed) 35 | 36 | self.ui.checkBox_delete_index_after_back.stateChanged.connect(self.emit_signal_delete_after_back) 37 | self.ui.spinBox_index_del_after_back.valueChanged.connect(self.delete_index_after_back_changed) 38 | 39 | def set_tips(self): 40 | """设置说明文本""" 41 | tips_delete_delete_character = '删除文件名中的指定字符' 42 | self.ui.checkBox_delete_character.setToolTip(tips_delete_delete_character) 43 | 44 | tips_delete_delete_index = '从第N个字符起删除x个字符(包含第N个字符)' 45 | self.ui.checkBox_delete_index.setToolTip(tips_delete_delete_index) 46 | 47 | tips_delete_delete_index_back = '从倒数第N个字符起删除x个字符(包含倒数第N个字符)' 48 | self.ui.checkBox_delete_index_back.setToolTip(tips_delete_delete_index_back) 49 | 50 | tips_delete_delete_index_after = '删除第N个字符后的全部字符(不包含第N个字符)' 51 | self.ui.checkBox_delete_index_after.setToolTip(tips_delete_delete_index_after) 52 | 53 | tips_delete_delete_index_after_back = '删除倒数第N个字符后的全部字符(不包含倒数第N个字符)' 54 | self.ui.checkBox_delete_index_after_back.setToolTip(tips_delete_delete_index_after_back) 55 | 56 | def emit_signal_delete_character(self): 57 | """发送信号""" 58 | delete_char = self.ui.lineEdit_delete_character.text() 59 | if delete_char: 60 | is_enable = self.ui.checkBox_delete_character.isChecked() 61 | else: 62 | is_enable = False 63 | 64 | rule_class = TypeNormal.DeleteChar(is_enable, delete_char) 65 | self.signal_delete_character.emit(rule_class) 66 | 67 | def delete_character_changed(self): 68 | """选项变动""" 69 | text = self.ui.lineEdit_delete_character.text() 70 | if len(text): 71 | self.ui.checkBox_delete_character.setChecked(True) 72 | else: 73 | self.ui.checkBox_delete_character.setChecked(False) 74 | 75 | self.emit_signal_delete_character() 76 | 77 | def emit_signal_delete_index(self): 78 | """发送信号""" 79 | is_enable = self.ui.checkBox_delete_index.isChecked() 80 | start_index = self.ui.spinBox_index.value() 81 | delete_count = self.ui.spinBox_delete_count.value() 82 | 83 | rule_class = TypeNormal.DeleteIndex(is_enable, start_index, delete_count) 84 | self.signal_delete_index.emit(rule_class) 85 | 86 | def emit_signal_delete_index_back(self): 87 | """发送信号""" 88 | is_enable = self.ui.checkBox_delete_index_back.isChecked() 89 | start_index = self.ui.spinBox_index_back.value() 90 | delete_count = self.ui.spinBox_delete_count_back.value() 91 | 92 | rule_class = TypeNormal.DeleteIndexBack(is_enable, start_index, delete_count) 93 | self.signal_delete_index_back.emit(rule_class) 94 | 95 | def emit_signal_delete_after(self): 96 | """发送信号""" 97 | index = self.ui.spinBox_index_del_after.value() 98 | is_enable = self.ui.checkBox_delete_index_after.isChecked() 99 | 100 | rule_class = TypeNormal.DeleteIndexAfter(is_enable, index) 101 | self.signal_delete_index_after.emit(rule_class) 102 | 103 | def emit_signal_delete_after_back(self): 104 | """发送信号""" 105 | index = self.ui.spinBox_index_del_after_back.value() 106 | is_enable = self.ui.checkBox_delete_index_after_back.isChecked() 107 | 108 | rule_class = TypeNormal.DeleteIndexAfterBack(is_enable, index) 109 | self.signal_delete_index_after_back.emit(rule_class) 110 | 111 | def delete_index_changed(self): 112 | """选项变动""" 113 | # 变动后直接勾选即可 114 | self.ui.checkBox_delete_index.setChecked(True) 115 | self.emit_signal_delete_index() 116 | 117 | def delete_index_back_changed(self): 118 | """选项变动""" 119 | # 变动后直接勾选即可 120 | self.ui.checkBox_delete_index_back.setChecked(True) 121 | self.emit_signal_delete_index_back() 122 | 123 | def delete_index_after_changed(self): 124 | """选项变动""" 125 | # 变动后直接勾选即可 126 | self.ui.checkBox_delete_index_after.setChecked(True) 127 | self.emit_signal_delete_after() 128 | 129 | def delete_index_after_back_changed(self): 130 | """选项变动""" 131 | # 变动后直接勾选即可 132 | self.ui.checkBox_delete_index_after_back.setChecked(True) 133 | self.emit_signal_delete_after_back() 134 | 135 | 136 | if __name__ == '__main__': 137 | app = QApplication() 138 | app.setStyle('Fusion') # 设置风格 139 | show_ui = WidgetDelete() 140 | show_ui.show() 141 | app.exec() 142 | -------------------------------------------------------------------------------- /ui/widget_filename_pattern.py: -------------------------------------------------------------------------------- 1 | import base64 2 | 3 | from PySide6.QtCore import Signal 4 | from PySide6.QtGui import QFont, QPixmap, QIcon 5 | from PySide6.QtWidgets import QWidget, QApplication, QPushButton 6 | 7 | from module.class_.class_rename_type import TypeNormal, TypePattern 8 | from res.icon import list_base64 9 | from ui.src.ui_widget_filename_pattern import Ui_Form 10 | 11 | 12 | class WidgetFilenamePattern(QWidget): 13 | signal_pattern = Signal(TypeNormal) 14 | 15 | def __init__(self, parent=None): 16 | super().__init__(parent) 17 | self.ui = Ui_Form() 18 | self.ui.setupUi(self) 19 | 20 | # self._show_layout_digit(False) # 隐藏数字的更多选项控件 21 | # self._show_layout_char(False)# 隐藏字符的更多选项控件 22 | self.set_tips() 23 | self._set_icon() 24 | self.ui.widget_preset_pattern.setVisible(False) 25 | 26 | self.ui.checkBox_pattern.stateChanged.connect(self.emit_signal) 27 | self.ui.lineEdit_pattern.textChanged.connect(self.pattern_changed) 28 | self.ui.spinBox_digit_start.valueChanged.connect(self.emit_signal) 29 | self.ui.spinBox_digit_step.valueChanged.connect(self.emit_signal) 30 | self.ui.spinBox_digit_length.valueChanged.connect(self.emit_signal) 31 | self.ui.checkBox_auto_fill_digit_length.stateChanged.connect(self.emit_signal) 32 | self.ui.checkBox_random_digit.stateChanged.connect(self.emit_signal) 33 | self.ui.checkBox_random_lowercase.stateChanged.connect(self.emit_signal) 34 | self.ui.checkBox_random_capital.stateChanged.connect(self.emit_signal) 35 | self.ui.checkBox_random_punctuation.stateChanged.connect(self.emit_signal) 36 | self.ui.spinBox_char_length.valueChanged.connect(self.emit_signal) 37 | self.ui.toolButton_choose_pattern.clicked.connect(self.show_preset_patterns) 38 | # self.ui.lineEdit_pattern.textChanged.connect(self._show_more_option) # 用于隐藏/显示数字、字符的更多选项 39 | self.ui.pushButton_preset_1.clicked.connect(self.set_preset_pattern) 40 | self.ui.pushButton_preset_2.clicked.connect(self.set_preset_pattern) 41 | self.ui.pushButton_preset_3.clicked.connect(self.set_preset_pattern) 42 | 43 | # 设置字体 44 | font = QFont() 45 | font.setPointSize(10) 46 | self.ui.lineEdit_pattern.setFont(font) 47 | 48 | def _set_icon(self): 49 | """设置图标""" 50 | pixmap = QPixmap() 51 | pixmap.loadFromData(base64.b64decode(list_base64)) 52 | self.ui.toolButton_choose_pattern.setIcon(QIcon(pixmap)) 53 | 54 | def set_tips(self): 55 | """设置说明文本""" 56 | tips_auto_dig = '示例:\n1 --> 001\n2 --> 002' 57 | self.ui.checkBox_auto_fill_digit_length.setToolTip(tips_auto_dig) 58 | 59 | tips_pattern = ('示例:' 60 | '\n1.修改为指定文本(例如修改为"目标文件名"):目标文件名' 61 | '\n2. 在文件名前添加父文件夹名(即<父目录名> - <原文件名>):/ - *' 62 | '\n3.在文件名后添加随机字符(即<原文件名>_<随机字符>):*_?' 63 | '\n4.在文件名后添加数字编号(即<原文件名>#<数字编号>):*###') 64 | self.ui.checkBox_pattern.setToolTip(tips_pattern) 65 | self.ui.lineEdit_pattern.setToolTip(tips_pattern) 66 | 67 | def show_preset_patterns(self): 68 | """显示预设模板""" 69 | self.ui.widget_preset_pattern.setVisible(not self.ui.widget_preset_pattern.isVisible()) 70 | 71 | def set_preset_pattern(self): 72 | """设置为预设模板""" 73 | button: QPushButton = self.sender() 74 | button_text = button.text() 75 | pattern = button_text.split('|')[1].strip() 76 | self.ui.lineEdit_pattern.setText(pattern) 77 | 78 | self.show_preset_patterns() 79 | 80 | def emit_signal(self): 81 | """发送信号""" 82 | pattern = self.ui.lineEdit_pattern.text() 83 | if len(pattern): 84 | is_enable = self.ui.checkBox_pattern.isChecked() 85 | else: 86 | is_enable = False 87 | 88 | digit_start = self.ui.spinBox_digit_start.value() 89 | digit_step = self.ui.spinBox_digit_step.value() 90 | digit_length = self.ui.spinBox_digit_length.value() 91 | auto_fill = self.ui.checkBox_auto_fill_digit_length.isChecked() 92 | 93 | random_digit = self.ui.checkBox_random_digit.isChecked() 94 | random_lowercase = self.ui.checkBox_random_lowercase.isChecked() 95 | random_capital = self.ui.checkBox_random_capital.isChecked() 96 | random_punctuation = self.ui.checkBox_random_punctuation.isChecked() 97 | random_length = self.ui.spinBox_char_length.value() 98 | 99 | rule_class = TypePattern(is_enable, pattern) 100 | rule_class.update_digit(digit_start, digit_step, digit_length, auto_fill) 101 | rule_class.update_char(random_digit, random_lowercase, random_capital, random_punctuation, random_length) 102 | self.signal_pattern.emit(rule_class) 103 | self._set_preview_tips(rule_class.get_preview()) 104 | 105 | def pattern_changed(self): 106 | """选项变动""" 107 | text = self.ui.lineEdit_pattern.text() 108 | if len(text): 109 | self.ui.checkBox_pattern.setChecked(True) 110 | else: 111 | self.ui.checkBox_pattern.setChecked(False) 112 | 113 | self.emit_signal() 114 | 115 | def _show_layout_digit(self, is_show): 116 | """显示数字选项布局""" 117 | self.ui.widget_digit.setVisible(is_show) 118 | 119 | def _show_layout_char(self, is_show): 120 | """显示字符选项布局""" 121 | self.ui.widget_char.setVisible(is_show) 122 | 123 | def _show_more_option(self): 124 | """显示更多选项""" 125 | pattern = self.ui.lineEdit_pattern.text() 126 | if '##' in pattern or '"' in pattern: 127 | self._show_layout_digit(True) 128 | else: 129 | self._show_layout_digit(False) 130 | 131 | if '?' in pattern: 132 | self._show_layout_char(True) 133 | else: 134 | self._show_layout_char(False) 135 | 136 | def _set_preview_tips(self, tip: str): 137 | """设置预览文本""" 138 | if tip: 139 | self.ui.lineEdit_pattern.setToolTip(tip) 140 | 141 | 142 | if __name__ == '__main__': 143 | app = QApplication() 144 | app.setStyle('Fusion') # 设置风格 145 | show_ui = WidgetFilenamePattern() 146 | show_ui.show() 147 | app.exec() 148 | -------------------------------------------------------------------------------- /module/class_/class_rename_method.py: -------------------------------------------------------------------------------- 1 | # 具体的重命名方法类,包含具体执行的内容 2 | import os 3 | 4 | from module import function_normal 5 | 6 | 7 | class MethodNormal: 8 | """重命名方法类-一般方法,增删查改""" 9 | 10 | class _Model: 11 | def __init__(self, index=None, char=None, new_char=None, count=None): 12 | self.index = index 13 | self.char = char 14 | self.new_char = new_char 15 | self.count = count 16 | 17 | def rename_method(self, filetitle: str): 18 | """执行该类的重命名方法 19 | :return: 适用规则后的新文本""" 20 | pass 21 | 22 | class Insert(_Model): 23 | """插入""" 24 | 25 | def __init__(self, index, char): 26 | super().__init__(index=index, char=char) 27 | 28 | def rename_method(self, filetitle: str): 29 | new_filetitle = filetitle[0:self.index] + self.char + filetitle[self.index:] 30 | return new_filetitle 31 | 32 | class InsertBack(Insert): 33 | """插入(反向)""" 34 | 35 | class DeleteIndex(_Model): 36 | """删除""" 37 | 38 | def __init__(self, index, count): 39 | super().__init__(index=index, count=count) 40 | 41 | def rename_method(self, filetitle: str): 42 | # index为第n个字符开始,将其设置为0和1为同一个含义 43 | if self.index == 0: 44 | index = 0 45 | new_filetitle = filetitle[index + self.count:] 46 | else: 47 | if self.index > 0: 48 | index = self.index - 1 49 | else: 50 | if abs(self.index) <= len(filetitle): 51 | index = len(filetitle) + self.index 52 | else: 53 | index = 0 54 | new_filetitle = filetitle[0:index] + filetitle[index + self.count:] 55 | 56 | return new_filetitle 57 | 58 | class DeleteIndexBack(DeleteIndex): 59 | """删除(反向)""" 60 | 61 | class Replace(_Model): 62 | """替换""" 63 | 64 | def __init__(self, char, new_char): 65 | super().__init__(char=char, new_char=new_char) 66 | 67 | def rename_method(self, filetitle: str): 68 | new_filetitle = filetitle.replace(self.char, self.new_char) 69 | return new_filetitle 70 | 71 | 72 | class MethodConvert: 73 | """重命名方法类-文件名转换""" 74 | 75 | class _Model: 76 | def __init__(self): 77 | pass 78 | 79 | def rename_method(self, filetitle: str): 80 | """执行该类的重命名方法 81 | :return: 适用规则后的新文本""" 82 | pass 83 | 84 | class ConvertToLowercase(_Model): 85 | """转为小写字母""" 86 | 87 | def rename_method(self, filetitle: str): 88 | new_filetitle = filetitle.lower() 89 | return new_filetitle 90 | 91 | class ConvertToCapital(_Model): 92 | """转为大写字母""" 93 | 94 | def rename_method(self, filetitle: str): 95 | new_filetitle = filetitle.upper() 96 | return new_filetitle 97 | 98 | class ConvertToChs(_Model): 99 | """转为简体中文""" 100 | 101 | def rename_method(self, filetitle: str): 102 | new_filetitle = function_normal.to_chs(filetitle) 103 | return new_filetitle 104 | 105 | class ConvertToCht(_Model): 106 | """转为繁体中文""" 107 | 108 | def rename_method(self, filetitle: str): 109 | new_filetitle = function_normal.to_cht(filetitle) 110 | return new_filetitle 111 | 112 | class ConvertToHalfWidth(_Model): 113 | """转为半角字符""" 114 | 115 | def rename_method(self, filetitle: str): 116 | new_filetitle = function_normal.to_half_width(filetitle) 117 | return new_filetitle 118 | 119 | class ConvertToFullWidth(_Model): 120 | """转为全角字符""" 121 | 122 | def rename_method(self, filetitle: str): 123 | new_filetitle = function_normal.to_full_width(filetitle) 124 | return new_filetitle 125 | 126 | class ClearExcessSpaces(_Model): 127 | """清除多余空格""" 128 | 129 | def rename_method(self, filetitle: str): 130 | while True: 131 | if filetitle[0] == ' ' or filetitle[-1] == ' ': 132 | filetitle = filetitle.strip(' ') 133 | elif ' ' in filetitle: 134 | filetitle = filetitle.replace(' ', ' ') 135 | else: 136 | break 137 | 138 | return filetitle 139 | 140 | 141 | class MethodFileExtension: 142 | """重命名方法类-处理文件扩展名(仅传入后缀)""" 143 | 144 | class _Model: 145 | def __init__(self): 146 | pass 147 | 148 | def rename_method(self, file_extension: str): 149 | """执行该类的重命名方法 150 | :return: 适用规则后的新文本""" 151 | pass 152 | 153 | class ChangeTo(_Model): 154 | """改为指定扩展名""" 155 | 156 | def __init__(self, file_extension): 157 | super().__init__() 158 | if file_extension and file_extension[0] != '.': 159 | self.file_extension = '.' + file_extension 160 | else: 161 | self.file_extension = file_extension 162 | 163 | def rename_method(self, file_extension: str): 164 | return self.file_extension 165 | 166 | class ConvertToLowercase(_Model): 167 | """转为小写字母""" 168 | 169 | def rename_method(self, file_extension: str): 170 | new_file_extension = file_extension.lower() 171 | return new_file_extension 172 | 173 | class ConvertToCapital(_Model): 174 | """转为大写字母""" 175 | 176 | def rename_method(self, file_extension: str): 177 | new_file_extension = file_extension.upper() 178 | return new_file_extension 179 | 180 | class ChangeToRealType(_Model): 181 | """改为真实文件类型""" 182 | 183 | def rename_method(self, filepath: str): 184 | # 注意,该函数需要传入原始路径,原调用方法传入的是后缀名,需要单独处理传入的参数 185 | if not os.path.exists(filepath) or not os.path.isfile(filepath): 186 | return '' 187 | 188 | new_file_extension = function_normal.guess_filetype(filepath) 189 | if new_file_extension: 190 | return '.' + new_file_extension # filetype库返回的后缀名不包含.,需要手工添加 191 | else: 192 | return '' 193 | -------------------------------------------------------------------------------- /ui/src/ui_widget_insert.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ################################################################################ 4 | ## Form generated from reading UI file 'ui_widget_insertyhKFeU.ui' 5 | ## 6 | ## Created by: Qt User Interface Compiler version 6.8.1 7 | ## 8 | ## WARNING! All changes made in this file will be lost when recompiling UI file! 9 | ################################################################################ 10 | 11 | from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, 12 | QMetaObject, QObject, QPoint, QRect, 13 | QSize, QTime, QUrl, Qt) 14 | from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, 15 | QFont, QFontDatabase, QGradient, QIcon, 16 | QImage, QKeySequence, QLinearGradient, QPainter, 17 | QPalette, QPixmap, QRadialGradient, QTransform) 18 | from PySide6.QtWidgets import (QApplication, QCheckBox, QGroupBox, QHBoxLayout, 19 | QLabel, QLineEdit, QSizePolicy, QSpinBox, 20 | QVBoxLayout, QWidget) 21 | 22 | class Ui_Form(object): 23 | def setupUi(self, Form): 24 | if not Form.objectName(): 25 | Form.setObjectName(u"Form") 26 | Form.resize(274, 130) 27 | self.verticalLayout_2 = QVBoxLayout(Form) 28 | self.verticalLayout_2.setSpacing(3) 29 | self.verticalLayout_2.setObjectName(u"verticalLayout_2") 30 | self.verticalLayout_2.setContentsMargins(3, 3, 3, 3) 31 | self.groupBox = QGroupBox(Form) 32 | self.groupBox.setObjectName(u"groupBox") 33 | self.verticalLayout = QVBoxLayout(self.groupBox) 34 | self.verticalLayout.setSpacing(3) 35 | self.verticalLayout.setObjectName(u"verticalLayout") 36 | self.verticalLayout.setContentsMargins(3, 3, 3, 3) 37 | self.horizontalLayout = QHBoxLayout() 38 | self.horizontalLayout.setObjectName(u"horizontalLayout") 39 | self.checkBox_add_prefix = QCheckBox(self.groupBox) 40 | self.checkBox_add_prefix.setObjectName(u"checkBox_add_prefix") 41 | 42 | self.horizontalLayout.addWidget(self.checkBox_add_prefix) 43 | 44 | self.lineEdit_add_prefix = QLineEdit(self.groupBox) 45 | self.lineEdit_add_prefix.setObjectName(u"lineEdit_add_prefix") 46 | 47 | self.horizontalLayout.addWidget(self.lineEdit_add_prefix) 48 | 49 | 50 | self.verticalLayout.addLayout(self.horizontalLayout) 51 | 52 | self.horizontalLayout_3 = QHBoxLayout() 53 | self.horizontalLayout_3.setObjectName(u"horizontalLayout_3") 54 | self.checkBox_add_suffix = QCheckBox(self.groupBox) 55 | self.checkBox_add_suffix.setObjectName(u"checkBox_add_suffix") 56 | 57 | self.horizontalLayout_3.addWidget(self.checkBox_add_suffix) 58 | 59 | self.lineEdit_add_suffix = QLineEdit(self.groupBox) 60 | self.lineEdit_add_suffix.setObjectName(u"lineEdit_add_suffix") 61 | 62 | self.horizontalLayout_3.addWidget(self.lineEdit_add_suffix) 63 | 64 | 65 | self.verticalLayout.addLayout(self.horizontalLayout_3) 66 | 67 | self.horizontalLayout_4 = QHBoxLayout() 68 | self.horizontalLayout_4.setObjectName(u"horizontalLayout_4") 69 | self.checkBox_insert_index = QCheckBox(self.groupBox) 70 | self.checkBox_insert_index.setObjectName(u"checkBox_insert_index") 71 | 72 | self.horizontalLayout_4.addWidget(self.checkBox_insert_index) 73 | 74 | self.spinBox_index = QSpinBox(self.groupBox) 75 | self.spinBox_index.setObjectName(u"spinBox_index") 76 | self.spinBox_index.setMinimum(1) 77 | self.spinBox_index.setMaximum(999) 78 | 79 | self.horizontalLayout_4.addWidget(self.spinBox_index) 80 | 81 | self.label = QLabel(self.groupBox) 82 | self.label.setObjectName(u"label") 83 | 84 | self.horizontalLayout_4.addWidget(self.label) 85 | 86 | self.lineEdit_insert_character = QLineEdit(self.groupBox) 87 | self.lineEdit_insert_character.setObjectName(u"lineEdit_insert_character") 88 | 89 | self.horizontalLayout_4.addWidget(self.lineEdit_insert_character) 90 | 91 | 92 | self.verticalLayout.addLayout(self.horizontalLayout_4) 93 | 94 | self.horizontalLayout_5 = QHBoxLayout() 95 | self.horizontalLayout_5.setObjectName(u"horizontalLayout_5") 96 | self.checkBox_insert_index_back = QCheckBox(self.groupBox) 97 | self.checkBox_insert_index_back.setObjectName(u"checkBox_insert_index_back") 98 | 99 | self.horizontalLayout_5.addWidget(self.checkBox_insert_index_back) 100 | 101 | self.spinBox_index_back = QSpinBox(self.groupBox) 102 | self.spinBox_index_back.setObjectName(u"spinBox_index_back") 103 | self.spinBox_index_back.setMinimum(1) 104 | self.spinBox_index_back.setMaximum(999) 105 | 106 | self.horizontalLayout_5.addWidget(self.spinBox_index_back) 107 | 108 | self.label_2 = QLabel(self.groupBox) 109 | self.label_2.setObjectName(u"label_2") 110 | 111 | self.horizontalLayout_5.addWidget(self.label_2) 112 | 113 | self.lineEdit_insert_character_back = QLineEdit(self.groupBox) 114 | self.lineEdit_insert_character_back.setObjectName(u"lineEdit_insert_character_back") 115 | 116 | self.horizontalLayout_5.addWidget(self.lineEdit_insert_character_back) 117 | 118 | 119 | self.verticalLayout.addLayout(self.horizontalLayout_5) 120 | 121 | 122 | self.verticalLayout_2.addWidget(self.groupBox) 123 | 124 | 125 | self.retranslateUi(Form) 126 | 127 | QMetaObject.connectSlotsByName(Form) 128 | # setupUi 129 | 130 | def retranslateUi(self, Form): 131 | Form.setWindowTitle(QCoreApplication.translate("Form", u"Form", None)) 132 | self.groupBox.setTitle(QCoreApplication.translate("Form", u"\u6dfb\u52a0", None)) 133 | self.checkBox_add_prefix.setText(QCoreApplication.translate("Form", u"\u6587\u4ef6\u540d\u524d\u63d2\u5165", None)) 134 | self.lineEdit_add_prefix.setPlaceholderText(QCoreApplication.translate("Form", u"\u9700\u8981\u63d2\u5165\u7684\u5b57\u7b26", None)) 135 | self.checkBox_add_suffix.setText(QCoreApplication.translate("Form", u"\u6587\u4ef6\u540d\u540e\u6dfb\u52a0", None)) 136 | self.lineEdit_add_suffix.setPlaceholderText(QCoreApplication.translate("Form", u"\u9700\u8981\u63d2\u5165\u7684\u5b57\u7b26", None)) 137 | self.checkBox_insert_index.setText(QCoreApplication.translate("Form", u"\u5728\u7b2c", None)) 138 | self.label.setText(QCoreApplication.translate("Form", u"\u4e2a\u5b57\u7b26\u540e\u63d2\u5165", None)) 139 | self.lineEdit_insert_character.setPlaceholderText(QCoreApplication.translate("Form", u"\u9700\u8981\u63d2\u5165\u7684\u5b57\u7b26", None)) 140 | self.checkBox_insert_index_back.setText(QCoreApplication.translate("Form", u"\u5728\u5012\u6570\u7b2c", None)) 141 | self.label_2.setText(QCoreApplication.translate("Form", u"\u4e2a\u5b57\u7b26\u540e\u63d2\u5165", None)) 142 | self.lineEdit_insert_character_back.setPlaceholderText(QCoreApplication.translate("Form", u"\u9700\u8981\u63d2\u5165\u7684\u5b57\u7b26", None)) 143 | # retranslateUi 144 | 145 | -------------------------------------------------------------------------------- /module/function_normal.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import inspect 3 | import os 4 | import time 5 | from typing import Union 6 | 7 | import filetype 8 | import unicodedata 9 | from opencc import OpenCC 10 | 11 | from module import WindowsSorted 12 | from module.class_.class_rename_info import RenameInfo 13 | from res.icon import * 14 | 15 | 16 | def print_function_info(mode: str = 'current'): 17 | """ 18 | 打印当前/上一个执行的函数信息 19 | :param mode: str类型,'current' 或 'last' 20 | """ 21 | # pass 22 | 23 | if mode == 'current': 24 | print(time.strftime('%H:%M:%S ', time.localtime()), 25 | inspect.getframeinfo(inspect.currentframe().f_back).function) 26 | elif mode == 'last': 27 | print(time.strftime('%H:%M:%S ', time.localtime()), 28 | inspect.getframeinfo(inspect.currentframe().f_back.f_back).function) 29 | 30 | 31 | def to_half_width(text): 32 | """将传入字符串转换为半角字符""" 33 | # 先将字符串进行Unicode规范化为NFKC格式(兼容性组合用序列) 34 | normalized_string = unicodedata.normalize('NFKC', text) 35 | 36 | # 对于ASCII范围内的全角字符,将其替换为对应的半角字符 37 | half_width_string = [] 38 | for char in normalized_string: 39 | code_point = ord(char) 40 | if 0xFF01 <= code_point <= 0xFF5E: 41 | half_width_string.append(chr(code_point - 0xFEE0)) 42 | else: 43 | half_width_string.append(char) 44 | 45 | return ''.join(half_width_string) 46 | 47 | 48 | def to_full_width(text): 49 | """将传入字符串转换为全角字符""" 50 | # 将字符串进行Unicode规范化为NFKC格式(兼容性组合用序列) 51 | normalized_string = unicodedata.normalize('NFKC', text) 52 | 53 | # 对于ASCII范围内的字符,将其替换为对应的全角字符 54 | full_width_string = [] 55 | for char in normalized_string: 56 | code_point = ord(char) 57 | if 0x0020 <= code_point <= 0x007E: 58 | full_width_string.append(chr(code_point + 0xFF00 - 0x0020)) 59 | else: 60 | full_width_string.append(char) 61 | 62 | return ''.join(full_width_string) 63 | 64 | 65 | def half_to_full_width(input_str): 66 | full_width_str = [] 67 | for char in input_str: 68 | code = ord(char) 69 | # Convert only if the character is within the ASCII range of half-width characters 70 | if 0x0021 <= code <= 0x007e: 71 | full_width_str.append(chr(code + 0xfee0)) 72 | else: 73 | full_width_str.append(char) 74 | return ''.join(full_width_str) 75 | 76 | 77 | def to_chs(text): 78 | """将字符串中的中文转换为简体中文""" 79 | cc = OpenCC('t2s') 80 | text_converted = cc.convert(text) 81 | return text_converted 82 | 83 | 84 | def to_cht(text): 85 | """将字符串中的中文转换为繁体中文""" 86 | cc = OpenCC('s2t') 87 | text_converted = cc.convert(text) 88 | return text_converted 89 | 90 | 91 | def guess_filetype(path) -> Union[str, bool]: 92 | """判断文件类型""" 93 | kind = filetype.guess(path) 94 | if kind is None: 95 | return False 96 | 97 | type_ = kind.extension 98 | if type_: 99 | return type_ 100 | else: 101 | return False 102 | 103 | 104 | def check_filename_feasible(filename: str, replace: bool = False, replace_word: str = '') -> Union[str, bool]: 105 | """检查一个文件名是否符合Windows文件命名规范 106 | :param filename: str,仅文件名(不含路径) 107 | :param replace: bool,是否替换非法字符 108 | :param replace_word: 替换不合规字符的字符""" 109 | # 官方文档:文件和文件夹不能命名为“.”或“..”,也不能包含以下任何字符: \ / : * ? " < > | 110 | except_word = ['\\', '/', ':', '*', '?', '"', '<', '>', '|'] 111 | if not replace: # 不替换时,仅检查 112 | # 检查. 113 | if filename[0] == '.': 114 | return False 115 | 116 | # 检查其余符号 117 | for key in except_word: 118 | if key in filename: 119 | return False 120 | return True 121 | 122 | else: 123 | for word in except_word: # 替换符号 124 | filename = filename.replace(word, replace_word) 125 | while filename[0] == '.': # 替换. 126 | filename = replace_word + filename[1:] 127 | 128 | return filename.strip() 129 | 130 | 131 | def sort_paths_by_filename(paths: list, order): 132 | """根据文件名排序路径""" 133 | f_dict = dict() 134 | for index, path in enumerate(paths): 135 | filename = os.path.basename(path) 136 | filename_index = filename + f'_____{index}' 137 | f_dict[filename_index] = path 138 | 139 | filenames_sorted = WindowsSorted.sort_list(list(f_dict.keys()), order) 140 | paths_sorted = [f_dict[i] for i in filenames_sorted] 141 | return paths_sorted 142 | 143 | 144 | def calc_final_path(rename_info_class: RenameInfo) -> RenameInfo: 145 | """计算最终重命名的文件名(无重复文件名)""" 146 | # 首先判断是否需要重命名(考虑大小写英文) 147 | old_path = rename_info_class.path_need 148 | calc_path = rename_info_class.calc_path 149 | if old_path.lower() == calc_path.lower(): 150 | rename_info_class.update_rename_no_dup() 151 | return rename_info_class 152 | 153 | # 然后判断同级是否有重复文件名(考虑大小写英文) 154 | parent_dirpath = rename_info_class.parent_dirpath 155 | listdir = [i.lower() for i in os.listdir(parent_dirpath)] 156 | calc_filename = rename_info_class.calc_filename 157 | if calc_filename.lower() not in listdir: # 文件夹内无重复文件名 158 | rename_info_class.update_rename_no_dup() 159 | return rename_info_class 160 | else: # 文件夹内有重复文件名,则需要添加后缀 161 | index = 2 162 | calc_filetitle = rename_info_class.calc_filetitle 163 | calc_file_extension = rename_info_class.calc_file_extension 164 | no_dup_filetitle = calc_filetitle + f' ({index})' 165 | no_dup_filename = no_dup_filetitle + calc_file_extension 166 | while True: 167 | if no_dup_filename.lower() not in listdir: 168 | break 169 | else: 170 | index += 1 171 | no_dup_filetitle = calc_filetitle + f' ({index})' 172 | no_dup_filename = no_dup_filetitle + calc_file_extension 173 | rename_info_class.update_rename(no_dup_filetitle) 174 | return rename_info_class 175 | 176 | 177 | def calc_cancelled_path(rename_info_class: RenameInfo) -> RenameInfo: 178 | """计算撤销重命名时的原始文件名(无重复文件名)""" 179 | # 首先判断是否需要重命名(考虑大小写英文) 180 | old_path = rename_info_class.path_need 181 | original_path = rename_info_class.original_path 182 | if old_path.lower() == original_path.lower(): 183 | rename_info_class.update_cancel_rename_no_dup() 184 | return rename_info_class 185 | 186 | # 然后判断同级是否有重复文件名(考虑大小写英文) 187 | parent_dirpath = rename_info_class.parent_dirpath 188 | listdir = [i.lower() for i in os.listdir(parent_dirpath)] 189 | original_filename = rename_info_class.original_filename 190 | if original_filename.lower() not in listdir: # 文件夹内无重复文件名 191 | rename_info_class.update_cancel_rename_no_dup() 192 | return rename_info_class 193 | else: # 文件夹内有重复文件名,则需要添加后缀 194 | index = 2 195 | original_filetitle = rename_info_class.original_filetitle 196 | original_file_extension = rename_info_class.original_file_extension 197 | no_dup_filetitle = original_filetitle + f' ({index})' 198 | no_dup_filename = no_dup_filetitle + original_file_extension 199 | while True: 200 | if no_dup_filename.lower() not in listdir: 201 | break 202 | else: 203 | index += 1 204 | no_dup_filetitle = original_filetitle + f' ({index})' 205 | no_dup_filename = no_dup_filetitle + original_file_extension 206 | rename_info_class.update_cancel_rename(no_dup_filetitle) 207 | return rename_info_class 208 | 209 | 210 | def get_icon_result(result: str): 211 | """获取结果对应的图标""" 212 | if result == 'renamed': 213 | return base64.b64decode(right_base64), '完成' 214 | elif result == 'unknown_error': 215 | return base64.b64decode(error_base64), '未知错误' 216 | elif result == 'skipped': 217 | return base64.b64decode(skip_base64), '跳过' 218 | elif result == 'not_exist': 219 | return base64.b64decode(not_exist_base64), '文件不存在' 220 | elif result == 'occupied': 221 | return base64.b64decode(occupied_base64), '文件被占用' 222 | elif result == 'cancelled': 223 | return base64.b64decode(cancelled_base64), '已撤销重命名操作' 224 | -------------------------------------------------------------------------------- /ui/src/ui_widget_delete.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 320 10 | 156 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 3 19 | 20 | 21 | 3 22 | 23 | 24 | 3 25 | 26 | 27 | 3 28 | 29 | 30 | 3 31 | 32 | 33 | 34 | 35 | 删除 36 | 37 | 38 | 39 | 3 40 | 41 | 42 | 3 43 | 44 | 45 | 3 46 | 47 | 48 | 3 49 | 50 | 51 | 3 52 | 53 | 54 | 55 | 56 | 3 57 | 58 | 59 | 60 | 61 | 删除文件名中的 62 | 63 | 64 | 65 | 66 | 67 | 68 | 需要删除的字符 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 3 78 | 79 | 80 | 81 | 82 | 从第 83 | 84 | 85 | 86 | 87 | 88 | 89 | 1 90 | 91 | 92 | 999 93 | 94 | 95 | 96 | 97 | 98 | 99 | 个字符开始,删除 100 | 101 | 102 | 103 | 104 | 105 | 106 | 1 107 | 108 | 109 | 999 110 | 111 | 112 | 113 | 114 | 115 | 116 | 个字符 117 | 118 | 119 | 120 | 121 | 122 | 123 | Qt::Orientation::Horizontal 124 | 125 | 126 | 127 | 40 128 | 20 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 3 139 | 140 | 141 | 142 | 143 | 从倒数第 144 | 145 | 146 | 147 | 148 | 149 | 150 | 1 151 | 152 | 153 | 999 154 | 155 | 156 | 157 | 158 | 159 | 160 | 个字符开始,删除 161 | 162 | 163 | 164 | 165 | 166 | 167 | 1 168 | 169 | 170 | 999 171 | 172 | 173 | 174 | 175 | 176 | 177 | 个字符 178 | 179 | 180 | 181 | 182 | 183 | 184 | Qt::Orientation::Horizontal 185 | 186 | 187 | 188 | 40 189 | 20 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 3 200 | 201 | 202 | 203 | 204 | 删除第 205 | 206 | 207 | 208 | 209 | 210 | 211 | 0 212 | 213 | 214 | 999 215 | 216 | 217 | 218 | 219 | 220 | 221 | 个字符后的全部字符 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 3 231 | 232 | 233 | 234 | 235 | 删除倒数第 236 | 237 | 238 | 239 | 240 | 241 | 242 | 1 243 | 244 | 245 | 999 246 | 247 | 248 | 249 | 250 | 251 | 252 | 个字符后的全部字符 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | -------------------------------------------------------------------------------- /ui/src/ui_widget_delete.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ################################################################################ 4 | ## Form generated from reading UI file 'ui_widget_deletePEMkKB.ui' 5 | ## 6 | ## Created by: Qt User Interface Compiler version 6.8.1 7 | ## 8 | ## WARNING! All changes made in this file will be lost when recompiling UI file! 9 | ################################################################################ 10 | 11 | from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, 12 | QMetaObject, QObject, QPoint, QRect, 13 | QSize, QTime, QUrl, Qt) 14 | from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, 15 | QFont, QFontDatabase, QGradient, QIcon, 16 | QImage, QKeySequence, QLinearGradient, QPainter, 17 | QPalette, QPixmap, QRadialGradient, QTransform) 18 | from PySide6.QtWidgets import (QApplication, QCheckBox, QGroupBox, QHBoxLayout, 19 | QLabel, QLineEdit, QSizePolicy, QSpacerItem, 20 | QSpinBox, QVBoxLayout, QWidget) 21 | 22 | class Ui_Form(object): 23 | def setupUi(self, Form): 24 | if not Form.objectName(): 25 | Form.setObjectName(u"Form") 26 | Form.resize(320, 156) 27 | self.verticalLayout = QVBoxLayout(Form) 28 | self.verticalLayout.setSpacing(3) 29 | self.verticalLayout.setObjectName(u"verticalLayout") 30 | self.verticalLayout.setContentsMargins(3, 3, 3, 3) 31 | self.groupBox = QGroupBox(Form) 32 | self.groupBox.setObjectName(u"groupBox") 33 | self.verticalLayout_2 = QVBoxLayout(self.groupBox) 34 | self.verticalLayout_2.setSpacing(3) 35 | self.verticalLayout_2.setObjectName(u"verticalLayout_2") 36 | self.verticalLayout_2.setContentsMargins(3, 3, 3, 3) 37 | self.horizontalLayout_2 = QHBoxLayout() 38 | self.horizontalLayout_2.setSpacing(3) 39 | self.horizontalLayout_2.setObjectName(u"horizontalLayout_2") 40 | self.checkBox_delete_character = QCheckBox(self.groupBox) 41 | self.checkBox_delete_character.setObjectName(u"checkBox_delete_character") 42 | 43 | self.horizontalLayout_2.addWidget(self.checkBox_delete_character) 44 | 45 | self.lineEdit_delete_character = QLineEdit(self.groupBox) 46 | self.lineEdit_delete_character.setObjectName(u"lineEdit_delete_character") 47 | 48 | self.horizontalLayout_2.addWidget(self.lineEdit_delete_character) 49 | 50 | self.horizontalLayout_2.setStretch(1, 1) 51 | 52 | self.verticalLayout_2.addLayout(self.horizontalLayout_2) 53 | 54 | self.horizontalLayout = QHBoxLayout() 55 | self.horizontalLayout.setSpacing(3) 56 | self.horizontalLayout.setObjectName(u"horizontalLayout") 57 | self.checkBox_delete_index = QCheckBox(self.groupBox) 58 | self.checkBox_delete_index.setObjectName(u"checkBox_delete_index") 59 | 60 | self.horizontalLayout.addWidget(self.checkBox_delete_index) 61 | 62 | self.spinBox_index = QSpinBox(self.groupBox) 63 | self.spinBox_index.setObjectName(u"spinBox_index") 64 | self.spinBox_index.setMinimum(1) 65 | self.spinBox_index.setMaximum(999) 66 | 67 | self.horizontalLayout.addWidget(self.spinBox_index) 68 | 69 | self.label = QLabel(self.groupBox) 70 | self.label.setObjectName(u"label") 71 | 72 | self.horizontalLayout.addWidget(self.label) 73 | 74 | self.spinBox_delete_count = QSpinBox(self.groupBox) 75 | self.spinBox_delete_count.setObjectName(u"spinBox_delete_count") 76 | self.spinBox_delete_count.setMinimum(1) 77 | self.spinBox_delete_count.setMaximum(999) 78 | 79 | self.horizontalLayout.addWidget(self.spinBox_delete_count) 80 | 81 | self.label_2 = QLabel(self.groupBox) 82 | self.label_2.setObjectName(u"label_2") 83 | 84 | self.horizontalLayout.addWidget(self.label_2) 85 | 86 | self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) 87 | 88 | self.horizontalLayout.addItem(self.horizontalSpacer) 89 | 90 | 91 | self.verticalLayout_2.addLayout(self.horizontalLayout) 92 | 93 | self.horizontalLayout_6 = QHBoxLayout() 94 | self.horizontalLayout_6.setSpacing(3) 95 | self.horizontalLayout_6.setObjectName(u"horizontalLayout_6") 96 | self.checkBox_delete_index_back = QCheckBox(self.groupBox) 97 | self.checkBox_delete_index_back.setObjectName(u"checkBox_delete_index_back") 98 | 99 | self.horizontalLayout_6.addWidget(self.checkBox_delete_index_back) 100 | 101 | self.spinBox_index_back = QSpinBox(self.groupBox) 102 | self.spinBox_index_back.setObjectName(u"spinBox_index_back") 103 | self.spinBox_index_back.setMinimum(1) 104 | self.spinBox_index_back.setMaximum(999) 105 | 106 | self.horizontalLayout_6.addWidget(self.spinBox_index_back) 107 | 108 | self.label_6 = QLabel(self.groupBox) 109 | self.label_6.setObjectName(u"label_6") 110 | 111 | self.horizontalLayout_6.addWidget(self.label_6) 112 | 113 | self.spinBox_delete_count_back = QSpinBox(self.groupBox) 114 | self.spinBox_delete_count_back.setObjectName(u"spinBox_delete_count_back") 115 | self.spinBox_delete_count_back.setMinimum(1) 116 | self.spinBox_delete_count_back.setMaximum(999) 117 | 118 | self.horizontalLayout_6.addWidget(self.spinBox_delete_count_back) 119 | 120 | self.label_7 = QLabel(self.groupBox) 121 | self.label_7.setObjectName(u"label_7") 122 | 123 | self.horizontalLayout_6.addWidget(self.label_7) 124 | 125 | self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) 126 | 127 | self.horizontalLayout_6.addItem(self.horizontalSpacer_2) 128 | 129 | 130 | self.verticalLayout_2.addLayout(self.horizontalLayout_6) 131 | 132 | self.horizontalLayout_3 = QHBoxLayout() 133 | self.horizontalLayout_3.setSpacing(3) 134 | self.horizontalLayout_3.setObjectName(u"horizontalLayout_3") 135 | self.checkBox_delete_index_after = QCheckBox(self.groupBox) 136 | self.checkBox_delete_index_after.setObjectName(u"checkBox_delete_index_after") 137 | 138 | self.horizontalLayout_3.addWidget(self.checkBox_delete_index_after) 139 | 140 | self.spinBox_index_del_after = QSpinBox(self.groupBox) 141 | self.spinBox_index_del_after.setObjectName(u"spinBox_index_del_after") 142 | self.spinBox_index_del_after.setMinimum(0) 143 | self.spinBox_index_del_after.setMaximum(999) 144 | 145 | self.horizontalLayout_3.addWidget(self.spinBox_index_del_after) 146 | 147 | self.label_3 = QLabel(self.groupBox) 148 | self.label_3.setObjectName(u"label_3") 149 | 150 | self.horizontalLayout_3.addWidget(self.label_3) 151 | 152 | self.horizontalLayout_3.setStretch(2, 1) 153 | 154 | self.verticalLayout_2.addLayout(self.horizontalLayout_3) 155 | 156 | self.horizontalLayout_5 = QHBoxLayout() 157 | self.horizontalLayout_5.setSpacing(3) 158 | self.horizontalLayout_5.setObjectName(u"horizontalLayout_5") 159 | self.checkBox_delete_index_after_back = QCheckBox(self.groupBox) 160 | self.checkBox_delete_index_after_back.setObjectName(u"checkBox_delete_index_after_back") 161 | 162 | self.horizontalLayout_5.addWidget(self.checkBox_delete_index_after_back) 163 | 164 | self.spinBox_index_del_after_back = QSpinBox(self.groupBox) 165 | self.spinBox_index_del_after_back.setObjectName(u"spinBox_index_del_after_back") 166 | self.spinBox_index_del_after_back.setMinimum(1) 167 | self.spinBox_index_del_after_back.setMaximum(999) 168 | 169 | self.horizontalLayout_5.addWidget(self.spinBox_index_del_after_back) 170 | 171 | self.label_5 = QLabel(self.groupBox) 172 | self.label_5.setObjectName(u"label_5") 173 | 174 | self.horizontalLayout_5.addWidget(self.label_5) 175 | 176 | self.horizontalLayout_5.setStretch(2, 1) 177 | 178 | self.verticalLayout_2.addLayout(self.horizontalLayout_5) 179 | 180 | 181 | self.verticalLayout.addWidget(self.groupBox) 182 | 183 | 184 | self.retranslateUi(Form) 185 | 186 | QMetaObject.connectSlotsByName(Form) 187 | # setupUi 188 | 189 | def retranslateUi(self, Form): 190 | Form.setWindowTitle(QCoreApplication.translate("Form", u"Form", None)) 191 | self.groupBox.setTitle(QCoreApplication.translate("Form", u"\u5220\u9664", None)) 192 | self.checkBox_delete_character.setText(QCoreApplication.translate("Form", u"\u5220\u9664\u6587\u4ef6\u540d\u4e2d\u7684", None)) 193 | self.lineEdit_delete_character.setPlaceholderText(QCoreApplication.translate("Form", u"\u9700\u8981\u5220\u9664\u7684\u5b57\u7b26", None)) 194 | self.checkBox_delete_index.setText(QCoreApplication.translate("Form", u"\u4ece\u7b2c", None)) 195 | self.label.setText(QCoreApplication.translate("Form", u"\u4e2a\u5b57\u7b26\u5f00\u59cb\uff0c\u5220\u9664", None)) 196 | self.label_2.setText(QCoreApplication.translate("Form", u"\u4e2a\u5b57\u7b26", None)) 197 | self.checkBox_delete_index_back.setText(QCoreApplication.translate("Form", u"\u4ece\u5012\u6570\u7b2c", None)) 198 | self.label_6.setText(QCoreApplication.translate("Form", u"\u4e2a\u5b57\u7b26\u5f00\u59cb\uff0c\u5220\u9664", None)) 199 | self.label_7.setText(QCoreApplication.translate("Form", u"\u4e2a\u5b57\u7b26", None)) 200 | self.checkBox_delete_index_after.setText(QCoreApplication.translate("Form", u"\u5220\u9664\u7b2c", None)) 201 | self.label_3.setText(QCoreApplication.translate("Form", u"\u4e2a\u5b57\u7b26\u540e\u7684\u5168\u90e8\u5b57\u7b26", None)) 202 | self.checkBox_delete_index_after_back.setText(QCoreApplication.translate("Form", u"\u5220\u9664\u5012\u6570\u7b2c", None)) 203 | self.label_5.setText(QCoreApplication.translate("Form", u"\u4e2a\u5b57\u7b26\u540e\u7684\u5168\u90e8\u5b57\u7b26", None)) 204 | # retranslateUi 205 | 206 | -------------------------------------------------------------------------------- /module/class_/class_rename_type.py: -------------------------------------------------------------------------------- 1 | # 具体的重命名类型类,用于连接主程序和具体方法 2 | import os 3 | import random 4 | import re 5 | import string 6 | 7 | from module import function_normal 8 | from module.class_.class_rename_method import MethodNormal, MethodFileExtension, MethodConvert 9 | 10 | 11 | class TypePattern: 12 | """重命名类型类-通用模板""" 13 | 14 | def __init__(self, is_enable=False, pattern=None): 15 | self.is_enable = is_enable 16 | self.pattern = pattern 17 | self.pattern_preview = None 18 | # 数字编号 19 | self.digit_start = None 20 | self.digit_step = None 21 | self.digit_length = None 22 | self.is_enable_auto_fill_digit_length = None 23 | # 随机字母 24 | self.is_enable_random_digit = None 25 | self.is_enable_random_lowercase = None 26 | self.is_enable_random_capital = None 27 | self.is_enable_random_punctuation = None 28 | self.random_length = None 29 | 30 | def update_digit(self, digit_start, digit_step, digit_length, auto_fill): 31 | """更新数字编号规则""" 32 | self.digit_start = digit_start 33 | self.digit_step = digit_step 34 | self.digit_length = digit_length 35 | self.is_enable_auto_fill_digit_length = auto_fill 36 | self._calc_preview() 37 | 38 | def update_char(self, random_digit, random_lowercase, random_capital, random_punctuation, random_length): 39 | """更新随机字符规则""" 40 | self.is_enable_random_digit = random_digit 41 | self.is_enable_random_lowercase = random_lowercase 42 | self.is_enable_random_capital = random_capital 43 | self.is_enable_random_punctuation = random_punctuation 44 | self.random_length = random_length 45 | self._calc_preview() 46 | 47 | def get_preview(self): 48 | return self.pattern_preview 49 | 50 | def _calc_preview(self): 51 | """计算模板的预览文本""" 52 | """ 53 | * 原文件名 54 | ##/" 数字编号 55 | ? 随机字符 56 | / 父目录名 57 | """ 58 | if self.pattern: 59 | self.pattern_preview = self.pattern.replace('*', '<原文件名>') 60 | self.pattern_preview = re.sub(r'(?') 62 | self.pattern_preview = self.pattern_preview.replace('"', '<数字编号>') 63 | self.pattern_preview = self.pattern_preview.replace('?', '<随机字符>') 64 | self.pattern_preview = self.pattern_preview.replace('/', '<父目录名>') 65 | 66 | def get_current_digit(self, index): 67 | """生成当前数字编号""" 68 | current_digit = str(self.digit_start + index * self.digit_step) 69 | if self.is_enable_auto_fill_digit_length: 70 | current_digit = current_digit.zfill(self.digit_length) 71 | 72 | return current_digit 73 | 74 | def get_current_random_character(self): 75 | """生成随机字符序列""" 76 | random_characters = '' 77 | if self.is_enable_random_digit: 78 | random_characters += string.digits 79 | if self.is_enable_random_lowercase: 80 | random_characters += string.ascii_lowercase 81 | if self.is_enable_random_capital: 82 | random_characters += string.ascii_uppercase 83 | if self.is_enable_random_punctuation: 84 | punctuation = string.punctuation 85 | punctuation = function_normal.check_filename_feasible(punctuation, replace=True) # 替换不能作为windows文件名的符号 86 | random_characters += punctuation 87 | 88 | if random_characters: 89 | random_str = ''.join(random.choice(random_characters) for _ in range(self.random_length)) 90 | else: 91 | random_str = '' 92 | return random_str 93 | 94 | @staticmethod 95 | def get_parent_dirname(path): 96 | """获取父目录名""" 97 | return os.path.basename(os.path.dirname(path)) 98 | 99 | def rename_method(self, filetitle: str, path: str, index: int): 100 | new_filetitle = filetitle 101 | if self.pattern: 102 | new_filetitle = self.pattern.replace('*', filetitle) # 替换原文件名 103 | new_filetitle = re.sub(r'(?_<\u6570\u5b57\u7f16\u53f7>", None)) 366 | self.pushButton_preset_1.setText(QCoreApplication.translate("Form", u"<\u7236\u6587\u4ef6\u5939\u540d> <\u539f\u6587\u4ef6\u540d> | / *", None)) 367 | self.pushButton_preset_2.setText(QCoreApplication.translate("Form", u"<\u539f\u6587\u4ef6\u540d> #<\u6570\u5b57\u7f16\u53f7> | * ###", None)) 368 | self.pushButton_preset_3.setText(QCoreApplication.translate("Form", u"<\u539f\u6587\u4ef6\u540d>_<\u968f\u673a\u5b57\u7b26> | *_?", None)) 369 | self.label_10.setText(QCoreApplication.translate("Form", u"\u7b26\u53f7\u8bf4\u660e\uff1a", None)) 370 | self.label_2.setText(QCoreApplication.translate("Form", u"* \u63d2\u5165\u539f\u6587\u4ef6\u540d", None)) 371 | self.label_5.setText(QCoreApplication.translate("Form", u"##\u6216\"\n" 372 | "\u63d2\u5165\u6570\u5b57\u7f16\u53f7", None)) 373 | self.label_8.setText(QCoreApplication.translate("Form", u"\u4f4d\u6570", None)) 374 | self.label_6.setText(QCoreApplication.translate("Form", u"\u8d77\u59cb", None)) 375 | self.label_7.setText(QCoreApplication.translate("Form", u"\u6b65\u957f", None)) 376 | self.checkBox_auto_fill_digit_length.setText(QCoreApplication.translate("Form", u"\u81ea\u52a8\u8865\u9f50\u4f4d\u6570", None)) 377 | self.label_4.setText(QCoreApplication.translate("Form", u"?\n" 378 | "\u63d2\u5165\u968f\u673a\u5b57\u7b26", None)) 379 | self.checkBox_random_digit.setText(QCoreApplication.translate("Form", u"\u6570\u5b57", None)) 380 | self.checkBox_random_lowercase.setText(QCoreApplication.translate("Form", u"\u5c0f\u5199\u5b57\u6bcd", None)) 381 | self.checkBox_random_capital.setText(QCoreApplication.translate("Form", u"\u5927\u5199\u5b57\u6bcd", None)) 382 | self.checkBox_random_punctuation.setText(QCoreApplication.translate("Form", u"\u7b26\u53f7", None)) 383 | self.label_9.setText(QCoreApplication.translate("Form", u"\u4f4d\u6570", None)) 384 | self.label_3.setText(QCoreApplication.translate("Form", u"/ \u63d2\u5165\u7236\u76ee\u5f55\u540d", None)) 385 | # retranslateUi 386 | 387 | -------------------------------------------------------------------------------- /ui/src/ui_widget_filename_pattern.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 483 10 | 426 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 3 19 | 20 | 21 | 3 22 | 23 | 24 | 3 25 | 26 | 27 | 3 28 | 29 | 30 | 3 31 | 32 | 33 | 34 | 35 | 36 | 37 | 命名模板: 38 | 39 | 40 | 41 | 42 | 43 | 44 | ... 45 | 46 | 47 | 48 | 49 | 50 | 51 | 例: *_## 为<原文件名>_<数字编号> 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 3 62 | 63 | 64 | 3 65 | 66 | 67 | 3 68 | 69 | 70 | 3 71 | 72 | 73 | 3 74 | 75 | 76 | 77 | 78 | <父文件夹名> <原文件名> | / * 79 | 80 | 81 | 82 | 83 | 84 | 85 | <原文件名> #<数字编号> | * ### 86 | 87 | 88 | 89 | 90 | 91 | 92 | <原文件名>_<随机字符> | *_? 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | Qt::Orientation::Horizontal 103 | 104 | 105 | 106 | 107 | 108 | 109 | 3 110 | 111 | 112 | 113 | 114 | 3 115 | 116 | 117 | 118 | 119 | 符号说明: 120 | 121 | 122 | 123 | 124 | 125 | 126 | Qt::Orientation::Horizontal 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | Qt::Orientation::Vertical 136 | 137 | 138 | 139 | 20 140 | 40 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 3 149 | 150 | 151 | 152 | 153 | * 插入原文件名 154 | 155 | 156 | 157 | 158 | 159 | 160 | Qt::Orientation::Horizontal 161 | 162 | 163 | 164 | 165 | 166 | 167 | 3 168 | 169 | 170 | 171 | 172 | ##或" 173 | 插入数字编号 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 3 182 | 183 | 184 | 0 185 | 186 | 187 | 0 188 | 189 | 190 | 0 191 | 192 | 193 | 0 194 | 195 | 196 | 197 | 198 | Qt::Orientation::Vertical 199 | 200 | 201 | 202 | 203 | 204 | 205 | 3 206 | 207 | 208 | 209 | 210 | 0 211 | 212 | 213 | 3 214 | 215 | 216 | 217 | 218 | -9999 219 | 220 | 221 | 9999 222 | 223 | 224 | 1 225 | 226 | 227 | 228 | 229 | 230 | 231 | 位数 232 | 233 | 234 | 235 | 236 | 237 | 238 | 起始 239 | 240 | 241 | 242 | 243 | 244 | 245 | 1 246 | 247 | 248 | 255 249 | 250 | 251 | 252 | 253 | 254 | 255 | -9999 256 | 257 | 258 | 9999 259 | 260 | 261 | QAbstractSpinBox::StepType::DefaultStepType 262 | 263 | 264 | 1 265 | 266 | 267 | 268 | 269 | 270 | 271 | 步长 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 自动补齐位数 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | Qt::Orientation::Horizontal 295 | 296 | 297 | 298 | 299 | 300 | 301 | 3 302 | 303 | 304 | 305 | 306 | ? 307 | 插入随机字符 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 3 316 | 317 | 318 | 0 319 | 320 | 321 | 0 322 | 323 | 324 | 0 325 | 326 | 327 | 0 328 | 329 | 330 | 331 | 332 | Qt::Orientation::Vertical 333 | 334 | 335 | 336 | 337 | 338 | 339 | 3 340 | 341 | 342 | 343 | 344 | 数字 345 | 346 | 347 | 348 | 349 | 350 | 351 | 小写字母 352 | 353 | 354 | 355 | 356 | 357 | 358 | 大写字母 359 | 360 | 361 | 362 | 363 | 364 | 365 | 符号 366 | 367 | 368 | 369 | 370 | 371 | 372 | 3 373 | 374 | 375 | 376 | 377 | 位数 378 | 379 | 380 | 381 | 382 | 383 | 384 | 1 385 | 386 | 387 | 255 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | Qt::Orientation::Horizontal 404 | 405 | 406 | 407 | 408 | 409 | 410 | / 插入父目录名 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | Qt::Orientation::Vertical 422 | 423 | 424 | 425 | 20 426 | 40 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | Qt::Orientation::Vertical 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 3 445 | 446 | 447 | 3 448 | 449 | 450 | 3 451 | 452 | 453 | 3 454 | 455 | 456 | 3 457 | 458 | 459 | 460 | 461 | 3 462 | 463 | 464 | 465 | 466 | 467 | 468 | Qt::Orientation::Horizontal 469 | 470 | 471 | 472 | 473 | 474 | 475 | 3 476 | 477 | 478 | 479 | 480 | 481 | 482 | Qt::Orientation::Vertical 483 | 484 | 485 | 486 | 20 487 | 40 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | -------------------------------------------------------------------------------- /ui/tableWidget_file_list.py: -------------------------------------------------------------------------------- 1 | import os 2 | from typing import Union 3 | 4 | from PySide6.QtCore import Signal, QItemSelectionModel 5 | from PySide6.QtGui import QDragEnterEvent, QPixmap, Qt 6 | from PySide6.QtWidgets import QTableWidget, QHeaderView, QTableWidgetItem, QAbstractItemView, QMessageBox 7 | 8 | from module import function_file_item, function_normal, WindowsSorted 9 | from module.class_.class_rename_info import RenameInfo 10 | from module.class_.class_rename_rule import RenameRule 11 | 12 | _title_line = ['类型', '路径', '文件名', '预览', '进度'] 13 | _column_width = 30 14 | _max_height = 16 15 | _data_role = 1000 16 | 17 | 18 | class TabWidgetFileList(QTableWidget): 19 | signal_drop_paths = Signal() 20 | 21 | def __init__(self, parent=None): 22 | super().__init__(parent) 23 | 24 | # 设置标题行 25 | self.setColumnCount(len(_title_line)) 26 | self.setHorizontalHeaderLabels(_title_line) 27 | self.hideColumn(_title_line.index('路径')) # 隐藏路径列,美观 28 | self.setColumnWidth(_title_line.index('类型'), _column_width) 29 | self.setColumnWidth(_title_line.index('进度'), _column_width) 30 | 31 | # ui设置 32 | self.setAcceptDrops(True) 33 | self.setDragDropMode(self.DragDropMode.InternalMove) 34 | self.setSelectionBehavior(QAbstractItemView.SelectRows) # 选择模式为整行 35 | self.verticalHeader().setVisible(False) # 不显示行号 36 | self.setVerticalScrollMode(QTableWidget.ScrollPerPixel) # 平滑滚动 37 | self.setHorizontalScrollMode(QTableWidget.ScrollPerPixel) # 平滑滚动 38 | self.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive) # 手动调整列宽 39 | # self.setSortingEnabled(True) # 设置排序 40 | self.horizontalHeader().sectionClicked.connect(self.sort_item) # 修改排序方法 41 | 42 | # 初始化 43 | self._current_order = None # 当前排序类型,None/ASC/DESC 44 | 45 | def sort_item(self, column: int): 46 | """排序行项目""" 47 | function_normal.print_function_info() 48 | if column == _title_line.index('文件名'): 49 | # 更新排序方法 50 | if self._current_order is None: 51 | self._current_order = 'ASC' 52 | elif self._current_order == 'ASC': 53 | self._current_order = 'DESC' 54 | elif self._current_order == 'DESC': 55 | self._current_order = 'ASC' 56 | else: 57 | self._current_order = 'ASC' 58 | # 冒泡排序(逐个上移/下移) 59 | n = self.rowCount() 60 | for i in range(n): 61 | for j in range(n - i - 1): 62 | # 提取相邻两行的文件名,按排序规则进行排序后判断是否需要互换位置 63 | filename_1 = self.item(j, _title_line.index('预览')).data(_data_role).filename_need 64 | filename_2 = self.item(j + 1, _title_line.index('预览')).data(_data_role).filename_need 65 | joined_list = [filename_1, filename_2] 66 | sorted_list = WindowsSorted.sort_list(joined_list, self._current_order) 67 | if joined_list != sorted_list: 68 | self.change_item_position(j, j + 1) 69 | # 刷新预览文件名 70 | self.calc_new_filename() 71 | 72 | def insert_path_item(self, paths: Union[list, str]): 73 | """插入路径行项目""" 74 | function_normal.print_function_info() 75 | if isinstance(paths, str): 76 | paths = [paths] 77 | 78 | paths = [os.path.normpath(i) for i in paths] 79 | 80 | for path in paths: 81 | if path not in self.paths_showed(): 82 | row_position = self.rowCount() 83 | self.insertRow(row_position) 84 | self.setRowHeight(row_position, _max_height) # 固定行高 85 | # 插入图标 86 | pixmap = QPixmap() 87 | pixmap.loadFromData(function_file_item.get_icon_filetype(path)) 88 | item_icon = QTableWidgetItem() 89 | item_icon.setData(1, pixmap) 90 | self.setItem(row_position, _title_line.index('类型'), item_icon) 91 | # 插入路径 92 | item_path = QTableWidgetItem(path) 93 | self.setItem(row_position, _title_line.index('路径'), item_path) 94 | # 插入文件名 95 | filename = function_file_item.get_filename(path) 96 | item_filename = QTableWidgetItem(filename) 97 | self.setItem(row_position, _title_line.index('文件名'), item_filename) 98 | self.item(row_position, _title_line.index('文件名')).setToolTip(filename) 99 | # 插入预览(空) 100 | item_preview = QTableWidgetItem() 101 | self.setItem(row_position, _title_line.index('预览'), item_preview) 102 | # 插入进度(空) 103 | item_preview = QTableWidgetItem() 104 | self.setItem(row_position, _title_line.index('进度'), item_preview) 105 | # 禁止编辑单元格 106 | for column in range(self.columnCount()): 107 | item = self.item(row_position, column) 108 | item.setFlags(item.flags() & ~Qt.ItemIsEditable) # 禁用单元格编辑 109 | 110 | self.calc_new_filename() 111 | self._current_order = None 112 | 113 | def calc_new_filename(self): 114 | """计算新的文件名,并显示在预览列""" 115 | function_normal.print_function_info() 116 | rule_class = RenameRule() 117 | for row in range(self.rowCount()): 118 | # 先检查单元格是否有附加数据,如果有附加数据,则在附加数据的基础上进行处理,否则按原始路径进行处理 119 | path = self.item(row, _title_line.index('路径')).text() 120 | maybe_info_class: RenameInfo = self.item(row, _title_line.index('预览')).data(_data_role) 121 | if maybe_info_class: 122 | rename_info_class: RenameInfo = rule_class.calc_new_name(maybe_info_class, 123 | row) # 调用类的方法计算预期文件名,在原信息类的基础上进行处理 124 | else: 125 | rename_info_class: RenameInfo = rule_class.calc_new_name(path, row) # 调用类的方法计算预期文件名,并生成新的重命名信息类 126 | self.set_cell_data(row, rename_info_class) 127 | # 更新预览单元格的文本 128 | calc_filename = rename_info_class.calc_filename 129 | need_filename = rename_info_class.filename_need 130 | if calc_filename == need_filename: 131 | self.item(row, _title_line.index('预览')).setText('') 132 | self.item(row, _title_line.index('预览')).setToolTip('') 133 | else: 134 | self.item(row, _title_line.index('预览')).setText(calc_filename) 135 | self.item(row, _title_line.index('预览')).setToolTip(calc_filename) 136 | # 更新原文件名单元格的文本(改名后需要更新为新路径的文件名) 137 | if self.item(row, _title_line.index('文件名')).text() != need_filename: 138 | self.item(row, _title_line.index('文件名')).setText(need_filename) 139 | 140 | def rename(self, is_auto_dup): 141 | """执行重命名""" 142 | function_normal.print_function_info() 143 | for row in range(self.rowCount()): 144 | rename_info_class: RenameInfo = self.item(row, _title_line.index('预览')).data(_data_role) 145 | # 判断原路径是否存在 146 | if not os.path.exists(rename_info_class.path_need): 147 | rename_info_class.set_result_not_exist() 148 | else: 149 | rename_info_class = function_normal.calc_final_path(rename_info_class) # 更新无重复文件名 150 | if rename_info_class.is_dup: # 存在重复文件名时进行提示 151 | if is_auto_dup: # 自动处理重命名冲突 152 | rename_info_class = self.rename_and_update_class(rename_info_class) # 更新重命名结果 153 | else: 154 | reply = self.show_dup_confirm_dialog(rename_info_class.filename_renamed) 155 | if reply: 156 | rename_info_class = self.rename_and_update_class(rename_info_class) # 更新重命名结果 157 | else: 158 | rename_info_class.set_result_skipped() 159 | else: 160 | rename_info_class = self.rename_and_update_class(rename_info_class) # 更新重命名结果 161 | 162 | # 更新 163 | self.set_cell_data(row, rename_info_class) 164 | self.set_result_icon(row, rename_info_class.rename_result) 165 | 166 | def show_dup_confirm_dialog(self, new_filename): 167 | """在重命名时,遇到重复文件名情况下弹出确认对话框""" 168 | function_normal.print_function_info() 169 | reply = QMessageBox.question(self, '重复文件名确认', 170 | f'目录中已存在相同文件名,是否重命名为:\n{new_filename}', 171 | QMessageBox.Yes | QMessageBox.No, QMessageBox.No) 172 | 173 | if reply == QMessageBox.Yes: 174 | return True 175 | else: 176 | return False 177 | 178 | @staticmethod 179 | def rename_and_update_class(rename_info_class: RenameInfo): 180 | """执行重命名""" 181 | function_normal.print_function_info() 182 | old_path = rename_info_class.path_need 183 | final_path = rename_info_class.path_renamed 184 | final_filetitle = rename_info_class.filetitle_renamed 185 | # 判断路径是否变化,是否需要执行重命名 186 | if old_path == final_path: 187 | rename_info_class.set_result_skipped() 188 | # 判断文件名是否符合命名规则(只有空格或.等情况) 189 | elif final_filetitle.count(' ') + final_filetitle.count('.') == len(final_filetitle): 190 | rename_info_class.set_result_unknown_error() 191 | # 否则正常执行重命名操作 192 | else: 193 | try: 194 | os.rename(old_path, final_path) 195 | rename_info_class.set_result_renamed() 196 | except FileNotFoundError: 197 | rename_info_class.set_result_not_exist() 198 | except PermissionError: 199 | rename_info_class.set_result_occupied() 200 | except Exception as e: 201 | print(e) 202 | rename_info_class.set_result_unknown_error() 203 | 204 | return rename_info_class 205 | 206 | @staticmethod 207 | def cancel_rename_and_update_class(rename_info_class: RenameInfo): 208 | """执行重命名""" 209 | function_normal.print_function_info() 210 | old_path = rename_info_class.path_need 211 | final_path = rename_info_class.path_cancel 212 | try: 213 | os.rename(old_path, final_path) 214 | rename_info_class.set_result_cancelled() 215 | except Exception as e: 216 | print(e) 217 | pass 218 | 219 | return rename_info_class 220 | 221 | def cancel_rename(self): 222 | """撤销重命名""" 223 | function_normal.print_function_info() 224 | for row in range(self.rowCount()): 225 | rename_info_class: RenameInfo = self.item(row, _title_line.index('预览')).data(_data_role) 226 | # 没有重命名信息类则直接跳过 227 | if not rename_info_class: 228 | continue 229 | # 判断原路径是否存在 230 | if not os.path.exists(rename_info_class.path_need): 231 | rename_info_class.set_result_not_exist() 232 | else: 233 | cancel_info_class = function_normal.calc_cancelled_path(rename_info_class) # 计算无重复的文件信息 234 | # 只在成功修改回原始文件名后才更新重命名结果参数 235 | if cancel_info_class.is_dup_cancel: # 存在重复文件名时进行提示 236 | reply = self.show_dup_confirm_dialog(cancel_info_class.filename_cancel) 237 | if reply: 238 | rename_info_class = self.cancel_rename_and_update_class(rename_info_class) 239 | else: 240 | rename_info_class = self.cancel_rename_and_update_class(rename_info_class) 241 | # 更新 242 | self.set_cell_data(row, rename_info_class) 243 | self.set_result_icon(row, rename_info_class.rename_result) 244 | 245 | def set_result_icon(self, row: int, result: str): 246 | """设置重命名结果的图标""" 247 | function_normal.print_function_info() 248 | icon_base64, tip = function_normal.get_icon_result(result) 249 | pixmap = QPixmap() 250 | pixmap.loadFromData(icon_base64) 251 | item_icon = QTableWidgetItem() 252 | item_icon.setData(1, pixmap) 253 | item_icon.setToolTip(tip) 254 | self.setItem(row, _title_line.index('进度'), item_icon) 255 | 256 | def set_cell_data(self, row: int, rename_info_class): 257 | """设置单元格附加数据""" 258 | self.item(row, _title_line.index('文件名')).setData(_data_role, rename_info_class) 259 | self.item(row, _title_line.index('预览')).setData(_data_role, rename_info_class) 260 | 261 | def paths_showed(self): 262 | """当前显示的路径列表""" 263 | function_normal.print_function_info() 264 | paths = [] 265 | for row in range(self.rowCount()): 266 | # 先尝试提取单元格中的重命名信息类,如果不存在则提取路径列的文本 267 | info_class: RenameInfo = self.item(row, _title_line.index('文件名')).data(_data_role) 268 | if info_class: 269 | path = info_class.path_need 270 | else: 271 | path = self.item(row, _title_line.index('路径')).text() 272 | paths.append(path) 273 | return paths 274 | 275 | def move_item_up(self): 276 | """将当前项目向上移动一位""" 277 | function_normal.print_function_info() 278 | selected_rows = sorted(set(item.row() for item in self.selectedItems())) 279 | 280 | if selected_rows[0] == 0: # 选中行中有首行时,不移动 281 | return 282 | 283 | self.selectionModel().clear() # 清空选中行 284 | for row in selected_rows: 285 | if row > 0: # 确保上面有一行可以交换 286 | for column in range(self.columnCount()): 287 | item_above = self.takeItem(row - 1, column) 288 | item_current = self.takeItem(row, column) 289 | self.setItem(row - 1, column, item_current) 290 | self.setItem(row, column, item_above) 291 | 292 | # 重新选中对应行 293 | self.selectionModel().select(self.model().index(row - 1, column), QItemSelectionModel.Select) 294 | 295 | self.calc_new_filename() 296 | self._current_order = None 297 | 298 | def move_item_down(self): 299 | """将当前项目向下移动一位""" 300 | function_normal.print_function_info() 301 | selected_rows = sorted(set(item.row() for item in self.selectedItems()), reverse=True) 302 | 303 | if selected_rows[0] == self.rowCount() - 1: # 选中行中有尾行时,不移动 304 | return 305 | 306 | self.selectionModel().clear() # 清空选中行 307 | for row in selected_rows: 308 | if row < self.rowCount() - 1: # 确保下面有一行可以交换 309 | for column in range(self.columnCount()): 310 | item_below = self.takeItem(row + 1, column) 311 | item_current = self.takeItem(row, column) 312 | self.setItem(row + 1, column, item_current) 313 | self.setItem(row, column, item_below) 314 | 315 | # 重新选中对应行 316 | self.selectionModel().select(self.model().index(row + 1, column), QItemSelectionModel.Select) 317 | 318 | self.calc_new_filename() 319 | self._current_order = None 320 | 321 | def change_item_position(self, row_1, row_2): 322 | """互换两个行项目的位置""" 323 | function_normal.print_function_info() 324 | for column in range(self.columnCount()): 325 | item_1 = self.takeItem(row_1, column) 326 | item_2 = self.takeItem(row_2, column) 327 | self.setItem(row_1, column, item_2) 328 | self.setItem(row_2, column, item_1) 329 | 330 | def move_item_top(self): 331 | """将当前项目移动到顶部""" 332 | function_normal.print_function_info() 333 | selected_rows = sorted(set(item.row() for item in self.selectedItems())) 334 | 335 | self.selectionModel().clear() # 清空选中行 336 | for index, row in enumerate(selected_rows): 337 | self.insertRow(index) # 插入新行 338 | self.setRowHeight(index, _max_height) # 锁定新行的行高 339 | for column in range(self.columnCount()): 340 | item_take = self.takeItem(row + 1, column) # 插入一行后行号需要下移一位+1 341 | self.setItem(index, column, item_take) 342 | self.removeRow(row + 1) # 插入一行后行号需要下移一位+1 343 | 344 | # 重新选中对应行,直接从首行开始按顺序选中即可 345 | for row, _ in enumerate(selected_rows): 346 | for column in range(self.columnCount()): 347 | self.selectionModel().select(self.model().index(row, column), QItemSelectionModel.Select) 348 | 349 | self.calc_new_filename() 350 | self._current_order = None 351 | 352 | def move_item_bottom(self): 353 | """将当前项目移动到底部""" 354 | function_normal.print_function_info() 355 | selected_rows = sorted(set(item.row() for item in self.selectedItems()), reverse=True) 356 | 357 | self.selectionModel().clear() # 清空选中行 358 | for index, row in enumerate(selected_rows): 359 | index = self.rowCount() - index 360 | self.insertRow(index) # 插入新行 361 | self.setRowHeight(index, _max_height) # 锁定新行的行高 362 | for column in range(self.columnCount()): 363 | item_take = self.takeItem(row, column) # 在下方插入,无需调整行号 364 | self.setItem(index, column, item_take) 365 | self.removeRow(row) 366 | 367 | # 重新选中对应行,直接从尾行开始按顺序选中即可 368 | for row, _ in enumerate(selected_rows): 369 | row = self.rowCount() - row - 1 370 | for column in range(self.columnCount()): 371 | self.selectionModel().select(self.model().index(row, column), QItemSelectionModel.Select) 372 | 373 | self.calc_new_filename() 374 | self._current_order = None 375 | 376 | def remove_item(self): 377 | """移除当前项目""" 378 | function_normal.print_function_info() 379 | remove_paths = [] 380 | for item in self.selectedItems(): 381 | try: 382 | row = item.row() 383 | path = self.item(row, _title_line.index('路径')).text() 384 | remove_paths.append(path) 385 | self.removeRow(row) 386 | except RuntimeError: 387 | # 由于默认选中整行,selectedItems()会返回该行的所有单元格,从而导致在删除该行的其他单元格时重复删除整行而报错 388 | pass 389 | 390 | self.calc_new_filename() 391 | self._current_order = None 392 | 393 | return remove_paths 394 | 395 | def clear_items(self): 396 | """清空所有项目""" 397 | function_normal.print_function_info() 398 | removed_path_class_dict = dict() 399 | while self.rowCount(): 400 | path = self.item(0, _title_line.index('路径')).text() 401 | info_class = self.item(0, _title_line.index('预览')).data(_data_role) 402 | removed_path_class_dict[path] = info_class 403 | self.removeRow(0) 404 | 405 | self._current_order = None 406 | 407 | return removed_path_class_dict 408 | 409 | def dragEnterEvent(self, event: QDragEnterEvent): 410 | if event.mimeData().hasUrls(): 411 | event.acceptProposedAction() 412 | 413 | def dropEvent(self, event): 414 | mime_data = event.mimeData() 415 | if mime_data.hasUrls(): 416 | paths = [] 417 | for url in mime_data.urls(): 418 | path = url.toLocalFile() 419 | paths.append(path) 420 | self.insert_path_item(paths) 421 | event.acceptProposedAction() 422 | 423 | def resizeEvent(self, event): 424 | # 更新列宽 425 | parent_size = self.size() 426 | max_width = parent_size.width() 427 | width_type = self.columnWidth(_title_line.index('类型')) 428 | width_progress = self.columnWidth(_title_line.index('进度')) 429 | width_filename = self.columnWidth(_title_line.index('文件名')) 430 | width_preview = max_width - width_type - width_progress - width_filename - 3 431 | self.setColumnWidth(_title_line.index('预览'), width_preview) 432 | --------------------------------------------------------------------------------