Added gtk demo app

This commit is contained in:
Qubasa
2023-11-21 18:13:30 +01:00
committed by Jörg Thalheim
parent 17c71d2f40
commit 832c41df7e
12 changed files with 164 additions and 15 deletions

View File

@@ -78,8 +78,7 @@ Let's get your development environment up and running:
``` ```
- Wait for the frontend to build. - Wait for the frontend to build.
NOTE: If you have the error "@clan/colors.json" you executed `npm install`, please do not do that. `direnv reload` will handle dependency management. Please delete node_modules with `rm -rf node_modules`. NOTE: If you have the error "@clan/colors.json" you executed `npm install`, please do not do that. `direnv reload` will handle dependency management. Please delete node_modules with `rm -rf node_modules`.
9. **Start the Frontend**: 9. **Start the Frontend**:
- To start the frontend, execute: - To start the frontend, execute:

View File

@@ -5,7 +5,7 @@ from pathlib import Path
from types import ModuleType from types import ModuleType
from typing import Any, Optional, Sequence from typing import Any, Optional, Sequence
from . import config, flakes, machines, secrets, vms, webui from . import config, flakes, gtkui, machines, secrets, vms, webui
from .custom_logger import setup_logging from .custom_logger import setup_logging
from .dirs import get_clan_flake_toplevel from .dirs import get_clan_flake_toplevel
from .ssh import cli as ssh_cli from .ssh import cli as ssh_cli
@@ -64,6 +64,9 @@ def create_parser(prog: Optional[str] = None) -> argparse.ArgumentParser:
subparsers = parser.add_subparsers() subparsers = parser.add_subparsers()
parser_gtkui = subparsers.add_parser("gtkui", help="start gtkui")
gtkui.register_parser(parser_gtkui)
parser_flake = subparsers.add_parser( parser_flake = subparsers.add_parser(
"flakes", help="create a clan flake inside the current directory" "flakes", help="create a clan flake inside the current directory"
) )

View File

@@ -1,9 +1,8 @@
import asyncio import asyncio
import logging import logging
import deal
import shlex import shlex
from pathlib import Path from pathlib import Path
from typing import Any, Callable, Coroutine, Dict, NamedTuple, Optional, Awaitable from typing import Any, Callable, Coroutine, Dict, NamedTuple, Optional
from .custom_logger import get_caller from .custom_logger import get_caller
from .errors import ClanError from .errors import ClanError
@@ -16,8 +15,8 @@ class CmdOut(NamedTuple):
stderr: str stderr: str
cwd: Optional[Path] = None cwd: Optional[Path] = None
@deal.raises(ClanError)
async def run(cmd: list[str], cwd: Optional[Path] = None) -> Awaitable[CmdOut]: async def run(cmd: list[str], cwd: Optional[Path] = None) -> CmdOut:
cwd_res = None cwd_res = None
if cwd is not None: if cwd is not None:
if not cwd.exists(): if not cwd.exists():
@@ -52,7 +51,6 @@ stdout:
return CmdOut(stdout.decode("utf-8"), stderr.decode("utf-8"), cwd=cwd) return CmdOut(stdout.decode("utf-8"), stderr.decode("utf-8"), cwd=cwd)
@deal.raises(ClanError)
def runforcli( def runforcli(
func: Callable[..., Coroutine[Any, Any, Dict[str, CmdOut]]], *args: Any func: Callable[..., Coroutine[Any, Any, Dict[str, CmdOut]]], *args: Any
) -> None: ) -> None:

View File

@@ -0,0 +1,10 @@
import argparse
from typing import Callable, Optional
start_app: Optional[Callable] = None
from .app import start_app
def register_parser(parser: argparse.ArgumentParser) -> None:
parser.set_defaults(func=start_app)

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.40.0 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<object class="GtkApplicationWindow" id="main-window">
<property name="can-focus">False</property>
<child>
<object class="GtkFixed">
<property name="name">asdasd</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkButton" id="help_button">
<property name="label" translatable="yes">May I help you?</property>
<property name="name">asdasd</property>
<property name="width-request">100</property>
<property name="height-request">80</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="image-position">top</property>
<signal name="clicked" handler="on_help_button_clicked" object="coffee_label" swapped="no"/>
</object>
<packing>
<property name="x">21</property>
<property name="y">21</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="coffee_label">
<property name="width-request">100</property>
<property name="height-request">80</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Get me some coffe! </property>
<property name="width-chars">0</property>
</object>
<packing>
<property name="x">178</property>
<property name="y">120</property>
</packing>
</child>
<style>
<class name="asdasd"/>
</style>
</object>
</child>
</object>
</interface>

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.40.0 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<object class="GtkApplicationWindow" id="main-window">
<property name="can-focus">False</property>
<child>
<object class="GtkFixed">
<property name="name">asdasd</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkButton" id="help_button">
<property name="label" translatable="yes">May I help you?</property>
<property name="name">asdasd</property>
<property name="width-request">100</property>
<property name="height-request">80</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="image-position">top</property>
<signal name="clicked" handler="on_help_button_clicked" object="coffee_label" swapped="no"/>
</object>
<packing>
<property name="x">21</property>
<property name="y">21</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="coffee_label">
<property name="width-request">100</property>
<property name="height-request">80</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Get me some coffe! </property>
<property name="width-chars">0</property>
</object>
<packing>
<property name="x">178</property>
<property name="y">120</property>
</packing>
</child>
<style>
<class name="asdasd"/>
</style>
</object>
</child>
</object>
</interface>

