170 lines
5.9 KiB
Python
170 lines
5.9 KiB
Python
import logging
|
|
import math
|
|
import time
|
|
import tkinter as tk
|
|
from tkinter import font, ttk
|
|
from tkinter.ttk import Progressbar
|
|
|
|
import grpc
|
|
|
|
from core.gui import appconfig, themes
|
|
from core.gui.coreclient import CoreClient
|
|
from core.gui.dialogs.error import ErrorDialog
|
|
from core.gui.graph.graph import CanvasGraph
|
|
from core.gui.images import ImageEnum, Images
|
|
from core.gui.menubar import Menubar
|
|
from core.gui.nodeutils import NodeUtils
|
|
from core.gui.statusbar import StatusBar
|
|
from core.gui.task import ProgressTask
|
|
from core.gui.toolbar import Toolbar
|
|
from core.gui.validation import InputValidation
|
|
|
|
WIDTH = 1000
|
|
HEIGHT = 800
|
|
|
|
|
|
class Application(ttk.Frame):
|
|
def __init__(self, proxy: bool):
|
|
super().__init__(master=None)
|
|
# load node icons
|
|
NodeUtils.setup()
|
|
|
|
# widgets
|
|
self.menubar = None
|
|
self.toolbar = None
|
|
self.right_frame = None
|
|
self.canvas = None
|
|
self.statusbar = None
|
|
self.validation = None
|
|
self.progress = None
|
|
self.time = None
|
|
|
|
# fonts
|
|
self.fonts_size = None
|
|
self.icon_text_font = None
|
|
self.edge_font = None
|
|
|
|
# setup
|
|
self.guiconfig = appconfig.read()
|
|
self.app_scale = self.guiconfig["scale"]
|
|
self.setup_scaling()
|
|
self.style = ttk.Style()
|
|
self.setup_theme()
|
|
self.core = CoreClient(self, proxy)
|
|
self.setup_app()
|
|
self.draw()
|
|
self.core.setup()
|
|
|
|
def setup_scaling(self):
|
|
self.fonts_size = {name: font.nametofont(name)["size"] for name in font.names()}
|
|
text_scale = self.app_scale if self.app_scale < 1 else math.sqrt(self.app_scale)
|
|
themes.scale_fonts(self.fonts_size, self.app_scale)
|
|
self.icon_text_font = font.Font(family="TkIconFont", size=int(12 * text_scale))
|
|
self.edge_font = font.Font(
|
|
family="TkDefaultFont", size=int(8 * text_scale), weight=font.BOLD
|
|
)
|
|
|
|
def setup_theme(self):
|
|
themes.load(self.style)
|
|
self.master.bind_class("Menu", "<<ThemeChanged>>", themes.theme_change_menu)
|
|
self.master.bind("<<ThemeChanged>>", themes.theme_change)
|
|
self.style.theme_use(self.guiconfig["preferences"]["theme"])
|
|
|
|
def setup_app(self):
|
|
self.master.title("CORE")
|
|
self.center()
|
|
self.master.protocol("WM_DELETE_WINDOW", self.on_closing)
|
|
image = Images.get(ImageEnum.CORE, 16)
|
|
self.master.tk.call("wm", "iconphoto", self.master._w, image)
|
|
self.validation = InputValidation(self)
|
|
self.master.option_add("*tearOff", tk.FALSE)
|
|
|
|
def center(self):
|
|
screen_width = self.master.winfo_screenwidth()
|
|
screen_height = self.master.winfo_screenheight()
|
|
x = int((screen_width / 2) - (WIDTH * self.app_scale / 2))
|
|
y = int((screen_height / 2) - (HEIGHT * self.app_scale / 2))
|
|
self.master.geometry(
|
|
f"{int(WIDTH * self.app_scale)}x{int(HEIGHT * self.app_scale)}+{x}+{y}"
|
|
)
|
|
|
|
def draw(self):
|
|
self.master.rowconfigure(0, weight=1)
|
|
self.master.columnconfigure(0, weight=1)
|
|
self.rowconfigure(0, weight=1)
|
|
self.columnconfigure(1, weight=1)
|
|
self.grid(sticky="nsew")
|
|
self.toolbar = Toolbar(self, self)
|
|
self.toolbar.grid(sticky="ns")
|
|
self.right_frame = ttk.Frame(self)
|
|
self.right_frame.columnconfigure(0, weight=1)
|
|
self.right_frame.rowconfigure(0, weight=1)
|
|
self.right_frame.grid(row=0, column=1, sticky="nsew")
|
|
self.draw_canvas()
|
|
self.draw_status()
|
|
self.progress = Progressbar(self.right_frame, mode="indeterminate")
|
|
self.menubar = Menubar(self.master, self)
|
|
|
|
def draw_canvas(self):
|
|
width = self.guiconfig["preferences"]["width"]
|
|
height = self.guiconfig["preferences"]["height"]
|
|
canvas_frame = ttk.Frame(self.right_frame)
|
|
canvas_frame.rowconfigure(0, weight=1)
|
|
canvas_frame.columnconfigure(0, weight=1)
|
|
canvas_frame.grid(sticky="nsew", pady=1)
|
|
self.canvas = CanvasGraph(canvas_frame, self, self.core, width, height)
|
|
self.canvas.grid(sticky="nsew")
|
|
scroll_y = ttk.Scrollbar(canvas_frame, command=self.canvas.yview)
|
|
scroll_y.grid(row=0, column=1, sticky="ns")
|
|
scroll_x = ttk.Scrollbar(
|
|
canvas_frame, orient=tk.HORIZONTAL, command=self.canvas.xview
|
|
)
|
|
scroll_x.grid(row=1, column=0, sticky="ew")
|
|
self.canvas.configure(xscrollcommand=scroll_x.set)
|
|
self.canvas.configure(yscrollcommand=scroll_y.set)
|
|
|
|
def draw_status(self):
|
|
self.statusbar = StatusBar(self.right_frame, self)
|
|
self.statusbar.grid(sticky="ew")
|
|
|
|
def progress_task(self, task: ProgressTask) -> None:
|
|
self.progress.grid(sticky="ew")
|
|
self.progress.start()
|
|
self.time = time.perf_counter()
|
|
task.app = self
|
|
task.start()
|
|
|
|
def progress_task_complete(self) -> None:
|
|
self.progress.stop()
|
|
self.progress.grid_forget()
|
|
total = time.perf_counter() - self.time
|
|
self.time = None
|
|
message = f"Task ran for {total:.3f} seconds"
|
|
self.statusbar.set_status(message)
|
|
|
|
def show_grpc_exception(self, title: str, e: grpc.RpcError) -> None:
|
|
logging.exception("app grpc exception", exc_info=e)
|
|
message = e.details()
|
|
self.show_error(title, message)
|
|
|
|
def show_exception(self, title: str, e: Exception) -> None:
|
|
logging.exception("app exception", exc_info=e)
|
|
self.show_error(title, str(e))
|
|
|
|
def show_error(self, title: str, message: str) -> None:
|
|
self.after(0, lambda: ErrorDialog(self, self, title, message).show())
|
|
|
|
def on_closing(self):
|
|
self.menubar.prompt_save_running_session(True)
|
|
|
|
def save_config(self):
|
|
appconfig.save(self.guiconfig)
|
|
|
|
def joined_session_update(self):
|
|
if self.core.is_runtime():
|
|
self.toolbar.set_runtime()
|
|
else:
|
|
self.toolbar.set_design()
|
|
|
|
def close(self):
|
|
self.master.destroy()
|