From 8734b9f22f578c9887d5dec2e581f8632d63a116 Mon Sep 17 00:00:00 2001 From: Huy Pham <42948410+hpham@users.noreply.github.com> Date: Mon, 10 Feb 2020 15:20:07 -0800 Subject: [PATCH 01/13] attempt adding scaling function to the gui --- daemon/core/gui/dialogs/preferences.py | 39 ++++++++ daemon/core/gui/graph/graph.py | 3 + daemon/core/gui/statusbar.py | 2 +- daemon/core/gui/toolbar.py | 133 +++++++++++++++++++++---- 4 files changed, 159 insertions(+), 18 deletions(-) diff --git a/daemon/core/gui/dialogs/preferences.py b/daemon/core/gui/dialogs/preferences.py index f60da652..9c3da76a 100644 --- a/daemon/core/gui/dialogs/preferences.py +++ b/daemon/core/gui/dialogs/preferences.py @@ -10,10 +10,14 @@ from core.gui.themes import FRAME_PAD, PADX, PADY if TYPE_CHECKING: from core.gui.app import Application +WIDTH = 1000 +HEIGHT = 800 + class PreferencesDialog(Dialog): def __init__(self, master: "Application", app: "Application"): super().__init__(master, app, "Preferences", modal=True) + self.gui_scale = tk.DoubleVar(value=self.app.canvas.app_scale) preferences = self.app.guiconfig["preferences"] self.editor = tk.StringVar(value=preferences["editor"]) self.theme = tk.StringVar(value=preferences["theme"]) @@ -64,6 +68,27 @@ class PreferencesDialog(Dialog): entry = ttk.Entry(frame, textvariable=self.gui3d) entry.grid(row=3, column=1, sticky="ew") + label = ttk.Label(frame, text="Scaling") + label.grid(row=4, column=0, pady=PADY, padx=PADX, sticky="w") + + scale_frame = ttk.Frame(frame) + scale_frame.grid(row=4, column=1, sticky="ew") + scale_frame.columnconfigure(0, weight=1) + scale = ttk.Scale( + scale_frame, + from_=0.5, + to=5, + value=1, + orient=tk.HORIZONTAL, + variable=self.gui_scale, + command=self.scale_adjust, + ) + scale.grid(row=0, column=0, sticky="ew") + entry = ttk.Entry( + scale_frame, textvariable=self.gui_scale, width=4, state="disabled" + ) + entry.grid(row=0, column=1) + def draw_buttons(self): frame = ttk.Frame(self.top) frame.grid(sticky="ew") @@ -89,3 +114,17 @@ class PreferencesDialog(Dialog): preferences["theme"] = self.theme.get() self.app.save_config() self.destroy() + + def scale_adjust(self, scale: str): + self.gui_scale.set(round(self.gui_scale.get(), 2)) + app_scale = self.gui_scale.get() + self.app.canvas.app_scale = app_scale + screen_width = self.app.master.winfo_screenwidth() + screen_height = self.app.master.winfo_screenheight() + scaled_width = WIDTH * app_scale + scaled_height = HEIGHT * app_scale + x = int(screen_width / 2 - scaled_width / 2) + y = int(screen_height / 2 - scaled_height / 2) + self.app.master.geometry(f"{int(scaled_width)}x{int(scaled_height)}+{x}+{y}") + + self.app.toolbar.scale(app_scale) diff --git a/daemon/core/gui/graph/graph.py b/daemon/core/gui/graph/graph.py index a56f1423..da394503 100644 --- a/daemon/core/gui/graph/graph.py +++ b/daemon/core/gui/graph/graph.py @@ -53,6 +53,9 @@ class CanvasGraph(tk.Canvas): self.marker_tool = None self.to_copy = [] + # app's scale, different scale values to support higher resolution display + self.app_scale = 1.0 + # background related self.wallpaper_id = None self.wallpaper = None diff --git a/daemon/core/gui/statusbar.py b/daemon/core/gui/statusbar.py index 6de511c4..7524e318 100644 --- a/daemon/core/gui/statusbar.py +++ b/daemon/core/gui/statusbar.py @@ -29,7 +29,7 @@ class StatusBar(ttk.Frame): def draw(self): self.columnconfigure(0, weight=1) - self.columnconfigure(1, weight=7) + self.columnconfigure(1, weight=5) self.columnconfigure(2, weight=1) self.columnconfigure(3, weight=1) self.columnconfigure(4, weight=1) diff --git a/daemon/core/gui/toolbar.py b/daemon/core/gui/toolbar.py index 063c2e5b..2b40cee7 100644 --- a/daemon/core/gui/toolbar.py +++ b/daemon/core/gui/toolbar.py @@ -1,6 +1,7 @@ import logging import time import tkinter as tk +from enum import Enum from functools import partial from tkinter import messagebox, ttk from tkinter.font import Font @@ -25,6 +26,12 @@ TOOLBAR_SIZE = 32 PICKER_SIZE = 24 +class NodeTypeEnum(Enum): + NODE = 0 + NETWORK = 1 + OTHER = 2 + + def icon(image_enum, width=TOOLBAR_SIZE): return Images.get(image_enum, width) @@ -47,6 +54,7 @@ class Toolbar(ttk.Frame): self.picker_font = Font(size=8) # design buttons + self.play_button = None self.select_button = None self.link_button = None self.node_button = None @@ -71,9 +79,21 @@ class Toolbar(ttk.Frame): # dialog self.marker_tool = None + # these variables help keep track of what images being drawn so that scaling is possible + # since ImageTk.PhotoImage does not have resize method + self.node_enum = None + self.network_enum = None + self.annotation_enum = None + # draw components self.draw() + def get_icon(self, image_enum, width=TOOLBAR_SIZE): + if not self.app.canvas: + return Images.get(image_enum, width) + else: + return Images.get(image_enum, int(width * self.app.canvas.app_scale)) + def draw(self): self.columnconfigure(0, weight=1) self.rowconfigure(0, weight=1) @@ -85,20 +105,26 @@ class Toolbar(ttk.Frame): self.design_frame = ttk.Frame(self) self.design_frame.grid(row=0, column=0, sticky="nsew") self.design_frame.columnconfigure(0, weight=1) - self.create_button( + self.play_button = self.create_button( self.design_frame, - icon(ImageEnum.START), + # icon(ImageEnum.START), + self.get_icon(ImageEnum.START), self.click_start, "start the session", ) self.select_button = self.create_button( self.design_frame, - icon(ImageEnum.SELECT), + # icon(ImageEnum.SELECT), + self.get_icon(ImageEnum.SELECT), self.click_selection, "selection tool", ) self.link_button = self.create_button( - self.design_frame, icon(ImageEnum.LINK), self.click_link, "link tool" + self.design_frame, + # icon(ImageEnum.LINK), + self.get_icon(ImageEnum.LINK), + self.click_link, + "link tool", ) self.create_node_button() self.create_network_button() @@ -130,18 +156,24 @@ class Toolbar(ttk.Frame): self.stop_button = self.create_button( self.runtime_frame, - icon(ImageEnum.STOP), + # icon(ImageEnum.STOP), + self.get_icon(ImageEnum.STOP), self.click_stop, "stop the session", ) self.runtime_select_button = self.create_button( self.runtime_frame, - icon(ImageEnum.SELECT), + # icon(ImageEnum.SELECT), + self.get_icon(ImageEnum.SELECT), self.click_runtime_selection, "selection tool", ) self.plot_button = self.create_button( - self.runtime_frame, icon(ImageEnum.PLOT), self.click_plot_button, "plot" + self.runtime_frame, + # icon(ImageEnum.PLOT), + self.get_icon(ImageEnum.PLOT), + self.click_plot_button, + "plot", ) self.runtime_marker_button = self.create_button( self.runtime_frame, @@ -165,22 +197,40 @@ class Toolbar(ttk.Frame): # draw default nodes for node_draw in NodeUtils.NODES: toolbar_image = icon(node_draw.image_enum) - image = icon(node_draw.image_enum, PICKER_SIZE) + # image = icon(node_draw.image_enum, PICKER_SIZE) + image = self.get_icon( + node_draw.image_enum, PICKER_SIZE * self.app.canvas.app_scale + ) func = partial( - self.update_button, self.node_button, toolbar_image, node_draw + self.update_button, + self.node_button, + toolbar_image, + node_draw, + NodeTypeEnum.NODE, + node_draw.image_enum, ) self.create_picker_button(image, func, self.node_picker, node_draw.label) # draw custom nodes for name in sorted(self.app.core.custom_nodes): node_draw = self.app.core.custom_nodes[name] toolbar_image = Images.get_custom(node_draw.image_file, TOOLBAR_SIZE) - image = Images.get_custom(node_draw.image_file, PICKER_SIZE) + image = Images.get_custom( + node_draw.image_file, int(PICKER_SIZE * self.app.canvas.app_scale) + ) func = partial( - self.update_button, self.node_button, toolbar_image, node_draw + self.update_button, + self.node_button, + toolbar_image, + node_draw, + NodeTypeEnum, + node_draw.image_file, ) self.create_picker_button(image, func, self.node_picker, name) # draw edit node - image = icon(ImageEnum.EDITNODE, PICKER_SIZE) + # image = icon(ImageEnum.EDITNODE, PICKER_SIZE) + image = self.get_icon( + ImageEnum.EDITNODE, PICKER_SIZE * self.app.canvas.app_scale + ) self.create_picker_button( image, self.click_edit_node, self.node_picker, "Custom" ) @@ -284,13 +334,24 @@ class Toolbar(ttk.Frame): dialog = CustomNodesDialog(self.app, self.app) dialog.show() - def update_button(self, button: ttk.Button, image: "ImageTk", node_draw: NodeDraw): + def update_button( + self, + button: ttk.Button, + image: "ImageTk", + node_draw: NodeDraw, + type_enum, + image_enum, + ): logging.debug("update button(%s): %s", button, node_draw) self.hide_pickers() button.configure(image=image) button.image = image self.app.canvas.mode = GraphMode.NODE self.app.canvas.node_draw = node_draw + if type_enum == NodeTypeEnum.NODE: + self.node_enum = image_enum + elif type_enum == NodeTypeEnum.NETWORK: + self.network_enum = image_enum def hide_pickers(self): logging.debug("hiding pickers") @@ -308,13 +369,14 @@ class Toolbar(ttk.Frame): """ Create network layer button """ - image = icon(ImageEnum.ROUTER) + image = icon(ImageEnum.ROUTER, TOOLBAR_SIZE) self.node_button = ttk.Button( self.design_frame, image=image, command=self.draw_node_picker ) self.node_button.image = image self.node_button.grid(sticky="ew") Tooltip(self.node_button, "Network-layer virtual nodes") + self.node_enum = ImageEnum.ROUTER def draw_network_picker(self): """ @@ -328,7 +390,12 @@ class Toolbar(ttk.Frame): self.create_picker_button( image, partial( - self.update_button, self.network_button, toolbar_image, node_draw + self.update_button, + self.network_button, + toolbar_image, + node_draw, + NodeTypeEnum.NETWORK, + node_draw.image_enum, ), self.network_picker, node_draw.label, @@ -350,6 +417,7 @@ class Toolbar(ttk.Frame): self.network_button.image = image self.network_button.grid(sticky="ew") Tooltip(self.network_button, "link-layer nodes") + self.network_enum = ImageEnum.HUB def draw_annotation_picker(self): """ @@ -368,7 +436,7 @@ class Toolbar(ttk.Frame): image = icon(image_enum, PICKER_SIZE) self.create_picker_button( image, - partial(self.update_annotation, toolbar_image, shape_type), + partial(self.update_annotation, toolbar_image, shape_type, image_enum), self.annotation_picker, shape_type.value, ) @@ -388,6 +456,7 @@ class Toolbar(ttk.Frame): self.annotation_button.image = image self.annotation_button.grid(sticky="ew") Tooltip(self.annotation_button, "background annotation tools") + self.annotation_enum = ImageEnum.MARKER def create_observe_button(self): menu_button = ttk.Menubutton( @@ -434,13 +503,16 @@ class Toolbar(ttk.Frame): if not response.result: messagebox.showerror("Stop Error", "Errors stopping session") - def update_annotation(self, image: "ImageTk.PhotoImage", shape_type: ShapeType): + def update_annotation( + self, image: "ImageTk.PhotoImage", shape_type: ShapeType, image_enum + ): logging.debug("clicked annotation: ") self.hide_pickers() self.annotation_button.configure(image=image) self.annotation_button.image = image self.app.canvas.mode = GraphMode.ANNOTATION self.app.canvas.annotation_type = shape_type + self.annotation_enum = image_enum if is_marker(shape_type): if self.marker_tool: self.marker_tool.destroy() @@ -465,3 +537,30 @@ class Toolbar(ttk.Frame): def click_two_node_button(self): logging.debug("Click TWONODE button") + + @classmethod + def scale_button(cls, button, image_enum, scale): + image = icon(image_enum, int(TOOLBAR_SIZE * scale)) + button.config(image=image) + button.image = image + + def scale(self, scale): + self.scale_button(self.play_button, ImageEnum.START, scale) + self.scale_button(self.select_button, ImageEnum.SELECT, scale) + self.scale_button(self.link_button, ImageEnum.LINK, scale) + self.scale_button(self.node_button, self.node_enum, scale) + self.scale_button(self.network_button, self.network_enum, scale) + self.scale_button(self.annotation_button, self.annotation_enum, scale) + + self.scale_button(self.runtime_select_button, ImageEnum.SELECT, scale) + self.scale_button(self.stop_button, ImageEnum.STOP, scale) + self.scale_button(self.plot_button, ImageEnum.PLOT, scale) + self.scale_button(self.runtime_marker_button, ImageEnum.MARKER, scale) + self.scale_button(self.node_command_button, ImageEnum.TWONODE, scale) + self.scale_button(self.run_command_button, ImageEnum.RUN, scale) + + # self.stop_button = None + # self.plot_button = None + # self.runtime_marker_button = None + # self.node_command_button = None + # self.run_command_button = None From 7fbbfa8c63954928b199d51ab2f5120780a1f0c9 Mon Sep 17 00:00:00 2001 From: Huy Pham <42948410+hpham@users.noreply.github.com> Date: Wed, 12 Feb 2020 08:35:14 -0800 Subject: [PATCH 02/13] scale font --- daemon/core/gui/app.py | 4 +++- daemon/core/gui/dialogs/preferences.py | 21 +++++++++++---------- daemon/core/gui/themes.py | 9 ++++++++- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/daemon/core/gui/app.py b/daemon/core/gui/app.py index e70b9f52..c3ab4b6b 100644 --- a/daemon/core/gui/app.py +++ b/daemon/core/gui/app.py @@ -1,5 +1,5 @@ import tkinter as tk -from tkinter import ttk +from tkinter import font, ttk from core.gui import appconfig, themes from core.gui.coreclient import CoreClient @@ -22,6 +22,8 @@ class Application(tk.Frame): # load node icons NodeUtils.setup() + self.fonts_size = {name: font.nametofont(name)["size"] for name in font.names()} + # widgets self.menubar = None self.toolbar = None diff --git a/daemon/core/gui/dialogs/preferences.py b/daemon/core/gui/dialogs/preferences.py index 9c3da76a..a10e7e89 100644 --- a/daemon/core/gui/dialogs/preferences.py +++ b/daemon/core/gui/dialogs/preferences.py @@ -5,7 +5,7 @@ from typing import TYPE_CHECKING from core.gui import appconfig from core.gui.dialogs.dialog import Dialog -from core.gui.themes import FRAME_PAD, PADX, PADY +from core.gui.themes import FRAME_PAD, PADX, PADY, scale_fonts if TYPE_CHECKING: from core.gui.app import Application @@ -119,12 +119,13 @@ class PreferencesDialog(Dialog): self.gui_scale.set(round(self.gui_scale.get(), 2)) app_scale = self.gui_scale.get() self.app.canvas.app_scale = app_scale - screen_width = self.app.master.winfo_screenwidth() - screen_height = self.app.master.winfo_screenheight() - scaled_width = WIDTH * app_scale - scaled_height = HEIGHT * app_scale - x = int(screen_width / 2 - scaled_width / 2) - y = int(screen_height / 2 - scaled_height / 2) - self.app.master.geometry(f"{int(scaled_width)}x{int(scaled_height)}+{x}+{y}") - - self.app.toolbar.scale(app_scale) + scale_fonts(self.app.fonts_size, app_scale) + # screen_width = self.app.master.winfo_screenwidth() + # screen_height = self.app.master.winfo_screenheight() + # scaled_width = WIDTH * app_scale + # scaled_height = HEIGHT * app_scale + # x = int(screen_width / 2 - scaled_width / 2) + # y = int(screen_height / 2 - scaled_height / 2) + # self.app.master.geometry(f"{int(scaled_width)}x{int(scaled_height)}+{x}+{y}") + # + # self.app.toolbar.scale(app_scale) diff --git a/daemon/core/gui/themes.py b/daemon/core/gui/themes.py index 26ee5379..482c86d2 100644 --- a/daemon/core/gui/themes.py +++ b/daemon/core/gui/themes.py @@ -1,5 +1,5 @@ import tkinter as tk -from tkinter import ttk +from tkinter import font, ttk THEME_DARK = "black" PADX = (0, 5) @@ -198,3 +198,10 @@ def theme_change(event: tk.Event): relief=tk.NONE, font=("TkDefaultFont", 8, "normal"), ) + + +def scale_fonts(fonts_size, scale): + for name in font.names(): + f = font.nametofont(name) + if name in fonts_size: + f.config(size=int(fonts_size[name] * scale)) From 3a466fd463e0d84587facd077d79a4e904389b99 Mon Sep 17 00:00:00 2001 From: Huy Pham <42948410+hpham@users.noreply.github.com> Date: Wed, 12 Feb 2020 14:13:28 -0800 Subject: [PATCH 03/13] remove custom size for custom style so that text can scale, scale the remaining node icons from the node picker, scale node's name --- daemon/core/gui/app.py | 1 + daemon/core/gui/dialogs/preferences.py | 30 ++++++++----- daemon/core/gui/graph/node.py | 4 +- daemon/core/gui/themes.py | 13 +++--- daemon/core/gui/toolbar.py | 58 ++++++++++---------------- 5 files changed, 50 insertions(+), 56 deletions(-) diff --git a/daemon/core/gui/app.py b/daemon/core/gui/app.py index c3ab4b6b..888c9c62 100644 --- a/daemon/core/gui/app.py +++ b/daemon/core/gui/app.py @@ -23,6 +23,7 @@ class Application(tk.Frame): NodeUtils.setup() self.fonts_size = {name: font.nametofont(name)["size"] for name in font.names()} + self.icon_text_font = font.Font(family="TkIconFont", size=12) # widgets self.menubar = None diff --git a/daemon/core/gui/dialogs/preferences.py b/daemon/core/gui/dialogs/preferences.py index a10e7e89..654f9e32 100644 --- a/daemon/core/gui/dialogs/preferences.py +++ b/daemon/core/gui/dialogs/preferences.py @@ -81,7 +81,6 @@ class PreferencesDialog(Dialog): value=1, orient=tk.HORIZONTAL, variable=self.gui_scale, - command=self.scale_adjust, ) scale.grid(row=0, column=0, sticky="ew") entry = ttk.Entry( @@ -113,19 +112,28 @@ class PreferencesDialog(Dialog): preferences["gui3d"] = self.gui3d.get() preferences["theme"] = self.theme.get() self.app.save_config() + self.scale_adjust() self.destroy() - def scale_adjust(self, scale: str): + def scale_adjust(self): self.gui_scale.set(round(self.gui_scale.get(), 2)) app_scale = self.gui_scale.get() self.app.canvas.app_scale = app_scale + + self.app.master.tk.call("tk", "scaling", app_scale) + + # scale fonts scale_fonts(self.app.fonts_size, app_scale) - # screen_width = self.app.master.winfo_screenwidth() - # screen_height = self.app.master.winfo_screenheight() - # scaled_width = WIDTH * app_scale - # scaled_height = HEIGHT * app_scale - # x = int(screen_width / 2 - scaled_width / 2) - # y = int(screen_height / 2 - scaled_height / 2) - # self.app.master.geometry(f"{int(scaled_width)}x{int(scaled_height)}+{x}+{y}") - # - # self.app.toolbar.scale(app_scale) + self.app.icon_text_font.config(size=int(12 * app_scale)) + + # scale application widow size + screen_width = self.app.master.winfo_screenwidth() + screen_height = self.app.master.winfo_screenheight() + scaled_width = WIDTH * app_scale + scaled_height = HEIGHT * app_scale + x = int(screen_width / 2 - scaled_width / 2) + y = int(screen_height / 2 - scaled_height / 2) + self.app.master.geometry(f"{int(scaled_width)}x{int(scaled_height)}+{x}+{y}") + + # scale toolbar icons and picker icons + self.app.toolbar.scale() diff --git a/daemon/core/gui/graph/node.py b/daemon/core/gui/graph/node.py index de3a9204..ff33d7e5 100644 --- a/daemon/core/gui/graph/node.py +++ b/daemon/core/gui/graph/node.py @@ -1,6 +1,5 @@ import logging import tkinter as tk -from tkinter import font from typing import TYPE_CHECKING import grpc @@ -42,14 +41,13 @@ class CanvasNode: self.id = self.canvas.create_image( x, y, anchor=tk.CENTER, image=self.image, tags=tags.NODE ) - text_font = font.Font(family="TkIconFont", size=12) label_y = self._get_label_y() self.text_id = self.canvas.create_text( x, label_y, text=self.core_node.name, tags=tags.NODE_NAME, - font=text_font, + font=self.app.icon_text_font, fill="#0000CD", ) self.tooltip = CanvasTooltip(self.canvas) diff --git a/daemon/core/gui/themes.py b/daemon/core/gui/themes.py index 482c86d2..7da0b1dd 100644 --- a/daemon/core/gui/themes.py +++ b/daemon/core/gui/themes.py @@ -176,27 +176,27 @@ def style_listbox(widget: tk.Widget): def theme_change(event: tk.Event): style = ttk.Style() - style.configure(Styles.picker_button, font=("TkDefaultFont", 8, "normal")) + style.configure(Styles.picker_button, font="TkSmallCaptionFont") style.configure( Styles.green_alert, background="green", padding=0, relief=tk.NONE, - font=("TkDefaultFont", 8, "normal"), + font="TkSmallCaptionFont", ) style.configure( Styles.yellow_alert, background="yellow", padding=0, relief=tk.NONE, - font=("TkDefaultFont", 8, "normal"), + font="TkSmallCaptionFont", ) style.configure( Styles.red_alert, background="red", padding=0, relief=tk.NONE, - font=("TkDefaultFont", 8, "normal"), + font="TkSmallCaptionFont", ) @@ -204,4 +204,7 @@ def scale_fonts(fonts_size, scale): for name in font.names(): f = font.nametofont(name) if name in fonts_size: - f.config(size=int(fonts_size[name] * scale)) + if name == "TkSmallCaptionFont": + f.config(size=int(fonts_size[name] * scale * 8 / 9)) + else: + f.config(size=int(fonts_size[name] * scale)) diff --git a/daemon/core/gui/toolbar.py b/daemon/core/gui/toolbar.py index 2b40cee7..8298cafb 100644 --- a/daemon/core/gui/toolbar.py +++ b/daemon/core/gui/toolbar.py @@ -4,7 +4,6 @@ import tkinter as tk from enum import Enum from functools import partial from tkinter import messagebox, ttk -from tkinter.font import Font from typing import TYPE_CHECKING, Callable from core.api.grpc import core_pb2 @@ -50,9 +49,6 @@ class Toolbar(ttk.Frame): self.master = app.master self.time = None - # picker data - self.picker_font = Font(size=8) - # design buttons self.play_button = None self.select_button = None @@ -198,9 +194,7 @@ class Toolbar(ttk.Frame): for node_draw in NodeUtils.NODES: toolbar_image = icon(node_draw.image_enum) # image = icon(node_draw.image_enum, PICKER_SIZE) - image = self.get_icon( - node_draw.image_enum, PICKER_SIZE * self.app.canvas.app_scale - ) + image = self.get_icon(node_draw.image_enum, PICKER_SIZE) func = partial( self.update_button, self.node_button, @@ -214,9 +208,7 @@ class Toolbar(ttk.Frame): for name in sorted(self.app.core.custom_nodes): node_draw = self.app.core.custom_nodes[name] toolbar_image = Images.get_custom(node_draw.image_file, TOOLBAR_SIZE) - image = Images.get_custom( - node_draw.image_file, int(PICKER_SIZE * self.app.canvas.app_scale) - ) + image = Images.get_custom(node_draw.image_file, PICKER_SIZE) func = partial( self.update_button, self.node_button, @@ -228,9 +220,7 @@ class Toolbar(ttk.Frame): self.create_picker_button(image, func, self.node_picker, name) # draw edit node # image = icon(ImageEnum.EDITNODE, PICKER_SIZE) - image = self.get_icon( - ImageEnum.EDITNODE, PICKER_SIZE * self.app.canvas.app_scale - ) + image = self.get_icon(ImageEnum.EDITNODE, PICKER_SIZE) self.create_picker_button( image, self.click_edit_node, self.node_picker, "Custom" ) @@ -386,7 +376,7 @@ class Toolbar(ttk.Frame): self.network_picker = ttk.Frame(self.master) for node_draw in NodeUtils.NETWORK_NODES: toolbar_image = icon(node_draw.image_enum) - image = icon(node_draw.image_enum, PICKER_SIZE) + image = self.get_icon(node_draw.image_enum, PICKER_SIZE) self.create_picker_button( image, partial( @@ -433,7 +423,7 @@ class Toolbar(ttk.Frame): ] for image_enum, shape_type in nodes: toolbar_image = icon(image_enum) - image = icon(image_enum, PICKER_SIZE) + image = self.get_icon(image_enum, PICKER_SIZE) self.create_picker_button( image, partial(self.update_annotation, toolbar_image, shape_type, image_enum), @@ -538,29 +528,23 @@ class Toolbar(ttk.Frame): def click_two_node_button(self): logging.debug("Click TWONODE button") - @classmethod - def scale_button(cls, button, image_enum, scale): - image = icon(image_enum, int(TOOLBAR_SIZE * scale)) + # def scale_button(cls, button, image_enum, scale): + def scale_button(self, button, image_enum): + image = icon(image_enum, int(TOOLBAR_SIZE * self.app.canvas.app_scale)) button.config(image=image) button.image = image - def scale(self, scale): - self.scale_button(self.play_button, ImageEnum.START, scale) - self.scale_button(self.select_button, ImageEnum.SELECT, scale) - self.scale_button(self.link_button, ImageEnum.LINK, scale) - self.scale_button(self.node_button, self.node_enum, scale) - self.scale_button(self.network_button, self.network_enum, scale) - self.scale_button(self.annotation_button, self.annotation_enum, scale) + def scale(self): + self.scale_button(self.play_button, ImageEnum.START) + self.scale_button(self.select_button, ImageEnum.SELECT) + self.scale_button(self.link_button, ImageEnum.LINK) + self.scale_button(self.node_button, self.node_enum) + self.scale_button(self.network_button, self.network_enum) + self.scale_button(self.annotation_button, self.annotation_enum) - self.scale_button(self.runtime_select_button, ImageEnum.SELECT, scale) - self.scale_button(self.stop_button, ImageEnum.STOP, scale) - self.scale_button(self.plot_button, ImageEnum.PLOT, scale) - self.scale_button(self.runtime_marker_button, ImageEnum.MARKER, scale) - self.scale_button(self.node_command_button, ImageEnum.TWONODE, scale) - self.scale_button(self.run_command_button, ImageEnum.RUN, scale) - - # self.stop_button = None - # self.plot_button = None - # self.runtime_marker_button = None - # self.node_command_button = None - # self.run_command_button = None + self.scale_button(self.runtime_select_button, ImageEnum.SELECT) + self.scale_button(self.stop_button, ImageEnum.STOP) + self.scale_button(self.plot_button, ImageEnum.PLOT) + self.scale_button(self.runtime_marker_button, ImageEnum.MARKER) + self.scale_button(self.node_command_button, ImageEnum.TWONODE) + self.scale_button(self.run_command_button, ImageEnum.RUN) From 55b6cbbd90331745c1144f04d6ac7da4bf34174c Mon Sep 17 00:00:00 2001 From: Huy Pham <42948410+hpham@users.noreply.github.com> Date: Thu, 13 Feb 2020 12:15:56 -0800 Subject: [PATCH 04/13] sacle toolbar button after choosing a node from node picker, scale canvas nodes and canvas node text --- daemon/core/gui/dialogs/preferences.py | 2 ++ daemon/core/gui/graph/graph.py | 13 ++++++++++++- daemon/core/gui/graph/node.py | 6 ++++++ daemon/core/gui/images.py | 21 +++++++++++++++++++++ daemon/core/gui/toolbar.py | 13 +++---------- 5 files changed, 44 insertions(+), 11 deletions(-) diff --git a/daemon/core/gui/dialogs/preferences.py b/daemon/core/gui/dialogs/preferences.py index 654f9e32..415fbe9a 100644 --- a/daemon/core/gui/dialogs/preferences.py +++ b/daemon/core/gui/dialogs/preferences.py @@ -137,3 +137,5 @@ class PreferencesDialog(Dialog): # scale toolbar icons and picker icons self.app.toolbar.scale() + + self.app.canvas.scale_graph() diff --git a/daemon/core/gui/graph/graph.py b/daemon/core/gui/graph/graph.py index da394503..a4ddd0b7 100644 --- a/daemon/core/gui/graph/graph.py +++ b/daemon/core/gui/graph/graph.py @@ -12,7 +12,7 @@ from core.gui.graph.enums import GraphMode, ScaleOption from core.gui.graph.node import CanvasNode from core.gui.graph.shape import Shape from core.gui.graph.shapeutils import ShapeType, is_draw_shape, is_marker -from core.gui.images import ImageEnum, Images +from core.gui.images import ImageEnum, Images, TypeToImage from core.gui.nodeutils import EdgeUtils, NodeUtils if TYPE_CHECKING: @@ -914,3 +914,14 @@ class CanvasGraph(tk.Canvas): width=self.itemcget(edge.id, "width"), fill=self.itemcget(edge.id, "fill"), ) + + def scale_graph(self): + for nid, canvas_node in self.nodes.items(): + image_enum = TypeToImage.get( + canvas_node.core_node.type, canvas_node.core_node.model + ) + img = Images.get(image_enum, int(ICON_SIZE * self.app_scale)) + self.itemconfig(nid, image=img) + canvas_node.image = img + + canvas_node.scale_text() diff --git a/daemon/core/gui/graph/node.py b/daemon/core/gui/graph/node.py index dbd8827b..b8faef13 100644 --- a/daemon/core/gui/graph/node.py +++ b/daemon/core/gui/graph/node.py @@ -106,6 +106,12 @@ class CanvasNode: image_box = self.canvas.bbox(self.id) return image_box[3] + NODE_TEXT_OFFSET + def scale_text(self): + text_bound = self.canvas.bbox(self.text_id) + prev_y = (text_bound[3] + text_bound[1]) / 2 + new_y = self._get_label_y() + self.canvas.move(self.text_id, 0, new_y - prev_y) + def move(self, x: int, y: int): x, y = self.canvas.get_scaled_coords(x, y) current_x, current_y = self.canvas.coords(self.id) diff --git a/daemon/core/gui/images.py b/daemon/core/gui/images.py index cd472764..f299c5a5 100644 --- a/daemon/core/gui/images.py +++ b/daemon/core/gui/images.py @@ -3,6 +3,7 @@ from tkinter import messagebox from PIL import Image, ImageTk +from core.api.grpc import core_pb2 from core.gui.appconfig import LOCAL_ICONS_PATH @@ -90,3 +91,23 @@ class ImageEnum(Enum): SHUTDOWN = "shutdown" CANCEL = "cancel" ERROR = "error" + + +class TypeToImage: + type_to_image = { + (core_pb2.NodeType.DEFAULT, "router"): ImageEnum.ROUTER, + (core_pb2.NodeType.DEFAULT, "PC"): ImageEnum.PC, + (core_pb2.NodeType.DEFAULT, "host"): ImageEnum.HOST, + (core_pb2.NodeType.DEFAULT, "mdr"): ImageEnum.MDR, + (core_pb2.NodeType.DEFAULT, "prouter"): ImageEnum.PROUTER, + (core_pb2.NodeType.HUB, ""): ImageEnum.HUB, + (core_pb2.NodeType.SWITCH, ""): ImageEnum.SWITCH, + (core_pb2.NodeType.WIRELESS_LAN, ""): ImageEnum.WLAN, + (core_pb2.NodeType.EMANE, ""): ImageEnum.EMANE, + (core_pb2.NodeType.RJ45, ""): ImageEnum.RJ45, + (core_pb2.NodeType.TUNNEL, ""): ImageEnum.TUNNEL, + } + + @classmethod + def get(cls, node_type, model): + return cls.type_to_image.get((node_type, model), None) diff --git a/daemon/core/gui/toolbar.py b/daemon/core/gui/toolbar.py index 1df24993..10075b74 100644 --- a/daemon/core/gui/toolbar.py +++ b/daemon/core/gui/toolbar.py @@ -103,21 +103,18 @@ class Toolbar(ttk.Frame): self.design_frame.columnconfigure(0, weight=1) self.play_button = self.create_button( self.design_frame, - # icon(ImageEnum.START), self.get_icon(ImageEnum.START), self.click_start, "start the session", ) self.select_button = self.create_button( self.design_frame, - # icon(ImageEnum.SELECT), self.get_icon(ImageEnum.SELECT), self.click_selection, "selection tool", ) self.link_button = self.create_button( self.design_frame, - # icon(ImageEnum.LINK), self.get_icon(ImageEnum.LINK), self.click_link, "link tool", @@ -152,21 +149,18 @@ class Toolbar(ttk.Frame): self.stop_button = self.create_button( self.runtime_frame, - # icon(ImageEnum.STOP), self.get_icon(ImageEnum.STOP), self.click_stop, "stop the session", ) self.runtime_select_button = self.create_button( self.runtime_frame, - # icon(ImageEnum.SELECT), self.get_icon(ImageEnum.SELECT), self.click_runtime_selection, "selection tool", ) self.plot_button = self.create_button( self.runtime_frame, - # icon(ImageEnum.PLOT), self.get_icon(ImageEnum.PLOT), self.click_plot_button, "plot", @@ -192,8 +186,7 @@ class Toolbar(ttk.Frame): self.node_picker = ttk.Frame(self.master) # draw default nodes for node_draw in NodeUtils.NODES: - toolbar_image = icon(node_draw.image_enum) - # image = icon(node_draw.image_enum, PICKER_SIZE) + toolbar_image = self.get_icon(node_draw.image_enum, TOOLBAR_SIZE) image = self.get_icon(node_draw.image_enum, PICKER_SIZE) func = partial( self.update_button, @@ -372,7 +365,7 @@ class Toolbar(ttk.Frame): self.hide_pickers() self.network_picker = ttk.Frame(self.master) for node_draw in NodeUtils.NETWORK_NODES: - toolbar_image = icon(node_draw.image_enum) + toolbar_image = self.get_icon(node_draw.image_enum, TOOLBAR_SIZE) image = self.get_icon(node_draw.image_enum, PICKER_SIZE) self.create_picker_button( image, @@ -419,7 +412,7 @@ class Toolbar(ttk.Frame): (ImageEnum.TEXT, ShapeType.TEXT), ] for image_enum, shape_type in nodes: - toolbar_image = icon(image_enum) + toolbar_image = self.get_icon(image_enum, TOOLBAR_SIZE) image = self.get_icon(image_enum, PICKER_SIZE) self.create_picker_button( image, From 0ea99ca809829c32ac891f4714e39731d9cc2bca Mon Sep 17 00:00:00 2001 From: Huy Pham <42948410+hpham@users.noreply.github.com> Date: Fri, 14 Feb 2020 13:34:00 -0800 Subject: [PATCH 05/13] scale edge text font (ipv4 and ipv6 address, scale edge, scale node when first drawn on canvas and when joining session --- daemon/core/gui/app.py | 1 + daemon/core/gui/dialogs/preferences.py | 1 + daemon/core/gui/graph/edges.py | 21 ++++++++++++++------- daemon/core/gui/graph/graph.py | 12 +++++++++--- daemon/core/gui/nodeutils.py | 25 ++++++++++++++----------- 5 files changed, 39 insertions(+), 21 deletions(-) diff --git a/daemon/core/gui/app.py b/daemon/core/gui/app.py index 888c9c62..3a19b9eb 100644 --- a/daemon/core/gui/app.py +++ b/daemon/core/gui/app.py @@ -24,6 +24,7 @@ class Application(tk.Frame): self.fonts_size = {name: font.nametofont(name)["size"] for name in font.names()} self.icon_text_font = font.Font(family="TkIconFont", size=12) + self.edge_font = font.Font(family="TkDefaultFont", size=8) # widgets self.menubar = None diff --git a/daemon/core/gui/dialogs/preferences.py b/daemon/core/gui/dialogs/preferences.py index 415fbe9a..45feef8c 100644 --- a/daemon/core/gui/dialogs/preferences.py +++ b/daemon/core/gui/dialogs/preferences.py @@ -125,6 +125,7 @@ class PreferencesDialog(Dialog): # scale fonts scale_fonts(self.app.fonts_size, app_scale) self.app.icon_text_font.config(size=int(12 * app_scale)) + self.app.edge_font.config(size=int(8 * app_scale)) # scale application widow size screen_width = self.app.master.winfo_screenwidth() diff --git a/daemon/core/gui/graph/edges.py b/daemon/core/gui/graph/edges.py index 1259ffa9..413d94ac 100644 --- a/daemon/core/gui/graph/edges.py +++ b/daemon/core/gui/graph/edges.py @@ -1,6 +1,5 @@ import logging import tkinter as tk -from tkinter.font import Font from typing import TYPE_CHECKING, Any, Tuple from core.gui import themes @@ -31,7 +30,10 @@ class CanvasWirelessEdge: self.dst = dst self.canvas = canvas self.id = self.canvas.create_line( - *position, tags=tags.WIRELESS_EDGE, width=1.5, fill="#009933" + *position, + tags=tags.WIRELESS_EDGE, + width=1.5 * self.canvas.app_scale, + fill="#009933", ) def delete(self): @@ -61,13 +63,18 @@ class CanvasEdge: self.dst_interface = None self.canvas = canvas self.id = self.canvas.create_line( - x1, y1, x2, y2, tags=tags.EDGE, width=EDGE_WIDTH, fill=EDGE_COLOR + x1, + y1, + x2, + y2, + tags=tags.EDGE, + width=EDGE_WIDTH * self.canvas.app_scale, + fill=EDGE_COLOR, ) self.text_src = None self.text_dst = None self.text_middle = None self.token = None - self.font = Font(size=8) self.link = None self.asymmetric_link = None self.throughput = None @@ -117,7 +124,7 @@ class CanvasEdge: y1, text=label_one, justify=tk.CENTER, - font=self.font, + font=self.canvas.app.edge_font, tags=tags.LINK_INFO, ) self.text_dst = self.canvas.create_text( @@ -125,7 +132,7 @@ class CanvasEdge: y2, text=label_two, justify=tk.CENTER, - font=self.font, + font=self.canvas.app.edge_font, tags=tags.LINK_INFO, ) @@ -146,7 +153,7 @@ class CanvasEdge: if self.text_middle is None: x, y = self.get_midpoint() self.text_middle = self.canvas.create_text( - x, y, tags=tags.THROUGHPUT, font=self.font, text=value + x, y, tags=tags.THROUGHPUT, font=self.canvas.app.edge_font, text=value ) else: self.canvas.itemconfig(self.text_middle, text=value) diff --git a/daemon/core/gui/graph/graph.py b/daemon/core/gui/graph/graph.py index a4ddd0b7..d65f8c1c 100644 --- a/daemon/core/gui/graph/graph.py +++ b/daemon/core/gui/graph/graph.py @@ -7,7 +7,7 @@ from PIL import Image, ImageTk from core.api.grpc import core_pb2 from core.gui.dialogs.shapemod import ShapeDialog from core.gui.graph import tags -from core.gui.graph.edges import CanvasEdge, CanvasWirelessEdge +from core.gui.graph.edges import EDGE_WIDTH, CanvasEdge, CanvasWirelessEdge from core.gui.graph.enums import GraphMode, ScaleOption from core.gui.graph.node import CanvasNode from core.gui.graph.shape import Shape @@ -223,10 +223,10 @@ class CanvasGraph(tk.Canvas): # peer to peer node is not drawn on the GUI if NodeUtils.is_ignore_node(core_node.type): continue - image = NodeUtils.node_image(core_node, self.app.guiconfig) + image = NodeUtils.node_image(core_node, self.app.guiconfig, self.app_scale) # if the gui can't find node's image, default to the "edit-node" image if not image: - image = Images.get(ImageEnum.EDITNODE, ICON_SIZE) + image = Images.get(ImageEnum.EDITNODE, int(ICON_SIZE * self.app_scale)) x = core_node.position.x y = core_node.position.y node = CanvasNode(self.master, x, y, core_node, image) @@ -666,6 +666,9 @@ class CanvasGraph(tk.Canvas): core_node = self.core.create_node( actual_x, actual_y, self.node_draw.node_type, self.node_draw.model ) + self.node_draw.image = Images.get( + self.node_draw.image_enum, int(ICON_SIZE * self.app_scale) + ) node = CanvasNode(self.master, x, y, core_node, self.node_draw.image) self.core.canvas_nodes[core_node.id] = node self.nodes[node.id] = node @@ -925,3 +928,6 @@ class CanvasGraph(tk.Canvas): canvas_node.image = img canvas_node.scale_text() + + for edge_id in self.find_withtag(tags.EDGE): + self.itemconfig(edge_id, width=int(EDGE_WIDTH * self.app_scale)) diff --git a/daemon/core/gui/nodeutils.py b/daemon/core/gui/nodeutils.py index c8ddb8fa..a9cee938 100644 --- a/daemon/core/gui/nodeutils.py +++ b/daemon/core/gui/nodeutils.py @@ -2,7 +2,7 @@ import logging from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, Union from core.api.grpc.core_pb2 import NodeType -from core.gui.images import ImageEnum, Images +from core.gui.images import ImageEnum, Images, TypeToImage if TYPE_CHECKING: from core.api.grpc import core_pb2 @@ -96,25 +96,28 @@ class NodeUtils: node_type: NodeType, model: str, gui_config: Dict[str, List[Dict[str, str]]], + scale=1.0, ) -> "ImageTk.PhotoImage": - if model == "": - model = None - try: - image = cls.NODE_ICONS[(node_type, model)] - return image - except KeyError: + + image_enum = TypeToImage.get(node_type, model) + if image_enum: + return Images.get(image_enum, int(ICON_SIZE * scale)) + else: image_stem = cls.get_image_file(gui_config, model) if image_stem: - return Images.get_with_image_file(image_stem, ICON_SIZE) + return Images.get_with_image_file(image_stem, int(ICON_SIZE * scale)) @classmethod def node_image( - cls, core_node: "core_pb2.Node", gui_config: Dict[str, List[Dict[str, str]]] + cls, + core_node: "core_pb2.Node", + gui_config: Dict[str, List[Dict[str, str]]], + scale=1.0, ) -> "ImageTk.PhotoImage": - image = cls.node_icon(core_node.type, core_node.model, gui_config) + image = cls.node_icon(core_node.type, core_node.model, gui_config, scale) if core_node.icon: try: - image = Images.create(core_node.icon, ICON_SIZE) + image = Images.create(core_node.icon, int(ICON_SIZE * scale)) except OSError: logging.error("invalid icon: %s", core_node.icon) return image From 4fd1338cf15ccea09ba66d525020b125d8583313 Mon Sep 17 00:00:00 2001 From: Huy Pham <42948410+hpham@users.noreply.github.com> Date: Mon, 17 Feb 2020 11:10:13 -0800 Subject: [PATCH 06/13] save application scale to gui configuration, and draw everything to the correct saved scale when starting the application --- daemon/core/gui/app.py | 29 +++++++++++++++++++++----- daemon/core/gui/appconfig.py | 1 + daemon/core/gui/dialogs/preferences.py | 10 +++++---- daemon/core/gui/graph/edges.py | 4 ++-- daemon/core/gui/graph/graph.py | 17 ++++++++------- daemon/core/gui/toolbar.py | 13 +++++------- 6 files changed, 47 insertions(+), 27 deletions(-) diff --git a/daemon/core/gui/app.py b/daemon/core/gui/app.py index 3a19b9eb..31651cfa 100644 --- a/daemon/core/gui/app.py +++ b/daemon/core/gui/app.py @@ -22,10 +22,6 @@ class Application(tk.Frame): # load node icons NodeUtils.setup() - self.fonts_size = {name: font.nametofont(name)["size"] for name in font.names()} - self.icon_text_font = font.Font(family="TkIconFont", size=12) - self.edge_font = font.Font(family="TkDefaultFont", size=8) - # widgets self.menubar = None self.toolbar = None @@ -33,8 +29,15 @@ class Application(tk.Frame): self.statusbar = None self.validation = 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) @@ -42,6 +45,20 @@ class Application(tk.Frame): self.draw() self.core.set_up() + def setup_scaling(self): + self.fonts_size = {name: font.nametofont(name)["size"] for name in font.names()} + for name in font.names(): + f = font.nametofont(name) + if name in self.fonts_size: + if name == "TkSmallCaptionFont": + f.config(size=int(self.fonts_size[name] * self.app_scale * 8 / 9)) + else: + f.config(size=int(self.fonts_size[name] * self.app_scale)) + self.icon_text_font = font.Font( + family="TkIconFont", size=int(12 * self.app_scale) + ) + self.edge_font = font.Font(family="TkDefaultFont", size=int(8 * self.app_scale)) + def setup_theme(self): themes.load(self.style) self.master.bind_class("Menu", "<>", themes.theme_change_menu) @@ -62,7 +79,9 @@ class Application(tk.Frame): screen_height = self.master.winfo_screenheight() x = int((screen_width / 2) - (WIDTH / 2)) y = int((screen_height / 2) - (HEIGHT / 2)) - self.master.geometry(f"{WIDTH}x{HEIGHT}+{x}+{y}") + self.master.geometry( + f"{int(WIDTH * self.app_scale)}x{int(HEIGHT * self.app_scale)}+{x}+{y}" + ) def draw(self): self.master.option_add("*tearOff", tk.FALSE) diff --git a/daemon/core/gui/appconfig.py b/daemon/core/gui/appconfig.py index f73a842c..97c76065 100644 --- a/daemon/core/gui/appconfig.py +++ b/daemon/core/gui/appconfig.py @@ -96,6 +96,7 @@ def check_directory(): "nodes": [], "recentfiles": [], "observers": [{"name": "hello", "cmd": "echo hello"}], + "scale": 1.0, } save(config) diff --git a/daemon/core/gui/dialogs/preferences.py b/daemon/core/gui/dialogs/preferences.py index 45feef8c..440fab40 100644 --- a/daemon/core/gui/dialogs/preferences.py +++ b/daemon/core/gui/dialogs/preferences.py @@ -17,7 +17,7 @@ HEIGHT = 800 class PreferencesDialog(Dialog): def __init__(self, master: "Application", app: "Application"): super().__init__(master, app, "Preferences", modal=True) - self.gui_scale = tk.DoubleVar(value=self.app.canvas.app_scale) + self.gui_scale = tk.DoubleVar(value=self.app.app_scale) preferences = self.app.guiconfig["preferences"] self.editor = tk.StringVar(value=preferences["editor"]) self.theme = tk.StringVar(value=preferences["theme"]) @@ -111,15 +111,17 @@ class PreferencesDialog(Dialog): preferences["editor"] = self.editor.get() preferences["gui3d"] = self.gui3d.get() preferences["theme"] = self.theme.get() + self.gui_scale.set(round(self.gui_scale.get(), 2)) + app_scale = self.gui_scale.get() + self.app.guiconfig["scale"] = app_scale + self.app.save_config() self.scale_adjust() self.destroy() def scale_adjust(self): - self.gui_scale.set(round(self.gui_scale.get(), 2)) app_scale = self.gui_scale.get() - self.app.canvas.app_scale = app_scale - + self.app.app_scale = app_scale self.app.master.tk.call("tk", "scaling", app_scale) # scale fonts diff --git a/daemon/core/gui/graph/edges.py b/daemon/core/gui/graph/edges.py index 413d94ac..9516de14 100644 --- a/daemon/core/gui/graph/edges.py +++ b/daemon/core/gui/graph/edges.py @@ -32,7 +32,7 @@ class CanvasWirelessEdge: self.id = self.canvas.create_line( *position, tags=tags.WIRELESS_EDGE, - width=1.5 * self.canvas.app_scale, + width=1.5 * self.canvas.app.app_scale, fill="#009933", ) @@ -68,7 +68,7 @@ class CanvasEdge: x2, y2, tags=tags.EDGE, - width=EDGE_WIDTH * self.canvas.app_scale, + width=EDGE_WIDTH * self.canvas.app.app_scale, fill=EDGE_COLOR, ) self.text_src = None diff --git a/daemon/core/gui/graph/graph.py b/daemon/core/gui/graph/graph.py index d65f8c1c..dd5025fd 100644 --- a/daemon/core/gui/graph/graph.py +++ b/daemon/core/gui/graph/graph.py @@ -53,9 +53,6 @@ class CanvasGraph(tk.Canvas): self.marker_tool = None self.to_copy = [] - # app's scale, different scale values to support higher resolution display - self.app_scale = 1.0 - # background related self.wallpaper_id = None self.wallpaper = None @@ -223,10 +220,14 @@ class CanvasGraph(tk.Canvas): # peer to peer node is not drawn on the GUI if NodeUtils.is_ignore_node(core_node.type): continue - image = NodeUtils.node_image(core_node, self.app.guiconfig, self.app_scale) + image = NodeUtils.node_image( + core_node, self.app.guiconfig, self.app.app_scale + ) # if the gui can't find node's image, default to the "edit-node" image if not image: - image = Images.get(ImageEnum.EDITNODE, int(ICON_SIZE * self.app_scale)) + image = Images.get( + ImageEnum.EDITNODE, int(ICON_SIZE * self.app.app_scale) + ) x = core_node.position.x y = core_node.position.y node = CanvasNode(self.master, x, y, core_node, image) @@ -667,7 +668,7 @@ class CanvasGraph(tk.Canvas): actual_x, actual_y, self.node_draw.node_type, self.node_draw.model ) self.node_draw.image = Images.get( - self.node_draw.image_enum, int(ICON_SIZE * self.app_scale) + self.node_draw.image_enum, int(ICON_SIZE * self.app.app_scale) ) node = CanvasNode(self.master, x, y, core_node, self.node_draw.image) self.core.canvas_nodes[core_node.id] = node @@ -923,11 +924,11 @@ class CanvasGraph(tk.Canvas): image_enum = TypeToImage.get( canvas_node.core_node.type, canvas_node.core_node.model ) - img = Images.get(image_enum, int(ICON_SIZE * self.app_scale)) + img = Images.get(image_enum, int(ICON_SIZE * self.app.app_scale)) self.itemconfig(nid, image=img) canvas_node.image = img canvas_node.scale_text() for edge_id in self.find_withtag(tags.EDGE): - self.itemconfig(edge_id, width=int(EDGE_WIDTH * self.app_scale)) + self.itemconfig(edge_id, width=int(EDGE_WIDTH * self.app.app_scale)) diff --git a/daemon/core/gui/toolbar.py b/daemon/core/gui/toolbar.py index 10075b74..d0702386 100644 --- a/daemon/core/gui/toolbar.py +++ b/daemon/core/gui/toolbar.py @@ -85,10 +85,7 @@ class Toolbar(ttk.Frame): self.draw() def get_icon(self, image_enum, width=TOOLBAR_SIZE): - if not self.app.canvas: - return Images.get(image_enum, width) - else: - return Images.get(image_enum, int(width * self.app.canvas.app_scale)) + return Images.get(image_enum, int(width * self.app.app_scale)) def draw(self): self.columnconfigure(0, weight=1) @@ -349,7 +346,7 @@ class Toolbar(ttk.Frame): """ Create network layer button """ - image = icon(ImageEnum.ROUTER, TOOLBAR_SIZE) + image = self.get_icon(ImageEnum.ROUTER, TOOLBAR_SIZE) self.node_button = ttk.Button( self.design_frame, image=image, command=self.draw_node_picker ) @@ -390,7 +387,7 @@ class Toolbar(ttk.Frame): Create link-layer node button and the options that represent different link-layer node types. """ - image = icon(ImageEnum.HUB) + image = self.get_icon(ImageEnum.HUB, TOOLBAR_SIZE) self.network_button = ttk.Button( self.design_frame, image=image, command=self.draw_network_picker ) @@ -429,7 +426,7 @@ class Toolbar(ttk.Frame): """ Create marker button and options that represent different marker types """ - image = icon(ImageEnum.MARKER) + image = self.get_icon(ImageEnum.MARKER, TOOLBAR_SIZE) self.annotation_button = ttk.Button( self.design_frame, image=image, command=self.draw_annotation_picker ) @@ -518,7 +515,7 @@ class Toolbar(ttk.Frame): # def scale_button(cls, button, image_enum, scale): def scale_button(self, button, image_enum): - image = icon(image_enum, int(TOOLBAR_SIZE * self.app.canvas.app_scale)) + image = icon(image_enum, int(TOOLBAR_SIZE * self.app.app_scale)) button.config(image=image) button.image = image From 1d911a763f8352736acc92ae3a90a7abd4874474 Mon Sep 17 00:00:00 2001 From: Huy Pham <42948410+hpham@users.noreply.github.com> Date: Mon, 17 Feb 2020 12:56:19 -0800 Subject: [PATCH 07/13] scale custom node icon and custom node drawn on canvas --- daemon/core/gui/graph/graph.py | 29 +++++++++++++++++++++-------- daemon/core/gui/toolbar.py | 8 ++++++-- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/daemon/core/gui/graph/graph.py b/daemon/core/gui/graph/graph.py index dd5025fd..f705565f 100644 --- a/daemon/core/gui/graph/graph.py +++ b/daemon/core/gui/graph/graph.py @@ -667,9 +667,14 @@ class CanvasGraph(tk.Canvas): core_node = self.core.create_node( actual_x, actual_y, self.node_draw.node_type, self.node_draw.model ) - self.node_draw.image = Images.get( - self.node_draw.image_enum, int(ICON_SIZE * self.app.app_scale) - ) + try: + self.node_draw.image = Images.get( + self.node_draw.image_enum, int(ICON_SIZE * self.app.app_scale) + ) + except AttributeError: + self.node_draw.image = Images.get_custom( + self.node_draw.image_file, int(ICON_SIZE * self.app.app_scale) + ) node = CanvasNode(self.master, x, y, core_node, self.node_draw.image) self.core.canvas_nodes[core_node.id] = node self.nodes[node.id] = node @@ -921,13 +926,21 @@ class CanvasGraph(tk.Canvas): def scale_graph(self): for nid, canvas_node in self.nodes.items(): - image_enum = TypeToImage.get( - canvas_node.core_node.type, canvas_node.core_node.model - ) - img = Images.get(image_enum, int(ICON_SIZE * self.app.app_scale)) + img = None + if NodeUtils.is_custom(canvas_node.core_node.model): + for custom_node in self.app.guiconfig["nodes"]: + if custom_node["name"] == canvas_node.core_node.model: + img = Images.get_custom( + custom_node["image"], int(ICON_SIZE * self.app.app_scale) + ) + else: + image_enum = TypeToImage.get( + canvas_node.core_node.type, canvas_node.core_node.model + ) + img = Images.get(image_enum, int(ICON_SIZE * self.app.app_scale)) + self.itemconfig(nid, image=img) canvas_node.image = img - canvas_node.scale_text() for edge_id in self.find_withtag(tags.EDGE): diff --git a/daemon/core/gui/toolbar.py b/daemon/core/gui/toolbar.py index d0702386..eff37257 100644 --- a/daemon/core/gui/toolbar.py +++ b/daemon/core/gui/toolbar.py @@ -197,8 +197,12 @@ class Toolbar(ttk.Frame): # draw custom nodes for name in sorted(self.app.core.custom_nodes): node_draw = self.app.core.custom_nodes[name] - toolbar_image = Images.get_custom(node_draw.image_file, TOOLBAR_SIZE) - image = Images.get_custom(node_draw.image_file, PICKER_SIZE) + toolbar_image = Images.get_custom( + node_draw.image_file, int(TOOLBAR_SIZE * self.app.app_scale) + ) + image = Images.get_custom( + node_draw.image_file, int(PICKER_SIZE * self.app.app_scale) + ) func = partial( self.update_button, self.node_button, From 87c9492d320c8d0ae6095557eca0f0271bcfb4ea Mon Sep 17 00:00:00 2001 From: Huy Pham <42948410+hpham@users.noreply.github.com> Date: Mon, 17 Feb 2020 15:14:52 -0800 Subject: [PATCH 08/13] scale antenna and mobility player buttons --- daemon/core/gui/app.py | 12 ++----- daemon/core/gui/coreclient.py | 2 +- daemon/core/gui/dialogs/mobilityplayer.py | 6 ++-- daemon/core/gui/dialogs/nodeservice.py | 2 +- daemon/core/gui/dialogs/preferences.py | 13 ++----- daemon/core/gui/graph/edges.py | 6 ++-- daemon/core/gui/graph/graph.py | 5 ++- daemon/core/gui/graph/node.py | 42 +++++++++++++++++------ daemon/core/gui/nodeutils.py | 4 +-- 9 files changed, 52 insertions(+), 40 deletions(-) diff --git a/daemon/core/gui/app.py b/daemon/core/gui/app.py index 31651cfa..8b18beeb 100644 --- a/daemon/core/gui/app.py +++ b/daemon/core/gui/app.py @@ -47,13 +47,7 @@ class Application(tk.Frame): def setup_scaling(self): self.fonts_size = {name: font.nametofont(name)["size"] for name in font.names()} - for name in font.names(): - f = font.nametofont(name) - if name in self.fonts_size: - if name == "TkSmallCaptionFont": - f.config(size=int(self.fonts_size[name] * self.app_scale * 8 / 9)) - else: - f.config(size=int(self.fonts_size[name] * self.app_scale)) + themes.scale_fonts(self.fonts_size, self.app_scale) self.icon_text_font = font.Font( family="TkIconFont", size=int(12 * self.app_scale) ) @@ -77,8 +71,8 @@ class Application(tk.Frame): def center(self): screen_width = self.master.winfo_screenwidth() screen_height = self.master.winfo_screenheight() - x = int((screen_width / 2) - (WIDTH / 2)) - y = int((screen_height / 2) - (HEIGHT / 2)) + 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}" ) diff --git a/daemon/core/gui/coreclient.py b/daemon/core/gui/coreclient.py index 7fa31ee3..7d8e832c 100644 --- a/daemon/core/gui/coreclient.py +++ b/daemon/core/gui/coreclient.py @@ -794,7 +794,7 @@ class CoreClient: image=image, emane=emane, ) - if NodeUtils.is_custom(model): + if NodeUtils.is_custom(node_type, model): services = NodeUtils.get_custom_node_services(self.app.guiconfig, model) node.services[:] = services logging.info( diff --git a/daemon/core/gui/dialogs/mobilityplayer.py b/daemon/core/gui/dialogs/mobilityplayer.py index dd2e9f9a..bb4d203c 100644 --- a/daemon/core/gui/dialogs/mobilityplayer.py +++ b/daemon/core/gui/dialogs/mobilityplayer.py @@ -100,17 +100,17 @@ class MobilityPlayerDialog(Dialog): for i in range(3): frame.columnconfigure(i, weight=1) - image = Images.get(ImageEnum.START, width=ICON_SIZE) + image = Images.get(ImageEnum.START, width=int(ICON_SIZE * self.app.app_scale)) self.play_button = ttk.Button(frame, image=image, command=self.click_play) self.play_button.image = image self.play_button.grid(row=0, column=0, sticky="ew", padx=PADX) - image = Images.get(ImageEnum.PAUSE, width=ICON_SIZE) + image = Images.get(ImageEnum.PAUSE, width=int(ICON_SIZE * self.app.app_scale)) self.pause_button = ttk.Button(frame, image=image, command=self.click_pause) self.pause_button.image = image self.pause_button.grid(row=0, column=1, sticky="ew", padx=PADX) - image = Images.get(ImageEnum.STOP, width=ICON_SIZE) + image = Images.get(ImageEnum.STOP, width=int(ICON_SIZE * self.app.app_scale)) self.stop_button = ttk.Button(frame, image=image, command=self.click_stop) self.stop_button.image = image self.stop_button.grid(row=0, column=2, sticky="ew", padx=PADX) diff --git a/daemon/core/gui/dialogs/nodeservice.py b/daemon/core/gui/dialogs/nodeservice.py index f2fe1db2..9a289aeb 100644 --- a/daemon/core/gui/dialogs/nodeservice.py +++ b/daemon/core/gui/dialogs/nodeservice.py @@ -38,7 +38,7 @@ class NodeServiceDialog(Dialog): if len(services) == 0: # not custom node type and node's services haven't been modified before if not NodeUtils.is_custom( - canvas_node.core_node.model + canvas_node.core_node.type, canvas_node.core_node.model ) and not self.app.core.service_been_modified(self.node_id): services = set(self.app.core.default_services[model]) # services of default type nodes were modified to be empty diff --git a/daemon/core/gui/dialogs/preferences.py b/daemon/core/gui/dialogs/preferences.py index 440fab40..dd1c1c04 100644 --- a/daemon/core/gui/dialogs/preferences.py +++ b/daemon/core/gui/dialogs/preferences.py @@ -129,16 +129,9 @@ class PreferencesDialog(Dialog): self.app.icon_text_font.config(size=int(12 * app_scale)) self.app.edge_font.config(size=int(8 * app_scale)) - # scale application widow size - screen_width = self.app.master.winfo_screenwidth() - screen_height = self.app.master.winfo_screenheight() - scaled_width = WIDTH * app_scale - scaled_height = HEIGHT * app_scale - x = int(screen_width / 2 - scaled_width / 2) - y = int(screen_height / 2 - scaled_height / 2) - self.app.master.geometry(f"{int(scaled_width)}x{int(scaled_height)}+{x}+{y}") + # scale application window + self.app.center() - # scale toolbar icons and picker icons + # scale toolbar and canvas items self.app.toolbar.scale() - self.app.canvas.scale_graph() diff --git a/daemon/core/gui/graph/edges.py b/daemon/core/gui/graph/edges.py index 9516de14..e5d7b5db 100644 --- a/daemon/core/gui/graph/edges.py +++ b/daemon/core/gui/graph/edges.py @@ -13,6 +13,8 @@ if TYPE_CHECKING: TEXT_DISTANCE = 0.30 EDGE_WIDTH = 3 EDGE_COLOR = "#ff0000" +WIRELESS_WIDTH = 1.5 +WIRELESS_COLOR = "#009933" class CanvasWirelessEdge: @@ -32,8 +34,8 @@ class CanvasWirelessEdge: self.id = self.canvas.create_line( *position, tags=tags.WIRELESS_EDGE, - width=1.5 * self.canvas.app.app_scale, - fill="#009933", + width=WIRELESS_WIDTH * self.canvas.app.app_scale, + fill=WIRELESS_COLOR, ) def delete(self): diff --git a/daemon/core/gui/graph/graph.py b/daemon/core/gui/graph/graph.py index f705565f..0869a0d0 100644 --- a/daemon/core/gui/graph/graph.py +++ b/daemon/core/gui/graph/graph.py @@ -927,7 +927,9 @@ class CanvasGraph(tk.Canvas): def scale_graph(self): for nid, canvas_node in self.nodes.items(): img = None - if NodeUtils.is_custom(canvas_node.core_node.model): + if NodeUtils.is_custom( + canvas_node.core_node.type, canvas_node.core_node.model + ): for custom_node in self.app.guiconfig["nodes"]: if custom_node["name"] == canvas_node.core_node.model: img = Images.get_custom( @@ -942,6 +944,7 @@ class CanvasGraph(tk.Canvas): self.itemconfig(nid, image=img) canvas_node.image = img canvas_node.scale_text() + canvas_node.scale_antennas() for edge_id in self.find_withtag(tags.EDGE): self.itemconfig(edge_id, width=int(EDGE_WIDTH * self.app.app_scale)) diff --git a/daemon/core/gui/graph/node.py b/daemon/core/gui/graph/node.py index b8faef13..3ed5b1d9 100644 --- a/daemon/core/gui/graph/node.py +++ b/daemon/core/gui/graph/node.py @@ -16,7 +16,8 @@ from core.gui.dialogs.wlanconfig import WlanConfigDialog from core.gui.errors import show_grpc_error from core.gui.graph import tags from core.gui.graph.tooltip import CanvasTooltip -from core.gui.nodeutils import NodeUtils +from core.gui.images import ImageEnum, Images +from core.gui.nodeutils import ANTENNA_SIZE, NodeUtils if TYPE_CHECKING: from core.gui.app import Application @@ -54,7 +55,8 @@ class CanvasNode: self.edges = set() self.interfaces = [] self.wireless_edges = set() - self.antennae = [] + self.antennas = [] + self.antenna_images = {} self.setup_bindings() def setup_bindings(self): @@ -70,33 +72,37 @@ class CanvasNode: def add_antenna(self): x, y = self.canvas.coords(self.id) - offset = len(self.antennae) * 8 + offset = len(self.antennas) * 8 * self.app.app_scale + img = Images.get(ImageEnum.ANTENNA, int(ANTENNA_SIZE * self.app.app_scale)) antenna_id = self.canvas.create_image( x - 16 + offset, - y - 23, + y - int(23 * self.app.app_scale), anchor=tk.CENTER, - image=NodeUtils.ANTENNA_ICON, + image=img, tags=tags.ANTENNA, ) - self.antennae.append(antenna_id) + self.antennas.append(antenna_id) + self.antenna_images[antenna_id] = img def delete_antenna(self): """ delete one antenna """ logging.debug("Delete an antenna on %s", self.core_node.name) - if self.antennae: - antenna_id = self.antennae.pop() + if self.antennas: + antenna_id = self.antennas.pop() self.canvas.delete(antenna_id) + self.antenna_images.pop(antenna_id, None) def delete_antennas(self): """ delete all antennas """ logging.debug("Remove all antennas for %s", self.core_node.name) - for antenna_id in self.antennae: + for antenna_id in self.antennas: self.canvas.delete(antenna_id) - self.antennae.clear() + self.antennas.clear() + self.antenna_images.clear() def redraw(self): self.canvas.itemconfig(self.id, image=self.image) @@ -135,7 +141,7 @@ class CanvasNode: self.canvas.move_selection(self.id, x_offset, y_offset) # move antennae - for antenna_id in self.antennae: + for antenna_id in self.antennas: self.canvas.move(antenna_id, x_offset, y_offset) # move edges @@ -299,3 +305,17 @@ class CanvasNode: if core_node.type == core_pb2.NodeType.DEFAULT and core_node.model == "mdr": self.canvas.create_edge(self, self.canvas.nodes[canvas_nid]) self.canvas.clear_selection() + + def scale_antennas(self): + for i in range(len(self.antennas)): + antenna_id = self.antennas[i] + image = Images.get( + ImageEnum.ANTENNA, int(ANTENNA_SIZE * self.app.app_scale) + ) + self.canvas.itemconfig(antenna_id, image=image) + self.antenna_images[antenna_id] = image + node_x, node_y = self.canvas.coords(self.id) + x, y = self.canvas.coords(antenna_id) + dx = node_x - 16 + (i * 8 * self.app.app_scale) - x + dy = node_y - int(23 * self.app.app_scale) - y + self.canvas.move(antenna_id, dx, dy) diff --git a/daemon/core/gui/nodeutils.py b/daemon/core/gui/nodeutils.py index a9cee938..81aa2cba 100644 --- a/daemon/core/gui/nodeutils.py +++ b/daemon/core/gui/nodeutils.py @@ -123,8 +123,8 @@ class NodeUtils: return image @classmethod - def is_custom(cls, model: str) -> bool: - return model not in cls.NODE_MODELS + def is_custom(cls, node_type: NodeType, model: str) -> bool: + return node_type == NodeType.DEFAULT and model not in cls.NODE_MODELS @classmethod def get_custom_node_services( From 8572e153f4a9a8129db2526c3a56236d371ea3d6 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Wed, 19 Feb 2020 21:21:21 -0800 Subject: [PATCH 09/13] fixed comparison logic for waypoints and added tests to help catch issue in the future --- daemon/core/location/mobility.py | 8 ++++---- daemon/tests/test_mobility.py | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 daemon/tests/test_mobility.py diff --git a/daemon/core/location/mobility.py b/daemon/core/location/mobility.py index 2b6051a4..55af58d9 100644 --- a/daemon/core/location/mobility.py +++ b/daemon/core/location/mobility.py @@ -570,10 +570,10 @@ class WayPoint: return not self == other def __lt__(self, other: "WayPoint") -> bool: - result = self.time < other.time - if result: - result = self.nodenum < other.nodenum - return result + if self.time == other.time: + return self.nodenum < other.nodenum + else: + return self.time < other.time class WayPointMobility(WirelessModel): diff --git a/daemon/tests/test_mobility.py b/daemon/tests/test_mobility.py new file mode 100644 index 00000000..e2e8f90e --- /dev/null +++ b/daemon/tests/test_mobility.py @@ -0,0 +1,17 @@ +import pytest + +from core.location.mobility import WayPoint + + +class TestMobility: + @pytest.mark.parametrize( + "wp1, wp2, expected", + [ + (WayPoint(10.0, 1, [0, 0], 1.0), WayPoint(1.0, 2, [0, 0], 1.0), False), + (WayPoint(1.0, 1, [0, 0], 1.0), WayPoint(10.0, 2, [0, 0], 1.0), True), + (WayPoint(1.0, 1, [0, 0], 1.0), WayPoint(1.0, 2, [0, 0], 1.0), True), + (WayPoint(1.0, 2, [0, 0], 1.0), WayPoint(1.0, 1, [0, 0], 1.0), False), + ], + ) + def test_waypoint_lessthan(self, wp1, wp2, expected): + assert (wp1 < wp2) == expected From 2a6c6ac286b9934b2ecbca713642e92aa6687e4f Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Thu, 20 Feb 2020 09:03:34 -0800 Subject: [PATCH 10/13] version bump for next release --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index c27d6f7f..905fbde0 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. # this defines the CORE version number, must be static for AC_INIT -AC_INIT(core, 6.0.0) +AC_INIT(core, 6.1.0) # autoconf and automake initialization AC_CONFIG_SRCDIR([netns/version.h.in]) From d128f2425dd042885b14a6b0e5c816358c848d79 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Thu, 20 Feb 2020 09:32:37 -0800 Subject: [PATCH 11/13] updated changelog --- CHANGELOG.md | 56 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 986d8abf..35ee1ef4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,35 @@ +## 2020-02-20 CORE 6.1.0 +* New + * config services - these services leverage a proper template engine and have configurable parameters, given enough time may replace existing services + * core-imn-to-xml - IMN to XML utility script + * replaced internal code for determining ip/mac address with netaddr library +* Enhancements + * added distributed package for built packages + * made use of python type hinting for functions and their return values + * updated Quagga zebra service to remove deprecated warning +* Removed + * removed stale ns3 code +* CORETK GUI + * added logging + * improved error dialog + * properly use global ipv6 addresses for nodes + * disable proxy usage by default, flag available to enable +* gRPC API + * add_link - now returns created interface information + * set_node_service - can now set files and directories to properly replicate previous usage + * get_emane_event_channel - return information related to the currently used emane event channel +* Bugfixes + * fixed session SDT functionality back to working order, due to python3 changes + * avoid shutting down services for nodes that are not up + * EMANE bypass model options will now display properly in GUIs + * XML scenarios will now properly read in custom node icons + * \#372 - fixed mobility waypoint comparisons + * \#370 - fixed radvd service + * \#368 - updated frr services to properly start staticd when needed + * \#358 - fixed systemd service install path + * \#350 - fixed frr babel wireless configuration + * \#354 - updated frr to reset interfaces to properly take configurations + ## 2020-01-01 CORE 6.0.0 * New * beta release of the python based tk GUI, use **coretk-gui** to try it out, plan will be to eventually sunset the old GUI once this is good enough @@ -114,11 +146,11 @@ * Added EMANE prefix configuration when looking for emane model manifest files * requires configuring **emane_prefix** in /etc/core/core.conf * Cleanup - * Refactoring of the core python package structure, trying to help provide better organization and + * Refactoring of the core python package structure, trying to help provide better organization and logical groupings * Issues * \#246 - Fixed network to network link handling when reading xml files - * \#236 - Fixed storing/reading of link configuration values within xml files + * \#236 - Fixed storing/reading of link configuration values within xml files * \#170 - FRR Service * \#155 - EMANE path configuration * \#233 - Python 3 support @@ -152,16 +184,16 @@ ## 2018-05-22 CORE 5.1 * DAEMON: - * removed and cleared out code that is either legacy or no longer supported (Xen, BSD, Kernel patching, RPM/DEB + * removed and cleared out code that is either legacy or no longer supported (Xen, BSD, Kernel patching, RPM/DEB specific files) * default nodes are now set in the node map * moved ns3 and netns directories to the top of the repo * changes to make use of fpm as the tool for building packages * removed usage of logzero to avoid dependency issues for built packages * removed daemon addons directory - * added CoreEmu to core.emulator.coreemu to help begin serving as the basis for a more formal API for scripting + * added CoreEmu to core.emulator.coreemu to help begin serving as the basis for a more formal API for scripting and creating new external APIs out of - * cleaned up logging, moved more logging to DEBUG from INFO, tried to mold INFO message to be more simple and + * cleaned up logging, moved more logging to DEBUG from INFO, tried to mold INFO message to be more simple and informative * EMANE 1.0.1-1.21 supported * updates to leverage EMANE python bindings for dynamically parsing phy/mac manifest files @@ -175,7 +207,7 @@ * updated broken help links in GUI Help->About * Packaging: * fixed PYTHON_PATH to PYTHONPATH in sysv script - * added make command to leverage FPM as the tool for creating deb/rpm packages going forward, there is documentation + * added make command to leverage FPM as the tool for creating deb/rpm packages going forward, there is documentation within README.md to try it out * TEST: * fixed some broken tests @@ -184,7 +216,7 @@ * \#142 - duplication of custom services * \#136 - sphinx-apidoc command not found * \#137 - make command fails when using distclean - + ## 2017-09-01 CORE 5.0 * DEVELOPMENT: * support for editorconfig to help standardize development across IDEs, from the defined configuration file @@ -339,7 +371,7 @@ * added "--addons" startup mode to pass control to code included from addons dir * added "Locked" entry to View menu to prevent moving items * use currently selected node type when invoking a topology generator - * updated throughput plots with resizing, color picker, plot labels, locked scales, and save/load plot + * updated throughput plots with resizing, color picker, plot labels, locked scales, and save/load plot configuration with imn file * improved session dialog * EMANE: @@ -356,11 +388,11 @@ * XML import and export * renamed "cored.py" to "cored", "coresendmsg.py" to "coresendmsg" * code reorganization and clean-up - * updated XML export to write NetworkPlan, MotionPlan, and ServicePlan within a Scenario tag, added new + * updated XML export to write NetworkPlan, MotionPlan, and ServicePlan within a Scenario tag, added new "Save As XML..." File menu entry * added script_start/pause/stop options to Ns2ScriptedMobility * "python" source sub-directory renamed to "daemon" - * added "cored -e" option to execute a Python script, adding its session to the active sessions list, allowing for + * added "cored -e" option to execute a Python script, adding its session to the active sessions list, allowing for GUI connection * support comma-separated list for custom_services_dir in core.conf file * updated kernel patches for Linux kernel 3.5 @@ -369,7 +401,7 @@ * integrate ns-3 node location between CORE and ns-3 simulation * added ns-3 random walk mobility example * updated ns-3 Wifi example to allow GUI connection and moving of nodes -* fixed the following bugs: 54, 103, 111, 136, 145, 153, 157, 160, 161, 162, 164, 165, 168, 170, 171, 173, 174, 176, +* fixed the following bugs: 54, 103, 111, 136, 145, 153, 157, 160, 161, 162, 164, 165, 168, 170, 171, 173, 174, 176, 184, 190, 193 ## 2012-09-25 CORE 4.4 @@ -410,7 +442,7 @@ * support /etc/core/environment and ~/.core/environment files * added Ns2ScriptedMobility model to Python, removed from the GUI * namespace nodes mount a private /sys - * fixed the following bugs: 80, 81, 84, 99, 104, 109, 110, 122, 124, 131, 133, 134, 135, 137, 140, 143, 144, 146, + * fixed the following bugs: 80, 81, 84, 99, 104, 109, 110, 122, 124, 131, 133, 134, 135, 137, 140, 143, 144, 146, 147, 151, 154, 155 ## 2012-03-07 CORE 4.3 From 44bf4e020cc8ed1179b150a49d87d258918beab1 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Thu, 20 Feb 2020 09:46:25 -0800 Subject: [PATCH 12/13] updated config services frr to match standard frr service --- .../configservices/frrservices/templates/frrboot.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/daemon/core/configservices/frrservices/templates/frrboot.sh b/daemon/core/configservices/frrservices/templates/frrboot.sh index 5a6a0e3d..3c14cd1a 100644 --- a/daemon/core/configservices/frrservices/templates/frrboot.sh +++ b/daemon/core/configservices/frrservices/templates/frrboot.sh @@ -74,6 +74,9 @@ bootfrr() fi bootdaemon "zebra" + if grep -q "^ip route " $FRR_CONF; then + bootdaemon "staticd" + fi for r in rip ripng ospf6 ospf bgp babel; do if grep -q "^router \\<$${}{r}\\>" $FRR_CONF; then bootdaemon "$${}{r}d" @@ -93,3 +96,10 @@ if [ "$1" != "zebra" ]; then fi confcheck bootfrr + +# reset interfaces +% for ifc, _, _ , _ in interfaces: +ip link set dev ${ifc.name} down +sleep 1 +ip link set dev ${ifc.name} up +% endfor From 20be527add0e0a78b4eebacf572a9d099d8a93ba Mon Sep 17 00:00:00 2001 From: Huy Pham <42948410+hpham@users.noreply.github.com> Date: Thu, 20 Feb 2020 10:02:13 -0800 Subject: [PATCH 13/13] remove extra code --- daemon/core/gui/dialogs/preferences.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/daemon/core/gui/dialogs/preferences.py b/daemon/core/gui/dialogs/preferences.py index dd1c1c04..83f50f07 100644 --- a/daemon/core/gui/dialogs/preferences.py +++ b/daemon/core/gui/dialogs/preferences.py @@ -10,9 +10,6 @@ from core.gui.themes import FRAME_PAD, PADX, PADY, scale_fonts if TYPE_CHECKING: from core.gui.app import Application -WIDTH = 1000 -HEIGHT = 800 - class PreferencesDialog(Dialog): def __init__(self, master: "Application", app: "Application"):