pygui consolidated menubar and menuaction code into one file, small updates to observer widgets to avoid using ifconfig
This commit is contained in:
parent
c43afa4b40
commit
7da7ea5d62
6 changed files with 231 additions and 294 deletions
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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("<Control-n>", lambda e: self.app.core.create_new_session())
|
||||
menu.add_command(label="Save", accelerator="Ctrl+S", command=self.save)
|
||||
self.app.bind_all("<Control-s>", self.save)
|
||||
menu.add_command(label="Save As...", command=self.menuaction.file_save_as_xml)
|
||||
self.app.bind_all("<Control-n>", lambda e: self.click_new())
|
||||
menu.add_command(label="Save", accelerator="Ctrl+S", command=self.click_save)
|
||||
self.app.bind_all("<Control-s>", 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("<Control-o>", self.menuaction.file_open_xml)
|
||||
self.app.bind_all("<Control-o>", 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(
|
||||
"<Control-q>", lambda _: self.prompt_save_running_session(True)
|
||||
)
|
||||
self.app.bind_all("<Control-q>", 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("<Control-c>", self.menuaction.copy)
|
||||
self.app.master.bind_all("<Control-v>", self.menuaction.paste)
|
||||
self.app.master.bind_all("<Control-d>", self.menuaction.delete)
|
||||
self.app.master.bind_all("<Control-c>", self.click_copy)
|
||||
self.app.master.bind_all("<Control-v>", self.click_paste)
|
||||
self.app.master.bind_all("<Control-d>", 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()
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue