fix merge conflicts, continued work on node service configuration

This commit is contained in:
Huy Pham 2019-11-13 09:30:49 -08:00
commit c652ad2321
19 changed files with 619 additions and 437 deletions

View file

@ -1,5 +1,6 @@
import logging
import tkinter as tk
from tkinter import ttk
from coretk import appconfig
from coretk.coreclient import CoreClient
@ -36,7 +37,7 @@ class Application(tk.Frame):
self.master.title("CORE")
self.master.geometry("1000x800")
self.master.protocol("WM_DELETE_WINDOW", self.on_closing)
image = Images.get(ImageEnum.CORE)
image = Images.get(ImageEnum.CORE, 16)
self.master.tk.call("wm", "iconphoto", self.master._w, image)
self.pack(fill=tk.BOTH, expand=True)
@ -53,17 +54,17 @@ class Application(tk.Frame):
self, self.core, background="#cccccc", scrollregion=(0, 0, 1200, 1000)
)
self.canvas.pack(fill=tk.BOTH, expand=True)
scroll_x = tk.Scrollbar(
scroll_x = ttk.Scrollbar(
self.canvas, orient=tk.HORIZONTAL, command=self.canvas.xview
)
scroll_x.pack(side=tk.BOTTOM, fill=tk.X)
scroll_y = tk.Scrollbar(self.canvas, command=self.canvas.yview)
scroll_y = ttk.Scrollbar(self.canvas, command=self.canvas.yview)
scroll_y.pack(side=tk.RIGHT, fill=tk.Y)
self.canvas.configure(xscrollcommand=scroll_x.set)
self.canvas.configure(yscrollcommand=scroll_y.set)
def draw_status(self):
self.statusbar = tk.Frame(self)
self.statusbar = ttk.Frame(self)
self.statusbar.pack(side=tk.BOTTOM, fill=tk.X)
def on_closing(self):

View file

@ -8,7 +8,7 @@ from core.api.grpc import client, core_pb2
from coretk.coretocanvas import CoreToCanvasMapping
from coretk.dialogs.sessions import SessionsDialog
from coretk.emaneodelnodeconfig import EmaneModelNodeConfig
from coretk.images import Images
from coretk.images import NODE_WIDTH, Images
from coretk.interface import Interface, InterfaceManager
from coretk.mobilitynodeconfig import MobilityNodeConfig
from coretk.servicenodeconfig import ServiceNodeConfig
@ -139,7 +139,7 @@ class CoreClient:
# read custom nodes
for config in self.app.config.get("nodes", []):
image_file = config["image"]
image = Images.get_custom(image_file)
image = Images.get_custom(image_file, NODE_WIDTH)
custom_node = CustomNode(
config["name"], image, image_file, set(config["services"])
)

View file

@ -11,6 +11,8 @@ from PIL import Image, ImageTk
from coretk.appconfig import BACKGROUNDS_PATH
from coretk.dialogs.dialog import Dialog
PADX = 5
class ScaleOption(enum.Enum):
NONE = 0
@ -65,10 +67,10 @@ class CanvasBackgroundDialog(Dialog):
entry = ttk.Entry(frame, textvariable=self.file_name)
entry.focus()
entry.grid(row=0, column=0, sticky="ew")
entry.grid(row=0, column=0, sticky="ew", padx=PADX)
button = ttk.Button(frame, text="...", command=self.click_open_image)
button.grid(row=0, column=1, sticky="ew")
button.grid(row=0, column=1, sticky="ew", padx=PADX)
button = ttk.Button(frame, text="Clear", command=self.click_clear)
button.grid(row=0, column=2, sticky="ew")
@ -105,7 +107,7 @@ class CanvasBackgroundDialog(Dialog):
checkbutton = ttk.Checkbutton(
self, text="Show grid", variable=self.show_grid_var
)
checkbutton.grid(row=4, column=0, sticky="ew", padx=5)
checkbutton.grid(row=4, column=0, sticky="ew", padx=PADX)
checkbutton = ttk.Checkbutton(
self,
@ -113,7 +115,7 @@ class CanvasBackgroundDialog(Dialog):
variable=self.adjust_to_dim_var,
command=self.click_adjust_canvas,
)
checkbutton.grid(row=5, column=0, sticky="ew", padx=5)
checkbutton.grid(row=5, column=0, sticky="ew", padx=PADX)
self.show_grid_var.set(1)
self.adjust_to_dim_var.set(0)
@ -125,7 +127,7 @@ class CanvasBackgroundDialog(Dialog):
frame.columnconfigure(1, weight=1)
button = ttk.Button(frame, text="Apply", command=self.click_apply)
button.grid(row=0, column=0, sticky="ew")
button.grid(row=0, column=0, sticky="ew", padx=PADX)
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=1, sticky="ew")
@ -206,7 +208,6 @@ class CanvasBackgroundDialog(Dialog):
return
def upper_left(self, img):
print("upperleft")
tk_img = ImageTk.PhotoImage(img)
# crop image if it is bigger than canvas

View file

