Unverified Commit 9ac2f901 authored by Gregory Cage's avatar Gregory Cage Committed by GitHub
Browse files

Merge pull request #109 from nova-model/89-close-button

Add exit button to themed app
parents 339ca990 08671cdd
Loading
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
### nova-trame, 0.22.1

* Themed app now has an Exit Button by default which closes the application and can stop any running jobs (thanks to Gregory Cage).

### nova-trame, 0.22.0

* DataSelector queries subdirectories on demand, which should improve performance for large directory trees (thanks to John Duggan).
+873 −693

File changed.

Preview size limit exceeded, changes collapsed.

+2 −2
Original line number Diff line number Diff line
@@ -6,7 +6,7 @@ Changelog = "https://code.ornl.gov/ndip/public-packages/nova-trame/blob/main/CHA

[tool.poetry]
name = "nova-trame"
version = "0.22.0"
version = "0.22.1"
description = "A Python Package for injecting curated themes and custom components into Trame applications"
authors = ["Duggan, John <dugganjw@ornl.gov>"]
readme = "README.md"
@@ -29,7 +29,7 @@ trame-vega = "*"
trame-vuetify = "*"
nova-mvvm = "*"
pydantic = "*"
nova-common = ">=0.2.0"
nova-common = ">=0.2.2"
blinker = "^1.9.0"
natsort = "^8.4.0"

+73 −0
Original line number Diff line number Diff line
"""Components used to control the lifecycle of a Themed Application."""

import logging
from typing import Any

from trame.app import get_server
from trame.widgets import vuetify3 as vuetify

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)


class ExitButton:
    """Exit button for Trame Applications."""

    def __init__(self, exit_callback: Any = None, job_status_callback: Any = None) -> None:
        self.server = get_server(None, client_type="vue3")
        self.server.state.nova_kill_jobs_on_exit = False
        self.server.state.nova_show_exit_dialog = False
        self.server.state.nova_show_stop_jobs_on_exit_checkbox = False
        self.server.state.nova_running_jobs = []
        self.server.state.nova_show_exit_progress = False
        self.exit_application_callback = exit_callback
        self.job_status_callback = job_status_callback
        self.create_ui()

    def create_ui(self) -> None:
        with vuetify.VBtn(
            "Exit",
            prepend_icon="mdi-close-box",
            classes="mr-4 bg-error",
            id="shutdown_app_theme_button",
            color="white",
            click=self.open_exit_dialog,
        ):
            with vuetify.VDialog(v_model="nova_show_exit_dialog", persistent="true"):
                with vuetify.VCard(classes="pa-4 ma-auto"):
                    vuetify.VCardTitle("Exit Application")
                    with vuetify.VCardText(
                        "Are you sure you want to exit this application?",
                        variant="outlined",
                    ):
                        vuetify.VCheckbox(
                            v_model="nova_kill_jobs_on_exit",
                            label="Stop All Jobs On Exit.",
                            v_if="nova_running_jobs.length > 0",
                        )
                        with vuetify.VList():
                            vuetify.VListSubheader("Running Jobs:", v_if="nova_running_jobs.length > 0")
                            vuetify.VListItem("{{ item }}", v_for="(item, index) in nova_running_jobs")
                    with vuetify.VCardActions(v_if="!nova_show_exit_progress"):
                        vuetify.VBtn(
                            "Exit App",
                            click=self.exit_application_callback,
                            color="error",
                        )
                        vuetify.VBtn(
                            "Stay In App",
                            click=self.close_exit_dialog,
                        )
                    with vuetify.VCardActions(v_else=True):
                        vuetify.VCardText(
                            "Exiting Application...",
                            variant="outlined",
                        )
                        vuetify.VProgressCircular(indeterminate=True)

    async def open_exit_dialog(self) -> None:
        self.server.state.nova_show_exit_dialog = True
        await self.job_status_callback()

    async def close_exit_dialog(self) -> None:
        self.server.state.nova_show_exit_dialog = False
+29 −0
Original line number Diff line number Diff line
"""Implementation of ThemedApp."""

import asyncio
import json
import logging
import sys
from asyncio import create_task
from functools import partial
from pathlib import Path
from typing import Optional

import blinker
import sass
from mergedeep import Strategy, merge
from trame.app import get_server
@@ -18,7 +21,9 @@ from trame_client.widgets import html
from trame_server.core import Server
from trame_server.state import State

from nova.common.signals import Signal
from nova.mvvm.pydantic_utils import validate_pydantic_parameter
from nova.trame.view.theme.exit_button import ExitButton
from nova.trame.view.utilities.local_storage import LocalStorageManager

THEME_PATH = Path(__file__).parent
@@ -138,6 +143,29 @@ class ThemedApp:
    async def init_theme(self) -> None:
        create_task(self._init_theme())

    async def get_jobs_callback(self) -> None:
        get_tools_signal = blinker.signal(Signal.GET_ALL_TOOLS)
        response = get_tools_signal.send()
        if response and len(response[0]) > 1:  # Make sure that the callback had a return value
            try:
                self.server.state.nova_running_jobs = [tool.id for tool in response[0][1]]
                if len(self.server.state.nova_running_jobs) > 0:
                    self.server.state.nova_show_stop_jobs_on_exit_checkbox = True
                    self.server.state.nova_kill_jobs_on_exit = True
                else:
                    self.server.state.nova_show_stop_jobs_on_exit_checkbox = False
            except Exception as e:
                logger.warning(f"Issue getting running jobs: {e}")

    async def exit_callback(self) -> None:
        logger.info(f"Closing App. Killing jobs: {self.server.state.nova_kill_jobs_on_exit}")
        if self.server.state.nova_kill_jobs_on_exit:
            self.server.state.nova_show_exit_progress = True
            await asyncio.sleep(2)
            stop_signal = blinker.signal(Signal.EXIT_SIGNAL)
            stop_signal.send()
        sys.exit(0)

    def set_theme(self, theme: Optional[str], force: bool = True) -> None:
        """Sets the theme of the application.

@@ -227,6 +255,7 @@ class ThemedApp:
                                                "Selected",
                                                v_if=f"nova__theme === '{theme['value']}'",
                                            )
                            ExitButton(self.exit_callback, self.get_jobs_callback)

                    with vuetify.VMain(classes="align-stretch d-flex flex-column h-screen"):
                        # [slot override example]