Merge pull request 'Added windows folder' (#624) from Qubasa-main into main

This commit is contained in:
clan-bot
2023-12-08 12:49:07 +00:00
10 changed files with 268 additions and 164 deletions

View File

@@ -20,14 +20,6 @@ class ClanScheme(Enum):
def __str__(self) -> str: def __str__(self) -> str:
return f"HTTP({self.url})" # The __str__ method returns a custom string representation return f"HTTP({self.url})" # The __str__ method returns a custom string representation
@member
@dataclass
class HTTPS:
url: str # The url field holds the HTTPS URL
def __str__(self) -> str:
return f"HTTPS({self.url})" # The __str__ method returns a custom string representation
@member @member
@dataclass @dataclass
class FILE: class FILE:
@@ -78,19 +70,30 @@ class ClanURI:
self._components = self._components._replace(query=new_query) self._components = self._components._replace(query=new_query)
self.params = ClanParameters(**params) self.params = ClanParameters(**params)
# Use the match statement to check the scheme and create a ClanScheme member with the value comb = (
match self._components.scheme: self._components.scheme,
case "http": self._components.netloc,
self._components.path,
self._components.params,
self._components.query,
self._components.fragment,
)
match comb:
case ("http" | "https", _, _, _, _, _):
self.scheme = ClanScheme.HTTP.value(self._components.geturl()) # type: ignore self.scheme = ClanScheme.HTTP.value(self._components.geturl()) # type: ignore
case "https": case ("file", "", path, "", "", "") | ("", "", path, "", "", ""): # type: ignore
self.scheme = ClanScheme.HTTPS.value(self._components.geturl()) # type: ignore self.scheme = ClanScheme.FILE.value(Path(path)) # type: ignore
case "file":
self.scheme = ClanScheme.FILE.value(Path(self._components.path)) # type: ignore
case _: case _:
raise ClanError(f"Unsupported scheme: {self._components.scheme}") raise ClanError(f"Unsupported uri components: {comb}")
def get_internal(self) -> str:
return self._nested_uri
@classmethod @classmethod
def from_path(cls, path: Path, params: ClanParameters) -> Self: # noqa def from_path(cls, path: Path, params: ClanParameters) -> Self: # noqa
urlparams = urllib.parse.urlencode(params.__dict__) urlparams = urllib.parse.urlencode(params.__dict__)
return cls(f"clan://{path}?{urlparams}")
return cls(f"clan://file://{path}?{urlparams}") def __str__(self) -> str:
return f"ClanURI({self._components.geturl()})"

View File

@@ -82,6 +82,13 @@ def nix_eval(flags: list[str]) -> list[str]:
return default_flags + flags return default_flags + flags
def nix_metadata(flake: str) -> dict[str, Any]:
cmd = nix_command(["flake", "metadata", "--json", flake])
proc = subprocess.run(cmd, check=True, text=True, stdout=subprocess.PIPE)
data = json.loads(proc.stdout)
return data
@deal.raises(ClanError) @deal.raises(ClanError)
def nix_shell(packages: list[str], cmd: list[str]) -> list[str]: def nix_shell(packages: list[str], cmd: list[str]) -> list[str]:
# we cannot use nix-shell inside the nix sandbox # we cannot use nix-shell inside the nix sandbox

View File

