Fully working clan select
This commit is contained in:
@@ -71,6 +71,8 @@ import the glade file through GTK template
|
|||||||
- [GI Python API](https://lazka.github.io/pgi-docs/#Gtk-3.0)
|
- [GI Python API](https://lazka.github.io/pgi-docs/#Gtk-3.0)
|
||||||
- https://developer.gnome.org/documentation/tutorials/application.html
|
- https://developer.gnome.org/documentation/tutorials/application.html
|
||||||
- [GTK3 Python] https://github.com/sam-m888/python-gtk3-tutorial/tree/master
|
- [GTK3 Python] https://github.com/sam-m888/python-gtk3-tutorial/tree/master
|
||||||
|
- https://gnome.pages.gitlab.gnome.org/libhandy/doc/1.8/index.html
|
||||||
|
- https://github.com/geigi/cozy
|
||||||
|
|
||||||
## My gripes with GTK
|
## My gripes with GTK
|
||||||
|
|
||||||
|
|||||||
@@ -1,68 +1,203 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import sys
|
||||||
|
|
||||||
import gi
|
import gi
|
||||||
|
|
||||||
gi.require_version("Gtk", "3.0")
|
gi.require_version("Gtk", "3.0")
|
||||||
from gi.repository import Gtk
|
gi.require_version("Gdk", "3.0")
|
||||||
|
from gi.repository import Gio, Gtk
|
||||||
|
|
||||||
|
from typing import Any, List
|
||||||
|
from pathlib import Path
|
||||||
|
from .constants import constants
|
||||||
|
|
||||||
|
class VM():
|
||||||
|
def __init__(self, url:str, autostart: bool, path: Path):
|
||||||
|
self.url = url
|
||||||
|
self.autostart = autostart
|
||||||
|
self.path = path
|
||||||
|
|
||||||
|
|
||||||
vms = [
|
vms = [
|
||||||
("clan://clan.lol", True, "/home/user/my-clan"),
|
VM("clan://clan.lol", True, "/home/user/my-clan"),
|
||||||
("clan://lassul.lol", False, "/home/user/my-clan"),
|
VM("clan://lassul.lol", False, "/home/user/my-clan"),
|
||||||
("clan://mic.lol", False, "/home/user/my-clan"),
|
VM("clan://mic.lol", False, "/home/user/my-clan"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
import gi
|
||||||
|
gi.require_version('Gtk', '3.0')
|
||||||
|
from gi.repository import Gtk
|
||||||
|
|
||||||
class MainWindow(Gtk.Window):
|
class SwitchTreeView(Gtk.ApplicationWindow):
|
||||||
def __init__(self) -> None:
|
def __init__(self, application):
|
||||||
|
super().__init__(application=application, title="Switch TreeView Example")
|
||||||
|
self.set_default_size(300, 200)
|
||||||
|
|
||||||
|
# Create a list store with two strings and a boolean
|
||||||
|
self.liststore = Gtk.ListStore(str, str, bool)
|
||||||
|
# Add some sample data
|
||||||
|
self.liststore.append(["Foo", "Bar", True])
|
||||||
|
self.liststore.append(["Baz", "Qux", False])
|
||||||
|
self.liststore.append(["Quux", "Quuz", True])
|
||||||
|
|
||||||
|
# Create a tree view with the list store as the model
|
||||||
|
self.treeview = Gtk.TreeView(model=self.liststore)
|
||||||
|
self.add(self.treeview)
|
||||||
|
|
||||||
|
# Create a cell renderer for text
|
||||||
|
renderer_text = Gtk.CellRendererText()
|
||||||
|
|
||||||
|
# Create a tree view column for the first string
|
||||||
|
column_text1 = Gtk.TreeViewColumn("Text 1", renderer_text, text=0)
|
||||||
|
# Add the column to the tree view
|
||||||
|
self.treeview.append_column(column_text1)
|
||||||
|
|
||||||
|
# Create another tree view column for the second string
|
||||||
|
column_text2 = Gtk.TreeViewColumn("Text 2", renderer_text, text=1)
|
||||||
|
# Add the column to the tree view
|
||||||
|
self.treeview.append_column(column_text2)
|
||||||
|
|
||||||
|
# Create a cell renderer for toggle
|
||||||
|
renderer_toggle = Gtk.CellRendererToggle()
|
||||||
|
# Set the cell renderer to be activatable
|
||||||
|
renderer_toggle.set_property("activatable", True)
|
||||||
|
# Connect a signal handler to the toggled signal
|
||||||
|
renderer_toggle.connect("toggled", self.on_cell_toggled)
|
||||||
|
|
||||||
|
# Create a tree view column for the switch
|
||||||
|
column_toggle = Gtk.TreeViewColumn("Switch", renderer_toggle, active=2)
|
||||||
|
# Add the column to the tree view
|
||||||
|
self.treeview.append_column(column_toggle)
|
||||||
|
self.show_all()
|
||||||
|
|
||||||
|
def on_cell_toggled(self, widget, path):
|
||||||
|
# Get the current value from the model
|
||||||
|
current_value = self.liststore[path][2]
|
||||||
|
# Toggle the value
|
||||||
|
self.liststore[path][2] = not current_value
|
||||||
|
# Print the updated value
|
||||||
|
print("Switched", path, "to", self.liststore[path][2])
|
||||||
|
|
||||||
|
|
||||||
|
class ClanSelectList(Gtk.Box):
|
||||||
|
def __init__(self, vms):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
self.vms = vms
|
||||||
|
|
||||||
|
self.list_store = Gtk.ListStore(str, bool, str)
|
||||||
|
for vm in vms:
|
||||||
|
items = list(vm.__dict__.values())
|
||||||
|
print(f"Table: {items}")
|
||||||
|
self.list_store.append(items)
|
||||||
|
|
||||||
|
self.tree_view = Gtk.TreeView(self.list_store)
|
||||||
|
for (idx, (key, value)) in enumerate(vm.__dict__.items()):
|
||||||
|
if isinstance(value, str):
|
||||||
|
renderer = Gtk.CellRendererText()
|
||||||
|
col = Gtk.TreeViewColumn(key.capitalize(), renderer, text=idx)
|
||||||
|
col.set_sort_column_id(idx)
|
||||||
|
self.tree_view.append_column(col)
|
||||||
|
if isinstance(value, bool):
|
||||||
|
renderer = Gtk.CellRendererToggle()
|
||||||
|
renderer.set_property("activatable", True)
|
||||||
|
renderer.connect("toggled", self.on_cell_toggled)
|
||||||
|
col = Gtk.TreeViewColumn(key.capitalize(), renderer, active=idx)
|
||||||
|
col.set_sort_column_id(idx)
|
||||||
|
self.tree_view.append_column(col)
|
||||||
|
|
||||||
|
selection = self.tree_view.get_selection()
|
||||||
|
selection.connect("changed", self.on_select_row)
|
||||||
|
|
||||||
|
|
||||||
|
self.set_border_width(10)
|
||||||
|
self.add(self.tree_view)
|
||||||
|
|
||||||
|
def on_cell_toggled(self, widget, path):
|
||||||
|
print(f"on_cell_toggled: {path}")
|
||||||
|
# Get the current value from the model
|
||||||
|
current_value = self.list_store[path][1]
|
||||||
|
|
||||||
|
print(f"current_value: {current_value}")
|
||||||
|
# Toggle the value
|
||||||
|
self.list_store[path][1] = not current_value
|
||||||
|
# Print the updated value
|
||||||
|
print("Switched", path, "to", self.list_store[path][1])
|
||||||
|
|
||||||
|
|
||||||
|
def on_select_row(self, selection):
|
||||||
|
model, row = selection.get_selected()
|
||||||
|
if row is not None:
|
||||||
|
print(f"Selected {model[row][0]}")
|
||||||
|
|
||||||
|
class ClanJoinPage(Gtk.Box):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.page = Gtk.Box()
|
||||||
|
self.set_border_width(10)
|
||||||
|
self.add(Gtk.Label(label="Add/Join another clan"))
|
||||||
|
|
||||||
|
class MainWindow(Gtk.ApplicationWindow):
|
||||||
|
def __init__(self, application: Gtk.Application) -> None:
|
||||||
|
super().__init__(application=application)
|
||||||
# Initialize the main window
|
# Initialize the main window
|
||||||
self.set_title("Clan VM Manager")
|
self.set_title("Clan VM Manager")
|
||||||
self.connect("delete-event", Gtk.main_quit)
|
self.connect("delete-event", self.on_quit)
|
||||||
|
|
||||||
|
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
|
||||||
|
self.add(vbox)
|
||||||
|
|
||||||
# Some styling
|
|
||||||
self.set_border_width(10)
|
self.set_border_width(10)
|
||||||
# self.set_default_size(500,300)
|
|
||||||
|
|
||||||
# Add a notebook layout
|
# Add a notebook layout
|
||||||
# https://python-gtk-3-tutorial.readthedocs.io/en/latest/layout.html#notebook
|
# https://python-gtk-3-tutorial.readthedocs.io/en/latest/layout.html#notebook
|
||||||
self.notebook = Gtk.Notebook()
|
self.notebook = Gtk.Notebook()
|
||||||
self.add(self.notebook)
|
vbox.add(self.notebook)
|
||||||
|
|
||||||
vms_store = Gtk.ListStore(str, bool, str)
|
self.notebook.append_page(ClanSelectList(vms), Gtk.Label(label="Overview"))
|
||||||
for vm in vms:
|
self.notebook.append_page(ClanJoinPage(), Gtk.Label(label="Add/Join"))
|
||||||
vms_store.append(list(vm))
|
|
||||||
|
|
||||||
self.machine_tree_view = Gtk.TreeView(vms_store)
|
|
||||||
for idx, title in enumerate(["Url", "Autostart", "Path"]):
|
|
||||||
renderer = Gtk.CellRendererText()
|
|
||||||
col = Gtk.TreeViewColumn(title, renderer, text=idx)
|
|
||||||
col.set_sort_column_id(idx)
|
|
||||||
self.machine_tree_view.append_column(col)
|
|
||||||
|
|
||||||
selection = self.machine_tree_view.get_selection()
|
|
||||||
selection.connect("changed", self.on_select_row)
|
|
||||||
|
|
||||||
self.machine_page = Gtk.Box()
|
|
||||||
self.machine_page.set_border_width(10)
|
|
||||||
self.machine_page.add(self.machine_tree_view)
|
|
||||||
self.notebook.append_page(self.machine_page, Gtk.Label(label="Overview"))
|
|
||||||
|
|
||||||
self.join_page = Gtk.Box()
|
|
||||||
self.join_page.set_border_width(10)
|
|
||||||
self.join_page.add(Gtk.Label(label="Add/Join another clan"))
|
|
||||||
self.notebook.append_page(self.join_page, Gtk.Label(label="Add/Join"))
|
|
||||||
|
|
||||||
# Must be called AFTER all components were added
|
# Must be called AFTER all components were added
|
||||||
self.show_all()
|
self.show_all()
|
||||||
|
|
||||||
|
def on_quit(self, *args):
|
||||||
|
Gio.Application.quit(self.get_application())
|
||||||
|
|
||||||
def on_select_row(self, selection):
|
def on_select_row(self, selection):
|
||||||
model, row = selection.get_selected()
|
model, row = selection.get_selected()
|
||||||
if row is not None:
|
if row is not None:
|
||||||
print(f"Selected {model[row][0]}")
|
print(f"Selected {model[row][0]}")
|
||||||
|
|
||||||
|
|
||||||
|
class Application(Gtk.Application):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(
|
||||||
|
application_id=constants["APPID"], flags=Gio.ApplicationFlags.FLAGS_NONE
|
||||||
|
)
|
||||||
|
self.init_style()
|
||||||
|
|
||||||
|
def do_startup(self):
|
||||||
|
Gtk.Application.do_startup(self)
|
||||||
|
Gtk.init(sys.argv)
|
||||||
|
|
||||||
|
def do_activate(self):
|
||||||
|
win = self.props.active_window
|
||||||
|
if not win:
|
||||||
|
#win = SwitchTreeView(application=self)
|
||||||
|
win = MainWindow(application=self)
|
||||||
|
win.present()
|
||||||
|
|
||||||
|
# TODO: For css styling
|
||||||
|
def init_style(self):
|
||||||
|
pass
|
||||||
|
# css_provider = Gtk.CssProvider()
|
||||||
|
# css_provider.load_from_resource(constants['RESOURCEID'] + '/style.css')
|
||||||
|
# screen = Gdk.Screen.get_default()
|
||||||
|
# style_context = Gtk.StyleContext()
|
||||||
|
# style_context.add_provider_for_screen(screen, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
|
||||||
|
|
||||||
|
|
||||||
def start_app(args: argparse.Namespace) -> None:
|
def start_app(args: argparse.Namespace) -> None:
|
||||||
MainWindow()
|
app = Application()
|
||||||
Gtk.main()
|
return app.run(sys.argv)
|
||||||
|
|||||||
7
pkgs/clan-vm-manager/clan_vm_manager/constants.py
Normal file
7
pkgs/clan-vm-manager/clan_vm_manager/constants.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
constants = {
|
||||||
|
"APPID": "clan.lol",
|
||||||
|
"APPVERSION": "2.0.0-beta",
|
||||||
|
"APPNAME": "clan-vm-manager",
|
||||||
|
"SYSNAME": "clan-manager",
|
||||||
|
"RESOURCEID": "/git.clan.lol/clan/clan-core",
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
, gobject-introspection
|
, gobject-introspection
|
||||||
, clan-cli
|
, clan-cli
|
||||||
, makeDesktopItem
|
, makeDesktopItem
|
||||||
|
, ipdb
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
source = ./.;
|
source = ./.;
|
||||||
@@ -33,7 +34,7 @@ python3.pkgs.buildPythonApplication {
|
|||||||
];
|
];
|
||||||
|
|
||||||
buildInputs = [ spice-gtk gtk3 gnome.adwaita-icon-theme ];
|
buildInputs = [ spice-gtk gtk3 gnome.adwaita-icon-theme ];
|
||||||
propagatedBuildInputs = [ pygobject3 clan-cli ];
|
propagatedBuildInputs = [ ipdb pygobject3 clan-cli ];
|
||||||
|
|
||||||
# also re-expose dependencies so we test them in CI
|
# also re-expose dependencies so we test them in CI
|
||||||
passthru.tests = {
|
passthru.tests = {
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ mkShell {
|
|||||||
shellHook = ''
|
shellHook = ''
|
||||||
ln -sfT ${clan-cli.nixpkgs} ../clan-cli/clan_cli/nixpkgs
|
ln -sfT ${clan-cli.nixpkgs} ../clan-cli/clan_cli/nixpkgs
|
||||||
|
|
||||||
|
export PYTHONBREAKPOINT=ipdb.set_trace
|
||||||
|
|
||||||
# prepend clan-cli for development
|
# prepend clan-cli for development
|
||||||
export PYTHONPATH=../clan-cli:$PYTHONPATH
|
export PYTHONPATH=../clan-cli:$PYTHONPATH
|
||||||
'';
|
'';
|
||||||
|
|||||||
Reference in New Issue
Block a user