feat: clear directory

This commit is contained in:
RedDeadDepresso
2024-09-29 18:36:06 +01:00
parent fd6ef77857
commit 9eb703d12a
10 changed files with 222 additions and 20 deletions

View File

@@ -0,0 +1,34 @@
from send2trash import send2trash
import traceback
from pathlib import Path
from PySide6.QtCore import QRunnable, Signal, QObject
class ClearSignalBus(QObject):
finishSignal = Signal(bool)
class ClearWorker(QRunnable):
def __init__(self, path: Path, deleteFolder=False) -> None:
super().__init__()
self.path = path
self.deleteFolder = deleteFolder
self.clearSignalBus = ClearSignalBus()
self.finishSignal = self.clearSignalBus.finishSignal
def run(self):
try:
if self.deleteFolder:
send2trash(self.path)
else:
self.deleteContents()
self.finishSignal.emit(True)
except Exception as e:
traceback.print_exc()
self.finishSignal.emit(False)
def deleteContents(self):
for item in self.path.iterdir():
send2trash(item)

View File

@@ -0,0 +1,32 @@
from PySide6.QtCore import Qt
from qfluentwidgets import InfoBar, InfoBarPosition
class Notification:
mainWindow = None
def success(self, title: str, content: str = ''):
InfoBar.success(
title=title,
content=content,
orient=Qt.Horizontal,
isClosable=True,
position=InfoBarPosition.TOP_RIGHT,
duration=2000,
parent=self.mainWindow
)
def error(self, title: str, content: str = ''):
InfoBar.error(
title=title,
content=content,
orient=Qt.Horizontal,
isClosable=True,
position=InfoBarPosition.TOP_RIGHT,
duration=2000,
parent=self.mainWindow
)
notification = Notification()

View File

@@ -1,5 +1,5 @@
import os
from PySide6.QtCore import QProcess, Signal, QObject, Slot
from PySide6.QtCore import QProcess, QObject, Slot
class ScriptManager(QObject):
@@ -68,3 +68,6 @@ class ScriptManager(QObject):
self._process = None
self.signalBus.stopSignal.emit()
def scriptRunning(self):
return self._process is not None

View File

@@ -1,9 +1,10 @@
# coding: utf-8
from PySide6.QtCore import QObject, Signal
from PySide6.QtCore import QObject, Signal, QThreadPool
from qfluentwidgets import SettingCardGroup
from app.common.logger import Logger
from app.common.script_manager import ScriptManager
class SignalBus(QObject):
""" Signal bus """
@@ -13,6 +14,7 @@ class SignalBus(QObject):
selectAllClicked = Signal()
clearAllClicked = Signal()
disableStartSignal = Signal(bool)
startSignal = Signal()
stopSignal = Signal()
loggerSignal = Signal(str)
@@ -21,6 +23,9 @@ class SignalBus(QObject):
super().__init__(parent)
self.logger = Logger(self)
self.scriptManager = ScriptManager(self)
self.threadPool = QThreadPool(self)
def scriptRunning(self) -> bool:
return self.scriptManager.scriptRunning()
signalBus = SignalBus()

View File

@@ -0,0 +1,35 @@
from PySide6.QtCore import Qt, Signal
from qfluentwidgets import MessageBox, CheckBox
class ClearMessageBox(MessageBox):
mainWindow = None
yesSignal = Signal()
cancelSignal = Signal()
def __init__(self, title: str, content: str, parent=None):
parent = parent if parent is not None else self.mainWindow
if parent is None:
raise Exception("Please, assign mainWindow")
super().__init__(title, content, parent)
self.yesButtonPressed = False
self.deleteFolder = False
self.checkBox = CheckBox('Delete folder')
self.textLayout.insertWidget(2, self.checkBox, 0, alignment=Qt.AlignLeft)
self.__connectSignalToSlot()
def __connectSignalToSlot(self):
self.checkBox.checkStateChanged.connect(self.onChecked)
self.yesSignal.connect(self.onYesSignal)
def onYesSignal(self):
self.yesButtonPressed = True
def onChecked(self, value: bool):
self.deleteFolder = value
def getResponse(self) -> tuple[bool, bool]:
return self.yesButtonPressed, self.deleteFolder

View File