@@ -2,7 +2,7 @@ from pathlib import Path
import pytest import pytest
from clan_cli.clan_uri import ClanScheme, ClanURI from clan_cli.clan_uri import ClanParameters, ClanScheme, ClanURI
from clan_cli.errors import ClanError from clan_cli.errors import ClanError
@@ -17,7 +17,7 @@ def test_local_uri() -> None:
def test_unsupported_schema() -> None: def test_unsupported_schema() -> None:
with pytest.raises(ClanError, match="Unsupported scheme: ftp"): with pytest.raises(ClanError, match="Unsupported uri components: .*"):
# Create a ClanURI object from an unsupported URI # Create a ClanURI object from an unsupported URI
ClanURI("clan://ftp://ftp.example.com") ClanURI("clan://ftp://ftp.example.com")
@@ -27,12 +27,24 @@ def test_is_remote() -> None:
uri = ClanURI("clan://https://example.com") uri = ClanURI("clan://https://example.com")
match uri.scheme: match uri.scheme:
case ClanScheme.HTTPS.value(url): case ClanScheme.HTTP.value(url):
assert url == "https://example.com" # type: ignore assert url == "https://example.com" # type: ignore
case _: case _:
assert False assert False
def test_direct_local_path() -> None:
# Create a ClanURI object from a remote URI
uri = ClanURI("clan://~/Downloads")
assert uri.get_internal() == "~/Downloads"
def test_direct_local_path2() -> None:
# Create a ClanURI object from a remote URI
uri = ClanURI("clan:///home/user/Downloads")
assert uri.get_internal() == "/home/user/Downloads"
def test_remote_with_clanparams() -> None: def test_remote_with_clanparams() -> None:
# Create a ClanURI object from a remote URI with parameters # Create a ClanURI object from a remote URI with parameters
uri = ClanURI("clan://https://example.com") uri = ClanURI("clan://https://example.com")
@@ -40,19 +52,47 @@ def test_remote_with_clanparams() -> None:
assert uri.params.flake_attr == "defaultVM" assert uri.params.flake_attr == "defaultVM"
match uri.scheme: match uri.scheme:
case ClanScheme.HTTPS.value(url): case ClanScheme.HTTP.value(url):
assert url == "https://example.com" # type: ignore assert url == "https://example.com" # type: ignore
case _: case _:
assert False assert False
def test_from_path_with_custom() -> None:
# Create a ClanURI object from a remote URI with parameters
uri_str = Path("/home/user/Downloads")
params = ClanParameters(flake_attr="myVM")
uri = ClanURI.from_path(uri_str, params)
assert uri.params.flake_attr == "myVM"
match uri.scheme:
case ClanScheme.FILE.value(path):
assert path == Path("/home/user/Downloads") # type: ignore
case _:
assert False
def test_from_path_with_default() -> None:
# Create a ClanURI object from a remote URI with parameters
uri_str = Path("/home/user/Downloads")
params = ClanParameters()
uri = ClanURI.from_path(uri_str, params)
assert uri.params.flake_attr == "defaultVM"
match uri.scheme:
case ClanScheme.FILE.value(path):
assert path == Path("/home/user/Downloads") # type: ignore
case _:
assert False
def test_remote_with_all_params() -> None: def test_remote_with_all_params() -> None:
# Create a ClanURI object from a remote URI with parameters # Create a ClanURI object from a remote URI with parameters
uri = ClanURI("clan://https://example.com?flake_attr=myVM&password=1234") uri = ClanURI("clan://https://example.com?flake_attr=myVM&password=1234")
assert uri.params.flake_attr == "myVM" assert uri.params.flake_attr == "myVM"
match uri.scheme: match uri.scheme:
case ClanScheme.HTTPS.value(url): case ClanScheme.HTTP.value(url):
assert url == "https://example.com?password=1234" # type: ignore assert url == "https://example.com?password=1234" # type: ignore
case _: case _:
assert False assert False

View File

@@ -1,23 +1,7 @@
import argparse import argparse
from .app import Application from .windows.join import register_join_parser
from .windows.overview import register_overview_parser, show_overview
def join_command(args: argparse.Namespace) -> None:
print("Joining the flake")
print(args.clan_uri)
app = Application()
return app.run()
def register_join_parser(parser: argparse.ArgumentParser) -> None:
parser.add_argument("clan_uri", type=str, help="clan URI to join")
parser.set_defaults(func=join_command)
def start_app(args: argparse.Namespace) -> None:
app = Application()
return app.run()
def main() -> None: def main() -> None:
@@ -31,7 +15,9 @@ def main() -> None:
) )
register_join_parser(subparser.add_parser("join", help="join a clan")) register_join_parser(subparser.add_parser("join", help="join a clan"))
register_overview_parser(subparser.add_parser("overview", help="overview screen"))
# Executed when no command is given # Executed when no command is given
parser.set_defaults(func=start_app) parser.set_defaults(func=show_overview)
args = parser.parse_args() args = parser.parse_args()
args.func(args) args.func(args)

View File