View File

@@ -0,0 +1,19 @@
# !/usr/bin/env python3
import argparse # noqa
from pathlib import Path # noqa
import gi # noqa
gi.require_version("Gtk", "3.0") # noqa
from gi.repository import Gtk # noqa
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()

View File

@@ -2,11 +2,14 @@ import json
import os import os
import subprocess import subprocess
import tempfile import tempfile
import deal
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
import deal
from .dirs import nixpkgs_flake, nixpkgs_source from .dirs import nixpkgs_flake, nixpkgs_source
from .errors import ClanError
@deal.raises(ClanError) @deal.raises(ClanError)
def nix_command(flags: list[str]) -> list[str]: def nix_command(flags: list[str]) -> list[str]:
@@ -25,6 +28,7 @@ def nix_flake_show(flake_url: str | Path) -> list[str]:
] ]
) )
@deal.raises(ClanError) @deal.raises(ClanError)
def nix_build( def nix_build(
flags: list[str], flags: list[str],
@@ -41,6 +45,7 @@ def nix_build(
+ flags + flags
) )
@deal.raises(ClanError) @deal.raises(ClanError)
def nix_config() -> dict[str, Any]: def nix_config() -> dict[str, Any]:
cmd = nix_command(["show-config", "--json"]) cmd = nix_command(["show-config", "--json"])
@@ -51,6 +56,7 @@ def nix_config() -> dict[str, Any]:
config[key] = value["value"] config[key] = value["value"]
return config return config
@deal.raises(ClanError) @deal.raises(ClanError)
def nix_eval(flags: list[str]) -> list[str]: def nix_eval(flags: list[str]) -> list[str]:
default_flags = nix_command( default_flags = nix_command(
@@ -78,6 +84,7 @@ def nix_eval(flags: list[str]) -> list[str]:
) )
return default_flags + flags return default_flags + flags
@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

@@ -186,6 +186,7 @@ def get_task(uuid: UUID) -> BaseTask:
T = TypeVar("T", bound="BaseTask") T = TypeVar("T", bound="BaseTask")
@deal.raises(ClanError) @deal.raises(ClanError)
def create_task(task_type: Type[T], *args: Any) -> T: def create_task(task_type: Type[T], *args: Any) -> T:
global POOL global POOL

View File

@@ -36,12 +36,17 @@
, deal , deal
, rope , rope
, clan-core-path , clan-core-path
, schemathesis , schemathesis ? null
}: }:
let let
dependencies = [ dependencies = [
argcomplete # optional dependency: if not enabled, shell completion will not work argcomplete # optional dependency: if not enabled, shell completion will not work
wrapGAppsHook
gtk3
glib
gobject-introspection
pygobject3
]; ];
pytestDependencies = runtimeDependencies ++ dependencies ++ [ pytestDependencies = runtimeDependencies ++ dependencies ++ [

View File

@@ -38,6 +38,11 @@ exclude = "clan_cli.nixpkgs"
module = "argcomplete.*" module = "argcomplete.*"
ignore_missing_imports = true ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "gi.*"
ignore_missing_imports = true
[[tool.mypy.overrides]] [[tool.mypy.overrides]]
module = "jsonschema.*" module = "jsonschema.*"
ignore_missing_imports = true ignore_missing_imports = true
@@ -58,4 +63,4 @@ ignore_missing_imports = true
line-length = 88 line-length = 88
select = [ "E", "F", "I", "N"] select = [ "E", "F", "I", "N"]
ignore = [ "E501" ] ignore = [ "E501", "E402" ]

View File

@@ -1,13 +1,13 @@
import deal import deal
from clan_cli import task_manager from clan_cli import nix, task_manager
from clan_cli import async_cmd
from clan_cli import nix
@deal.cases(task_manager.get_task) @deal.cases(task_manager.get_task)
def test_get_task(case: deal.TestCase) -> None: def test_get_task(case: deal.TestCase) -> None:
case() case()
@deal.cases(task_manager.create_task) @deal.cases(task_manager.create_task)
def test_create_task(case: deal.TestCase) -> None: def test_create_task(case: deal.TestCase) -> None:
case() case()
@@ -17,18 +17,22 @@ def test_create_task(case: deal.TestCase) -> None:
def test_nix_command(case: deal.TestCase) -> None: def test_nix_command(case: deal.TestCase) -> None:
case() case()
@deal.cases(nix.nix_build) @deal.cases(nix.nix_build)
def test_nix_build(case: deal.TestCase) -> None: def test_nix_build(case: deal.TestCase) -> None:
case() case()
@deal.cases(nix.nix_config) @deal.cases(nix.nix_config)
def test_nix_config(case: deal.TestCase) -> None: def test_nix_config(case: deal.TestCase) -> None:
case() case()
@deal.cases(nix.nix_eval) @deal.cases(nix.nix_eval)
def test_nix_eval(case: deal.TestCase) -> None: def test_nix_eval(case: deal.TestCase) -> None:
case() case()
@deal.cases(nix.nix_shell) @deal.cases(nix.nix_shell)
def test_nix_shell(case: deal.TestCase) -> None: def test_nix_shell(case: deal.TestCase) -> None:
case() case()