diff --git a/daemon/core/gui/app.py b/daemon/core/gui/app.py index 195f0e91..a7dd6549 100644 --- a/daemon/core/gui/app.py +++ b/daemon/core/gui/app.py @@ -6,7 +6,6 @@ from core.gui import appconfig, themes from core.gui.coreclient import CoreClient from core.gui.graph.graph import CanvasGraph from core.gui.images import ImageEnum, Images -from core.gui.menuaction import MenuAction from core.gui.menubar import Menubar from core.gui.nodeutils import NodeUtils from core.gui.statusbar import StatusBar @@ -104,8 +103,7 @@ class Application(tk.Frame): self.statusbar.pack(side=tk.BOTTOM, fill=tk.X) def on_closing(self): - menu_action = MenuAction(self, self.master) - menu_action.on_quit() + self.menubar.prompt_save_running_session(True) def save_config(self): appconfig.save(self.guiconfig) diff --git a/daemon/core/gui/coreclient.py b/daemon/core/gui/coreclient.py index 61350d05..76bbc424 100644 --- a/daemon/core/gui/coreclient.py +++ b/daemon/core/gui/coreclient.py @@ -32,15 +32,15 @@ if TYPE_CHECKING: GUI_SOURCE = "gui" OBSERVERS = { - "processes": "ps", - "ifconfig": "ifconfig", + "List Processes": "ps", + "Show Interfaces": "ip address", "IPV4 Routes": "ip -4 ro", "IPV6 Routes": "ip -6 ro", - "Listening sockets": "netstat -tuwnl", - "IPv4 MFC entries": "ip -4 mroute show", - "IPv6 MFC entries": "ip -6 mroute show", - "firewall rules": "iptables -L", - "IPSec policies": "setkey -DP", + "Listening Sockets": "netstat -tuwnl", + "IPv4 MFC Entries": "ip -4 mroute show", + "IPv6 MFC Entries": "ip -6 mroute show", + "Firewall Rules": "iptables -L", + "IPSec Policies": "setkey -DP", } @@ -100,10 +100,8 @@ class CoreClient: self.mobility_players = {} self.handling_throughputs = None self.handling_events = None - self.xml_dir = None self.xml_file = None - self.modified_service_nodes = set() @property @@ -454,7 +452,8 @@ class CoreClient: response = self.client.delete_session(session_id) logging.info("deleted session(%s), Result: %s", session_id, response) except grpc.RpcError as e: - # use the right master widget so the error dialog displays right on top of it + # use the right master widget so the error dialog displays + # right on top of it master = self.app if parent_frame: master = parent_frame diff --git a/daemon/core/gui/menuaction.py b/daemon/core/gui/menuaction.py deleted file mode 100644 index ac191a69..00000000 --- a/daemon/core/gui/menuaction.py +++ /dev/null @@ -1,203 +0,0 @@ -""" -The actions taken when each menubar option is clicked -""" - -import logging -import os -import tkinter as tk -import webbrowser -from tkinter import filedialog, messagebox -from typing import TYPE_CHECKING - -import grpc - -from core.gui.appconfig import XMLS_PATH -from core.gui.dialogs.about import AboutDialog -from core.gui.dialogs.canvassizeandscale import SizeAndScaleDialog -from core.gui.dialogs.canvaswallpaper import CanvasWallpaperDialog -from core.gui.dialogs.hooks import HooksDialog -from core.gui.dialogs.observers import ObserverDialog -from core.gui.dialogs.preferences import PreferencesDialog -from core.gui.dialogs.servers import ServersDialog -from core.gui.dialogs.sessionoptions import SessionOptionsDialog -from core.gui.dialogs.sessions import SessionsDialog -from core.gui.dialogs.throughput import ThroughputDialog -from core.gui.task import BackgroundTask - -MAX_FILES = 3 - -if TYPE_CHECKING: - from core.gui.app import Application - - -class MenuAction: - def __init__(self, app: "Application", master: tk.Tk): - self.master = master - self.app = app - self.canvas = app.canvas - - def cleanup_old_session(self, session_id: int): - try: - res = self.app.core.client.get_session(session_id) - logging.debug("retrieve session(%s), %s", session_id, res) - stop_response = self.app.core.stop_session() - logging.debug("stop session(%s), result: %s", session_id, stop_response) - delete_response = self.app.core.delete_session(session_id) - logging.debug( - "deleted session(%s), result: %s", session_id, delete_response - ) - except grpc.RpcError: - logging.debug("session is not alive") - - def prompt_save_running_session(self, quitapp: bool = False): - """ - Prompt use to stop running session before application is closed - """ - result = True - if self.app.core.is_runtime(): - result = messagebox.askyesnocancel("Exit", "Stop the running session?") - - if result: - callback = None - if quitapp: - callback = self.app.quit - task = BackgroundTask( - self.app, - self.cleanup_old_session, - callback, - (self.app.core.session_id,), - ) - task.start() - elif quitapp: - self.app.quit() - - def on_quit(self, event: tk.Event = None): - """ - Prompt user whether so save running session, and then close the application - """ - self.prompt_save_running_session(quitapp=True) - - def file_save_as_xml(self, event: tk.Event = None): - init_dir = self.app.core.xml_dir - if not init_dir: - init_dir = str(XMLS_PATH) - file_path = filedialog.asksaveasfilename( - initialdir=init_dir, - title="Save As", - filetypes=(("EmulationScript XML files", "*.xml"), ("All files", "*")), - defaultextension=".xml", - ) - if file_path: - self.add_recent_file_to_gui_config(file_path) - self.app.core.save_xml(file_path) - self.app.core.xml_file = file_path - - def file_open_xml(self, event: tk.Event = None): - init_dir = self.app.core.xml_dir - if not init_dir: - init_dir = str(XMLS_PATH) - file_path = filedialog.askopenfilename( - initialdir=init_dir, - title="Open", - filetypes=(("XML Files", "*.xml"), ("All Files", "*")), - ) - self.open_xml_task(file_path) - - def open_xml_task(self, filename): - if filename: - self.add_recent_file_to_gui_config(filename) - self.app.core.xml_file = filename - self.app.core.xml_dir = str(os.path.dirname(filename)) - self.prompt_save_running_session() - self.app.statusbar.progress_bar.start(5) - task = BackgroundTask(self.app, self.app.core.open_xml, args=(filename,)) - task.start() - - def gui_preferences(self): - dialog = PreferencesDialog(self.app, self.app) - dialog.show() - - def canvas_size_and_scale(self): - dialog = SizeAndScaleDialog(self.app, self.app) - dialog.show() - - def canvas_set_wallpaper(self): - dialog = CanvasWallpaperDialog(self.app, self.app) - dialog.show() - - def help_core_github(self): - webbrowser.open_new("https://github.com/coreemu/core") - - def help_core_documentation(self): - webbrowser.open_new("http://coreemu.github.io/core/") - - def session_options(self): - logging.debug("Click options") - dialog = SessionOptionsDialog(self.app, self.app) - if not dialog.has_error: - dialog.show() - - def session_change_sessions(self): - logging.debug("Click change sessions") - dialog = SessionsDialog(self.app, self.app) - dialog.show() - - def session_hooks(self): - logging.debug("Click hooks") - dialog = HooksDialog(self.app, self.app) - dialog.show() - - def session_servers(self): - logging.debug("Click emulation servers") - dialog = ServersDialog(self.app, self.app) - dialog.show() - - def edit_observer_widgets(self) -> None: - dialog = ObserverDialog(self.app, self.app) - dialog.show() - - def show_about(self) -> None: - dialog = AboutDialog(self.app, self.app) - dialog.show() - - def throughput(self) -> None: - if not self.app.core.handling_throughputs: - self.app.core.enable_throughputs() - else: - self.app.core.cancel_throughputs() - - def copy(self, event: tk.Event = None) -> None: - self.app.canvas.copy() - - def paste(self, event: tk.Event = None) -> None: - self.app.canvas.paste() - - def delete(self, event: tk.Event = None) -> None: - self.app.canvas.delete_selected_objects() - - def config_throughput(self) -> None: - dialog = ThroughputDialog(self.app, self.app) - dialog.show() - - def add_recent_file_to_gui_config(self, file_path) -> None: - recent_files = self.app.guiconfig["recentfiles"] - num_files = len(recent_files) - if num_files == 0: - recent_files.insert(0, file_path) - elif 0 < num_files <= MAX_FILES: - if file_path in recent_files: - recent_files.remove(file_path) - recent_files.insert(0, file_path) - else: - if num_files == MAX_FILES: - recent_files.pop() - recent_files.insert(0, file_path) - else: - logging.error("unexpected number of recent files") - self.app.save_config() - self.app.menubar.update_recent_files() - - def new_session(self): - self.prompt_save_running_session() - self.app.core.create_new_session() - self.app.core.xml_file = None diff --git a/daemon/core/gui/menubar.py b/daemon/core/gui/menubar.py index 0aa22e0c..97977f83 100644 --- a/daemon/core/gui/menubar.py +++ b/daemon/core/gui/menubar.py @@ -1,35 +1,50 @@ import logging import os import tkinter as tk +import webbrowser from functools import partial +from tkinter import filedialog, messagebox from typing import TYPE_CHECKING -import core.gui.menuaction as action +from core.gui.appconfig import XMLS_PATH from core.gui.coreclient import OBSERVERS +from core.gui.dialogs.about import AboutDialog +from core.gui.dialogs.canvassizeandscale import SizeAndScaleDialog +from core.gui.dialogs.canvaswallpaper import CanvasWallpaperDialog from core.gui.dialogs.executepython import ExecutePythonDialog +from core.gui.dialogs.hooks import HooksDialog +from core.gui.dialogs.observers import ObserverDialog +from core.gui.dialogs.preferences import PreferencesDialog +from core.gui.dialogs.servers import ServersDialog +from core.gui.dialogs.sessionoptions import SessionOptionsDialog +from core.gui.dialogs.sessions import SessionsDialog +from core.gui.dialogs.throughput import ThroughputDialog +from core.gui.task import BackgroundTask if TYPE_CHECKING: from core.gui.app import Application +MAX_FILES = 3 + class Menubar(tk.Menu): """ Core menubar """ - def __init__(self, master: tk.Tk, app: "Application", cnf={}, **kwargs): + def __init__(self, master: tk.Tk, app: "Application", **kwargs) -> None: """ Create a CoreMenubar instance """ - super().__init__(master, cnf, **kwargs) + super().__init__(master, **kwargs) self.master.config(menu=self) self.app = app - self.menuaction = action.MenuAction(app, master) + self.core = app.core self.recent_menu = None self.edit_menu = None self.draw() - def draw(self): + def draw(self) -> None: """ Create core menubar and bind the hot keys to their matching command """ @@ -42,24 +57,22 @@ class Menubar(tk.Menu): self.draw_session_menu() self.draw_help_menu() - def draw_file_menu(self): + def draw_file_menu(self) -> None: """ Create file menu """ menu = tk.Menu(self) menu.add_command( - label="New Session", - accelerator="Ctrl+N", - command=self.menuaction.new_session, + label="New Session", accelerator="Ctrl+N", command=self.click_new ) - self.app.bind_all("", lambda e: self.app.core.create_new_session()) - menu.add_command(label="Save", accelerator="Ctrl+S", command=self.save) - self.app.bind_all("", self.save) - menu.add_command(label="Save As...", command=self.menuaction.file_save_as_xml) + self.app.bind_all("", lambda e: self.click_new()) + menu.add_command(label="Save", accelerator="Ctrl+S", command=self.click_save) + self.app.bind_all("", self.click_save) + menu.add_command(label="Save As...", command=self.click_save_xml) menu.add_command( - label="Open...", command=self.menuaction.file_open_xml, accelerator="Ctrl+O" + label="Open...", command=self.click_open_xml, accelerator="Ctrl+O" ) - self.app.bind_all("", self.menuaction.file_open_xml) + self.app.bind_all("", self.click_open_xml) self.recent_menu = tk.Menu(menu) for i in self.app.guiconfig["recentfiles"]: self.recent_menu.add_command( @@ -70,51 +83,47 @@ class Menubar(tk.Menu): menu.add_command(label="Execute Python Script...", command=self.execute_python) menu.add_separator() menu.add_command( - label="Quit", accelerator="Ctrl+Q", command=self.menuaction.on_quit + label="Quit", + accelerator="Ctrl+Q", + command=lambda: self.prompt_save_running_session(True), + ) + self.app.bind_all( + "", lambda _: self.prompt_save_running_session(True) ) - self.app.bind_all("", self.menuaction.on_quit) self.add_cascade(label="File", menu=menu) - def draw_edit_menu(self): + def draw_edit_menu(self) -> None: """ Create edit menu """ menu = tk.Menu(self) - menu.add_command(label="Preferences", command=self.menuaction.gui_preferences) + menu.add_command(label="Preferences", command=self.click_preferences) menu.add_command(label="Undo", accelerator="Ctrl+Z", state=tk.DISABLED) menu.add_command(label="Redo", accelerator="Ctrl+Y", state=tk.DISABLED) menu.add_separator() menu.add_command(label="Cut", accelerator="Ctrl+X", state=tk.DISABLED) + menu.add_command(label="Copy", accelerator="Ctrl+C", command=self.click_copy) + menu.add_command(label="Paste", accelerator="Ctrl+V", command=self.click_paste) menu.add_command( - label="Copy", accelerator="Ctrl+C", command=self.menuaction.copy - ) - menu.add_command( - label="Paste", accelerator="Ctrl+V", command=self.menuaction.paste - ) - menu.add_command( - label="Delete", accelerator="Ctrl+D", command=self.menuaction.delete + label="Delete", accelerator="Ctrl+D", command=self.click_delete ) self.add_cascade(label="Edit", menu=menu) - self.app.master.bind_all("", self.menuaction.copy) - self.app.master.bind_all("", self.menuaction.paste) - self.app.master.bind_all("", self.menuaction.delete) + self.app.master.bind_all("", self.click_copy) + self.app.master.bind_all("", self.click_paste) + self.app.master.bind_all("", self.click_delete) self.edit_menu = menu - def draw_canvas_menu(self): + def draw_canvas_menu(self) -> None: """ Create canvas menu """ menu = tk.Menu(self) - menu.add_command( - label="Size / Scale", command=self.menuaction.canvas_size_and_scale - ) - menu.add_command( - label="Wallpaper", command=self.menuaction.canvas_set_wallpaper - ) + menu.add_command(label="Size / Scale", command=self.click_canvas_size_and_scale) + menu.add_command(label="Wallpaper", command=self.click_canvas_wallpaper) self.add_cascade(label="Canvas", menu=menu) - def draw_view_menu(self): + def draw_view_menu(self) -> None: """ Create view menu """ @@ -130,7 +139,7 @@ class Menubar(tk.Menu): menu.add_command(label="Grid", state=tk.DISABLED) self.add_cascade(label="View", menu=menu) - def draw_tools_menu(self): + def draw_tools_menu(self) -> None: """ Create tools menu """ @@ -140,7 +149,7 @@ class Menubar(tk.Menu): menu.add_command(label="MAC Addresses", state=tk.DISABLED) self.add_cascade(label="Tools", menu=menu) - def create_observer_widgets_menu(self, widget_menu: tk.Menu): + def create_observer_widgets_menu(self, widget_menu: tk.Menu) -> None: """ Create observer widget menu item and create the sub menu items inside """ @@ -148,14 +157,14 @@ class Menubar(tk.Menu): menu = tk.Menu(widget_menu) menu.var = var menu.add_command( - label="Edit Observers", command=self.menuaction.edit_observer_widgets + label="Edit Observers", command=self.click_edit_observer_widgets ) menu.add_separator() menu.add_radiobutton( label="None", variable=var, value="none", - command=lambda: self.app.core.set_observer(None), + command=lambda: self.core.set_observer(None), ) for name in sorted(OBSERVERS): cmd = OBSERVERS[name] @@ -163,19 +172,19 @@ class Menubar(tk.Menu): label=name, variable=var, value=name, - command=partial(self.app.core.set_observer, cmd), + command=partial(self.core.set_observer, cmd), ) - for name in sorted(self.app.core.custom_observers): - observer = self.app.core.custom_observers[name] + for name in sorted(self.core.custom_observers): + observer = self.core.custom_observers[name] menu.add_radiobutton( label=name, variable=var, value=name, - command=partial(self.app.core.set_observer, observer.cmd), + command=partial(self.core.set_observer, observer.cmd), ) widget_menu.add_cascade(label="Observer Widgets", menu=menu) - def create_adjacency_menu(self, widget_menu: tk.Menu): + def create_adjacency_menu(self, widget_menu: tk.Menu) -> None: """ Create adjacency menu item and the sub menu items inside """ @@ -187,17 +196,15 @@ class Menubar(tk.Menu): menu.add_command(label="Enable OSLRv2?", state=tk.DISABLED) widget_menu.add_cascade(label="Adjacency", menu=menu) - def create_throughput_menu(self, widget_menu: tk.Menu): + def create_throughput_menu(self, widget_menu: tk.Menu) -> None: menu = tk.Menu(widget_menu) menu.add_command( - label="Configure Throughput", command=self.menuaction.config_throughput - ) - menu.add_checkbutton( - label="Enable Throughput?", command=self.menuaction.throughput + label="Configure Throughput", command=self.click_config_throughput ) + menu.add_checkbutton(label="Enable Throughput?", command=self.click_throughput) widget_menu.add_cascade(label="Throughput", menu=menu) - def draw_widgets_menu(self): + def draw_widgets_menu(self) -> None: """ Create widget menu """ @@ -207,60 +214,107 @@ class Menubar(tk.Menu): self.create_throughput_menu(menu) self.add_cascade(label="Widgets", menu=menu) - def draw_session_menu(self): + def draw_session_menu(self) -> None: """ Create session menu """ menu = tk.Menu(self) - menu.add_command( - label="Sessions", command=self.menuaction.session_change_sessions - ) - menu.add_command(label="Servers", command=self.menuaction.session_servers) - menu.add_command(label="Options", command=self.menuaction.session_options) - menu.add_command(label="Hooks", command=self.menuaction.session_hooks) + menu.add_command(label="Sessions", command=self.click_sessions) + menu.add_command(label="Servers", command=self.click_servers) + menu.add_command(label="Options", command=self.click_session_options) + menu.add_command(label="Hooks", command=self.click_hooks) self.add_cascade(label="Session", menu=menu) - def draw_help_menu(self): + def draw_help_menu(self) -> None: """ Create help menu """ menu = tk.Menu(self) - menu.add_command( - label="Core GitHub (www)", command=self.menuaction.help_core_github - ) - menu.add_command( - label="Core Documentation (www)", - command=self.menuaction.help_core_documentation, - ) - menu.add_command(label="About", command=self.menuaction.show_about) + menu.add_command(label="Core GitHub (www)", command=self.click_core_github) + menu.add_command(label="Core Documentation (www)", command=self.click_core_doc) + menu.add_command(label="About", command=self.click_about) self.add_cascade(label="Help", menu=menu) - def open_recent_files(self, filename: str): + def open_recent_files(self, filename: str) -> None: if os.path.isfile(filename): logging.debug("Open recent file %s", filename) - self.menuaction.open_xml_task(filename) + self.open_xml_task(filename) else: logging.warning("File does not exist %s", filename) - def update_recent_files(self): + def update_recent_files(self) -> None: self.recent_menu.delete(0, tk.END) for i in self.app.guiconfig["recentfiles"]: self.recent_menu.add_command( label=i, command=partial(self.open_recent_files, i) ) - def save(self, event=None): - xml_file = self.app.core.xml_file + def click_save(self, _event=None) -> None: + xml_file = self.core.xml_file if xml_file: - self.app.core.save_xml(xml_file) + self.core.save_xml(xml_file) else: - self.menuaction.file_save_as_xml() + self.click_save_xml() + + def click_save_xml(self, _event: tk.Event = None) -> None: + init_dir = self.core.xml_dir + if not init_dir: + init_dir = str(XMLS_PATH) + file_path = filedialog.asksaveasfilename( + initialdir=init_dir, + title="Save As", + filetypes=(("XML files", "*.xml"), ("All files", "*")), + defaultextension=".xml", + ) + if file_path: + self.add_recent_file_to_gui_config(file_path) + self.core.save_xml(file_path) + self.core.xml_file = file_path + + def click_open_xml(self, _event: tk.Event = None) -> None: + init_dir = self.core.xml_dir + if not init_dir: + init_dir = str(XMLS_PATH) + file_path = filedialog.askopenfilename( + initialdir=init_dir, + title="Open", + filetypes=(("XML Files", "*.xml"), ("All Files", "*")), + ) + if file_path: + self.open_xml_task(file_path) + + def open_xml_task(self, filename: str) -> None: + self.add_recent_file_to_gui_config(filename) + self.core.xml_file = filename + self.core.xml_dir = str(os.path.dirname(filename)) + self.prompt_save_running_session() + self.app.statusbar.progress_bar.start(5) + task = BackgroundTask(self.app, self.core.open_xml, args=(filename,)) + task.start() def execute_python(self): dialog = ExecutePythonDialog(self.app, self.app) dialog.show() - def change_menubar_item_state(self, is_runtime: bool): + def add_recent_file_to_gui_config(self, file_path) -> None: + recent_files = self.app.guiconfig["recentfiles"] + num_files = len(recent_files) + if num_files == 0: + recent_files.insert(0, file_path) + elif 0 < num_files <= MAX_FILES: + if file_path in recent_files: + recent_files.remove(file_path) + recent_files.insert(0, file_path) + else: + if num_files == MAX_FILES: + recent_files.pop() + recent_files.insert(0, file_path) + else: + logging.error("unexpected number of recent files") + self.app.save_config() + self.app.menubar.update_recent_files() + + def change_menubar_item_state(self, is_runtime: bool) -> None: for i in range(self.edit_menu.index("end")): try: label_name = self.edit_menu.entrycget(i, "label") @@ -271,3 +325,92 @@ class Menubar(tk.Menu): self.edit_menu.entryconfig(i, state="normal") except tk.TclError: logging.debug("Ignore separators") + + def prompt_save_running_session(self, quit_app: bool = False) -> None: + """ + Prompt use to stop running session before application is closed + + :param quit_app: True to quit app, False otherwise + """ + result = True + if self.core.is_runtime(): + result = messagebox.askyesnocancel("Exit", "Stop the running session?") + if result: + callback = None + if quit_app: + callback = self.app.quit + task = BackgroundTask(self.app, self.core.delete_session, callback) + task.start() + elif quit_app: + self.app.quit() + + def click_new(self) -> None: + self.prompt_save_running_session() + self.core.create_new_session() + self.core.xml_file = None + + def click_preferences(self) -> None: + dialog = PreferencesDialog(self.app, self.app) + dialog.show() + + def click_canvas_size_and_scale(self) -> None: + dialog = SizeAndScaleDialog(self.app, self.app) + dialog.show() + + def click_canvas_wallpaper(self) -> None: + dialog = CanvasWallpaperDialog(self.app, self.app) + dialog.show() + + def click_core_github(self) -> None: + webbrowser.open_new("https://github.com/coreemu/core") + + def click_core_doc(self) -> None: + webbrowser.open_new("http://coreemu.github.io/core/") + + def click_about(self) -> None: + dialog = AboutDialog(self.app, self.app) + dialog.show() + + def click_throughput(self) -> None: + if not self.core.handling_throughputs: + self.core.enable_throughputs() + else: + self.core.cancel_throughputs() + + def click_config_throughput(self) -> None: + dialog = ThroughputDialog(self.app, self.app) + dialog.show() + + def click_copy(self, _event: tk.Event = None) -> None: + self.app.canvas.copy() + + def click_paste(self, _event: tk.Event = None) -> None: + self.app.canvas.paste() + + def click_delete(self, _event: tk.Event = None) -> None: + self.app.canvas.delete_selected_objects() + + def click_session_options(self) -> None: + logging.debug("Click options") + dialog = SessionOptionsDialog(self.app, self.app) + if not dialog.has_error: + dialog.show() + + def click_sessions(self) -> None: + logging.debug("Click change sessions") + dialog = SessionsDialog(self.app, self.app) + dialog.show() + + def click_hooks(self) -> None: + logging.debug("Click hooks") + dialog = HooksDialog(self.app, self.app) + dialog.show() + + def click_servers(self) -> None: + logging.debug("Click emulation servers") + dialog = ServersDialog(self.app, self.app) + dialog.show() + + def click_edit_observer_widgets(self) -> None: + dialog = ObserverDialog(self.app, self.app) + dialog.show() diff --git a/daemon/core/gui/task.py b/daemon/core/gui/task.py index eb6655f8..bee69be3 100644 --- a/daemon/core/gui/task.py +++ b/daemon/core/gui/task.py @@ -21,7 +21,8 @@ class BackgroundTask: def run(self): result = self.task(*self.args) logging.info("task completed") - # if start session fails, a response with Result: False and a list of exceptions is returned + # if start session fails, a response with Result: False and a list of + # exceptions is returned if not getattr(result, "result", True): if len(getattr(result, "exceptions", [])) > 0: self.master.after( diff --git a/daemon/core/gui/toolbar.py b/daemon/core/gui/toolbar.py index efd93b76..d6707fb2 100644 --- a/daemon/core/gui/toolbar.py +++ b/daemon/core/gui/toolbar.py @@ -499,7 +499,6 @@ class Toolbar(ttk.Frame): def click_two_node_button(self): logging.debug("Click TWONODE button") - # def scale_button(cls, button, image_enum, scale): def scale_button(self, button, image_enum): image = icon(image_enum, int(TOOLBAR_SIZE * self.app.app_scale)) button.config(image=image)