@@ -1,110 +1,31 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import sys
from typing import Any
import gi import gi
from .models import VMBase
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import Gio, Gtk from gi.repository import Gio, Gtk
from .constants import constants from .constants import constants
from .ui.clan_join_page import ClanJoinPage
from .ui.clan_select_list import ClanEdit, ClanList
class MainWindow(Gtk.ApplicationWindow):
def __init__(self, application: Gtk.Application) -> None:
super().__init__(application=application)
# Initialize the main window
self.set_title("cLAN Manager")
self.connect("delete-event", self.on_quit)
self.set_default_size(800, 600)
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6, expand=True)
self.add(vbox)
# Add a notebook layout
# https://python-gtk-3-tutorial.readthedocs.io/en/latest/layout.html#notebook
self.notebook = Gtk.Notebook()
self.stack = Gtk.Stack()
# self.stack_switcher = Gtk.StackSwitcher()
self.list_hooks = {
"remount_list": self.remount_list_view,
"remount_edit": self.remount_edit_view,
"set_selected": self.set_selected,
}
clan_list = ClanList(**self.list_hooks, selected_vm=None) # type: ignore
# Add named stacks
self.stack.add_titled(clan_list, "list", "List")
self.stack.add_titled(
ClanJoinPage(stack=self.remount_list_view), "join", "Join"
)
self.stack.add_titled(
ClanEdit(remount_list=self.remount_list_view, selected_vm=None),
"edit",
"Edit",
)
vbox.add(self.stack)
# Must be called AFTER all components were added
self.show_all()
def set_selected(self, sel: VMBase | None) -> None:
self.selected_vm = sel
if self.selected_vm:
print(f"APP selected + {self.selected_vm.name}")
def remount_list_view(self) -> None:
widget = self.stack.get_child_by_name("list")
print("Remounting ClanListView")
if widget:
widget.destroy()
clan_list = ClanList(**self.list_hooks, selected_vm=self.selected_vm) # type: ignore
self.stack.add_titled(clan_list, "list", "List")
self.show_all()
self.stack.set_visible_child_name("list")
def remount_edit_view(self) -> None:
print("Remounting ClanEdit")
widget = self.stack.get_child_by_name("edit")
if widget:
widget.destroy()
self.stack.add_titled(
ClanEdit(remount_list=self.remount_list_view, selected_vm=self.selected_vm),
"edit",
"Edit",
)
self.show_all()
self.stack.set_visible_child_name("edit")
def on_quit(self, *args: Any) -> None:
Gio.Application.quit(self.get_application())
class Application(Gtk.Application): class Application(Gtk.Application):
def __init__(self) -> None: def __init__(self, window: Gtk.ApplicationWindow) -> None:
super().__init__( super().__init__(
application_id=constants["APPID"], flags=Gio.ApplicationFlags.FLAGS_NONE application_id=constants["APPID"], flags=Gio.ApplicationFlags.FLAGS_NONE
) )
self.init_style() self.init_style()
self.window = window
def do_startup(self) -> None: def do_startup(self) -> None:
Gtk.Application.do_startup(self) Gtk.Application.do_startup(self)
Gtk.init(sys.argv) Gtk.init()
def do_activate(self) -> None: def do_activate(self) -> None:
win = self.props.active_window win = self.props.active_window
if not win: if not win:
# win = SwitchTreeView(application=self) win = self.window
win = MainWindow(application=self) win.set_application(self)
win.present() win.present()
# TODO: For css styling # TODO: For css styling

View File

