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 import os
from PySide6.QtCore import QProcess, Signal, QObject, Slot from PySide6.QtCore import QProcess, QObject, Slot
class ScriptManager(QObject): class ScriptManager(QObject):
@@ -68,3 +68,6 @@ class ScriptManager(QObject):
self._process = None self._process = None
self.signalBus.stopSignal.emit() self.signalBus.stopSignal.emit()
def scriptRunning(self):
return self._process is not None

View File

@@ -1,9 +1,10 @@
# coding: utf-8 # coding: utf-8
from PySide6.QtCore import QObject, Signal from PySide6.QtCore import QObject, Signal, QThreadPool
from qfluentwidgets import SettingCardGroup from qfluentwidgets import SettingCardGroup
from app.common.logger import Logger from app.common.logger import Logger
from app.common.script_manager import ScriptManager from app.common.script_manager import ScriptManager
class SignalBus(QObject): class SignalBus(QObject):
""" Signal bus """ """ Signal bus """
@@ -13,6 +14,7 @@ class SignalBus(QObject):
selectAllClicked = Signal() selectAllClicked = Signal()
clearAllClicked = Signal() clearAllClicked = Signal()
disableStartSignal = Signal(bool)
startSignal = Signal() startSignal = Signal()
stopSignal = Signal() stopSignal = Signal()
loggerSignal = Signal(str) loggerSignal = Signal(str)
@@ -21,6 +23,9 @@ class SignalBus(QObject):
super().__init__(parent) super().__init__(parent)
self.logger = Logger(self) self.logger = Logger(self)
self.scriptManager = ScriptManager(self) self.scriptManager = ScriptManager(self)
self.threadPool = QThreadPool(self)
def scriptRunning(self) -> bool:
return self.scriptManager.scriptRunning()
signalBus = SignalBus() 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 os
import subprocess
from pathlib import Path from pathlib import Path
from PySide6.QtCore import Qt from PySide6.QtCore import Qt, Slot
from PySide6.QtGui import QIcon from PySide6.QtGui import QIcon
from PySide6.QtWidgets import QFileDialog from PySide6.QtWidgets import QFileDialog
from qfluentwidgets import SettingCard, FluentIconBase, FluentIcon, CommandBar, Action, LineEdit, LineEditButton from qfluentwidgets import SettingCard, FluentIconBase, FluentIcon, CommandBar, Action, LineEdit, LineEditButton
from typing import Union 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): class FolderLineEdit(LineEdit):
@@ -26,7 +29,9 @@ class FolderLineEdit(LineEdit):
class FolderSettingCard(SettingCard): 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 Parameters
---------- ----------
@@ -46,9 +51,11 @@ class FolderSettingCard(SettingCard):
parent widget parent widget
""" """
super().__init__(icon, title, content, parent) super().__init__(icon, title, content, parent)
self.titleGroup = titleGroup
self.configItem = configItem self.configItem = configItem
self.lineEdit = FolderLineEdit() self.lineEdit = FolderLineEdit()
self.lineEdit.setText(configItem.value) self.lineEdit.setText(configItem.value)
self.clearable = clearable
self.__initCommandBar() self.__initCommandBar()
self.__initLayout() self.__initLayout()
@@ -56,13 +63,18 @@ class FolderSettingCard(SettingCard):
def __initCommandBar(self): def __initCommandBar(self):
self.commandBar = CommandBar() self.commandBar = CommandBar()
explorerAction = Action(FluentIcon.FOLDER, 'Show in Explorer') self.explorerAction = Action(FluentIcon.FOLDER, 'Show in Explorer')
explorerAction.triggered.connect(self.openExplorer) self.explorerAction.triggered.connect(self.openExplorer)
self.commandBar.addHiddenAction(explorerAction) self.commandBar.addHiddenAction(self.explorerAction)
browseAction = Action(FluentIcon.EDIT, 'Browse') self.browseAction = Action(FluentIcon.EDIT, 'Browse')
browseAction.triggered.connect(self.browse) self.browseAction.triggered.connect(self.browse)
self.commandBar.addHiddenAction(browseAction) 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): def __initLayout(self):
self.setFixedHeight(70) self.setFixedHeight(70)
@@ -72,26 +84,38 @@ class FolderSettingCard(SettingCard):
self.hBoxLayout.addSpacing(16) self.hBoxLayout.addSpacing(16)
def __connectSignalToSlot(self): 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: if not text:
cfg.set(self.configItem, "") cfg.set(self.configItem, "")
self.lineEdit.setValid(True) self.lineEdit.setValid(True)
self.explorerAction.setDisabled(True)
self.setDisabledClear(True)
return return
path = Path(text) path = Path(text)
if path.is_absolute() and path.is_dir() and path.exists(): if path.is_absolute() and path.is_dir() and path.exists():
cfg.set(self.configItem, text) cfg.set(self.configItem, text)
self.lineEdit.setValid(True) self.lineEdit.setValid(True)
self.setDisabledClear(False)
self.explorerAction.setDisabled(False)
else: else:
self.lineEdit.setValid(False) self.lineEdit.setValid(False)
self.setDisabledClear(True)
self.explorerAction.setDisabled(True)
@Slot()
def openExplorer(self): def openExplorer(self):
if self.configItem.value: if self.configItem.value:
file_path = os.path.normpath(self.configItem.value) path = os.path.realpath(self.configItem.value)
subprocess.Popen(f'explorer /select,"{file_path}"') os.startfile(path)
@Slot()
def browse(self): def browse(self):
""" download folder card clicked slot """ """ download folder card clicked slot """
folder = QFileDialog.getExistingDirectory( folder = QFileDialog.getExistingDirectory(
@@ -99,3 +123,58 @@ class FolderSettingCard(SettingCard):
if not folder or cfg.get(self.configItem) == folder: if not folder or cfg.get(self.configItem) == folder:
return return
self.lineEdit.setText(folder) 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.startSignal.connect(lambda: self.startButton.setText("Stop"))
signalBus.stopSignal.connect(lambda: self.startButton.setText("Start")) signalBus.stopSignal.connect(lambda: self.startButton.setText("Start"))
signalBus.disableStartSignal.connect(lambda state: self.startButton.setDisabled(state))
def onSelectAllClicked(self): def onSelectAllClicked(self):
signalBus.selectAllClicked.emit() signalBus.selectAllClicked.emit()

View File

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

View File

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

View File

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