From b163b06596bb17b1e19c7eee6fd311e6cd16f7b3 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Fri, 19 Feb 2021 10:35:59 -0800 Subject: [PATCH] pygui: added simple error dialog call to app, also added blocking option for some error dialogs when needed --- daemon/core/gui/app.py | 29 +++++++++++++++++++++-------- daemon/core/gui/coreclient.py | 11 +++++------ daemon/core/gui/dialogs/error.py | 14 ++++++++------ daemon/core/gui/toolbar.py | 4 +++- 4 files changed, 37 insertions(+), 21 deletions(-) diff --git a/daemon/core/gui/app.py b/daemon/core/gui/app.py index d7754b90..eddb60d3 100644 --- a/daemon/core/gui/app.py +++ b/daemon/core/gui/app.py @@ -1,7 +1,7 @@ import logging import math import tkinter as tk -from tkinter import PhotoImage, font, ttk +from tkinter import PhotoImage, font, messagebox, ttk from tkinter.ttk import Progressbar from typing import Any, Dict, Optional, Type @@ -168,17 +168,30 @@ class Application(ttk.Frame): def hide_info(self) -> None: self.infobar.grid_forget() - def show_grpc_exception(self, title: str, e: grpc.RpcError) -> None: + def show_grpc_exception( + self, message: str, e: grpc.RpcError, blocking: bool = False + ) -> None: logging.exception("app grpc exception", exc_info=e) - message = e.details() - self.show_error(title, message) + dialog = ErrorDialog(self, "GRPC Exception", message, e.details()) + if blocking: + dialog.show() + else: + self.after(0, lambda: dialog.show()) - def show_exception(self, title: str, e: Exception) -> None: + def show_exception(self, message: str, e: Exception) -> None: logging.exception("app exception", exc_info=e) - self.show_error(title, str(e)) + self.after( + 0, lambda: ErrorDialog(self, "App Exception", message, str(e)).show() + ) - def show_error(self, title: str, message: str) -> None: - self.after(0, lambda: ErrorDialog(self, title, message).show()) + def show_exception_data(self, title: str, message: str, details: str) -> None: + self.after(0, lambda: ErrorDialog(self, title, message, details).show()) + + def show_error(self, title: str, message: str, blocking: bool = False) -> None: + if blocking: + messagebox.showerror(title, message, parent=self) + else: + self.after(0, lambda: messagebox.showerror(title, message, parent=self)) def on_closing(self) -> None: if self.toolbar.picker: diff --git a/daemon/core/gui/coreclient.py b/daemon/core/gui/coreclient.py index c3f48751..d2f0cb87 100644 --- a/daemon/core/gui/coreclient.py +++ b/daemon/core/gui/coreclient.py @@ -41,7 +41,6 @@ from core.api.grpc.wrappers import ( from core.gui import nodeutils as nutils from core.gui.appconfig import XMLS_PATH, CoreServer, Observer from core.gui.dialogs.emaneinstall import EmaneInstallDialog -from core.gui.dialogs.error import ErrorDialog from core.gui.dialogs.mobilityplayer import MobilityPlayer from core.gui.dialogs.sessions import SessionsDialog from core.gui.graph.edges import CanvasEdge @@ -418,10 +417,11 @@ class CoreClient: if session_id: session_ids = set(x.id for x in sessions) if session_id not in session_ids: - dialog = ErrorDialog( - self.app, "Join Session Error", f"{session_id} does not exist" + self.app.show_error( + "Join Session Error", + f"{session_id} does not exist", + blocking=True, ) - dialog.show() self.app.close() else: self.join_session(session_id) @@ -433,8 +433,7 @@ class CoreClient: dialog.show() except grpc.RpcError as e: logging.exception("core setup error") - dialog = ErrorDialog(self.app, "Setup Error", e.details()) - dialog.show() + self.app.show_grpc_exception("Setup Error", e, blocking=True) self.app.close() def edit_node(self, core_node: Node) -> None: diff --git a/daemon/core/gui/dialogs/error.py b/daemon/core/gui/dialogs/error.py index 731564ec..726f8617 100644 --- a/daemon/core/gui/dialogs/error.py +++ b/daemon/core/gui/dialogs/error.py @@ -13,9 +13,11 @@ if TYPE_CHECKING: class ErrorDialog(Dialog): - def __init__(self, app: "Application", title: str, details: str) -> None: - super().__init__(app, "CORE Exception") - self.title: str = title + def __init__( + self, app: "Application", title: str, message: str, details: str + ) -> None: + super().__init__(app, title) + self.message: str = message self.details: str = details self.error_message: Optional[CodeText] = None self.draw() @@ -25,13 +27,13 @@ class ErrorDialog(Dialog): self.top.rowconfigure(1, weight=1) image = images.from_enum(ImageEnum.ERROR, width=images.ERROR_SIZE) label = ttk.Label( - self.top, text=self.title, image=image, compound=tk.LEFT, anchor=tk.CENTER + self.top, text=self.message, image=image, compound=tk.LEFT, anchor=tk.CENTER ) label.image = image - label.grid(sticky=tk.EW, pady=PADY) + label.grid(sticky=tk.W, pady=PADY) self.error_message = CodeText(self.top) self.error_message.text.insert("1.0", self.details) self.error_message.text.config(state=tk.DISABLED) - self.error_message.grid(sticky=tk.NSEW, pady=PADY) + self.error_message.grid(sticky=tk.EW, pady=PADY) button = ttk.Button(self.top, text="Close", command=lambda: self.destroy()) button.grid(sticky=tk.EW) diff --git a/daemon/core/gui/toolbar.py b/daemon/core/gui/toolbar.py index 1e3e69a5..844a68f2 100644 --- a/daemon/core/gui/toolbar.py +++ b/daemon/core/gui/toolbar.py @@ -310,7 +310,9 @@ class Toolbar(ttk.Frame): enable_buttons(self.design_frame, enabled=True) if exceptions: message = "\n".join(exceptions) - self.app.show_error("Start Session Error", message) + self.app.show_exception_data( + "Start Exception", "Session failed to start", message + ) def set_runtime(self) -> None: enable_buttons(self.runtime_frame, enabled=True)