@@ -1,6 +1,5 @@
from collections import OrderedDict from collections import OrderedDict
from dataclasses import dataclass from dataclasses import dataclass
from enum import Enum
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
@@ -13,22 +12,12 @@ from gi.repository import GdkPixbuf
from clan_vm_manager import assets from clan_vm_manager import assets
class Status(Enum):
OFF = "Off"
RUNNING = "Running"
# SUSPENDED = "Suspended"
# UNKNOWN = "Unknown"
def __str__(self) -> str:
return self.value
@dataclass(frozen=True) @dataclass(frozen=True)
class VMBase: class VMBase:
icon: Path | GdkPixbuf.Pixbuf icon: Path | GdkPixbuf.Pixbuf
name: str name: str
url: str url: str
status: Status status: bool
_path: Path _path: Path
@staticmethod @staticmethod
@@ -38,7 +27,7 @@ class VMBase:
"Icon": GdkPixbuf.Pixbuf, "Icon": GdkPixbuf.Pixbuf,
"Name": str, "Name": str,
"URL": str, "URL": str,
"Status": str, "Online": bool,
"_Path": str, "_Path": str,
} }
) )
@@ -53,7 +42,7 @@ class VMBase:
"Icon": str(self.icon), "Icon": str(self.icon),
"Name": self.name, "Name": self.name,
"URL": self.url, "URL": self.url,
"Status": str(self.status), "Online": self.status,
"_Path": str(self._path), "_Path": str(self._path),
} }
) )
@@ -92,7 +81,7 @@ def get_initial_vms(start: int = 0, end: int | None = None) -> list[VM]:
name="Cybernet Clan", name="Cybernet Clan",
url="clan://cybernet.lol", url="clan://cybernet.lol",
_path=Path(__file__).parent.parent / "test_democlan", _path=Path(__file__).parent.parent / "test_democlan",
status=Status.RUNNING, status=False,
), ),
), ),
VM( VM(
@@ -101,7 +90,7 @@ def get_initial_vms(start: int = 0, end: int | None = None) -> list[VM]:
name="Zenith Clan", name="Zenith Clan",
url="clan://zenith.lol", url="clan://zenith.lol",
_path=Path(__file__).parent.parent / "test_democlan", _path=Path(__file__).parent.parent / "test_democlan",
status=Status.OFF, status=False,
) )
), ),
VM( VM(
@@ -110,7 +99,7 @@ def get_initial_vms(start: int = 0, end: int | None = None) -> list[VM]:
name="Firestorm Clan", name="Firestorm Clan",
url="clan://firestorm.lol", url="clan://firestorm.lol",
_path=Path(__file__).parent.parent / "test_democlan", _path=Path(__file__).parent.parent / "test_democlan",
status=Status.OFF, status=False,
), ),
), ),
VM( VM(
@@ -119,7 +108,7 @@ def get_initial_vms(start: int = 0, end: int | None = None) -> list[VM]:
name="Placeholder Clan", name="Placeholder Clan",
url="clan://demo.lol", url="clan://demo.lol",
_path=Path(__file__).parent.parent / "test_democlan", _path=Path(__file__).parent.parent / "test_democlan",
status=Status.OFF, status=True,
), ),
), ),
] ]
@@ -132,7 +121,7 @@ def get_initial_vms(start: int = 0, end: int | None = None) -> list[VM]:
"name": "Demo Clan", "name": "Demo Clan",
"url": "clan://demo.lol", "url": "clan://demo.lol",
"_path": entry.path, "_path": entry.path,
"status": Status.OFF, "status": False,
} }
vms.append(VM(base=VMBase(**new_vm))) vms.append(VM(base=VMBase(**new_vm)))

View File

