You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

199 lines
8.8 KiB
Python

import logging
import os.path
import sys
from typing import Dict, List, Tuple
from PyQt6 import QtWidgets
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QStandardItemModel, QStandardItem
from window.mod_manager_window_export import Ui_MainWindow
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent, parent_logger: logging.Logger, version: str = ""):
super(MainWindow, self).__init__()
self.parent = parent
self.__logger = logging.getLogger("MainWindow")
for handler in parent_logger.handlers:
self.__logger.addHandler(handler)
self.__logger.setLevel(parent_logger.level)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.setWindowTitle(f"LC Mod Manager {version}")
self.ui.actionAdd_new_mod.triggered.connect(self.on_action_add_new_mod)
self.ui.actionrefresh_mods.triggered.connect(self.on_action_refresh_mods)
self.ui.actionCheck_for_Updates.triggered.connect(self.on_action_check_for_updates)
self.ui.actionSet_game_folder.triggered.connect(self.on_action_set_game_folder)
self.ui.actionRemove_ALL_manager_files.triggered.connect(self.on_action_remove_all_files)
self.ui.DeleteModFilesButton.pressed.connect(self.on_pressed_delete_mod_files_button)
self.ui.ApplyChangesButton.pressed.connect(self.on_pressed_apply_changes_button)
self.ui.DisacrdChangesButton.pressed.connect(self.on_pressed_discard_changes_button)
self.ui.TODOButton.hide()
self.ui.ApplyChangesButton.hide()
self.ui.DisacrdChangesButton.hide()
self.ui.DeleteModFilesButton.hide()
self.ui.actionCheck_for_Updates.setDisabled(True)
def set_available_mods(self, available_mods: Dict[Tuple[str, str], Dict]):
"""
Sets the given mods as the list of available mods
:param available_mods:
:return:
"""
keys = [(name + "|" + version, (name, version)) for name, version in available_mods]
keys.sort(key=lambda x: x[0])
item_model = QStandardItemModel(self.ui.AvailableModsList)
for key in keys:
mod_name, mod_version = key[1][0], key[1][1]
item = QStandardItem(mod_name + f" ({mod_version})")
item.setFlags(Qt.ItemFlag.ItemIsUserCheckable | Qt.ItemFlag.ItemIsEnabled)
item.setData(Qt.CheckState.Unchecked, Qt.ItemDataRole.CheckStateRole)
item.setData((mod_name, mod_version), Qt.ItemDataRole.UserRole)
item_model.appendRow(item)
if self.parent.is_mod_installed(mod_name, mod_version):
item.setCheckState(Qt.CheckState.Checked)
item_model.itemChanged.connect(self.on_available_mod_item_changed)
self.ui.AvailableModsList.setModel(item_model)
def set_installed_mods(self, installed_mods: Dict[str, str]):
"""
Sets the given mods as the list of installed mods
:param installed_mods: string list of all the mods as to be written to the list
:return:
"""
keys = [(name + "|" + version, (name, version)) for name, version in installed_mods]
keys.sort(key=lambda x: x[0])
item_model = QStandardItemModel(self.ui.InstalledModsListView)
for key in keys:
mod_name, mod_version = key[1][0], key[1][1]
if (mod_name, mod_version) in self.parent.available_mods.keys():
mod_version = self.parent.available_mods[(mod_name, mod_version)]['version']
else:
mod_version = "Not Tracked"
item = QStandardItem(mod_name + f" ({mod_version})")
item.setFlags(Qt.ItemFlag.ItemIsUserCheckable | Qt.ItemFlag.ItemIsEnabled)
item.setData(Qt.CheckState.Unchecked, Qt.ItemDataRole.CheckStateRole)
item.setData((mod_name, mod_version), Qt.ItemDataRole.UserRole)
item_model.appendRow(item)
item.setCheckState(Qt.CheckState.Checked)
item_model.itemChanged.connect(self.on_installed_mod_item_changed)
self.ui.InstalledModsListView.setModel(item_model)
# UI Callback functions
## Actions
def on_action_add_new_mod(self):
self.__logger.debug("Action: \"add new mod\" triggered!")
dialog = QtWidgets.QFileDialog(self, "Select Lethal Company mod")
dialog.setFileMode(QtWidgets.QFileDialog.FileMode.ExistingFiles)
result = dialog.getOpenFileName(filter='ZIP (*.zip)')
self.__logger.debug(f"user selected \"{result[0]}\"")
if result == ('', ''):
self.__logger.debug("Action: \"add new mod\" was cancelled!")
return
if not os.path.isfile(result[0]):
dialog = QtWidgets.QMessageBox()
dialog.setWindowTitle("Not a file")
dialog.setInformativeText(f"The given file \"{result}\" did not look like a file!")
dialog.setIcon(QtWidgets.QMessageBox.Icon.Warning)
dialog.exec()
return
if not self.parent.is_valid_mod_file(result[0]):
dialog = QtWidgets.QMessageBox()
dialog.setWindowTitle("Not a valid mod file")
dialog.setInformativeText(
f"The given file \"{result}\" did not look like a mod file. Is the manifest.json present?")
dialog.setIcon(QtWidgets.QMessageBox.Icon.Warning)
dialog.exec()
return
self.parent.add_mod_file(result[0])
def on_action_refresh_mods(self):
self.__logger.debug("Action: \"refresh mods\" triggered!")
self.parent.index_stored_mods()
self.parent.index_installed_mods()
def on_action_check_for_updates(self):
self.__logger.debug("Action: \"check for updates\" triggered!")
def on_action_set_game_folder(self):
self.__logger.debug("Action: \"set game folder\" triggered!")
dialog = QtWidgets.QFileDialog(self, "Select Lethal Company folder")
dialog.setFileMode(QtWidgets.QFileDialog.FileMode.Directory)
dialog.setOptions(QtWidgets.QFileDialog.Option.ShowDirsOnly)
result = dialog.getExistingDirectory()
dir_accepted = self.parent.set_game_folder(result)
attempt = 0
while not dir_accepted:
attempt += 1
dialog = QtWidgets.QMessageBox()
dialog.setWindowTitle("Invalid game path")
dialog.setInformativeText(f"The given path \"{result}\" did not look like the Lethal Company game folder!\n"
f"If you can't find it. Try using steam and select \"browse local files\"!")
dialog.setIcon(QtWidgets.QMessageBox.Icon.Warning)
dialog.exec()
dialog = QtWidgets.QFileDialog(self, "Select Lethal Company folder")
dialog.setFileMode(QtWidgets.QFileDialog.FileMode.Directory)
dialog.setOptions(QtWidgets.QFileDialog.Option.ShowDirsOnly)
result = dialog.getExistingDirectory()
dir_accepted = self.parent.set_game_folder(result)
if attempt > 2:
return
def on_action_remove_all_files(self):
self.__logger.debug("Action: \"remove all manager files\" triggered!")
self.parent.nuke_manager_files()
## Buttons
def on_pressed_delete_mod_files_button(self):
self.__logger.debug("Pressed button: \"Delete Mod Files\"")
raise NotImplementedError
def on_pressed_apply_changes_button(self):
self.__logger.debug("Pressed button: \"Apply Changes\"")
raise NotImplementedError
def on_pressed_discard_changes_button(self):
self.__logger.debug("Pressed button: \"Discard Changes\"")
raise NotImplementedError
def on_available_mod_item_changed(self, item: QStandardItem):
self.__logger.debug(f"Available Mod list item \"{item.text()}\" changed to {item.checkState()}")
mod_name, mod_version = item.data(Qt.ItemDataRole.UserRole)
if item.checkState() == Qt.CheckState.Checked:
self.parent.install_mod(mod_name, mod_version)
elif item.checkState() == Qt.CheckState.Unchecked:
self.parent.uninstall_mod(mod_name, mod_version)
def on_installed_mod_item_changed(self, item: QStandardItem):
self.__logger.debug(f"Installed Mod list item \"{item.text()}\" changed to {item.checkState()}")
mod_name, mod_version = item.data(Qt.ItemDataRole.UserRole)
if item.checkState() == Qt.CheckState.Unchecked: # mod should be uninstalled
accepted = QtWidgets.QMessageBox.question(self, "Really uninstall mod?",
f"Do you really want to uninstall the mod \"{mod_name}\"?\n"
"This could lead to permanent data loss if it wasn't tracked!")
if accepted:
self.parent.uninstall_mod(mod_name, mod_version)
else:
return