From 3acf6bd642748fc0d08585e9d8eb45ca8708c539 Mon Sep 17 00:00:00 2001 From: loathingKernel <142770+loathingKernel@users.noreply.github.com> Date: Tue, 14 Jan 2025 15:29:39 +0200 Subject: [PATCH] Wrappers: add checkbox to enable/disable wrappers per game --- .../tabs/settings/widgets/wrappers.py | 32 ++++-- rare/models/wrapper.py | 13 ++- rare/shared/wrappers.py | 97 ++++++++++++------- 3 files changed, 102 insertions(+), 40 deletions(-) diff --git a/rare/components/tabs/settings/widgets/wrappers.py b/rare/components/tabs/settings/widgets/wrappers.py index f29858375..f22869918 100644 --- a/rare/components/tabs/settings/widgets/wrappers.py +++ b/rare/components/tabs/settings/widgets/wrappers.py @@ -28,7 +28,7 @@ QPushButton, QLineEdit, QVBoxLayout, - QComboBox, + QComboBox, QCheckBox, ) from rare.models.wrapper import Wrapper @@ -104,6 +104,8 @@ def __on_index_changed(self, index: int): class WrapperWidget(QFrame): + # object: current + disable_wrapper = Signal(object) # object: current, object: new update_wrapper = Signal(object, object) # object: current @@ -115,9 +117,11 @@ def __init__(self, wrapper: Wrapper, parent=None): self.setSizePolicy(QSizePolicy.Policy.Maximum, QSizePolicy.Policy.Fixed) self.setToolTip(wrapper.as_str) - text_lbl = QLabel(wrapper.name, parent=self) - text_lbl.setFont(QFont("monospace")) - text_lbl.setEnabled(wrapper.is_editable) + self.text_lbl = QCheckBox(wrapper.name, parent=self) + self.text_lbl.setChecked(wrapper.is_enabled) + self.text_lbl.setFont(QFont("monospace")) + self.text_lbl.setEnabled(wrapper.is_editable) + self.text_lbl.checkStateChanged.connect(self.__on_state_changed) image_lbl = QLabel(parent=self) image_lbl.setPixmap(qta_icon("mdi.drag-vertical").pixmap(QSize(20, 20))) @@ -142,7 +146,7 @@ def __init__(self, wrapper: Wrapper, parent=None): layout = QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(image_lbl) - layout.addWidget(text_lbl) + layout.addWidget(self.text_lbl) layout.addWidget(manage_button) self.setLayout(layout) @@ -155,6 +159,12 @@ def __init__(self, wrapper: Wrapper, parent=None): def data(self) -> Wrapper: return self.wrapper + @Slot(Qt.CheckState) + def __on_state_changed(self, state: Qt.CheckState) -> None: + new_wrapper = Wrapper(command=self.wrapper.command, enabled=self.text_lbl.isChecked()) + self.update_wrapper.emit(self.wrapper, new_wrapper) + self.deleteLater() + @Slot() def __on_delete(self) -> None: self.delete_wrapper.emit(self.wrapper) @@ -293,6 +303,7 @@ def __add_wrapper(self, wrapper: Wrapper, position: int = -1): self.wrapper_container.addWidget(widget) else: self.wrapper_container.insertWidget(position, widget) + # widget.disable_wrapper.connect(self.__disable_wrapper) widget.update_wrapper.connect(self.__update_wrapper) widget.delete_wrapper.connect(self.__delete_wrapper) @@ -319,7 +330,7 @@ def add_user_wrapper(self, wrapper: Wrapper, position: int = -1): ) return - if wrapper.checksum in self.wrappers.get_game_md5sum_list(self.app_name): + if wrapper.checksum in self.wrappers.get_game_csum_list(self.app_name): QMessageBox.warning( self, self.tr("Warning"), @@ -340,6 +351,15 @@ def add_user_wrapper(self, wrapper: Wrapper, position: int = -1): self.add_wrapper(wrapper, position) + @Slot(object) + def __disable_wrapper(self, wrapper: Wrapper): + wrappers = self.wrappers.get_game_wrapper_list(self.app_name) + index = wrappers.index(wrapper) + wrappers.remove(wrapper) + wrappers.insert(index, wrapper) + self.wrappers.set_game_wrapper_list(self.app_name, wrappers) + self.__add_wrapper(wrapper, index) + @Slot(object) def __delete_wrapper(self, wrapper: Wrapper): wrappers = self.wrappers.get_game_wrapper_list(self.app_name) diff --git a/rare/models/wrapper.py b/rare/models/wrapper.py index 376952d8e..56e826afa 100644 --- a/rare/models/wrapper.py +++ b/rare/models/wrapper.py @@ -13,10 +13,13 @@ class WrapperType(IntEnum): class Wrapper: - def __init__(self, command: Union[str, List[str]], name: str = None, wtype: WrapperType = None): + def __init__( + self, command: Union[str, List[str]], name: str = None, wtype: WrapperType = None, enabled: bool = True + ): self.__command: List[str] = shlex.split(command) if isinstance(command, str) else command self.__name: str = name if name is not None else os.path.basename(self.__command[0]) self.__wtype: WrapperType = wtype if wtype is not None else WrapperType.USER_DEFINED + self.__enabled: bool = enabled or self.__wtype == WrapperType.COMPAT_TOOL @property def is_compat_tool(self) -> bool: @@ -26,6 +29,14 @@ def is_compat_tool(self) -> bool: def is_editable(self) -> bool: return self.__wtype in {WrapperType.USER_DEFINED, WrapperType.LEGENDARY_IMPORT} + @property + def is_enabled(self) -> bool: + return self.__enabled or self.is_compat_tool + + @is_enabled.setter + def is_enabled(self, state: bool) -> None: + self.__enabled = state if not self.is_compat_tool else True + @property def checksum(self) -> str: return md5(self.as_str.encode("utf-8")).hexdigest() diff --git a/rare/shared/wrappers.py b/rare/shared/wrappers.py index d6eaca9ee..bb5cf5ad2 100644 --- a/rare/shared/wrappers.py +++ b/rare/shared/wrappers.py @@ -2,7 +2,7 @@ import os from logging import getLogger import shlex -from typing import List, Dict, Iterable +from typing import List, Dict, Iterable, Union, Tuple, Set from rare.utils import config_helper as config from PySide6.QtCore import QSettings @@ -14,6 +14,31 @@ logger = getLogger("Wrappers") +class WrapperEntry: + def __init__(self, checksum: str, enabled: bool = True): + self.__checksum: str = checksum + self.__enabled: bool = enabled + + @property + def checksum(self) -> str: + return self.__checksum + + @property + def enabled(self) -> bool: + return self.__enabled + + @classmethod + def from_dict(cls, data: Dict): + return cls(checksum=data.get("checksum"), enabled=data.get("enabled", True)) + + @property + def __dict__(self): + return dict(checksum=self.__checksum, enabled=self.__enabled) + + # def __eq__(self, other) -> bool: + # return self.checksum == other.checksum + + class Wrappers: def __init__(self): self.__file = os.path.join(config_dir(), "wrappers.json") @@ -30,9 +55,15 @@ def __init__(self): for wrap_id, wrapper in self.__wrappers_dict.get("wrappers", {}).items(): self.__wrappers.update({wrap_id: Wrapper.from_dict(wrapper)}) - self.__applists: Dict[str, List[str]] = {} + self.__applists: Dict[str, List[WrapperEntry]] = {} for app_name, wrapper_list in self.__wrappers_dict.get("applists", {}).items(): - self.__applists.update({app_name: wrapper_list}) + if all(isinstance(x, str) for x in wrapper_list): + wlist = [WrapperEntry(y) for y in wrapper_list] + elif all(isinstance(x, dict) for x in wrapper_list): + wlist = [WrapperEntry.from_dict(y) for y in wrapper_list] + else: + wlist = [] + self.__applists.update({app_name: wlist}) def import_wrappers(self, core: LegendaryCore, settings: QSettings, app_names: List): for app_name in app_names: @@ -69,34 +100,31 @@ def user_wrappers(self) -> Iterable[Wrapper]: # yield wrap def get_game_wrapper_string(self, app_name: str) -> str: - commands = [wrapper.as_str for wrapper in self.get_game_wrapper_list(app_name)] + commands = [wrapper.as_str for wrapper in self.get_game_wrapper_list(app_name) if wrapper.is_enabled] return " ".join(commands) def get_game_wrapper_list(self, app_name: str) -> List[Wrapper]: - _wrappers = [] - for wrap_id in self.__applists.get(app_name, []): - if wrap := self.__wrappers.get(wrap_id, None): - _wrappers.append(wrap) - return _wrappers + wrappers = [] + for entry in self.__applists.get(app_name, []): + if wrap := self.__wrappers.get(entry.checksum, None): + wrap.is_enabled = entry.enabled + wrappers.append(wrap) + return wrappers - def get_game_md5sum_list(self, app_name: str) -> List[str]: - return self.__applists.get(app_name, []) + def get_game_csum_list(self, app_name: str) -> Set[str]: + return {entry.checksum for entry in self.__applists.get(app_name, [])} def set_game_wrapper_list(self, app_name: str, wrappers: List[Wrapper]) -> None: _wrappers = sorted(wrappers, key=lambda w: w.is_compat_tool) for w in _wrappers: if (md5sum := w.checksum) in self.__wrappers.keys(): if w != self.__wrappers[md5sum]: - logger.error( - "Non-unique md5sum for different wrappers %s, %s", - w.name, - self.__wrappers[md5sum].name, - ) + logger.error("Equal csum for unequal wrappers %s, %s", w.name, self.__wrappers[md5sum].name) if w.is_compat_tool: self.__wrappers.update({md5sum: w}) else: self.__wrappers.update({md5sum: w}) - self.__applists[app_name] = [w.checksum for w in _wrappers] + self.__applists[app_name] = [WrapperEntry(w.checksum, w.is_enabled) for w in _wrappers] self.__save_config(app_name) self.__save_wrappers() @@ -105,8 +133,8 @@ def __save_config(self, app_name: str): config.save_option(app_name, "wrapper", command_string) def __save_wrappers(self): - existing = {wrap_id for wrap_id in self.__wrappers.keys()} - in_use = {wrap_id for wrappers in self.__applists.values() for wrap_id in wrappers} + existing = {csum for csum in self.__wrappers.keys()} + in_use = {entry.checksum for wrappers in self.__applists.values() for entry in wrappers} for redudant in existing.difference(in_use): del self.__wrappers[redudant] @@ -128,15 +156,16 @@ def __save_wrappers(self): config_dir = os.getcwd global config config = Namespace() - config.set_option = lambda x, y, z: print(x, y, z) - config.remove_option = lambda x, y: print(x, y) - config.save_config = lambda: print() + config.set_option = lambda x, y, z: print("set_option:", x, y, z) + config.remove_option = lambda x, y: print("remove_option:", x, y) + config.save_config = lambda: print("save_config:") + config.save_option = lambda x, y, z: print("save_option:", x, y, z) wr = Wrappers() w1 = Wrapper(command=["/usr/bin/w1"], wtype=WrapperType.NONE) w2 = Wrapper(command=["/usr/bin/w2"], wtype=WrapperType.COMPAT_TOOL) - w3 = Wrapper(command=["/usr/bin/w3"], wtype=WrapperType.USER_DEFINED) + w3 = Wrapper(command=["/usr/bin/w3"], wtype=WrapperType.USER_DEFINED, enabled=False) w4 = Wrapper(command=["/usr/bin/w4"], wtype=WrapperType.USER_DEFINED) wr.set_game_wrapper_list("testgame", [w1, w2, w3, w4]) @@ -147,19 +176,21 @@ def __save_wrappers(self): wr.set_game_wrapper_list("testgame", [w1, w2, w3, w6]) w7 = Wrapper(command=["/usr/bin/w2"], wtype=WrapperType.COMPAT_TOOL) - wrs = wr.get_game_wrapper_list("testgame") - wrs.remove(w7) - wr.set_game_wrapper_list("testgame", wrs) + app_wrappers = wr.get_game_wrapper_list("testgame") + pprint([w.as_str for w in app_wrappers]) + # item = next(item for item in app_wrappers if item.checksum == w3.checksum) + app_wrappers.remove(w3) + wr.set_game_wrapper_list("testgame", app_wrappers) game_wrappers = wr.get_game_wrapper_list("testgame") - pprint(game_wrappers) + pprint([w.as_str for w in game_wrappers]) game_wrappers = wr.get_game_wrapper_list("testgame2") - pprint(game_wrappers) + pprint([w.as_str for w in game_wrappers]) - for i, tool in enumerate(steam.find_tools()): - wt = Wrapper(command=tool.command(), name=tool.name, wtype=WrapperType.COMPAT_TOOL) - wr.set_game_wrapper_list(f"compat_game_{i}", [wt]) - print(wt.as_str) + # for i, tool in enumerate(steam.find_tools()): + # wt = Wrapper(command=tool.command(), name=tool.name, wtype=WrapperType.COMPAT_TOOL) + # wr.set_game_wrapper_list(f"compat_game_{i}", [wt]) + # print(wt.as_str) for wrp in wr.user_wrappers: - pprint(wrp) + pprint(wrp.as_str)