@@ -74,9 +74,9 @@ class ClanList(Gtk.Box):
Is the composition of Is the composition of
the ClanListToolbar the ClanListToolbar
the clanListView the clanListView
# ------------------------# # ------------------------ #
# - Tools <Join> < Edit> # # - Tools <Start> <Stop> < Edit> #
# ------------------------# # ------------------------ #
# - List Items # - List Items
# - <...> # - <...>
# ------------------------# # ------------------------#
@@ -89,12 +89,14 @@ class ClanList(Gtk.Box):
remount_edit: Callable[[], None], remount_edit: Callable[[], None],
set_selected: Callable[[VMBase | None], None], set_selected: Callable[[VMBase | None], None],
selected_vm: VMBase | None, selected_vm: VMBase | None,
show_toolbar: bool = True,
) -> None: ) -> None:
super().__init__(orientation=Gtk.Orientation.VERTICAL, expand=True) super().__init__(orientation=Gtk.Orientation.VERTICAL, expand=True)
self.remount_edit_view = remount_edit self.remount_edit_view = remount_edit
self.remount_list_view = remount_list self.remount_list_view = remount_list
self.set_selected = set_selected self.set_selected = set_selected
self.show_toolbar = show_toolbar
# TODO: We should use somekind of useState hook here. # TODO: We should use somekind of useState hook here.
# that updates the list of VMs when the user changes something # that updates the list of VMs when the user changes something
@@ -109,9 +111,10 @@ class ClanList(Gtk.Box):
"on_stop_clicked": self.on_stop_clicked, "on_stop_clicked": self.on_stop_clicked,
"on_edit_clicked": self.on_edit_clicked, "on_edit_clicked": self.on_edit_clicked,
} }
self.toolbar = ClanListToolbar(**button_hooks) if show_toolbar:
self.toolbar.set_is_selected(self.selected_vm is not None) self.toolbar = ClanListToolbar(**button_hooks)
self.add(self.toolbar) self.toolbar.set_is_selected(self.selected_vm is not None)
self.add(self.toolbar)
self.list_hooks = { self.list_hooks = {
"on_select_row": self.on_select_vm, "on_select_row": self.on_select_vm,
@@ -134,10 +137,11 @@ class ClanList(Gtk.Box):
def on_select_vm(self, vm: VMBase) -> None: def on_select_vm(self, vm: VMBase) -> None:
print(f"on_select_vm: {vm.name}") print(f"on_select_vm: {vm.name}")
if vm is None: if self.show_toolbar:
self.toolbar.set_is_selected(False) if vm is None:
else: self.toolbar.set_is_selected(False)
self.toolbar.set_is_selected(True) else:
self.toolbar.set_is_selected(True)
self.set_selected(vm) self.set_selected(vm)
self.selected_vm = vm self.selected_vm = vm
@@ -153,10 +157,14 @@ class ClanListToolbar(Gtk.Toolbar):
) -> None: ) -> None:
super().__init__(orientation=Gtk.Orientation.HORIZONTAL) super().__init__(orientation=Gtk.Orientation.HORIZONTAL)
self.start_button = Gtk.ToolButton(label="Join") self.start_button = Gtk.ToolButton(label="Start")
self.start_button.connect("clicked", on_start_clicked) self.start_button.connect("clicked", on_start_clicked)
self.add(self.start_button) self.add(self.start_button)
self.stop_button = Gtk.ToolButton(label="Stop")
self.stop_button.connect("clicked", on_stop_clicked)
self.add(self.stop_button)
self.edit_button = Gtk.ToolButton(label="Edit") self.edit_button = Gtk.ToolButton(label="Edit")
self.edit_button.connect("clicked", on_edit_clicked) self.edit_button.connect("clicked", on_edit_clicked)
self.add(self.edit_button) self.add(self.edit_button)
@@ -165,9 +173,11 @@ class ClanListToolbar(Gtk.Toolbar):
if s: if s:
self.edit_button.set_sensitive(True) self.edit_button.set_sensitive(True)
self.start_button.set_sensitive(True) self.start_button.set_sensitive(True)
self.stop_button.set_sensitive(True)
else: else:
self.edit_button.set_sensitive(False) self.edit_button.set_sensitive(False)
self.start_button.set_sensitive(False) self.start_button.set_sensitive(False)
self.stop_button.set_sensitive(False)
class ClanEditToolbar(Gtk.Toolbar): class ClanEditToolbar(Gtk.Toolbar):
@@ -224,7 +234,6 @@ class ClanListView(Gtk.Box):
return return
selection = self.tree_view.get_selection() selection = self.tree_view.get_selection()
idx = self.find_vm(vm) idx = self.find_vm(vm)
print(f"Set selected vm: {vm.name} at {idx}")
selection.select_path(idx) selection.select_path(idx)
def insertVM(self, vm: VMBase) -> None: def insertVM(self, vm: VMBase) -> None:
@@ -239,7 +248,6 @@ class ClanListView(Gtk.Box):
model, row = selection.get_selected() model, row = selection.get_selected()
if row is not None: if row is not None:
vm = VMBase(*model[row]) vm = VMBase(*model[row])
print(f"Selected {vm.name}")
self.on_select_row(vm) self.on_select_row(vm)
def _on_double_click( def _on_double_click(
@@ -259,13 +267,18 @@ def setColRenderers(tree_view: Gtk.TreeView) -> None:
if key.startswith("_"): if key.startswith("_"):
continue continue
match gtype:
case GdkPixbuf.Pixbuf: if issubclass(gtype, GdkPixbuf.Pixbuf):
renderer = Gtk.CellRendererPixbuf() renderer = Gtk.CellRendererPixbuf()
col = Gtk.TreeViewColumn(key, renderer, pixbuf=idx) col = Gtk.TreeViewColumn(key, renderer, pixbuf=idx)
case str: # noqa elif issubclass(gtype, bool):
renderer = Gtk.CellRendererText() renderer = Gtk.CellRendererToggle()
col = Gtk.TreeViewColumn(key, renderer, text=idx) col = Gtk.TreeViewColumn(key, renderer, active=idx)
elif issubclass(gtype, str):
renderer = Gtk.CellRendererText()
col = Gtk.TreeViewColumn(key, renderer, text=idx)
else:
raise Exception(f"Unknown type: {gtype}")
# CommonSetup for all columns # CommonSetup for all columns
if col: if col:

View File

@@ -0,0 +1,48 @@
import argparse
from typing import Any
import gi
from clan_cli.clan_uri import ClanURI
from ..app import Application
gi.require_version("Gtk", "3.0")
from gi.repository import Gio, Gtk
class JoinWindow(Gtk.ApplicationWindow):
def __init__(self) -> None:
super().__init__()
# Initialize the main window
self.set_title("cLAN Manager")
self.connect("delete-event", self.on_quit)
self.set_default_size(800, 600)
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6, expand=True)
self.add(vbox)
# Add a notebook layout
# https://python-gtk-3-tutorial.readthedocs.io/en/latest/layout.html#notebook
self.notebook = Gtk.Notebook()
self.stack = Gtk.Stack()
self.stack.add_titled(Gtk.Label("Join cLan"), "join", "Join")
vbox.add(self.stack)
# Must be called AFTER all components were added
self.show_all()
def on_quit(self, *args: Any) -> None:
Gio.Application.quit(self.get_application())
def show_join(args: argparse.Namespace) -> None:
print(f"Joining clan {args.clan_uri}")
app = Application(JoinWindow())
return app.run()
def register_join_parser(parser: argparse.ArgumentParser) -> None:
parser.add_argument("clan_uri", type=ClanURI, help="clan URI to join")
parser.set_defaults(func=show_join)

View File

@@ -0,0 +1,97 @@
from typing import Any
import gi
from ..models import VMBase
gi.require_version("Gtk", "3.0")
import argparse
from gi.repository import Gio, Gtk
from ..app import Application
from ..ui.clan_join_page import ClanJoinPage
from ..ui.clan_select_list import ClanEdit, ClanList
class OverviewWindow(Gtk.ApplicationWindow):
def __init__(self) -> None:
super().__init__()
# Initialize the main window
self.set_title("cLAN Manager")
self.connect("delete-event", self.on_quit)
self.set_default_size(800, 600)
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6, expand=True)
self.add(vbox)
# Add a notebook layout
# https://python-gtk-3-tutorial.readthedocs.io/en/latest/layout.html#notebook
self.notebook = Gtk.Notebook()
self.stack = Gtk.Stack()
# self.stack_switcher = Gtk.StackSwitcher()
self.list_hooks = {
"remount_list": self.remount_list_view,
"remount_edit": self.remount_edit_view,
"set_selected": self.set_selected,
}
clan_list = ClanList(**self.list_hooks, selected_vm=None) # type: ignore
# Add named stacks
self.stack.add_titled(clan_list, "list", "List")
self.stack.add_titled(
ClanJoinPage(stack=self.remount_list_view), "join", "Join"
)
self.stack.add_titled(
ClanEdit(remount_list=self.remount_list_view, selected_vm=None),
"edit",
"Edit",
)
vbox.add(self.stack)
# Must be called AFTER all components were added
self.show_all()
def set_selected(self, sel: VMBase | None) -> None:
self.selected_vm = sel
if self.selected_vm:
print(f"APP selected + {self.selected_vm.name}")
def remount_list_view(self) -> None:
widget = self.stack.get_child_by_name("list")
print("Remounting ClanListView")
if widget:
widget.destroy()
clan_list = ClanList(**self.list_hooks, selected_vm=self.selected_vm) # type: ignore
self.stack.add_titled(clan_list, "list", "List")
self.show_all()
self.stack.set_visible_child_name("list")
def remount_edit_view(self) -> None:
print("Remounting ClanEdit")
widget = self.stack.get_child_by_name("edit")
if widget:
widget.destroy()
self.stack.add_titled(
ClanEdit(remount_list=self.remount_list_view, selected_vm=self.selected_vm),
"edit",
"Edit",
)
self.show_all()
self.stack.set_visible_child_name("edit")
def on_quit(self, *args: Any) -> None:
Gio.Application.quit(self.get_application())
def show_overview(args: argparse.Namespace) -> None:
app = Application(OverviewWindow())
return app.run()
def register_overview_parser(parser: argparse.ArgumentParser) -> None:
parser.set_defaults(func=show_overview)