python 소스
import sys
import os
from collections import defaultdict
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QComboBox, QLineEdit, QPushButton, QListWidget, QListWidgetItem, QFileDialog, QMessageBox, QAbstractItemView
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFontMetrics
class PrefixRenamer(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("공통 프리픽스 파일명 일괄 삭제")
self.resize(800, 500)
self.backup_rename = {}
self.file_info = []
self.prefix_groups = {}
self.layout = QVBoxLayout()
self.setLayout(self.layout)
# 디렉토리 입력 및 버튼
top_layout = QHBoxLayout()
self.dir_edit = QLineEdit()
self.dir_btn = QPushButton("선택")
self.run_btn = QPushButton("실행")
top_layout.addWidget(QLabel("디렉토리"))
top_layout.addWidget(self.dir_edit, stretch=4)
top_layout.addWidget(self.dir_btn)
top_layout.addWidget(self.run_btn)
self.layout.addLayout(top_layout)
# prefix 콤보박스, 텍스트박스, 조회 버튼
prefix_layout = QHBoxLayout()
self.prefix_combo = QComboBox()
self.prefix_label = QLabel("공통prefix 목록 및 개수")
self.prefix_text = QLineEdit()
self.lookup_btn = QPushButton("조회")
prefix_layout.addWidget(self.prefix_label)
prefix_layout.addWidget(self.prefix_combo)
prefix_layout.addWidget(self.prefix_text)
prefix_layout.addWidget(self.lookup_btn)
self.layout.addLayout(prefix_layout)
# 파일 목록 및 버튼
body_layout = QHBoxLayout()
self.file_list = QListWidget()
self.file_list.setSelectionMode(QAbstractItemView.MultiSelection)
self.file_list.setStyleSheet("text-align:right")
right_layout = QVBoxLayout()
self.rename_btn = QPushButton("공통prefix삭제")
self.undo_btn = QPushButton("원복")
right_layout.addWidget(self.rename_btn)
right_layout.addWidget(self.undo_btn)
right_layout.addStretch()
body_layout.addWidget(self.file_list, stretch=3)
body_layout.addLayout(right_layout)
self.layout.addLayout(body_layout)
# 시그널 연결
self.dir_btn.clicked.connect(self.choose_dir)
self.run_btn.clicked.connect(self.scan_files)
self.prefix_combo.currentIndexChanged.connect(self.update_prefix_text)
self.lookup_btn.clicked.connect(self.lookup_prefix)
self.rename_btn.clicked.connect(self.remove_prefix)
self.undo_btn.clicked.connect(self.restore_names)
self.prefix_combo.showPopup = self.combo_show_popup_custom
def combo_show_popup_custom(self):
if self.prefix_combo.count() == 0:
from PyQt5.QtWidgets import QComboBox
QComboBox.showPopup(self.prefix_combo)
return
font_metrics = QFontMetrics(self.prefix_combo.font())
max_width = max(font_metrics.width(self.prefix_combo.itemText(i)) for i in range(self.prefix_combo.count()))
max_width += 30
self.prefix_combo.view().setMinimumWidth(max_width)
from PyQt5.QtWidgets import QComboBox
QComboBox.showPopup(self.prefix_combo)
def choose_dir(self):
d = QFileDialog.getExistingDirectory(self, "디렉토리 선택")
if d:
self.dir_edit.setText(d)
def scan_files(self):
self.file_info.clear()
root = self.dir_edit.text()
if not root or not os.path.isdir(root):
QMessageBox.warning(self, "경고", "유효한 디렉토리를 선택하세요.")
return
for dirpath, _, filenames in os.walk(root):
for f in filenames:
full_path = os.path.join(dirpath, f)
self.file_info.append( (full_path, f) )
self.analyze_prefixes()
def analyze_prefixes(self):
prefixmap = defaultdict(list)
for full, name in self.file_info:
for i in range(1, len(name)+1):
prefix = name[:i]
prefixmap[prefix].append(full)
filtered = {prefix: files for prefix, files in prefixmap.items() if len(files) >= 2}
sorted_prefixes = sorted(filtered.keys(), key=lambda x: (-len(x), x))
self.prefix_combo.clear()
self.prefix_groups = filtered
for p in sorted_prefixes:
self.prefix_combo.addItem(f"{p} : {len(filtered[p])}개", p)
# 추가: 콤보박스 첫 항목 자동 선택 및 텍스트 박스, 파일 리스트 갱신
if self.prefix_combo.count() > 0:
self.prefix_combo.setCurrentIndex(0)
self.update_prefix_text()
self.lookup_prefix()
else:
self.prefix_text.clear()
self.file_list.clear()
def update_prefix_text(self):
prefix = self.prefix_combo.currentData()
if prefix:
self.prefix_text.setText(prefix)
else:
self.prefix_text.clear()
def lookup_prefix(self):
prefix = self.prefix_text.text().strip()
if not prefix:
self.file_list.clear()
return
files = self.prefix_groups.get(prefix, [])
self.file_list.clear()
for fp in files:
item = QListWidgetItem(fp)
item.setSelected(True)
self.file_list.addItem(item)
def remove_prefix(self):
prefix = self.prefix_text.text().strip()
if not prefix:
QMessageBox.warning(self, "경고", "prefix 그룹을 입력하거나 선택해주세요.")
return
sel_items = self.file_list.selectedItems()
if not sel_items:
QMessageBox.warning(self, "경고", "파일을 선택하세요.")
return
for it in sel_items:
full_path = it.text()
parent, fname = os.path.split(full_path)
if fname.startswith(prefix):
newname = fname[len(prefix):]
new_full = os.path.join(parent, newname)
try:
os.rename(full_path, new_full)
self.backup_rename[new_full] = (full_path, fname)
it.setText(new_full)
except Exception as e:
QMessageBox.warning(self, "에러", f"{full_path} → {new_full} 변경 실패\n{str(e)}")
self.scan_files()
def restore_names(self):
for new_full, (old_full, fname) in list(self.backup_rename.items()):
if os.path.exists(new_full):
try:
os.rename(new_full, old_full)
del self.backup_rename[new_full]
except Exception as e:
QMessageBox.warning(self, "원복 실패", f"{new_full} → {old_full} 변경 실패\n{str(e)}")
self.scan_files()
if __name__ == '__main__':
app = QApplication(sys.argv)
win = PrefixRenamer()
win.show()
sys.exit(app.exec_())