@@ -1,13 +1,16 @@
import os
import subprocess
from pathlib import Path
from PySide6.QtCore import Qt
from PySide6.QtCore import Qt, Slot
from PySide6.QtGui import QIcon
from PySide6.QtWidgets import QFileDialog
from qfluentwidgets import SettingCard, FluentIconBase, FluentIcon, CommandBar, Action, LineEdit, LineEditButton
from typing import Union
from ..common.config import cfg
from app.common.notification import notification
from app.common.config import cfg
from app.common.signal_bus import signalBus
from app.common.clear_worker import ClearWorker
from app.components.clear_messagebox import ClearMessageBox
class FolderLineEdit(LineEdit):
@@ -26,7 +29,9 @@ class FolderLineEdit(LineEdit):
class FolderSettingCard(SettingCard):
def __init__(self, configItem, icon: Union[str, QIcon, FluentIconBase], title, content=None, parent=None):
clearQueue = set()
def __init__(self, configItem, icon: Union[str, QIcon, FluentIconBase], titleGroup, title, content=None, parent=None, clearable=True):
"""
Parameters
----------
@@ -46,9 +51,11 @@ class FolderSettingCard(SettingCard):
parent widget
"""
super().__init__(icon, title, content, parent)
self.titleGroup = titleGroup
self.configItem = configItem
self.lineEdit = FolderLineEdit()
self.lineEdit.setText(configItem.value)
self.clearable = clearable
self.__initCommandBar()
self.__initLayout()
@@ -56,13 +63,18 @@ class FolderSettingCard(SettingCard):
def __initCommandBar(self):
self.commandBar = CommandBar()
explorerAction = Action(FluentIcon.FOLDER, 'Show in Explorer')
explorerAction.triggered.connect(self.openExplorer)
self.commandBar.addHiddenAction(explorerAction)
self.explorerAction = Action(FluentIcon.FOLDER, 'Show in Explorer')
self.explorerAction.triggered.connect(self.openExplorer)
self.commandBar.addHiddenAction(self.explorerAction)
browseAction = Action(FluentIcon.EDIT, 'Browse')
browseAction.triggered.connect(self.browse)
self.commandBar.addHiddenAction(browseAction)
self.browseAction = Action(FluentIcon.EDIT, 'Browse')
self.browseAction.triggered.connect(self.browse)
self.commandBar.addHiddenAction(self.browseAction)
if self.clearable:
self.clearAction = Action(FluentIcon.DELETE, 'Clear')
self.clearAction.triggered.connect(self.showClearDialog)
self.commandBar.addHiddenAction(self.clearAction)
def __initLayout(self):
self.setFixedHeight(70)
@@ -72,26 +84,38 @@ class FolderSettingCard(SettingCard):
self.hBoxLayout.addSpacing(16)
def __connectSignalToSlot(self):
self.lineEdit.textChanged.connect(self.pathValid)
self.lineEdit.textChanged.connect(self.validatePath)
if self.clearable:
signalBus.startSignal.connect(lambda: self.setDisabledClear(True))
signalBus.stopSignal.connect(lambda: self.setDisabledClear(False))
def pathValid(self, text):
@Slot(str)
def validatePath(self, text: str):
if not text:
cfg.set(self.configItem, "")
self.lineEdit.setValid(True)
self.explorerAction.setDisabled(True)
self.setDisabledClear(True)
return
path = Path(text)
if path.is_absolute() and path.is_dir() and path.exists():
cfg.set(self.configItem, text)
self.lineEdit.setValid(True)
self.setDisabledClear(False)
self.explorerAction.setDisabled(False)
else:
self.lineEdit.setValid(False)
self.setDisabledClear(True)
self.explorerAction.setDisabled(True)
@Slot()
def openExplorer(self):
if self.configItem.value:
file_path = os.path.normpath(self.configItem.value)
subprocess.Popen(f'explorer /select,"{file_path}"')
path = os.path.realpath(self.configItem.value)
os.startfile(path)
@Slot()
def browse(self):
""" download folder card clicked slot """
folder = QFileDialog.getExistingDirectory(
@@ -99,3 +123,58 @@ class FolderSettingCard(SettingCard):
if not folder or cfg.get(self.configItem) == folder:
return
self.lineEdit.setText(folder)
@Slot()
def showClearDialog(self):
path = Path(self.lineEdit.text())
if path in self.clearQueue:
notification.error(f"{self.titleGroup}'s directory is already being cleared!")
return
if not (path.is_absolute() and path.is_dir() and path.exists()):
notification.error(f"{self.titleGroup} directory path is not valid!")
return
w = ClearMessageBox("Delete all files", f"Are you sure you want to delete all files in {self.titleGroup}'s directory:\n\n{path}?")
w.exec()
yesPressed, deleteFolder = w.getResponse()
if yesPressed:
self.clear(path, deleteFolder)
@Slot()
def clear(self, path: Path, deleteFolder: bool):
signalBus.disableStartSignal.emit(True)
self.clearQueue.add(path)
self.lineEdit.setDisabled(True)
self.clearAction.setDisabled(True)
worker = ClearWorker(path, deleteFolder)
worker.finishSignal.connect(lambda successful: self.afterClear(path, deleteFolder, successful))
signalBus.threadPool.start(worker)
@Slot()
def afterClear(self, path: Path, deleteFolder: bool, successful: bool):
if successful:
if deleteFolder:
self.lineEdit.clear()
notification.success(f"Successfully deleted {self.titleGroup}'s directory")
else:
notification.success(f"Successfully cleared {self.titleGroup}'s directory")
else:
notification.error(f"Error clearing {self.titleGroup}'s directory")
self.lineEdit.setDisabled(False)
self.clearAction.setDisabled(False)
self.clearQueue.remove(path)
if not self.clearQueue:
signalBus.disableStartSignal.emit(False)
def setDisabledClear(self, value: bool):
if not self.clearable:
return
if not value and signalBus.scriptRunning():
value = True
self.clearAction.setDisabled(value)

View File

@@ -41,6 +41,7 @@ class NavigationActionButtons(QWidget):
signalBus.startSignal.connect(lambda: self.startButton.setText("Stop"))
signalBus.stopSignal.connect(lambda: self.startButton.setText("Start"))
signalBus.disableStartSignal.connect(lambda state: self.startButton.setDisabled(state))
def onSelectAllClicked(self):
signalBus.selectAllClicked.emit()

View File

@@ -10,9 +10,11 @@ from qfluentwidgets import FluentIcon as FIF
from .logger_interface import LoggerInterface
from .setting_interface import SettingInterface
from ..common.notification import notification
from ..common.config import ZH_SUPPORT_URL, EN_SUPPORT_URL, cfg
from ..common.signal_bus import signalBus
from ..common import resource
from ..components.clear_messagebox import ClearMessageBox
from ..components.navigation_checkbox import NavigationCheckBox
from ..components.navigation_action_buttons import NavigationActionButtons
from ..components.navigation_logo import NavigationLogoWidget
@@ -68,6 +70,10 @@ class MainWindow(FluentWindow):
self.addSubInterface(
self.settingInterface, FIF.SETTING, self.tr('Settings'), NavigationItemPosition.BOTTOM)
notification.mainWindow = self
ClearMessageBox.mainWindow = self
def initWindow(self):
self.resize(960, 780)
self.setMinimumWidth(760)

View File

@@ -34,8 +34,10 @@ class SettingInterface(ScrollArea):
self.gamePathCard = FolderSettingCard(
cfg.gamePath,
FIF.GAME,
'Core',
self.tr("Koikatsu directory"),
parent=self.coreGroup
parent=self.coreGroup,
clearable=False
)
# createBackup
@@ -44,6 +46,7 @@ class SettingInterface(ScrollArea):
self.backupPathCard = FolderSettingCard(
cfg.backupPath,
FIF.ZIP_FOLDER,
'Create Backup',
self.tr("Backup directory"),
parent=self.backupGroup
)
@@ -82,7 +85,8 @@ class SettingInterface(ScrollArea):
self.fckksPathCard = FolderSettingCard(
cfg.fccksPath,
FIF.DOWNLOAD,
self.tr("Backup directory"),
'Filter & Convert',
self.tr("Input directory"),
parent=self.fckksGroup
)
self.convertCard = SwitchSettingCard(
@@ -99,6 +103,7 @@ class SettingInterface(ScrollArea):
self.installPathCard = FolderSettingCard(
cfg.installPath,
FIF.DOWNLOAD,
'Install Chara',
self.tr("Input directory"),
parent=self.installGroup
)
@@ -124,6 +129,7 @@ class SettingInterface(ScrollArea):
self.removePathCard = FolderSettingCard(
cfg.removePath,
FIF.DOWNLOAD,
'Remove Chara',
self.tr("Input directory"),
parent=self.removeGroup
)

View File

@@ -7,6 +7,7 @@ PySide6_Addons==6.7.2
PySide6_Essentials==6.7.2
PySideSix-Frameless-Window==0.4.3
pywin32==306
Send2Trash==1.8.3
setuptools==75.1.0
shiboken6==6.7.2
wheel==0.44.0