├── 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 |
--------------------------------------------------------------------------------