@ -8,6 +8,9 @@ from coretk.dialogs.canvasbackground import ScaleOption
from coretk.dialogs.dialog import Dialog
DRAW_OBJECT_TAGS = ["edge", "node", "nodename", "linkinfo", "antenna"]
FRAME_BAD = 5
PAD = (0, 0, 5, 0)
PADX = 5
class SizeAndScaleDialog(Dialog):
@ -49,101 +52,105 @@ class SizeAndScaleDialog(Dialog):
self.draw_buttons()
def draw_size(self):
label = ttk.Label(self, text="Size", font=self.section_font)
label.grid(sticky="w")
label_frame = ttk.Labelframe(self, text="Size", padding=FRAME_BAD)
label_frame.grid(sticky="ew")
label_frame.columnconfigure(0, weight=1)
# draw size row 1
frame = ttk.Frame(self)
frame = ttk.Frame(label_frame)
frame.grid(sticky="ew", pady=3)
frame.columnconfigure(1, weight=1)
frame.columnconfigure(3, weight=1)
label = ttk.Label(frame, text="Width")
label.grid(row=0, column=0, sticky="w")
label.grid(row=0, column=0, sticky="w", padx=PADX)
entry = ttk.Entry(frame, textvariable=self.pixel_width)
entry.grid(row=0, column=1, sticky="ew")
entry.grid(row=0, column=1, sticky="ew", padx=PADX)
label = ttk.Label(frame, text="x Height")
label.grid(row=0, column=2, sticky="w")
label.grid(row=0, column=2, sticky="w", padx=PADX)
entry = ttk.Entry(frame, textvariable=self.pixel_height)
entry.grid(row=0, column=3, sticky="ew")
entry.grid(row=0, column=3, sticky="ew", padx=PADX)
label = ttk.Label(frame, text="Pixels")
label.grid(row=0, column=4, sticky="w")
# draw size row 2
frame = ttk.Frame(self)
frame = ttk.Frame(label_frame)
frame.grid(sticky="ew", pady=3)
frame.columnconfigure(1, weight=1)
frame.columnconfigure(3, weight=1)
label = ttk.Label(frame, text="Width")
label.grid(row=0, column=0, sticky="w")
label.grid(row=0, column=0, sticky="w", padx=PADX)
entry = ttk.Entry(frame, textvariable=self.meters_width)
entry.grid(row=0, column=1, sticky="ew")
entry.grid(row=0, column=1, sticky="ew", padx=PADX)
label = ttk.Label(frame, text="x Height")
label.grid(row=0, column=2, sticky="w")
label.grid(row=0, column=2, sticky="w", padx=PADX)
entry = ttk.Entry(frame, textvariable=self.meters_height)
entry.grid(row=0, column=3, sticky="ew")
entry.grid(row=0, column=3, sticky="ew", padx=PADX)
label = ttk.Label(frame, text="Meters")
label.grid(row=0, column=4, sticky="w")
def draw_scale(self):
label = ttk.Label(self, text="Scale", font=self.section_font)
label.grid(sticky="w")
label_frame = ttk.Labelframe(self, text="Scale", padding=FRAME_BAD)
label_frame.grid(sticky="ew")
label_frame.columnconfigure(0, weight=1)
frame = ttk.Frame(self)
frame = ttk.Frame(label_frame)
frame.grid(sticky="ew")
frame.columnconfigure(1, weight=1)
label = ttk.Label(frame, text="100 Pixels =")
label.grid(row=0, column=0, sticky="w")
label.grid(row=0, column=0, sticky="w", padx=PADX)
entry = ttk.Entry(frame, textvariable=self.scale)
entry.grid(row=0, column=1, sticky="ew")
entry.grid(row=0, column=1, sticky="ew", padx=PADX)
label = ttk.Label(frame, text="Meters")
label.grid(row=0, column=2, sticky="w")
def draw_reference_point(self):
label = ttk.Label(self, text="Reference point", font=self.section_font)
label.grid(sticky="w")
label = ttk.Label(
self, text="Default is (0, 0), the upper left corner of the canvas"
)
label.grid(sticky="w")
label_frame = ttk.Labelframe(self, text="Reference Point", padding=FRAME_BAD)
label_frame.grid(sticky="ew")
label_frame.columnconfigure(0, weight=1)
frame = ttk.Frame(self)
label = ttk.Label(
label_frame, text="Default is (0, 0), the upper left corner of the canvas"
)
label.grid()
frame = ttk.Frame(label_frame)
frame.grid(sticky="ew", pady=3)
frame.columnconfigure(1, weight=1)
frame.columnconfigure(3, weight=1)
label = ttk.Label(frame, text="X")
label.grid(row=0, column=0, sticky="w")
label.grid(row=0, column=0, sticky="w", padx=PADX)
x_var = tk.StringVar(value=0)
entry = ttk.Entry(frame, textvariable=x_var)
entry.grid(row=0, column=1, sticky="ew")
entry.grid(row=0, column=1, sticky="ew", padx=PADX)
label = ttk.Label(frame, text="Y")
label.grid(row=0, column=2, sticky="w")
label.grid(row=0, column=2, sticky="w", padx=PADX)
y_var = tk.StringVar(value=0)
entry = ttk.Entry(frame, textvariable=y_var)
entry.grid(row=0, column=3, sticky="ew")
entry.grid(row=0, column=3, sticky="ew", padx=PADX)
label = ttk.Label(self, text="Translates To")
label.grid(sticky="w")
label = ttk.Label(label_frame, text="Translates To")
label.grid()
frame = ttk.Frame(self)
frame = ttk.Frame(label_frame)
frame.grid(sticky="ew", pady=3)
frame.columnconfigure(1, weight=1)
frame.columnconfigure(3, weight=1)
frame.columnconfigure(5, weight=1)
label = ttk.Label(frame, text="Lat")
label.grid(row=0, column=0, sticky="w")
label.grid(row=0, column=0, sticky="w", padx=PADX)
entry = ttk.Entry(frame, textvariable=self.lat)
entry.grid(row=0, column=1, sticky="ew")
entry.grid(row=0, column=1, sticky="ew", padx=PADX)
label = ttk.Label(frame, text="Lon")
label.grid(row=0, column=2, sticky="w")
label.grid(row=0, column=2, sticky="w", padx=PADX)
entry = ttk.Entry(frame, textvariable=self.lon)
entry.grid(row=0, column=3, sticky="ew")
entry.grid(row=0, column=3, sticky="ew", padx=PADX)
label = ttk.Label(frame, text="Alt")
label.grid(row=0, column=4, sticky="w")
label.grid(row=0, column=4, sticky="w", padx=PADX)
entry = ttk.Entry(frame, textvariable=self.alt)
entry.grid(row=0, column=5, sticky="ew")
@ -160,10 +167,10 @@ class SizeAndScaleDialog(Dialog):
frame.grid(sticky="ew")
button = ttk.Button(frame, text="Apply", command=self.click_apply)
button.grid(row=0, column=0, pady=5, sticky="ew")
button.grid(row=0, column=0, sticky="ew", padx=PADX)
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=1, pady=5, sticky="ew")
button.grid(row=0, column=1, sticky="ew")
def redraw_grid(self):
"""

View file

@ -2,16 +2,18 @@ import tkinter as tk
from coretk.images import ImageEnum, Images
DIALOG_PAD = 5
class Dialog(tk.Toplevel):
def __init__(self, master, app, title, modal=False):
super().__init__(master, padx=5, pady=5)
super().__init__(master, padx=DIALOG_PAD, pady=DIALOG_PAD)
self.withdraw()
self.app = app
self.modal = modal
self.title(title)
self.protocol("WM_DELETE_WINDOW", self.destroy)
image = Images.get(ImageEnum.CORE)
image = Images.get(ImageEnum.CORE, 16)
self.tk.call("wm", "iconphoto", self._w, image)
def show(self):

View file

@ -182,25 +182,31 @@ class EmaneConfiguration(Dialog):
def draw_option_buttons(self, parent):
f = ttk.Frame(parent)
f.grid(row=4, column=0, sticky="nsew")
f.columnconfigure(0, weight=1)
f.columnconfigure(1, weight=1)
image = Images.get(ImageEnum.EDITNODE, 16)
b = ttk.Button(
f,
text=self.emane_models[0] + " options",
image=Images.get(ImageEnum.EDITNODE),
image=image,
compound=tk.RIGHT,
command=self.draw_model_options,
)
b.image = image
b.grid(row=0, column=0, padx=10, pady=2, sticky="nsew")
image = Images.get(ImageEnum.EDITNODE, 16)
b = ttk.Button(
f,
text="EMANE options",
image=Images.get(ImageEnum.EDITNODE),
image=image,
compound=tk.RIGHT,
command=self.draw_emane_options,
)
b.image = image
b.grid(row=0, column=1, padx=10, pady=2, sticky="nsew")
f.grid(row=4, column=0, sticky="nsew")
def combobox_select(self, event):
"""
@ -271,7 +277,7 @@ class EmaneConfiguration(Dialog):
b = ttk.Button(
f,
image=Images.get(ImageEnum.EDITNODE),
image=Images.get(ImageEnum.EDITNODE, 8),
text="EMANE Wiki",
compound=tk.RIGHT,
command=lambda: webbrowser.open_new(

View file

@ -54,7 +54,7 @@ class IconDialog(Dialog):
),
)
if file_path:
self.image = Images.create(file_path)
self.image = Images.create(file_path, 32, 32)
self.image_label.config(image=self.image)
self.file_path.set(file_path)

View file

@ -2,11 +2,10 @@
mobility configuration
"""
import os
import tkinter as tk
from pathlib import Path
from tkinter import filedialog
from tkinter import filedialog, ttk
from coretk import appconfig
from coretk.dialogs.dialog import Dialog
@ -40,9 +39,9 @@ class MobilityConfiguration(Dialog):
return var
def open_file(self, entry):
configs_dir = os.path.join(Path.home(), ".core/configs")
if os.path.isdir(configs_dir):
filename = filedialog.askopenfilename(initialdir=configs_dir, title="Open")
filename = filedialog.askopenfilename(
initialdir=str(appconfig.MOBILITY_PATH), title="Open"
)
if filename:
entry.delete(0, tk.END)
entry.insert(0, filename)
@ -58,26 +57,26 @@ class MobilityConfiguration(Dialog):
def create_label_entry_filebrowser(
self, parent_frame, text_label, entry_text, filebrowser=False
):
f = tk.Frame(parent_frame, bg="#d9d9d9")
lbl = tk.Label(f, text=text_label, bg="#d9d9d9")
f = ttk.Frame(parent_frame, bg="#d9d9d9")
lbl = ttk.Label(f, text=text_label, bg="#d9d9d9")
lbl.grid(padx=3, pady=3)
# f.grid()
e = tk.Entry(f, textvariable=self.create_string_var(entry_text), bg="#ffffff")
e = ttk.Entry(f, textvariable=self.create_string_var(entry_text), bg="#ffffff")
e.grid(row=0, column=1, padx=3, pady=3)
if filebrowser:
b = tk.Button(f, text="...", command=lambda: self.open_file(e))
b = ttk.Button(f, text="...", command=lambda: self.open_file(e))
b.grid(row=0, column=2, padx=3, pady=3)
f.grid(sticky=tk.E)
def mobility_script_parameters(self):
lbl = tk.Label(self, text="node ns2script")
lbl.grid(sticky=tk.W + tk.E)
lbl = ttk.Label(self, text="node ns2script")
lbl.grid(sticky="ew")
sb = tk.Scrollbar(self, orient=tk.VERTICAL)
sb.grid(row=1, column=1, sticky=tk.N + tk.S + tk.E)
sb = ttk.Scrollbar(self, orient=tk.VERTICAL)
sb.grid(row=1, column=1, sticky="ns")
f = tk.Frame(self, bg="#d9d9d9")
lbl = tk.Label(
f = ttk.Frame(self, bg="#d9d9d9")
lbl = ttk.Label(
f, text="ns-2 Mobility Scripts Parameters", bg="#d9d9d9", relief=tk.RAISED
)
lbl.grid(row=0, column=0, sticky=tk.W)
@ -99,21 +98,21 @@ class MobilityConfiguration(Dialog):
f1, "Refresh time (ms)", self.node_config["refresh_ms"]
)
# f12 = tk.Frame(f1)
# f12 = ttk.Frame(f1)
#
# lbl = tk.Label(f12, text="Refresh time (ms)")
# lbl = ttk.Label(f12, text="Refresh time (ms)")
# lbl.grid()
#
# e = tk.Entry(f12, textvariable=self.create_string_var("50"))
# e = ttk.Entry(f12, textvariable=self.create_string_var("50"))
# e.grid(row=0, column=1)
# f12.grid()
f13 = tk.Frame(f1)
f13 = ttk.Frame(f1)
lbl = tk.Label(f13, text="loop")
lbl = ttk.Label(f13, text="loop")
lbl.grid()
om = tk.OptionMenu(
om = ttk.OptionMenu(
f13, self.create_string_var("On"), "On", "Off", command=self.set_loop_value
)
om.grid(row=0, column=1)
@ -123,24 +122,24 @@ class MobilityConfiguration(Dialog):
self.create_label_entry_filebrowser(
f1, "auto-start seconds (0.0 for runtime)", self.node_config["autostart"]
)
# f14 = tk.Frame(f1)
# f14 = ttk.Frame(f1)
#
# lbl = tk.Label(f14, text="auto-start seconds (0.0 for runtime)")
# lbl = ttk.Label(f14, text="auto-start seconds (0.0 for runtime)")
# lbl.grid()
#
# e = tk.Entry(f14, textvariable=self.create_string_var(""))
# e = ttk.Entry(f14, textvariable=self.create_string_var(""))
# e.grid(row=0, column=1)
#
# f14.grid()
self.create_label_entry_filebrowser(
f1, "node mapping (optional, e.g. 0:1, 1:2, 2:3)", self.node_config["map"]
)
# f15 = tk.Frame(f1)
# f15 = ttk.Frame(f1)
#
# lbl = tk.Label(f15, text="node mapping (optional, e.g. 0:1, 1:2, 2:3)")
# lbl = ttk.Label(f15, text="node mapping (optional, e.g. 0:1, 1:2, 2:3)")
# lbl.grid()
#
# e = tk.Entry(f15, textvariable=self.create_string_var(""))
# e = ttk.Entry(f15, textvariable=self.create_string_var(""))
# e.grid(row=0, column=1)
#
# f15.grid()
@ -230,9 +229,9 @@ class MobilityConfiguration(Dialog):
:return: nothing
"""
f = tk.Frame(self)
b = tk.Button(f, text="Apply", command=self.ns2script_apply)
f = ttk.Frame(self)
b = ttk.Button(f, text="Apply", command=self.ns2script_apply)
b.grid()
b = tk.Button(f, text="Cancel", command=self.destroy)
b = ttk.Button(f, text="Cancel", command=self.destroy)
b.grid(row=0, column=1)
f.grid()

