mirror of
https://github.com/aclist/dztui.git
synced 2026-10-30 22:52:42 +01:00
feat: uninstall wizard WIP
This commit is contained in:
1 parent
63c2b5acd4
commit
93d870b5bf
5 files changed
+293
-19
No files matched your search
@@ -160,6 +160,13 @@ class Shortcuts:
|
|||||||
NEW_ENTRY["tags"] = {}
|
NEW_ENTRY["tags"] = {}
|
||||||
return NEW_ENTRY
|
return NEW_ENTRY
|
||||||
|
|
||||||
|
def delete_shortcut(self, start_path: Path) -> None:
|
||||||
|
for s in self.shortcuts["shortcuts"]:
|
||||||
|
if self.shortcuts[s]["StartDir"] == str(start_path):
|
||||||
|
del self.shortcuts[s]
|
||||||
|
break
|
||||||
|
self.save_shortcuts()
|
||||||
|
|
||||||
def save_shortcuts(self) -> None:
|
def save_shortcuts(self) -> None:
|
||||||
try:
|
try:
|
||||||
backup = self.shortcuts_path.with_suffix(".vdf.bak")
|
backup = self.shortcuts_path.with_suffix(".vdf.bak")
|
||||||
|
|||||||
+10
-14
@@ -8,30 +8,26 @@ from dzgui.init.prefix import get_version
|
|||||||
from dzgui.util.map_count import set_map_count
|
from dzgui.util.map_count import set_map_count
|
||||||
from dzgui.util.strings import flags
|
from dzgui.util.strings import flags
|
||||||
|
|
||||||
|
from dzgui.views.dialogs.uninstall import UninstallWizard
|
||||||
|
from dzgui.config.xdg import get_xdg_paths
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description=flags.description)
|
parser = argparse.ArgumentParser(description=flags.description)
|
||||||
parser.add_argument("-d", "--debug", action="store_true", help=flags.debug)
|
group = parser.add_mutually_exclusive_group()
|
||||||
parser.add_argument("-m", "--map", action="store_true", help=flags.map_count)
|
group.add_argument("-d", "--debug", action="store_true", help=flags.debug)
|
||||||
parser.add_argument("-u", "--uninstall", action="store_true", help=flags.uninstall)
|
group.add_argument("-m", "--map", action="store_true", help=flags.map_count)
|
||||||
parser.add_argument("-v", "--version", action="store_true", help=flags.version)
|
group.add_argument("-u", "--uninstall", action="store_true", help=flags.uninstall)
|
||||||
|
group.add_argument("-v", "--version", action="store_true", help=flags.version)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
def uninstall() -> None:
|
|
||||||
# TODO: uninstall data files (-u)
|
|
||||||
# -u removes state, log, freedesktop
|
|
||||||
# XDG_SHARE_HOME/dzgui
|
|
||||||
# XDG_STATE_HOME/dzgui
|
|
||||||
# XDG_DATA_HOME/dzgui
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
# TODO: isolate single flags
|
# TODO: isolate single flags
|
||||||
if args.version is True:
|
if args.version is True:
|
||||||
print(get_version())
|
print(get_version())
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
if args.uninstall is True:
|
if args.uninstall is True:
|
||||||
uninstall()
|
paths = get_xdg_paths()
|
||||||
|
UninstallWizard(False, paths)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
if args.debug is True:
|
if args.debug is True:
|
||||||
warnings.filterwarnings("default", category=DeprecationWarning)
|
warnings.filterwarnings("default", category=DeprecationWarning)
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
pg1_title = "Choose uninstall targets"
|
||||||
|
pg1_heading = "Choose uninstall targets"
|
||||||
|
pg1_blurb = "Here you can choose which DZGUI configuration/state files to remove. "
|
||||||
|
|
||||||
|
pg2_title = "Complete"
|
||||||
|
pg2_heading = "Uninstall complete"
|
||||||
|
pg2_blurb = "Finished removing the selected files. See the system log for errors."
|
||||||
|
|
||||||
|
config_label = "Configuration files"
|
||||||
|
config_details = (
|
||||||
|
"User preferences and API key. "
|
||||||
|
"Keep this file if you want to import it again later."
|
||||||
|
)
|
||||||
|
desktop_label = "Desktop shortcut"
|
||||||
|
desktop_details = "Shortcut set on the desktop, if applicable."
|
||||||
|
|
||||||
|
start_menu_label = "Start menu shortcut"
|
||||||
|
start_menu_details = (
|
||||||
|
"Shortcut set under the Games category of the start menu, if applicable."
|
||||||
|
)
|
||||||
|
state_label = "Long-term state files"
|
||||||
|
state_details = (
|
||||||
|
"UI size, filter preferences, column size, server notes, and mod signatures."
|
||||||
|
)
|
||||||
|
steam_shortcut = "Steam shortcut"
|
||||||
|
steam_details = "Shortcut to DZGUI installed within Steam, if applicable. Requires Steam to be restarted to reflect the change."
|
||||||
|
|
||||||
|
|
||||||
|
path_remove_prefix = "The following path will be removed:"
|
||||||
|
not_installed = "It looks like DZGUI was not previously set up; nothing to uninstall."
|
||||||
|
standalone_uninstall = (
|
||||||
|
"It looks like you are running the standalone version of DZGUI. "
|
||||||
|
"To finalize the uninstall process, you can remove the 'dzgui' file."
|
||||||
|
)
|
||||||
|
system_uninstall = (
|
||||||
|
"It looks like DZGUI was installed via your system package manager or directly from source code.\n"
|
||||||
|
"To finalize the uninstall process, follow the standard removal procedure for that context."
|
||||||
|
)
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Self, TYPE_CHECKING
|
||||||
|
|
||||||
|
from dzgui.api.shortcuts import Shortcuts
|
||||||
|
from dzgui.const.constants import APP_NAME
|
||||||
|
from dzgui.strings import uninstall
|
||||||
|
from dzgui.util._json import read_json
|
||||||
|
from dzgui.util.css import load_css
|
||||||
|
from dzgui.views.components.box import HBox
|
||||||
|
from dzgui.views.dialogs.wizard import ScrolledWizardPage, CheckboxWithLabel
|
||||||
|
|
||||||
|
import gi
|
||||||
|
|
||||||
|
gi.require_version("Gtk", "3.0")
|
||||||
|
from gi.repository import Gtk, GLib, Pango # noqa E402
|
||||||
|
|
||||||
|
|
||||||
|
class Assistant(Gtk.Assistant):
|
||||||
|
def __init__(self, is_deck: bool, paths: dict[str, str]):
|
||||||
|
super().__init__()
|
||||||
|
if is_deck:
|
||||||
|
# NOTE: deemed to be "safe" dimensions that exclude taskbar size
|
||||||
|
self.set_default_size(1085, 670)
|
||||||
|
else:
|
||||||
|
self.set_default_size(1500, 900)
|
||||||
|
|
||||||
|
self.set_forward_page_func(self._advance_page)
|
||||||
|
|
||||||
|
pyapp = os.getenv("PYAPP")
|
||||||
|
|
||||||
|
self.page1 = UninstallPage(paths, pyapp)
|
||||||
|
self.page2 = CompletionPage(pyapp)
|
||||||
|
|
||||||
|
self.append_page(self.page1)
|
||||||
|
self.set_page_type(self.page1, Gtk.AssistantPageType.INTRO)
|
||||||
|
self.set_page_title(self.page1, uninstall.pg1_title)
|
||||||
|
self.set_page_complete(self.page1, True)
|
||||||
|
|
||||||
|
self.append_page(self.page2)
|
||||||
|
self.set_page_title(self.page2, uninstall.pg2_title)
|
||||||
|
self.set_page_type(self.page2, Gtk.AssistantPageType.SUMMARY)
|
||||||
|
|
||||||
|
self.connect("cancel", self.destroy_and_quit)
|
||||||
|
self.connect("close", self.destroy_and_quit)
|
||||||
|
self.show_all()
|
||||||
|
load_css()
|
||||||
|
|
||||||
|
def _advance_page(self, index: int) -> int:
|
||||||
|
cur_page = self.get_nth_page(index)
|
||||||
|
if cur_page == self.page1:
|
||||||
|
self.page1.uninstall()
|
||||||
|
return index + 1
|
||||||
|
|
||||||
|
def destroy_and_quit(self, widget: Self) -> None:
|
||||||
|
self.destroy()
|
||||||
|
Gtk.main_quit()
|
||||||
|
|
||||||
|
|
||||||
|
class Window(Gtk.Window):
|
||||||
|
def __init__(self, is_deck: bool, paths: dict[str, str]) -> None:
|
||||||
|
super().__init__(title=APP_NAME, icon_name=APP_NAME)
|
||||||
|
self.assistant = Assistant(is_deck, paths)
|
||||||
|
|
||||||
|
|
||||||
|
class CompletionPage(ScrolledWizardPage):
|
||||||
|
def __init__(self, pyapp: str | None):
|
||||||
|
super().__init__(heading=uninstall.pg2_heading, description=uninstall.pg2_blurb)
|
||||||
|
|
||||||
|
text = (
|
||||||
|
uninstall.standalone_uninstall
|
||||||
|
if pyapp is not None
|
||||||
|
else uninstall.system_uninstall
|
||||||
|
)
|
||||||
|
|
||||||
|
label = Gtk.Label(
|
||||||
|
label=text,
|
||||||
|
wrap_mode=Pango.WrapMode.WORD,
|
||||||
|
margin=10,
|
||||||
|
max_width_chars=80,
|
||||||
|
justify=Gtk.Justification.CENTER,
|
||||||
|
)
|
||||||
|
frame = Gtk.Frame()
|
||||||
|
frame.add(label)
|
||||||
|
self.add_start(frame)
|
||||||
|
|
||||||
|
|
||||||
|
class FileLabel(HBox):
|
||||||
|
def __init__(self, path: Path) -> None:
|
||||||
|
super().__init__(spacing=10)
|
||||||
|
|
||||||
|
label = Gtk.Label(label=uninstall.path_remove_prefix, halign=Gtk.Align.START)
|
||||||
|
textview = Gtk.TextView(
|
||||||
|
editable=False,
|
||||||
|
halign=Gtk.Align.START,
|
||||||
|
left_margin=10,
|
||||||
|
right_margin=10,
|
||||||
|
)
|
||||||
|
textview.set_buffer(Gtk.TextBuffer(text=str(path)))
|
||||||
|
self.extend([label, textview])
|
||||||
|
|
||||||
|
|
||||||
|
class CheckboxWithPath(CheckboxWithLabel):
|
||||||
|
def __init__(
|
||||||
|
self, label: str, details: str, path: Path, active: bool = True
|
||||||
|
) -> None:
|
||||||
|
super().__init__(text=label, blurb_text=details)
|
||||||
|
|
||||||
|
self.conf_path = path
|
||||||
|
self.set_active(active)
|
||||||
|
fl = FileLabel(path)
|
||||||
|
self.indent_below(fl)
|
||||||
|
|
||||||
|
def get_conf_path(self) -> Path:
|
||||||
|
return self.conf_path
|
||||||
|
|
||||||
|
|
||||||
|
class UninstallPage(ScrolledWizardPage):
|
||||||
|
def __init__(self, paths: dict[str, str], pyapp: str | None):
|
||||||
|
super().__init__(heading=uninstall.pg1_heading, description=uninstall.pg1_blurb)
|
||||||
|
|
||||||
|
self.pyapp = pyapp
|
||||||
|
|
||||||
|
config = Path(paths["XDG_CONFIG_HOME"])
|
||||||
|
state = Path(paths["XDG_STATE_HOME"])
|
||||||
|
self.share = Path(paths["XDG_DATA_HOME"])
|
||||||
|
shortcut = self.share.parent.joinpath("applications/dzgui.desktop")
|
||||||
|
desktop = config.parent.parent.joinpath("Desktop/dzgui.desktop")
|
||||||
|
self.steam_path = self.parse_steam_path(config, self.share)
|
||||||
|
|
||||||
|
self.boxes: list[CheckboxWithPath] = []
|
||||||
|
self.checks_area = Gtk.Box(
|
||||||
|
orientation=Gtk.Orientation.VERTICAL, spacing=20, margin_top=20
|
||||||
|
)
|
||||||
|
self.config_box = CheckboxWithPath(
|
||||||
|
uninstall.config_label, uninstall.config_details, config, active=False
|
||||||
|
)
|
||||||
|
self.desktop_box = CheckboxWithPath(
|
||||||
|
uninstall.desktop_label, uninstall.desktop_details, desktop
|
||||||
|
)
|
||||||
|
self.start_menu_box = CheckboxWithPath(
|
||||||
|
uninstall.start_menu_label, uninstall.start_menu_details, shortcut
|
||||||
|
)
|
||||||
|
self.share_box = CheckboxWithPath(
|
||||||
|
uninstall.desktop_label, uninstall.desktop_details, desktop
|
||||||
|
)
|
||||||
|
self.state_box = CheckboxWithPath(
|
||||||
|
uninstall.state_label, uninstall.state_details, state
|
||||||
|
)
|
||||||
|
self.steam_box = CheckboxWithLabel(
|
||||||
|
uninstall.steam_shortcut, uninstall.steam_details
|
||||||
|
)
|
||||||
|
|
||||||
|
for box in (
|
||||||
|
self.config_box,
|
||||||
|
self.state_box,
|
||||||
|
self.desktop_box,
|
||||||
|
self.start_menu_box,
|
||||||
|
):
|
||||||
|
self.checks_area.add(box)
|
||||||
|
self.boxes.append(box)
|
||||||
|
|
||||||
|
self.checks_area.add(self.steam_box)
|
||||||
|
|
||||||
|
self.add_start(self.checks_area)
|
||||||
|
self.show_all()
|
||||||
|
|
||||||
|
def parse_steam_path(self, config: Path, steam: Path) -> Path | None:
|
||||||
|
try:
|
||||||
|
file = config.joinpath("config.json")
|
||||||
|
conf = read_json(file)
|
||||||
|
steam = conf["default_steam_path"]
|
||||||
|
self.steam_path = Path(steam)
|
||||||
|
return Path(steam)
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def wipe_conf_file(self, box: CheckboxWithPath) -> None:
|
||||||
|
if box.get_active():
|
||||||
|
path = box.get_conf_path()
|
||||||
|
try:
|
||||||
|
# TODO:
|
||||||
|
# path.unlink()
|
||||||
|
print(f"Deleted '{path}'")
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
def wipe_steam_shortcut(self) -> None:
|
||||||
|
# TODO:
|
||||||
|
pass
|
||||||
|
# try:
|
||||||
|
# shortcuts = Shortcuts(self.steam_path)
|
||||||
|
# shortcuts.delete_shortcut(self.share)
|
||||||
|
# except Exception as e:
|
||||||
|
# print(e)
|
||||||
|
|
||||||
|
def wipe_pyapp(self) -> None:
|
||||||
|
if self.pyapp is None:
|
||||||
|
return
|
||||||
|
res = subprocess.run([self.pyapp, "self", "remove"])
|
||||||
|
print(res)
|
||||||
|
|
||||||
|
def uninstall(self) -> None:
|
||||||
|
for box in self.boxes:
|
||||||
|
self.wipe_conf_file(box)
|
||||||
|
if self.steam_box.get_active():
|
||||||
|
self.wipe_steam_shortcut()
|
||||||
|
# TODO:
|
||||||
|
# self.wipe_pyapp()
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class UninstallWizard(Gtk.Application):
|
||||||
|
def __init__(self, is_deck: bool, paths: dict[str, str]) -> None:
|
||||||
|
super().__init__()
|
||||||
|
config = Path(paths["XDG_CONFIG_HOME"])
|
||||||
|
# TODO: tests
|
||||||
|
if not config.is_dir() or not config.joinpath("config.json").is_file():
|
||||||
|
print(uninstall.not_installed)
|
||||||
|
return
|
||||||
|
GLib.set_prgname(APP_NAME)
|
||||||
|
self.win = Window(is_deck, paths)
|
||||||
|
Gtk.main()
|
||||||
@@ -99,6 +99,8 @@ class ScrolledWizardPage(Gtk.ScrolledWindow):
|
|||||||
self.box.pack_start(image, expand=False, fill=True, padding=0)
|
self.box.pack_start(image, expand=False, fill=True, padding=0)
|
||||||
|
|
||||||
self.add(self.box)
|
self.add(self.box)
|
||||||
|
self.box.pack_start(self.heading, expand=False, fill=True, padding=0)
|
||||||
|
self.box.pack_start(self.description, expand=False, fill=True, padding=0)
|
||||||
|
|
||||||
def get_page_type(self) -> Gtk.AssistantPageType:
|
def get_page_type(self) -> Gtk.AssistantPageType:
|
||||||
return self.page_type
|
return self.page_type
|
||||||
@@ -123,12 +125,9 @@ class EnumeratedWizardPage(ScrolledWizardPage):
|
|||||||
def __init__(self, enum: PageNum, heading: str, description: str) -> None:
|
def __init__(self, enum: PageNum, heading: str, description: str) -> None:
|
||||||
super().__init__(heading=heading, description=description)
|
super().__init__(heading=heading, description=description)
|
||||||
|
|
||||||
|
|
||||||
self.enum = enum
|
self.enum = enum
|
||||||
self.prog = Progress()
|
self.prog = Progress()
|
||||||
self.box.pack_end(self.prog, expand=False, fill=False, padding=0)
|
self.box.pack_end(self.prog, expand=False, fill=False, padding=0)
|
||||||
self.box.pack_start(self.heading, expand=False, fill=True, padding=0)
|
|
||||||
self.box.pack_start(self.description, expand=False, fill=True, padding=0)
|
|
||||||
|
|
||||||
def get_enum(self) -> PageNum:
|
def get_enum(self) -> PageNum:
|
||||||
return self.enum
|
return self.enum
|
||||||
@@ -541,7 +540,9 @@ class Assistant(Gtk.Assistant):
|
|||||||
return
|
return
|
||||||
self.set_page_complete(page, True)
|
self.set_page_complete(page, True)
|
||||||
|
|
||||||
def _add_page(self, page: EnumeratedWizardPage, ptype: Gtk.AssistantPageType) -> None:
|
def _add_page(
|
||||||
|
self, page: EnumeratedWizardPage, ptype: Gtk.AssistantPageType
|
||||||
|
) -> None:
|
||||||
self.append_page(page)
|
self.append_page(page)
|
||||||
self.set_page_type(page, ptype)
|
self.set_page_type(page, ptype)
|
||||||
self.set_page_title(page, page.get_title())
|
self.set_page_title(page, page.get_title())
|
||||||
@@ -567,15 +568,21 @@ class CheckboxWithLabel(Gtk.Box):
|
|||||||
def __init__(self, text: str, blurb_text: str) -> None:
|
def __init__(self, text: str, blurb_text: str) -> None:
|
||||||
super().__init__(orientation=Gtk.Orientation.VERTICAL, spacing=5)
|
super().__init__(orientation=Gtk.Orientation.VERTICAL, spacing=5)
|
||||||
|
|
||||||
|
self.indent = 20
|
||||||
self.button = Gtk.CheckButton(label=text)
|
self.button = Gtk.CheckButton(label=text)
|
||||||
self.button.set_active(True)
|
self.button.set_active(True)
|
||||||
label = Gtk.Label(label="", halign=Gtk.Align.START, margin_start=20)
|
label = Gtk.Label(label="", halign=Gtk.Align.START, margin_start=self.indent)
|
||||||
|
|
||||||
wrapped = textwrap.fill(blurb_text, width=100)
|
wrapped = textwrap.fill(blurb_text, width=100)
|
||||||
label.set_markup(f"- {wrapped}")
|
label.set_markup(f"- {wrapped}")
|
||||||
|
|
||||||
for el in self.button, label:
|
for el in self.button, label:
|
||||||
self.add(el)
|
self.add(el)
|
||||||
|
|
||||||
|
def indent_below(self, widget: Gtk.Widget) -> None:
|
||||||
|
widget.set_margin_start(self.indent)
|
||||||
|
self.add(widget)
|
||||||
|
|
||||||
def get_checkbox(self) -> Gtk.CheckButton:
|
def get_checkbox(self) -> Gtk.CheckButton:
|
||||||
return self.button
|
return self.button
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user