Update .gitignore, DataClass.py, and 19 more files...
오랜만에 서버 정리하고 커밋. 파일 위치를 정리했다. 캘리버 DB 를 열고 정보를 열람. Pupil 을 통해 다운받은 정보를 관리하기 위해 새로운 클래스 추가
This commit is contained in:
467
MgrCalibreUI.py
Normal file
467
MgrCalibreUI.py
Normal file
@@ -0,0 +1,467 @@
|
||||
import sys
|
||||
import os
|
||||
import shutil
|
||||
import UtilPack as util
|
||||
import DataClass_Pupil as pupil
|
||||
import MgrCalibreDB
|
||||
import MgrPupilColDB
|
||||
#import MgrCalibreLibs as calLib
|
||||
|
||||
|
||||
from PyQt5.QtCore import Qt, QSettings, QRect
|
||||
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QPushButton, QVBoxLayout, QLineEdit, \
|
||||
QHBoxLayout, QTableWidget, QTableWidgetItem, QAbstractItemView, QHeaderView, QFileDialog, \
|
||||
QListWidget, QListWidgetItem, QMessageBox
|
||||
from PyQt5.QtGui import QResizeEvent, QCloseEvent, QColor
|
||||
|
||||
#QApplication.setAttribute(Qt.AA_ShareOpenGLContexts)
|
||||
|
||||
class MyApp(QMainWindow):
|
||||
m_DictDuplicate: dict[int, list[str]] = {}
|
||||
m_ListIncomplMngas: list[str] = []
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.loadINI()
|
||||
self.initDB()
|
||||
self.initUI()
|
||||
|
||||
#
|
||||
def closeEvent(self, a0: 'QCloseEvent | None'):
|
||||
filenameLog = f"{util.GetCurrentTime()}_DbgLog.txt"
|
||||
pathDbgLog = os.path.join( self.pathLog, filenameLog)
|
||||
util.SaveDbgMessages(pathDbgLog)
|
||||
self.saveINI()
|
||||
super().closeEvent(a0)
|
||||
|
||||
#
|
||||
def resizeEvent(self, a0: 'QResizeEvent | None'):
|
||||
PScreen = QApplication.primaryScreen()
|
||||
rcDesktop: QRect = QRect(0, 0, 800, 600)
|
||||
if PScreen is not None:
|
||||
rcDesktop = PScreen.availableGeometry()
|
||||
|
||||
rcWnd = self.geometry()
|
||||
if rcWnd.width() > rcDesktop.width():
|
||||
rcWnd.setWidth(rcDesktop.width())
|
||||
|
||||
print(f"Resize Event: {rcWnd.width()}x{rcWnd.height()} at {rcWnd.x()},{rcWnd.y()}")
|
||||
|
||||
super().resizeEvent(a0)
|
||||
|
||||
#
|
||||
def loadINI(self):
|
||||
settings = QSettings('MyApp', 'settings')
|
||||
|
||||
self.pathLog = settings.value('LoggingPath', "~/Workspace/Log", type=str)
|
||||
if False == os.path.exists(self.pathLog):
|
||||
os.makedirs(self.pathLog, exist_ok=True)
|
||||
|
||||
self.pathDB = settings.value('PupilDBPath', "~/Workspace/DB", type=str)
|
||||
if False == os.path.exists(self.pathDB):
|
||||
os.makedirs(self.pathDB, exist_ok=True)
|
||||
|
||||
self.pathLastCalibre = settings.value('LastCalibrePath', "~/캘리버 서재", type=str)
|
||||
if False == os.path.exists(self.pathDB):
|
||||
os.makedirs(self.pathDB, exist_ok=True)
|
||||
|
||||
self.fileNamePupilDB = "MyMangaData.db"
|
||||
self.fileNameCallibreDB = "metadata.db"
|
||||
|
||||
window_size = settings.value('window/size', '800, 600')
|
||||
window_position = settings.value('window/position', '100, 100')
|
||||
|
||||
ItemCnt = settings.value('folders/count', 0, type=int)
|
||||
for idx in range(ItemCnt):
|
||||
item_text = settings.value(f'folders/{idx}', '', type=str)
|
||||
if item_text:
|
||||
self.listWidget_Folders.addItem(item_text)
|
||||
|
||||
#
|
||||
def saveINI(self):
|
||||
settings = QSettings('MyApp', 'settings')
|
||||
|
||||
# 키-값 형식으로 데이터 저장
|
||||
settings.setValue('window/size', "1024,768")
|
||||
settings.setValue('window/position', "100,100")
|
||||
|
||||
# 폴더 목록 저장
|
||||
ListItems: list[QListWidgetItem] = self.listWidget_Folders.items(None)
|
||||
for idx in range(len(ListItems)):
|
||||
item = ListItems[idx]
|
||||
settings.setValue(f'folders/{idx}', item.text())
|
||||
|
||||
settings.setValue('folders/count', self.listWidget_Folders.count())
|
||||
|
||||
#
|
||||
def initDB(self):
|
||||
pathPupilDB = os.path.join(self.pathDB, self.fileNamePupilDB)
|
||||
util.DbgOut(f"Loading Pupil DB from {pathPupilDB}", True)
|
||||
self.DBPupil = MgrPupilColDB.MgrPupilColDB(pathPupilDB)
|
||||
|
||||
pathCalibreDB = os.path.join(self.pathLastCalibre, self.fileNameCallibreDB)
|
||||
util.DbgOut(f"Loading Calibre DB from {pathCalibreDB}", True)
|
||||
self.DBCalibre = MgrCalibreDB.MgrCalibreDB(pathCalibreDB)
|
||||
|
||||
#
|
||||
def MakeUI_Left(self):
|
||||
layout = QVBoxLayout()
|
||||
|
||||
layout_top = QHBoxLayout()
|
||||
self.listWidget_Folders = QListWidget()
|
||||
self.listWidget_Folders.setFixedHeight(100)
|
||||
|
||||
layout_Btns = QVBoxLayout()
|
||||
btn_FolderAdd = QPushButton("폴더 추가")
|
||||
btn_FolderAdd.clicked.connect(self.on_btnFolderAdd_clicked)
|
||||
btn_FolderDel = QPushButton("폴더 삭제")
|
||||
btn_FolderDel.clicked.connect(self.on_btnFolderDel_clicked)
|
||||
btn_FolderParss = QPushButton("폴더 파싱")
|
||||
btn_FolderParss.clicked.connect(self.on_btnFolderParse_clicked)
|
||||
|
||||
layout_Btns.addWidget(btn_FolderAdd)
|
||||
layout_Btns.addWidget(btn_FolderDel)
|
||||
layout_Btns.addWidget(btn_FolderParss)
|
||||
|
||||
layout_top.addWidget(self.listWidget_Folders)
|
||||
layout_top.addLayout(layout_Btns)
|
||||
|
||||
self.tableWidget_Src = QTableWidget()
|
||||
self.tableWidget_Src.verticalHeader().setVisible(False)
|
||||
self.tableWidget_Src.setEditTriggers(QAbstractItemView.NoEditTriggers)
|
||||
self.tableWidget_Src.setSortingEnabled(True)
|
||||
self.tableWidget_Src.setColumnCount(5)
|
||||
self.tableWidget_Src.setHorizontalHeaderLabels(["Path", "Title", "H.ID", "Img Cnt", "Dw Cnt"])
|
||||
self.tableWidget_Src.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||||
self.tableWidget_Src.horizontalHeader().sectionClicked.connect(self.on_tableWidget_Src_headerClicked)
|
||||
self.tableWidget_Src.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
self.tableWidget_Src.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||
self.tableWidget_Src.itemSelectionChanged.connect(self.on_tableWidget_Src_itemSelectionChanged)
|
||||
|
||||
layout.addLayout(layout_top)
|
||||
layout.addWidget(self.tableWidget_Src)
|
||||
|
||||
return layout
|
||||
|
||||
#
|
||||
def MakeUI_Center(self):
|
||||
layout = QVBoxLayout()
|
||||
|
||||
btn_Emptyfolder = QPushButton("빈 폴더 이동")
|
||||
btn_Emptyfolder.clicked.connect(self.on_btn_Emptyfolder_clicked)
|
||||
btn_ChkDuplicate = QPushButton("중복 검사 및 제거")
|
||||
btn_ChkDuplicate.clicked.connect(self.on_btn_ChkDuplicate_clicked)
|
||||
btn_Archive = QPushButton("압축 및 데이터 저장")
|
||||
btn_Archive.clicked.connect(self.on_btn_Archive_clicked)
|
||||
btn_EnterCalibre = QPushButton("컬리버에 삽입")
|
||||
btn_EnterCalibre.clicked.connect(self.on_btn_EnterCalibre_clicked)
|
||||
|
||||
layout.addWidget(btn_Emptyfolder)
|
||||
layout.addWidget(btn_ChkDuplicate)
|
||||
layout.addWidget(btn_Archive)
|
||||
layout.addWidget(btn_EnterCalibre)
|
||||
|
||||
return layout
|
||||
|
||||
#
|
||||
def MakeUI_Right(self):
|
||||
layout_top = QHBoxLayout()
|
||||
self.edit_DB = QLineEdit(self)
|
||||
self.edit_DB.setReadOnly(True)
|
||||
self.edit_DB.setText(self.pathLastCalibre)
|
||||
btn_DB = QPushButton("...")
|
||||
btn_DB.setFixedWidth(50)
|
||||
btn_DB.clicked.connect(self.on_btnDB_clicked)
|
||||
layout_top.addWidget(self.edit_DB)
|
||||
layout_top.addWidget(btn_DB)
|
||||
|
||||
self.tableWidget_DB = QTableWidget()
|
||||
self.tableWidget_DB.verticalHeader().setVisible(False)
|
||||
self.tableWidget_DB.setColumnCount(5)
|
||||
self.tableWidget_DB.setHorizontalHeaderLabels(["Title", "Author", "Cover", "Exts", "ID"])
|
||||
self.tableWidget_DB.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||||
self.tableWidget_DB.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
self.tableWidget_DB.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||
self.tableWidget_DB.itemSelectionChanged.connect(self.on_tableWidget_DB_itemSelectionChanged)
|
||||
|
||||
layout = QVBoxLayout()
|
||||
layout.addLayout(layout_top)
|
||||
layout.addWidget(self.tableWidget_DB)
|
||||
|
||||
return layout
|
||||
|
||||
#
|
||||
def MakeUI(self):
|
||||
layout_L = self.MakeUI_Left()
|
||||
layout_C = self.MakeUI_Center()
|
||||
layout_R = self.MakeUI_Right()
|
||||
|
||||
# 레이아웃 설정
|
||||
layout = QHBoxLayout()
|
||||
layout.addLayout(layout_L, stretch = 10)
|
||||
layout.addLayout(layout_C, stretch = 1)
|
||||
layout.addLayout(layout_R, stretch = 10)
|
||||
|
||||
return layout
|
||||
|
||||
#
|
||||
def initUI(self):
|
||||
layout = self.MakeUI()
|
||||
|
||||
# 레이아웃을 윈도우에 적용
|
||||
central_widget = QWidget()
|
||||
central_widget.setLayout(layout)
|
||||
self.setCentralWidget(central_widget)
|
||||
self.setWindowTitle('Manga Database')
|
||||
self.resize(800, 600)
|
||||
self.show()
|
||||
|
||||
# 테이블의 특정 행에 배경색을 설정한다
|
||||
# nRow: 배경색을 설정할 행 번호, color: 배경색 (Qt.GlobalColor)
|
||||
def SrcTableRowBgColor(self, nRow:int, color:Qt.GlobalColor) -> None:
|
||||
for col in range(self.tableWidget_Src.columnCount()):
|
||||
item = self.tableWidget_Src.item(nRow, col)
|
||||
if item:
|
||||
item.setBackground(Qt.GlobalColor(color))
|
||||
|
||||
#
|
||||
def LoadSrcFolder(self, path:str)-> None:
|
||||
# 폴더가 없으면 리턴
|
||||
if True == util.IsEmptyStr(path) or False == os.path.exists(path):
|
||||
return
|
||||
|
||||
# 폴더의 내용물을 읽어온다.
|
||||
listFiles = util.ListChildDirectories(path)
|
||||
util.DbgOut(f"Src Count : {len(listFiles)}", True)
|
||||
|
||||
# 테이블 초기화
|
||||
nLastRow = self.tableWidget_Src.rowCount()
|
||||
self.tableWidget_Src.setRowCount(nLastRow + len(listFiles))
|
||||
|
||||
nRow = nLastRow
|
||||
# 테이블에 데이터 추가
|
||||
for item in listFiles:
|
||||
pathDir = os.path.join(path, item)
|
||||
if False == os.path.isdir(pathDir):
|
||||
util.DbgOut(f"Not a directory: {pathDir}", True)
|
||||
continue
|
||||
|
||||
item_0 = QTableWidgetItem(pathDir)
|
||||
self.tableWidget_Src.setItem(nRow, 0, item_0)
|
||||
|
||||
FullPath = os.path.join(pathDir, ".metadata")
|
||||
data = pupil.PupilData(FullPath)
|
||||
util.DbgOut(f"Loaded MetaData Path : {FullPath}", True)
|
||||
|
||||
item_1 = QTableWidgetItem(data.GetTitle())
|
||||
self.tableWidget_Src.setItem(nRow, 1, item_1)
|
||||
|
||||
strID = data.GetHitomiID()
|
||||
if True == util.IsEmptyStr(strID):
|
||||
strID = util.GetTextInBrakets(item)[0]
|
||||
|
||||
item_2 = QTableWidgetItem(strID)
|
||||
self.tableWidget_Src.setItem(nRow, 2, item_2)
|
||||
|
||||
nImgListLen = data.GetImgFileCount()
|
||||
item_3 = QTableWidgetItem(f"{nImgListLen}")
|
||||
self.tableWidget_Src.setItem(nRow, 3, item_3)
|
||||
|
||||
nImgFileCnt = len(util.ListContainFiles(pathDir))
|
||||
item_4 = QTableWidgetItem(f"{nImgFileCnt}")
|
||||
self.tableWidget_Src.setItem(nRow, 4, item_4)
|
||||
|
||||
# 중복 검사 를 위해 경로를 딕셔너리에 저장
|
||||
nID = int(strID)
|
||||
if nID not in self.m_DictDuplicate:
|
||||
self.m_DictDuplicate[nID] = []
|
||||
self.m_DictDuplicate[nID].append(pathDir)
|
||||
|
||||
# 중복된 만화가 있으면 배경색을 변경
|
||||
if len(self.m_DictDuplicate[nID]) > 1 :
|
||||
self.SrcTableRowBgColor(nRow, Qt.GlobalColor.green)
|
||||
|
||||
# JSon 데이터의 파일 개수와 실제 다운받은 파일 개수가 다르면 리스트에 저장, 표시한다.
|
||||
if 0 >= nImgFileCnt or 0 >= nImgListLen or nImgFileCnt != nImgListLen:
|
||||
self.m_ListNotCompleteMngas.append(pathDir)
|
||||
self.SrcTableRowBgColor(nRow, Qt.GlobalColor.lightGray)
|
||||
|
||||
nRow += 1
|
||||
del data
|
||||
|
||||
## metadata.db
|
||||
def LoadCalibreDB(self, path:str ) -> None:
|
||||
pathDB = os.path.join(path, "metadata.db")
|
||||
if False == os.path.exists(pathDB):
|
||||
util.DbgOut(f"Calibre DB not found: {pathDB}", True)
|
||||
return
|
||||
|
||||
DB = MgrCalibreDB.MgrCalibreDB(pathDB)
|
||||
listItems = DB.GetBookListforUI_ArcList()
|
||||
for item in listItems:
|
||||
strID = item[4]
|
||||
strTitle = item[0]
|
||||
strAuthor = item[1]
|
||||
strHasCover = item[2]
|
||||
strBookPath = item[3]
|
||||
|
||||
nRow = self.tableWidget_DB.rowCount()
|
||||
self.tableWidget_DB.setRowCount(nRow + 1)
|
||||
|
||||
self.tableWidget_DB.setItem(nRow, 0, QTableWidgetItem(strTitle))
|
||||
self.tableWidget_DB.setItem(nRow, 1, QTableWidgetItem(strAuthor))
|
||||
|
||||
itemCover = QTableWidgetItem(strHasCover)
|
||||
if "0" == strHasCover:
|
||||
itemCover.setForeground(QColor(255, 0, 0))
|
||||
|
||||
self.tableWidget_DB.setItem(nRow, 2, itemCover)
|
||||
|
||||
# format, uncompressed_size, name
|
||||
tupleData = DB.GetDataByBookID(int(strID))
|
||||
itemExt = QTableWidgetItem(tupleData[0])
|
||||
pathArc = os.path.join(path, strBookPath, f"{tupleData[2]}.{tupleData[0]}")
|
||||
if False == os.path.exists(pathArc):
|
||||
itemExt.setForeground(QColor(255, 0, 0))
|
||||
|
||||
self.tableWidget_DB.setItem(nRow, 3, itemExt)
|
||||
self.tableWidget_DB.setItem(nRow, 4, QTableWidgetItem(strID))
|
||||
|
||||
#
|
||||
def on_btnFolderAdd_clicked(self):
|
||||
folder_path = QFileDialog.getExistingDirectory(self, '폴더 선택', '')
|
||||
|
||||
if True == util.IsEmptyStr(folder_path):
|
||||
return
|
||||
|
||||
pathAbs = os.path.abspath(folder_path)
|
||||
if self.listWidget_Folders.findItems(pathAbs, Qt.MatchFlag.MatchExactly):
|
||||
util.DbgOut(f"폴더가 이미 추가되어 있습니다: {pathAbs}", True)
|
||||
return
|
||||
|
||||
self.listWidget_Folders.addItem(pathAbs)
|
||||
|
||||
#
|
||||
def on_btnFolderDel_clicked(self):
|
||||
selected_items = self.listWidget_Folders.selectedItems()
|
||||
for item in selected_items:
|
||||
row = self.listWidget_Folders.row(item)
|
||||
self.listWidget_Folders.takeItem(row)
|
||||
|
||||
#
|
||||
def on_btnFolderParse_clicked(self):
|
||||
itemCount = self.listWidget_Folders.count()
|
||||
if 0 == itemCount:
|
||||
util.DbgOut("폴더가 선택되지 않았습니다.", True)
|
||||
return
|
||||
|
||||
# 중복 검사을 위한 딕셔너리 초기화
|
||||
self.m_DictDuplicate.clear()
|
||||
# 다운로드가 완료되지 않은 만화 목록 초기화
|
||||
self.m_ListNotCompleteMngas.clear()
|
||||
# 테이블 위젯 비우기
|
||||
self.tableWidget_Src.setRowCount(0)
|
||||
|
||||
for idx in range(itemCount):
|
||||
item = self.listWidget_Folders.item(idx)
|
||||
if item is None:
|
||||
continue
|
||||
|
||||
self.LoadSrcFolder(item.text())
|
||||
|
||||
#
|
||||
def on_btnDB_clicked(self):
|
||||
folder_path = QFileDialog.getExistingDirectory(self, '폴더 선택', '')
|
||||
|
||||
if True == util.IsEmptyStr(folder_path):
|
||||
return
|
||||
|
||||
if self.edit_DB.text() == folder_path:
|
||||
return
|
||||
|
||||
self.edit_DB.setText(folder_path)
|
||||
self.LoadCalibreDB(folder_path)
|
||||
|
||||
#
|
||||
def on_tableWidget_Src_headerClicked(self, logicalIndex: int):
|
||||
# 정렬 상태 확인 및 변경
|
||||
HorHeader = self.tableWidget_Src.horizontalHeader()
|
||||
if HorHeader is None:
|
||||
return
|
||||
|
||||
if HorHeader.sortIndicatorOrder() == Qt.SortOrder.AscendingOrder:
|
||||
sort_order = Qt.SortOrder.DescendingOrder
|
||||
else:
|
||||
sort_order = Qt.SortOrder.AscendingOrder
|
||||
|
||||
HorHeader.setSortIndicator(logicalIndex, sort_order)
|
||||
self.tableWidget_Src.sortItems(logicalIndex, HorHeader.sortIndicatorOrder() )
|
||||
|
||||
#
|
||||
def on_tableWidget_Src_itemSelectionChanged(self):
|
||||
pass
|
||||
|
||||
#
|
||||
def on_tableWidget_DB_itemSelectionChanged(self):
|
||||
pass
|
||||
|
||||
#
|
||||
def on_btn_Emptyfolder_clicked(self):
|
||||
folder_path = QFileDialog.getExistingDirectory(self, '폴더 선택', '')
|
||||
|
||||
for pathItem in self.m_ListNotCompleteMngas:
|
||||
# 유효하지 않은 경로면 건드리지 말자
|
||||
if False == os.path.exists(pathItem):
|
||||
continue
|
||||
|
||||
# 폴더 이동
|
||||
try:
|
||||
baseName = os.path.basename(pathItem)
|
||||
pathDest = os.path.join(folder_path, baseName)
|
||||
shutil.move(pathItem, pathDest)
|
||||
|
||||
if True == os.path.exists(pathDest):
|
||||
util.DbgOut(f"폴더 이동 완료: {pathItem} -> {pathDest}")
|
||||
|
||||
except Exception as e:
|
||||
strMsg = f"폴더 이동 중 오류 발생: {e}"
|
||||
util.DbgOut(strMsg, True)
|
||||
QMessageBox.critical(self, "Error", strMsg)
|
||||
|
||||
#
|
||||
def on_btn_ChkDuplicate_clicked(self):
|
||||
if len(self.m_DictDuplicate) == 0:
|
||||
util.DbgOut("중복 검사할 데이터가 없습니다.", True)
|
||||
return
|
||||
|
||||
for nID, paths in self.m_DictDuplicate.items():
|
||||
if len(paths) <= 1:
|
||||
continue
|
||||
|
||||
# 첫번째 만화의 해시를 검사한다.
|
||||
# -해시가 멀쩡하면 정상으로 판단, 2번째부터는 그냥 지운다.
|
||||
# 만약 해시가 이상하면 해시가 멀쩡한 놈을 찾는다.
|
||||
# -멀쩡한 놈을 찾으면 그걸 보존, 다른 나머지를 지운다.
|
||||
# 해시가 전부 이상하면 일단 전부 딴데로 백업.
|
||||
# - 해당하는 파일을 다운받거나, 중복되는 것 중에서 조합해서 복원. 그건 다른 데서 하자.
|
||||
|
||||
|
||||
#
|
||||
def on_btn_Archive_clicked(self):
|
||||
pass
|
||||
|
||||
#
|
||||
def on_btn_EnterCalibre_clicked(self):
|
||||
pass
|
||||
|
||||
"""
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
app.setQuitOnLastWindowClosed(True)
|
||||
main_window = MyApp()
|
||||
#main_window.show()
|
||||
|
||||
sys.exit(app.exec_())
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user