From 6df833d59baeb30db65890710bb514852ac4e2de Mon Sep 17 00:00:00 2001 From: Qubasa Date: Sat, 25 Nov 2023 01:55:01 +0100 Subject: [PATCH] Added basic GTK window handling --- .gitignore | 1 + pkgs/clan-vm-manager/README.md | 61 ++++++++++ .../clan-vm-manager/clan_vm_manager/app.glade | 22 +++- pkgs/clan-vm-manager/clan_vm_manager/app.py | 112 ++++++++++++++++-- .../clan_vm_manager/second.glade | 44 +++++++ pkgs/clan-vm-manager/pyproject.toml | 2 +- 6 files changed, 225 insertions(+), 17 deletions(-) create mode 100644 pkgs/clan-vm-manager/README.md create mode 100644 pkgs/clan-vm-manager/clan_vm_manager/second.glade diff --git a/.gitignore b/.gitignore index e7f41543..222f9bef 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ result* /pkgs/clan-cli/clan_cli/webui/assets /machines nixos.qcow2 +**/*.glade~ # python __pycache__ diff --git a/pkgs/clan-vm-manager/README.md b/pkgs/clan-vm-manager/README.md new file mode 100644 index 00000000..f20a57b7 --- /dev/null +++ b/pkgs/clan-vm-manager/README.md @@ -0,0 +1,61 @@ +## Developing GTK3 Applications + +Here we will document on how to develop GTK3 application UI in python. First we want to setup +an example code base to look into. In this case gnome-music. + +## Setup gnome-music as code reference + +gnome-music does not use glade + +Clone gnome-music and check out the tag v40.0 +[gnome-music](https://github.com/GNOME/gnome-music/tree/40.0) + +```bash +git clone git@github.com:GNOME/gnome-music.git && cd gnome-music && git checkout 40.0 +``` + +Checkout nixpkgs version `468cb5980b56d348979488a74a9b5de638400160` for the correct gnome-music devshell then execute: + +```bash + +nix develop /home/username/Projects/nixpkgs#gnome.gnome-music +``` + +Look into the file `gnome-music.in` which bootstraps the application. + +## Setup gnu-cash as reference + +Gnucash uses glade with complex UI +Setup gnucash + +```bash +git clone git@github.com:Gnucash/gnucash.git +git checkout ed4921271c863c7f6e0c800e206b25ac6e9ba4da + +cd nixpkgs +git checkout 015739d7bffa7da4e923978040a2f7cba6af3270 +nix develop /home/username/Projects/nixpkgs#gnucash +mkdir build && cd build +cmake .. +cd .. +make +``` + +### Glade + +Make sure to check the 'composit' box in glade in the GtkApplicationWindow to be able to +import the glade file through GTK template + +## Links + +- Another python glade project [syncthing-gtk](https://github.com/kozec/syncthing-gtk) + +- Other python glade project [linuxcnc](https://github.com/podarok/linuxcnc/tree/master) + +- Install [Glade UI Toolbuilder](https://gitlab.gnome.org/GNOME/glade) + +- To understand GTK3 Components look into the [Python GTK3 Tutorial](https://python-gtk-3-tutorial.readthedocs.io/en/latest/search.html?q=ApplicationWindow&check_keywords=yes&area=default) + +- Also look into [PyGObject](https://pygobject.readthedocs.io/en/latest/guide/gtk_template.html) to know more about threading and async etc. +- [GI Python API](https://lazka.github.io/pgi-docs/#Gtk-4.0/classes/ApplicationWindow.html#Gtk.ApplicationWindow) +- https://developer.gnome.org/documentation/tutorials/application.html diff --git a/pkgs/clan-vm-manager/clan_vm_manager/app.glade b/pkgs/clan-vm-manager/clan_vm_manager/app.glade index 65a645d0..e42a9b31 100644 --- a/pkgs/clan-vm-manager/clan_vm_manager/app.glade +++ b/pkgs/clan-vm-manager/clan_vm_manager/app.glade @@ -2,8 +2,9 @@ - + diff --git a/pkgs/clan-vm-manager/clan_vm_manager/app.py b/pkgs/clan-vm-manager/clan_vm_manager/app.py index 0122b9df..9b6f76db 100644 --- a/pkgs/clan-vm-manager/clan_vm_manager/app.py +++ b/pkgs/clan-vm-manager/clan_vm_manager/app.py @@ -1,19 +1,105 @@ -# !/usr/bin/env python3 +#!/usr/bin/env python3 -import argparse # noqa -from pathlib import Path # noqa +import argparse +from pathlib import Path -import gi # noqa +import gi -gi.require_version("Gtk", "3.0") # noqa -from gi.repository import Gtk # noqa +gi.require_version("Gtk", "3.0") +from gi.repository import Gio, Gtk + +glade_dir = Path(__file__).parent + + +# +# 1. our .glade file (may contain paths) +# +@Gtk.Template.from_file(glade_dir / "app.glade") +class AppWindow(Gtk.ApplicationWindow): + # + # 2. the GtkApplicationWindow class + # + __gtype_name__ = "main-window" + + # + # 3. the Button name we saved above + # + help_button: Gtk.Button = Gtk.Template.Child() + next_button: Gtk.Button = Gtk.Template.Child() + + @Gtk.Template.Callback() + def onDestroy(self, _): + Gio.Application.quit(self.get_application()) + + # + # 4. the signal handler name we saved above + # + @Gtk.Template.Callback() + def onButtonPressed(self, widget, **_kwargs): + assert self.help_button == widget + print(widget.get_label()) + + @Gtk.Template.Callback() + def onNextButtonPressed(self, widget, **_kwargs): + assert self.next_button == widget + # Hide the first window + self.hide() + + # Show the second window + self.get_application().window2.show_all() + + +# Decorate the second window class with the template +@Gtk.Template.from_file(glade_dir / "second.glade") +class SecondWindow(Gtk.ApplicationWindow): + # + # the GtkApplicationWindow class + # + __gtype_name__ = "second-window" + + # import the button from the template with name 'back_button' + back_button: Gtk.Button = Gtk.Template.Child() + + @Gtk.Template.Callback() + def onDestroy(self, _): + Gio.Application.quit(self.get_application()) + + # + # 'onBackButtonPressed' is the name of the signal handler we saved in glade + # + @Gtk.Template.Callback() + def onBackButtonPressed(self, widget, **_kwargs): + assert self.back_button == widget + # Hide the second window + self.hide() + # Show the first window + self.get_application().window1.show_all() + + +class Application(Gtk.Application): + def __init__(self, *args, **kwargs): + super().__init__( + *args, + application_id="clan.lol.Gtk1", + flags=Gio.ApplicationFlags.FLAGS_NONE, + **kwargs, + ) + self.window = None + + def do_activate(self): + # Load the first window from the template + self.window1 = AppWindow(application=self) + # Add the first window to the application + self.add_window(self.window1) + # Show the first window + self.window1.show_all() + + # Load the second window from the template + self.window2 = SecondWindow(application=self) + # Add the second window to the application + self.add_window(self.window2) def start_app(args: argparse.Namespace) -> None: - builder = Gtk.Builder() - glade_file = Path(__file__).parent / "app.glade" - builder.add_from_file(str(glade_file)) - window = builder.get_object("main-window") - window.show_all() - - Gtk.main() + app = Application() + app.run() diff --git a/pkgs/clan-vm-manager/clan_vm_manager/second.glade b/pkgs/clan-vm-manager/clan_vm_manager/second.glade new file mode 100644 index 00000000..d4f556fd --- /dev/null +++ b/pkgs/clan-vm-manager/clan_vm_manager/second.glade @@ -0,0 +1,44 @@ + + + + + + diff --git a/pkgs/clan-vm-manager/pyproject.toml b/pkgs/clan-vm-manager/pyproject.toml index 0ec43fce..77276d4a 100644 --- a/pkgs/clan-vm-manager/pyproject.toml +++ b/pkgs/clan-vm-manager/pyproject.toml @@ -25,4 +25,4 @@ ignore_missing_imports = true line-length = 88 select = ["E", "F", "I", "N"] -ignore = ["E501", "E402"] +ignore = ["E501", "E402", "N802"]