View file

@ -1,13 +1,11 @@
import tkinter as tk
from tkinter import ttk
from coretk.coreclient import DEFAULT_NODES
from coretk.dialogs.dialog import Dialog
from coretk.dialogs.icondialog import IconDialog
from coretk.dialogs.nodeservice import NodeService
NETWORKNODETYPES = ["switch", "hub", "wlan", "rj45", "tunnel"]
DEFAULTNODES = ["router", "host", "PC"]
class NodeConfigDialog(Dialog):
def __init__(self, master, app, canvas_node):
@ -34,17 +32,17 @@ class NodeConfigDialog(Dialog):
self.draw_third_row()
def draw_first_row(self):
frame = tk.Frame(self)
frame = ttk.Frame(self)
frame.grid(row=0, column=0, pady=2, sticky="ew")
frame.columnconfigure(0, weight=1)
frame.columnconfigure(1, weight=1)
frame.columnconfigure(2, weight=1)
entry = tk.Entry(frame, textvariable=self.name)
entry = ttk.Entry(frame, textvariable=self.name)
entry.grid(row=0, column=0, padx=2, sticky="ew")
combobox = ttk.Combobox(
frame, textvariable=self.type, values=DEFAULTNODES, state="readonly"
frame, textvariable=self.type, values=DEFAULT_NODES, state="readonly"
)
combobox.grid(row=0, column=1, padx=2, sticky="ew")
@ -57,15 +55,15 @@ class NodeConfigDialog(Dialog):
combobox.grid(row=0, column=2, sticky="ew")
def draw_second_row(self):
frame = tk.Frame(self)
frame = ttk.Frame(self)
frame.grid(row=1, column=0, pady=2, sticky="ew")
frame.columnconfigure(0, weight=1)
frame.columnconfigure(1, weight=1)
button = tk.Button(frame, text="Services", command=self.click_services)
button = ttk.Button(frame, text="Services", command=self.click_services)
button.grid(row=0, column=0, padx=2, sticky="ew")
self.image_button = tk.Button(
self.image_button = ttk.Button(
frame,
text="Icon",
image=self.image,
@ -75,15 +73,15 @@ class NodeConfigDialog(Dialog):
self.image_button.grid(row=0, column=1, sticky="ew")
def draw_third_row(self):
frame = tk.Frame(self)
frame = ttk.Frame(self)
frame.grid(row=2, column=0, sticky="ew")
frame.columnconfigure(0, weight=1)
frame.columnconfigure(1, weight=1)
button = tk.Button(frame, text="Apply", command=self.config_apply)
button = ttk.Button(frame, text="Apply", command=self.config_apply)
button.grid(row=0, column=0, padx=2, sticky="ew")
button = tk.Button(frame, text="Cancel", command=self.destroy)
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=1, sticky="ew")
def click_services(self):

View file

@ -2,7 +2,7 @@
core node services
"""
import tkinter as tk
from tkinter import messagebox
from tkinter import messagebox, ttk
from coretk.dialogs.dialog import Dialog
from coretk.dialogs.serviceconfiguration import ServiceConfiguration
@ -10,20 +10,22 @@ from coretk.widgets import CheckboxList, ListboxScroll
class NodeService(Dialog):
def __init__(self, master, app, canvas_node, current_services=set()):
def __init__(self, master, app, canvas_node, services=None):
super().__init__(master, app, "Node Services", modal=True)
self.canvas_node = canvas_node
self.groups = None
self.services = None
self.current = None
self.current_services = current_services
if services is None:
services = set()
self.current_services = services
self.draw()
def draw(self):
self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
frame = tk.Frame(self)
frame = ttk.Frame(self)
frame.grid(stick="nsew")
frame.rowconfigure(0, weight=1)
for i in range(3):
@ -45,15 +47,15 @@ class NodeService(Dialog):
for service in sorted(self.current_services):
self.current.listbox.insert(tk.END, service)
frame = tk.Frame(self)
frame = ttk.Frame(self)
frame.grid(stick="ew")
for i in range(3):
frame.columnconfigure(i, weight=1)
button = tk.Button(frame, text="Configure", command=self.click_configure)
button = ttk.Button(frame, text="Configure", command=self.click_configure)
button.grid(row=0, column=0, sticky="ew")
button = tk.Button(frame, text="Save", command=self.click_save)
button = ttk.Button(frame, text="Save", command=self.click_save)
button.grid(row=0, column=1, sticky="ew")
button = tk.Button(frame, text="Cancel", command=self.click_cancel)
button = ttk.Button(frame, text="Cancel", command=self.click_cancel)
button.grid(row=0, column=2, sticky="ew")
# trigger group change

View file

@ -1,5 +1,5 @@
"Service configuration dialog"
import logging
import tkinter as tk
from tkinter import ttk
@ -11,14 +11,12 @@ from coretk.widgets import ListboxScroll
class ServiceConfiguration(Dialog):
def __init__(self, master, app, service_name, canvas_node):
super().__init__(master, app, service_name + " service", modal=True)
super().__init__(master, app, f"{service_name} service", modal=True)
self.app = app
self.canvas_node = canvas_node
self.service_name = service_name
self.radiovar = tk.IntVar()
self.radiovar.set(2)
self.documentnew_img = Images.get(ImageEnum.DOCUMENTNEW)
self.editdelete_img = Images.get(ImageEnum.EDITDELETE)
self.metadata = ""
self.filenames = []
self.dependencies = []
@ -29,6 +27,8 @@ class ServiceConfiguration(Dialog):
self.validation_mode = None
self.validation_time = None
self.validation_period = None
self.documentnew_img = Images.get(ImageEnum.DOCUMENTNEW, 16)
self.editdelete_img = Images.get(ImageEnum.EDITDELETE, 16)
self.tab_parent = None
self.metadata_entry = None
@ -60,24 +60,23 @@ class ServiceConfiguration(Dialog):
def draw(self):
# self.columnconfigure(1, weight=1)
frame = tk.Frame(self)
frame1 = tk.Frame(frame)
label = tk.Label(frame1, text=self.service_name)
frame = ttk.Frame(self)
frame1 = ttk.Frame(frame)
label = ttk.Label(frame1, text=self.service_name)
label.grid(row=0, column=0, sticky="ew")
frame1.grid(row=0, column=0)
frame2 = tk.Frame(frame)
frame2 = ttk.Frame(frame)
# frame2.columnconfigure(0, weight=1)
# frame2.columnconfigure(1, weight=4)
label = tk.Label(frame2, text="Meta-data")
label = ttk.Label(frame2, text="Meta-data")
label.grid(row=0, column=0)
self.metadata_entry = tk.Entry(
frame2, textvariable=tk.StringVar(value=self.metadata)
)
self.metadata_entry = ttk.Entry(frame2, textvariable=self.metadata)
self.metadata_entry.grid(row=0, column=1)
frame2.grid(row=1, column=0)
frame.grid(row=0, column=0)
frame = tk.Frame(self)
frame = ttk.Frame(self)
self.tab_parent = ttk.Notebook(frame)
tab1 = ttk.Frame(self.tab_parent)
tab2 = ttk.Frame(self.tab_parent)
@ -96,13 +95,13 @@ class ServiceConfiguration(Dialog):
frame.grid(row=1, column=0, sticky="nsew")
# tab 1
label = tk.Label(
label = ttk.Label(
tab1, text="Config files and scripts that are generated for this service."
)
label.grid(row=0, column=0, sticky="nsew")
frame = tk.Frame(tab1)
label = tk.Label(frame, text="File name: ")
frame = ttk.Frame(tab1)
label = ttk.Label(frame, text="File name: ")
label.grid(row=0, column=0)
self.filename_combobox = ttk.Combobox(frame, values=self.filenames)
self.filename_combobox.grid(row=0, column=1)
@ -111,48 +110,51 @@ class ServiceConfiguration(Dialog):
self.filename_combobox.bind(
"<<ComboboxSelected>>", self.display_service_file_data
)
button = tk.Button(frame, image=self.documentnew_img)
button = ttk.Button(frame, image=self.documentnew_img)
button.bind("<Button-1>", self.add_filename)
button.grid(row=0, column=2)
button = tk.Button(frame, image=self.editdelete_img)
button = ttk.Button(frame, image=self.editdelete_img)
button.bind("<Button-1>", self.delete_filename)
button.grid(row=0, column=3)
frame.grid(row=1, column=0, sticky="nsew")
frame = tk.Frame(tab1)
button = tk.Radiobutton(
frame = ttk.Frame(tab1)
button = ttk.Radiobutton(
frame,
variable=self.radiovar,
text="Copy this source file:",
indicatoron=True,
value=1,
state="disabled",
)
button.grid(row=0, column=0)
entry = tk.Entry(frame, state=tk.DISABLED)
entry = ttk.Entry(frame, state=tk.DISABLED)
entry.grid(row=0, column=1)
button = tk.Button(frame, image=Images.get(ImageEnum.FILEOPEN))
image = Images.get(ImageEnum.FILEOPEN, 16)
button = ttk.Button(frame, image=image)
button.image = image
button.grid(row=0, column=2)
frame.grid(row=2, column=0, sticky="nsew")
frame = tk.Frame(tab1)
button = tk.Radiobutton(
frame = ttk.Frame(tab1)
button = ttk.Radiobutton(
frame,
variable=self.radiovar,
text="Use text below for file contents:",
indicatoron=True,
value=2,
)
button.grid(row=0, column=0)
button = tk.Button(frame, image=Images.get(ImageEnum.FILEOPEN))
image = Images.get(ImageEnum.FILEOPEN, 16)
button = ttk.Button(frame, image=image)
button.image = image
button.grid(row=0, column=1)
button = tk.Button(frame, image=Images.get(ImageEnum.DOCUMENTSAVE))
image = Images.get(ImageEnum.DOCUMENTSAVE, 16)
button = ttk.Button(frame, image=image)
button.image = image
button.grid(row=0, column=2)
frame.grid(row=3, column=0, sticky="nsew")
# tab 2
label = tk.Label(
label = ttk.Label(
tab2,
text="Directories required by this service that are unique for each node.",
)
@ -162,23 +164,24 @@ class ServiceConfiguration(Dialog):
for i in range(3):
label_frame = None
if i == 0:
label_frame = tk.LabelFrame(tab3, text="Startup commands")
label_frame = ttk.LabelFrame(tab3, text="Startup commands")
commands = self.startup_commands
elif i == 1:
label_frame = tk.LabelFrame(tab3, text="Shutdown commands")
label_frame = ttk.LabelFrame(tab3, text="Shutdown commands")
commands = self.shutdown_commands
elif i == 2:
label_frame = tk.LabelFrame(tab3, text="Validation commands")
label_frame = ttk.LabelFrame(tab3, text="Validation commands")
commands = self.validation_commands
label_frame.columnconfigure(0, weight=1)
frame = tk.Frame(label_frame)
frame = ttk.Frame(label_frame)
frame.columnconfigure(0, weight=1)
entry = tk.Entry(frame, textvariable=tk.StringVar())
entry = ttk.Entry(frame, textvariable=tk.StringVar())
entry.grid(row=0, column=0, stick="nsew")
button = tk.Button(frame, image=self.documentnew_img)
button = ttk.Button(frame, image=self.documentnew_img)
button.bind("<Button-1>", self.add_command)
button.grid(row=0, column=1, sticky="nsew")
button = tk.Button(frame, image=self.editdelete_img)
button = ttk.Button(frame, image=self.editdelete_img)
button.grid(row=0, column=2, sticky="nsew")
button.bind("<Button-1>", self.delete_command)
frame.grid(row=0, column=0, sticky="nsew")
@ -200,9 +203,9 @@ class ServiceConfiguration(Dialog):
for i in range(2):
label_frame = None
if i == 0:
label_frame = tk.LabelFrame(tab4, text="Executables")
label_frame = ttk.LabelFrame(tab4, text="Executables")
elif i == 1:
label_frame = tk.LabelFrame(tab4, text="Dependencies")
label_frame = ttk.LabelFrame(tab4, text="Dependencies")
label_frame.columnconfigure(0, weight=1)
listbox_scroll = ListboxScroll(label_frame)
listbox_scroll.listbox.config(height=4)
@ -217,18 +220,18 @@ class ServiceConfiguration(Dialog):
listbox_scroll.listbox.insert("end", dependency)
for i in range(3):
frame = tk.Frame(tab4)
frame = ttk.Frame(tab4)
frame.columnconfigure(0, weight=1)
if i == 0:
label = tk.Label(frame, text="Validation time:")
self.validation_time_entry = tk.Entry(
label = ttk.Label(frame, text="Validation time:")
self.validation_time_entry = ttk.Entry(
frame,
state="disabled",
textvariable=tk.StringVar(value=self.validation_time),
)
self.validation_time_entry.grid(row=i, column=1)
elif i == 1:
label = tk.Label(frame, text="Validation mode:")
label = ttk.Label(frame, text="Validation mode:")
if self.validation_mode == core_pb2.ServiceValidationMode.BLOCKING:
mode = "BLOCKING"
elif (
@ -237,36 +240,36 @@ class ServiceConfiguration(Dialog):
mode = "NON_BLOCKING"
elif self.validation_mode == core_pb2.ServiceValidationMode.TIMER:
mode = "TIMER"
self.validation_mode_entry = tk.Entry(
self.validation_mode_entry = ttk.Entry(
frame, state="disabled", textvariable=tk.StringVar(value=mode)
)
self.validation_mode_entry.grid(row=i, column=1)
elif i == 2:
label = tk.Label(frame, text="Validation period:")
self.validation_period_entry = tk.Entry(
label = ttk.Label(frame, text="Validation period:")
self.validation_period_entry = ttk.Entry(
frame, state="disabled", textvariable=tk.StringVar()
)
self.validation_period_entry.grid(row=i, column=1)
label.grid(row=i, column=0)
frame.grid(row=2 + i, column=0, sticky="nsew")
button = tk.Button(
button = ttk.Button(
self, text="only store values that have changed from their defaults"
)
button.grid(row=2, column=0)
frame = tk.Frame(self)
button = tk.Button(frame, text="Apply", command=self.click_apply)
frame = ttk.Frame(self)
button = ttk.Button(frame, text="Apply", command=self.click_apply)
button.grid(row=0, column=0, sticky="nsew")
button = tk.Button(
button = ttk.Button(
frame, text="Dafults", command=self.click_defaults, state="disabled"
)
button.grid(row=0, column=1, sticky="nsew")
button = tk.Button(
button = ttk.Button(
frame, text="Copy...", command=self.click_copy, state="disabled"
)
button.grid(row=0, column=2, sticky="nsew")
button = tk.Button(frame, text="Cancel", command=self.destroy)
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=3, sticky="nsew")
frame.grid(row=3, column=0)
@ -330,20 +333,27 @@ class ServiceConfiguration(Dialog):
validate_commands,
shutdown_commands,
)
logging.info(
"%s, %s, %s, %s, %s",
metadata,
filenames,
startup_commands,
shutdown_commands,
validate_commands,
)
# wipe nodes and links when finished by setting to DEFINITION state
self.app.core.client.set_session_state(
self.app.core.session_id, core_pb2.SessionState.DEFINITION
)
print(metadata, filenames)
def display_service_file_data(self, event):
print("not implemented")
def click_defaults(self):
print("not implemented")
logging.info("not implemented")
def click_copy(self):
print("not implemented")
logging.info("not implemented")
def click_cancel(self):
print("not implemented")
logging.info("not implemented")

View file

@ -73,30 +73,36 @@ class SessionsDialog(Dialog):
for i in range(4):
frame.columnconfigure(i, weight=1)
frame.grid(row=3, sticky="ew")
image = Images.get(ImageEnum.DOCUMENTNEW, 16)
b = ttk.Button(
frame,
image=Images.get(ImageEnum.DOCUMENTNEW),
text="New",
compound=tk.LEFT,
command=self.click_new,
frame, image=image, text="New", compound=tk.LEFT, command=self.click_new
)
b.image = image
b.grid(row=0, padx=2, sticky="ew")
image = Images.get(ImageEnum.FILEOPEN, 16)
b = ttk.Button(
frame,
image=Images.get(ImageEnum.FILEOPEN),
image=image,
text="Connect",
compound=tk.LEFT,
command=self.click_connect,
)
b.image = image
b.grid(row=0, column=1, padx=2, sticky="ew")
image = Images.get(ImageEnum.EDITDELETE, 16)
b = ttk.Button(
frame,
image=Images.get(ImageEnum.EDITDELETE),
image=image,
text="Shutdown",
compound=tk.LEFT,
command=self.click_shutdown,
)
b.image = image
b.grid(row=0, column=2, padx=2, sticky="ew")
b = ttk.Button(frame, text="Cancel", command=self.click_new)
b.grid(row=0, column=3, padx=2, sticky="ew")

View file

@ -3,6 +3,7 @@ wlan configuration
"""
import tkinter as tk
from tkinter import ttk
from coretk.dialogs.dialog import Dialog
from coretk.dialogs.icondialog import IconDialog
@ -10,12 +11,6 @@ from coretk.dialogs.icondialog import IconDialog
class WlanConfigDialog(Dialog):
def __init__(self, master, app, canvas_node, config):
"""
create an instance of WlanConfiguration
:param coretk.grpah.CanvasGraph canvas: canvas object
:param coretk.graph.CanvasNode canvas_node: canvas node object
"""
super().__init__(
master, app, f"{canvas_node.name} Wlan Configuration", modal=True
)
@ -48,14 +43,14 @@ class WlanConfigDialog(Dialog):
:return: nothing
"""
frame = tk.Frame(self)
frame = ttk.Frame(self)
frame.grid(pady=2, sticky="ew")
frame.columnconfigure(0, weight=1)
entry = tk.Entry(frame, textvariable=self.name, bg="white")
entry = ttk.Entry(frame, textvariable=self.name)
entry.grid(row=0, column=0, padx=2, sticky="ew")
self.image_button = tk.Button(frame, image=self.image, command=self.click_icon)
self.image_button = ttk.Button(frame, image=self.image, command=self.click_icon)
self.image_button.grid(row=0, column=1, padx=3)
def draw_wlan_config(self):
@ -64,15 +59,15 @@ class WlanConfigDialog(Dialog):
:return: nothing
"""
label = tk.Label(self, text="Wireless")
label = ttk.Label(self, text="Wireless")
label.grid(sticky="w", pady=2)
frame = tk.Frame(self)
frame = ttk.Frame(self)
frame.grid(pady=2, sticky="ew")
for i in range(2):
frame.columnconfigure(i, weight=1)
label = tk.Label(
label = ttk.Label(
frame,
text=(
"The basic range model calculates on/off "
@ -81,29 +76,29 @@ class WlanConfigDialog(Dialog):
)
label.grid(row=0, columnspan=2, pady=2, sticky="ew")
label = tk.Label(frame, text="Range")
label = ttk.Label(frame, text="Range")
label.grid(row=1, column=0, sticky="w")
entry = tk.Entry(frame, textvariable=self.range_var)
entry = ttk.Entry(frame, textvariable=self.range_var)
entry.grid(row=1, column=1, sticky="ew")
label = tk.Label(frame, text="Bandwidth (bps)")
label = ttk.Label(frame, text="Bandwidth (bps)")
label.grid(row=2, column=0, sticky="w")
entry = tk.Entry(frame, textvariable=self.bandwidth_var)
entry = ttk.Entry(frame, textvariable=self.bandwidth_var)
entry.grid(row=2, column=1, sticky="ew")
label = tk.Label(frame, text="Delay (us)")
label = ttk.Label(frame, text="Delay (us)")
label.grid(row=3, column=0, sticky="w")
entry = tk.Entry(frame, textvariable=self.delay_var)
entry = ttk.Entry(frame, textvariable=self.delay_var)
entry.grid(row=3, column=1, sticky="ew")
label = tk.Label(frame, text="Loss (%)")
label = ttk.Label(frame, text="Loss (%)")
label.grid(row=4, column=0, sticky="w")
entry = tk.Entry(frame, textvariable=self.loss_var)
entry = ttk.Entry(frame, textvariable=self.loss_var)
entry.grid(row=4, column=1, sticky="ew")
label = tk.Label(frame, text="Jitter (us)")
label = ttk.Label(frame, text="Jitter (us)")
label.grid(row=5, column=0, sticky="w")
entry = tk.Entry(frame, textvariable=self.jitter_var)
entry = ttk.Entry(frame, textvariable=self.jitter_var)
entry.grid(row=5, column=1, sticky="ew")
def draw_subnet(self):
@ -113,19 +108,19 @@ class WlanConfigDialog(Dialog):
:return: nothing
"""
frame = tk.Frame(self)
frame = ttk.Frame(self)
frame.grid(pady=3, sticky="ew")
frame.columnconfigure(1, weight=1)
frame.columnconfigure(3, weight=1)
label = tk.Label(frame, text="IPv4 Subnet")
label = ttk.Label(frame, text="IPv4 Subnet")
label.grid(row=0, column=0, sticky="w")
entry = tk.Entry(frame, textvariable=self.ip4_subnet)
entry = ttk.Entry(frame, textvariable=self.ip4_subnet)
entry.grid(row=0, column=1, sticky="ew")
label = tk.Label(frame, text="IPv6 Subnet")
label = ttk.Label(frame, text="IPv6 Subnet")
label.grid(row=0, column=2, sticky="w")
entry = tk.Entry(frame, textvariable=self.ip6_subnet)
entry = ttk.Entry(frame, textvariable=self.ip6_subnet)
entry.grid(row=0, column=3, sticky="ew")
def draw_wlan_buttons(self):
@ -135,18 +130,18 @@ class WlanConfigDialog(Dialog):
:return:
"""
frame = tk.Frame(self)
frame = ttk.Frame(self)
frame.grid(pady=2, sticky="ew")
for i in range(3):
frame.columnconfigure(i, weight=1)
button = tk.Button(frame, text="ns-2 mobility script...")
button = ttk.Button(frame, text="ns-2 mobility script...")
button.grid(row=0, column=0, padx=2, sticky="ew")
button = tk.Button(frame, text="Link to all routers")
button = ttk.Button(frame, text="Link to all routers")
button.grid(row=0, column=1, padx=2, sticky="ew")
button = tk.Button(frame, text="Choose WLAN members")
button = ttk.Button(frame, text="Choose WLAN members")
button.grid(row=0, column=2, padx=2, sticky="ew")
def draw_apply_buttons(self):
@ -155,15 +150,15 @@ class WlanConfigDialog(Dialog):
:return: nothing
"""
frame = tk.Frame(self)
frame = ttk.Frame(self)
frame.grid(sticky="ew")
for i in range(2):
frame.columnconfigure(i, weight=1)
button = tk.Button(frame, text="Apply", command=self.click_apply)
button = ttk.Button(frame, text="Apply", command=self.click_apply)
button.grid(row=0, column=0, padx=2, sticky="ew")
button = tk.Button(frame, text="Cancel", command=self.destroy)
button = ttk.Button(frame, text="Cancel", command=self.destroy)
button.grid(row=0, column=1, padx=2, sticky="ew")
def click_icon(self):
@ -188,7 +183,6 @@ class WlanConfigDialog(Dialog):
jitter = self.jitter_var.get()
# set wireless node configuration here
wlanconfig_manager = self.app.core.wlanconfig_management
wlanconfig_manager.set_custom_config(
node_id=self.canvas_node.core_id,

View file

@ -146,9 +146,7 @@ class CanvasGraph(tk.Canvas):
# peer to peer node is not drawn on the GUI
if node.type != core_pb2.NodeType.PEER_TO_PEER:
# draw nodes on the canvas
image, name = Images.convert_type_and_model_to_image(
node.type, node.model
)
image, name = Images.node_icon(node.type, node.model)
n = CanvasNode(
node.position.x, node.position.y, image, name, self.master, node.id
)

View file

@ -77,6 +77,7 @@ class WlanAntennaManager:
self.quantity = 0
self._max = 5
self.antennas = []
self.image = Images.get(ImageEnum.ANTENNA, 32)
# distance between each antenna
self.offset = 0
@ -94,7 +95,7 @@ class WlanAntennaManager:
x - 16 + self.offset,
y - 16,
anchor=tk.CENTER,
image=Images.get(ImageEnum.ANTENNA),
image=self.image,
tags="antenna",
)
)

View file

@ -6,35 +6,37 @@ from PIL import Image, ImageTk
from core.api.grpc import core_pb2
from coretk.appconfig import LOCAL_ICONS_PATH
NODE_WIDTH = 32
class Images:
images = {}
@classmethod
def create(cls, file_path):
def create(cls, file_path, width, height=None):
if height is None:
height = width
image = Image.open(file_path)
image = image.resize((width, height), Image.ANTIALIAS)
return ImageTk.PhotoImage(image)
@classmethod
def load_all(cls):
for image in LOCAL_ICONS_PATH.glob("*"):
cls.load(image.stem, str(image))
cls.images[image.stem] = str(image)
@classmethod
def load(cls, name, file_path):
tk_image = cls.create(file_path)
cls.images[name] = tk_image
def get(cls, image_enum, width, height=None):
file_path = cls.images[image_enum.value]
return cls.create(file_path, width, height)
@classmethod
def get(cls, image):
return cls.images[image.value]
def get_custom(cls, name, width, height):
file_path = cls.images[name]
return cls.create(file_path, width, height)
@classmethod
def get_custom(cls, name):
return cls.images[name]
@classmethod
def convert_type_and_model_to_image(cls, node_type, node_model):
def node_icon(cls, node_type, node_model):
"""
Retrieve image based on type and model
:param core_pb2.NodeType node_type: core node type
@ -43,34 +45,48 @@ class Images:
:rtype: tuple(PhotoImage, str)
:return: the matching image and its name
"""
image_enum = ImageEnum.ROUTER
name = "unknown"
if node_type == core_pb2.NodeType.SWITCH:
return Images.get(ImageEnum.SWITCH), "switch"
if node_type == core_pb2.NodeType.HUB:
return Images.get(ImageEnum.HUB), "hub"
if node_type == core_pb2.NodeType.WIRELESS_LAN:
return Images.get(ImageEnum.WLAN), "wlan"
if node_type == core_pb2.NodeType.EMANE:
return Images.get(ImageEnum.EMANE), "emane"
if node_type == core_pb2.NodeType.RJ45:
return Images.get(ImageEnum.RJ45), "rj45"
if node_type == core_pb2.NodeType.TUNNEL:
return Images.get(ImageEnum.TUNNEL), "tunnel"
if node_type == core_pb2.NodeType.DEFAULT:
image_enum = ImageEnum.SWITCH
name = "switch"
elif node_type == core_pb2.NodeType.HUB:
image_enum = ImageEnum.HUB
name = "hub"
elif node_type == core_pb2.NodeType.WIRELESS_LAN:
image_enum = ImageEnum.WLAN
name = "wlan"
elif node_type == core_pb2.NodeType.EMANE:
image_enum = ImageEnum.EMANE
name = "emane"
elif node_type == core_pb2.NodeType.RJ45:
image_enum = ImageEnum.RJ45
name = "rj45"
elif node_type == core_pb2.NodeType.TUNNEL:
image_enum = ImageEnum.TUNNEL
name = "tunnel"
elif node_type == core_pb2.NodeType.DEFAULT:
if node_model == "router":
return Images.get(ImageEnum.ROUTER), "router"
if node_model == "host":
return Images.get(ImageEnum.HOST), "host"
if node_model == "PC":
return Images.get(ImageEnum.PC), "PC"
if node_model == "mdr":
return Images.get(ImageEnum.MDR), "mdr"
if node_model == "prouter":
return Images.get(ImageEnum.PROUTER), "prouter"
if node_model == "OVS":
return Images.get(ImageEnum.OVS), "ovs"
image_enum = ImageEnum.ROUTER
name = "router"
elif node_model == "host":
image_enum = ImageEnum.HOST
name = "host"
elif node_model == "PC":
image_enum = ImageEnum.PC
name = "PC"
elif node_model == "mdr":
image_enum = ImageEnum.MDR
name = "mdr"
elif node_model == "prouter":
image_enum = ImageEnum.PROUTER
name = "prouter"
else:
logging.debug("INVALID INPUT OR NOT CONSIDERED YET")
logging.error("invalid node model: %s", node_model)
else:
logging.error("invalid node type: %s", node_type)
return Images.get(image_enum, NODE_WIDTH), name
class ImageEnum(Enum):

162
coretk/coretk/theme.py Normal file
View file

@ -0,0 +1,162 @@
import tkinter as tk
from tkinter import ttk
class Colors:
disabledfg = "DarkGrey"
frame = "#424242"
dark = "#222222"
darker = "#121212"
darkest = "black"
lighter = "#626262"
lightest = "#ffffff"
selectbg = "#4a6984"
selectfg = "#ffffff"
white = "white"
black = "black"
style = ttk.Style()
style.theme_create(
"black",
"clam",
{
".": {
"configure": {
"background": Colors.frame,
"foreground": Colors.white,
"bordercolor": Colors.darkest,
"darkcolor": Colors.dark,
"lightcolor": Colors.lighter,
"troughcolor": Colors.darker,
"selectbackground": Colors.selectbg,
"selectforeground": Colors.selectfg,
"selectborderwidth": 0,
"font": "TkDefaultFont",
},
"map": {
"background": [("disabled", Colors.frame), ("active", Colors.lighter)],
"foreground": [("disabled", Colors.disabledfg)],
"selectbackground": [("!focus", Colors.darkest)],
"selectforeground": [("!focus", Colors.white)],
},
},
"TButton": {
"configure": {"width": 8, "padding": (5, 1), "relief": tk.RAISED},
"map": {
"relief": [("pressed", tk.SUNKEN)],
"shiftrelief": [("pressed", 1)],
},
},
"TMenubutton": {
"configure": {"width": 11, "padding": (5, 1), "relief": tk.RAISED}
},
"TCheckbutton": {
"configure": {
"indicatorbackground": Colors.white,
"indicatormargin": (1, 1, 4, 1),
}
},
"TRadiobutton": {
"configure": {
"indicatorbackground": Colors.white,
"indicatormargin": (1, 1, 4, 1),
}
},
"TEntry": {
"configure": {
"fieldbackground": Colors.white,
"foreground": Colors.black,
"padding": (2, 0),
}
},
"TCombobox": {
"configure": {
"fieldbackground": Colors.white,
"foreground": Colors.black,
"padding": (2, 0),
}
},
"TNotebook.Tab": {
"configure": {"padding": (6, 2, 6, 2)},
"map": {"background": [("selected", Colors.lighter)]},
},
"Treeview": {
"configure": {
"fieldbackground": Colors.white,
"background": Colors.white,
"foreground": Colors.black,
},
"map": {
"background": [("selected", Colors.selectbg)],
"foreground": [("selected", Colors.selectfg)],
},
},
},
)
style.theme_use("black")
def update_menu(event):
bg = style.lookup(".", "background")
fg = style.lookup(".", "foreground")
abg = style.lookup(".", "lightcolor")
event.widget.config(
background=bg, foreground=fg, activebackground=abg, activeforeground=fg
)
class Application(ttk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.master.bind_class("Menu", "<<ThemeChanged>>", update_menu)
self.master.geometry("800x600")
menu = tk.Menu(self.master)
menu.add_command(label="Command1")
menu.add_command(label="Command2")
submenu = tk.Menu(menu, tearoff=False)
submenu.add_command(label="Command1")
submenu.add_command(label="Command2")
menu.add_cascade(label="Submenu", menu=submenu)
self.master.config(menu=menu)
self.master.columnconfigure(0, weight=1)
self.master.rowconfigure(0, weight=1)
notebook = ttk.Notebook(self.master)
notebook.grid(sticky="nsew")
frame = ttk.Frame(notebook)
frame.grid(sticky="nsew")
ttk.Label(frame, text="Label").grid()
ttk.Entry(frame).grid()
ttk.Button(frame, text="Button").grid()
ttk.Combobox(frame, values=("one", "two", "three")).grid()
menubutton = ttk.Menubutton(frame, text="MenuButton")
menubutton.grid()
mbmenu = tk.Menu(menubutton, tearoff=False)
menubutton.config(menu=mbmenu)
mbmenu.add_command(label="Menu1")
mbmenu.add_command(label="Menu2")
submenu = tk.Menu(mbmenu, tearoff=False)
submenu.add_command(label="Command1")
submenu.add_command(label="Command2")
mbmenu.add_cascade(label="Submenu", menu=submenu)
ttk.Radiobutton(frame, text="Radio Button").grid()
ttk.Checkbutton(frame, text="Check Button").grid()
tv = ttk.Treeview(frame, columns=("one", "two", "three"), show="headings")
tv.grid()
tv.column("one", stretch=tk.YES)
tv.heading("one", text="ID")
tv.column("two", stretch=tk.YES)
tv.heading("two", text="State")
tv.column("three", stretch=tk.YES)
tv.heading("three", text="Node Count")
tv.insert("", tk.END, text="1", values=("v1", "v2", "v3"))
tv.insert("", tk.END, text="2", values=("v1", "v2", "v3"))
notebook.add(frame, text="Tab1")
frame = ttk.Frame(notebook)
frame.grid(sticky="nsew")
notebook.add(frame, text="Tab2")
if __name__ == "__main__":
app = Application()
app.mainloop()

View file

@ -1,43 +1,44 @@
import logging
import tkinter as tk
from functools import partial
from tkinter import ttk
from coretk.dialogs.customnodes import CustomNodesDialog
from coretk.graph import GraphMode
from coretk.images import ImageEnum, Images
from coretk.tooltip import CreateToolTip
from coretk.tooltip import Tooltip
WIDTH = 32
class Toolbar(tk.Frame):
def icon(image_enum):
return Images.get(image_enum, WIDTH)
class Toolbar(ttk.Frame):
"""
Core toolbar class
"""
def __init__(self, master, app, cnf={}, **kwargs):
def __init__(self, master, app, **kwargs):
"""
Create a CoreToolbar instance
:param tkinter.Frame edit_frame: edit frame
"""
super().__init__(master, cnf, **kwargs)
super().__init__(master, **kwargs)
self.app = app
self.master = app.master
self.radio_value = tk.IntVar()
self.exec_radio_value = tk.IntVar()
# button dimension
self.width = 32
self.height = 32
# Reference to the option menus
self.selection_tool_button = None
self.link_layer_option_menu = None
self.marker_option_menu = None
self.network_layer_option_menu = None
# design buttons
self.select_button = None
self.link_button = None
self.node_button = None
self.network_button = None
self.annotation_button = None
# runtime buttons
# frames
self.design_frame = None
self.runtime_frame = None
@ -56,89 +57,77 @@ class Toolbar(tk.Frame):
self.design_frame.tkraise()
def draw_design_frame(self):
self.design_frame = tk.Frame(self)
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_regular_button(
self.create_button(
self.design_frame,
Images.get(ImageEnum.START),
self.click_start_session_tool,
icon(ImageEnum.START),
self.click_start,
"start the session",
)
self.create_radio_button(
self.select_button = self.create_button(
self.design_frame,
Images.get(ImageEnum.SELECT),
self.click_selection_tool,
self.radio_value,
1,
icon(ImageEnum.SELECT),
self.click_selection,
"selection tool",
)
self.create_radio_button(
self.design_frame,
Images.get(ImageEnum.LINK),
self.click_link_tool,
self.radio_value,
2,
"link tool",
self.link_button = self.create_button(
self.design_frame, icon(ImageEnum.LINK), self.click_link, "link tool"
)
self.create_node_button()
self.create_network_button()
self.create_annotation_button()
self.radio_value.set(1)
def design_select(self, button):
logging.info("selecting design button: %s", button)
self.select_button.state(["!pressed"])
self.link_button.state(["!pressed"])
self.node_button.state(["!pressed"])
self.network_button.state(["!pressed"])
self.annotation_button.state(["!pressed"])
button.state(["pressed"])
def draw_runtime_frame(self):
self.runtime_frame = tk.Frame(self)
self.runtime_frame = ttk.Frame(self)
self.runtime_frame.grid(row=0, column=0, sticky="nsew")
self.runtime_frame.columnconfigure(0, weight=1)
self.create_regular_button(
self.create_button(
self.runtime_frame,
Images.get(ImageEnum.STOP),
self.click_stop_button,
icon(ImageEnum.STOP),
self.click_stop,
"stop the session",
)
self.create_radio_button(
self.create_button(
self.runtime_frame,
Images.get(ImageEnum.SELECT),
self.click_selection_tool,
self.exec_radio_value,
1,
icon(ImageEnum.SELECT),
self.click_selection,
"selection tool",
)
self.create_observe_button()
self.create_radio_button(
self.runtime_frame,
Images.get(ImageEnum.PLOT),
self.click_plot_button,
self.exec_radio_value,
2,
"plot",
# self.create_observe_button()
self.create_button(
self.runtime_frame, icon(ImageEnum.PLOT), self.click_plot_button, "plot"
)
self.create_radio_button(
self.create_button(
self.runtime_frame,
Images.get(ImageEnum.MARKER),
icon(ImageEnum.MARKER),
self.click_marker_button,
self.exec_radio_value,
3,
"marker",
)
self.create_radio_button(
self.create_button(
self.runtime_frame,
Images.get(ImageEnum.TWONODE),
icon(ImageEnum.TWONODE),
self.click_two_node_button,
self.exec_radio_value,
4,
"run command from one node to another",
)
self.create_regular_button(
self.runtime_frame, Images.get(ImageEnum.RUN), self.click_run_button, "run"
self.create_button(
self.runtime_frame, icon(ImageEnum.RUN), self.click_run_button, "run"
)
self.exec_radio_value.set(1)
def draw_node_picker(self):
self.hide_pickers()
self.node_picker = tk.Frame(self.master, padx=1, pady=1)
self.node_picker = ttk.Frame(self.master)
nodes = [
(ImageEnum.ROUTER, "router"),
(ImageEnum.HOST, "host"),
@ -148,26 +137,28 @@ class Toolbar(tk.Frame):
]
# draw default nodes
for image_enum, tooltip in nodes:
image = Images.get(image_enum)
image = icon(image_enum)
func = partial(self.update_button, self.node_button, image, tooltip)
self.create_button(image, func, self.node_picker, tooltip)
self.create_picker_button(image, func, self.node_picker, tooltip)
# draw custom nodes
for name in sorted(self.app.core.custom_nodes):
custom_node = self.app.core.custom_nodes[name]
image = custom_node.image
func = partial(self.update_button, self.node_button, image, name)
self.create_button(image, func, self.node_picker, name)
self.create_picker_button(image, func, self.node_picker, name)
# draw edit node
image = Images.get(ImageEnum.EDITNODE)
self.create_button(
image = icon(ImageEnum.EDITNODE)
self.create_picker_button(
image, self.click_edit_node, self.node_picker, "custom nodes"
)
self.show_picker(self.node_button, self.node_picker)
self.design_select(self.node_button)
self.node_button.after(
0, lambda: self.show_picker(self.node_button, self.node_picker)
)
def show_picker(self, button, picker):
first_button = self.winfo_children()[0]
x = button.winfo_rootx() - first_button.winfo_rootx() + 40
y = button.winfo_rooty() - first_button.winfo_rooty() - 1
x = self.winfo_width() + 1
y = button.winfo_rooty() - picker.master.winfo_rooty() - 1
picker.place(x=x, y=y)
self.app.bind_all("<ButtonRelease-1>", lambda e: self.hide_pickers())
picker.wait_visibility()
@ -175,7 +166,7 @@ class Toolbar(tk.Frame):
self.wait_window(picker)
self.app.unbind_all("<ButtonRelease-1>")
def create_button(self, image, func, frame, tooltip):
def create_picker_button(self, image, func, frame, tooltip):
"""
Create button and put it on the frame
@ -185,37 +176,25 @@ class Toolbar(tk.Frame):
:param str tooltip: tooltip text
:return: nothing
"""
button = tk.Button(frame, width=self.width, height=self.height, image=image)
button = ttk.Button(frame, image=image)
button.image = image
button.bind("<ButtonRelease-1>", lambda e: func())
button.grid(pady=1)
CreateToolTip(button, tooltip)
Tooltip(button, tooltip)
def create_radio_button(self, frame, image, func, variable, value, tooltip_msg):
button = tk.Radiobutton(
frame,
indicatoron=False,
width=self.width,
height=self.height,
image=image,
value=value,
variable=variable,
command=func,
)
button.grid()
CreateToolTip(button, tooltip_msg)
def create_button(self, frame, image, func, tooltip):
button = ttk.Button(frame, image=image, command=func)
button.image = image
button.grid(sticky="ew")
Tooltip(button, tooltip)
return button
def create_regular_button(self, frame, image, func, tooltip):
button = tk.Button(
frame, width=self.width, height=self.height, image=image, command=func
)
button.grid()
CreateToolTip(button, tooltip)
def click_selection_tool(self):
def click_selection(self):
logging.debug("clicked selection tool")
self.design_select(self.select_button)
self.app.canvas.mode = GraphMode.SELECT
def click_start_session_tool(self):
def click_start(self):
"""
Start session handler redraw buttons, send node and link messages to grpc
server.
@ -227,8 +206,9 @@ class Toolbar(tk.Frame):
self.app.core.start_session()
self.runtime_frame.tkraise()
def click_link_tool(self):
def click_link(self):
logging.debug("Click LINK button")
self.design_select(self.link_button)
self.app.canvas.mode = GraphMode.EDGE
def click_edit_node(self):
@ -240,6 +220,7 @@ class Toolbar(tk.Frame):
logging.info("update button(%s): %s", button, name)
self.hide_pickers()
button.configure(image=image)
button.image = image
self.app.canvas.mode = GraphMode.NODE
self.app.canvas.draw_node_image = image
self.app.canvas.draw_node_name = name
@ -262,29 +243,22 @@ class Toolbar(tk.Frame):
:return: nothing
"""
router_image = Images.get(ImageEnum.ROUTER)
self.node_button = tk.Radiobutton(
self.design_frame,
indicatoron=False,
variable=self.radio_value,
value=3,
width=self.width,
height=self.height,
image=router_image,
image = icon(ImageEnum.ROUTER)
self.node_button = ttk.Button(
self.design_frame, image=image, command=self.draw_node_picker
)
self.node_button.bind("<ButtonRelease-1>", lambda e: self.draw_node_picker())
self.node_button.grid()
CreateToolTip(self.node_button, "Network-layer virtual nodes")
self.node_button.image = image
self.node_button.grid(sticky="ew")
Tooltip(self.node_button, "Network-layer virtual nodes")
def draw_network_picker(self):
"""
Draw the options for link-layer button
Draw the options for link-layer button.
:param tkinter.RadioButton link_layer_button: link-layer button
:return: nothing
"""
self.hide_pickers()
self.network_picker = tk.Frame(self.master, padx=1, pady=1)
self.network_picker = ttk.Frame(self.master)
nodes = [
(ImageEnum.HUB, "hub", "ethernet hub"),
(ImageEnum.SWITCH, "switch", "ethernet switch"),
@ -294,14 +268,17 @@ class Toolbar(tk.Frame):
(ImageEnum.TUNNEL, "tunnel", "tunnel tool"),
]
for image_enum, name, tooltip in nodes:
image = Images.get(image_enum)
self.create_button(
image = icon(image_enum)
self.create_picker_button(
image,
partial(self.update_button, self.network_button, image, name),
self.network_picker,
tooltip,
)
self.show_picker(self.network_button, self.network_picker)
self.design_select(self.network_button)
self.network_button.after(
0, lambda: self.show_picker(self.network_button, self.network_picker)
)
def create_network_button(self):
"""
@ -309,31 +286,22 @@ class Toolbar(tk.Frame):
:return: nothing
"""
hub_image = Images.get(ImageEnum.HUB)
self.network_button = tk.Radiobutton(
self.design_frame,
indicatoron=False,
variable=self.radio_value,
value=4,
width=self.width,
height=self.height,
image=hub_image,
image = icon(ImageEnum.HUB)
self.network_button = ttk.Button(
self.design_frame, image=image, command=self.draw_network_picker
)
self.network_button.bind(
"<ButtonRelease-1>", lambda e: self.draw_network_picker()
)
self.network_button.grid()
CreateToolTip(self.network_button, "link-layer nodes")
self.network_button.image = image
self.network_button.grid(sticky="ew")
Tooltip(self.network_button, "link-layer nodes")
def draw_annotation_picker(self):
"""
Draw the options for marker button
Draw the options for marker button.
:param tkinter.Radiobutton main_button: the main button
:return: nothing
"""
self.hide_pickers()
self.annotation_picker = tk.Frame(self.master, padx=1, pady=1)
self.annotation_picker = ttk.Frame(self.master)
nodes = [
(ImageEnum.MARKER, "marker"),
(ImageEnum.OVAL, "oval"),
@ -341,13 +309,17 @@ class Toolbar(tk.Frame):
(ImageEnum.TEXT, "text"),
]
for image_enum, tooltip in nodes:
self.create_button(
Images.get(image_enum),
partial(self.update_annotation, image_enum),
image = icon(image_enum)
self.create_picker_button(
image,
partial(self.update_annotation, image),
self.annotation_picker,
tooltip,
)
self.show_picker(self.annotation_button, self.annotation_picker)
self.design_select(self.annotation_button)
self.annotation_button.after(
0, lambda: self.show_picker(self.annotation_button, self.annotation_picker)
)
def create_annotation_button(self):
"""
@ -355,53 +327,39 @@ class Toolbar(tk.Frame):
:return: nothing
"""
marker_image = Images.get(ImageEnum.MARKER)
self.annotation_button = tk.Radiobutton(
self.design_frame,
indicatoron=False,
variable=self.radio_value,
value=5,
width=self.width,
height=self.height,
image=marker_image,
image = icon(ImageEnum.MARKER)
self.annotation_button = ttk.Button(
self.design_frame, image=image, command=self.draw_annotation_picker
)
self.annotation_button.bind(
"<ButtonRelease-1>", lambda e: self.draw_annotation_picker()
)
self.annotation_button.grid()
CreateToolTip(self.annotation_button, "background annotation tools")
self.annotation_button.image = image
self.annotation_button.grid(sticky="ew")
Tooltip(self.annotation_button, "background annotation tools")
def create_observe_button(self):
menu_button = tk.Menubutton(
self.runtime_frame,
image=Images.get(ImageEnum.OBSERVE),
width=self.width,
height=self.height,
direction=tk.RIGHT,
relief=tk.RAISED,
menu_button = ttk.Menubutton(
self.runtime_frame, image=icon(ImageEnum.OBSERVE), direction=tk.RIGHT
)
menu_button.menu = tk.Menu(menu_button, tearoff=0)
menu_button["menu"] = menu_button.menu
menu_button.grid()
menu_button.grid(sticky="ew")
menu = tk.Menu(menu_button, tearoff=0)
menu_button["menu"] = menu
menu.add_command(label="None")
menu.add_command(label="processes")
menu.add_command(label="ifconfig")
menu.add_command(label="IPv4 routes")
menu.add_command(label="IPv6 routes")
menu.add_command(label="OSPFv2 neighbors")
menu.add_command(label="OSPFv3 neighbors")
menu.add_command(label="Listening sockets")
menu.add_command(label="IPv4 MFC entries")
menu.add_command(label="IPv6 MFC entries")
menu.add_command(label="firewall rules")
menu.add_command(label="IPSec policies")
menu.add_command(label="docker logs")
menu.add_command(label="OSPFv3 MDR level")
menu.add_command(label="PIM neighbors")
menu.add_command(label="Edit...")
menu_button.menu.add_command(label="None")
menu_button.menu.add_command(label="processes")
menu_button.menu.add_command(label="ifconfig")
menu_button.menu.add_command(label="IPv4 routes")
menu_button.menu.add_command(label="IPv6 routes")
menu_button.menu.add_command(label="OSPFv2 neighbors")
menu_button.menu.add_command(label="OSPFv3 neighbors")
menu_button.menu.add_command(label="Listening sockets")
menu_button.menu.add_command(label="IPv4 MFC entries")
menu_button.menu.add_command(label="IPv6 MFC entries")
menu_button.menu.add_command(label="firewall rules")
menu_button.menu.add_command(label="IPSec policies")
menu_button.menu.add_command(label="docker logs")
menu_button.menu.add_command(label="OSPFv3 MDR level")
menu_button.menu.add_command(label="PIM neighbors")
menu_button.menu.add_command(label="Edit...")
def click_stop_button(self):
def click_stop(self):
"""
redraw buttons on the toolbar, send node and link messages to grpc server
@ -411,10 +369,11 @@ class Toolbar(tk.Frame):
self.app.core.stop_session()
self.design_frame.tkraise()
def update_annotation(self, image_enum):
def update_annotation(self, image):
logging.info("clicked annotation: ")
self.hide_pickers()
self.annotation_button.configure(image=Images.get(image_enum))
self.annotation_button.configure(image=image)
self.annotation_button.image = image
def click_run_button(self):
logging.debug("Click on RUN button")

View file

@ -1,7 +1,8 @@
import tkinter as tk
from tkinter import ttk
class CreateToolTip(object):
class Tooltip(object):
"""
Create tool tip for a given widget
"""
@ -9,10 +10,29 @@ class CreateToolTip(object):
def __init__(self, widget, text="widget info"):
self.widget = widget
self.text = text
self.widget.bind("<Enter>", self.enter)
self.widget.bind("<Leave>", self.close)
self.widget.bind("<Enter>", self.on_enter)
self.widget.bind("<Leave>", self.on_leave)
self.waittime = 400
self.id = None
self.tw = None
def on_enter(self, event=None):
self.schedule()
def on_leave(self, event=None):
self.unschedule()
self.close(event)
def schedule(self):
self.unschedule()
self.id = self.widget.after(self.waittime, self.enter)
def unschedule(self):
id_ = self.id
self.id = None
if id_:
self.widget.after_cancel(id_)
def enter(self, event=None):
x, y, cx, cy = self.widget.bbox("insert")
x += self.widget.winfo_rootx()
@ -21,13 +41,13 @@ class CreateToolTip(object):
self.tw = tk.Toplevel(self.widget)
self.tw.wm_overrideredirect(True)
self.tw.wm_geometry("+%d+%d" % (x, y))
label = tk.Label(
label = ttk.Label(
self.tw,
text=self.text,
justify=tk.LEFT,
background="#ffffe6",
relief="solid",
borderwidth=1,
background="#FFFFEA",
relief=tk.SOLID,
borderwidth=0,
)
label.grid(padx=1)