diff --git a/coretk/coretk/app.py b/coretk/coretk/app.py index 2c4826a9..c2eeadc3 100644 --- a/coretk/coretk/app.py +++ b/coretk/coretk/app.py @@ -12,6 +12,7 @@ from coretk.menubar import Menubar from coretk.nodeutils import NodeUtils from coretk.statusbar import StatusBar from coretk.toolbar import Toolbar +from coretk.validation import InputValidation class Application(tk.Frame): @@ -25,6 +26,7 @@ class Application(tk.Frame): self.toolbar = None self.canvas = None self.statusbar = None + self.validation = None # setup self.guiconfig = appconfig.read() @@ -48,6 +50,7 @@ class Application(tk.Frame): image = Images.get(ImageEnum.CORE, 16) self.master.tk.call("wm", "iconphoto", self.master._w, image) self.pack(fill=tk.BOTH, expand=True) + self.validation = InputValidation(self) def center(self): width = 1000 diff --git a/coretk/coretk/coreclient.py b/coretk/coretk/coreclient.py index 5ba2384a..e68b887d 100644 --- a/coretk/coretk/coreclient.py +++ b/coretk/coretk/coreclient.py @@ -86,6 +86,7 @@ class CoreClient: self.service_configs = {} self.file_configs = {} self.mobility_players = {} + self.throughput = False def reset(self): # helpers @@ -180,22 +181,11 @@ class CoreClient: canvas_node.move(x, y) def handle_throughputs(self, event): - # interface_throughputs = event.interface_throughputs - # # print(interface_throughputs) - # # return - # # for i in interface_throughputs: - # # print("") - # # # return - # print(event) - # throughputs_belong_to_session = [] - # print(self.node_ids) - # for throughput in interface_throughputs: - # if throughput.node_id in self.node_ids: - # throughputs_belong_to_session.append(throughput) - # print(throughputs_belong_to_session) - self.app.canvas.throughput_draw.process_grpc_throughput_event( - event.interface_throughputs - ) + # print(event.interface_throughputs) + if self.throughput: + self.app.canvas.throughput_draw.process_grpc_throughput_event( + event.interface_throughputs + ) def join_session(self, session_id, query_location=True): # update session and title @@ -595,8 +585,11 @@ class CoreClient: self.client.set_session_state( self.session_id, core_pb2.SessionState.DEFINITION ) + + # temp + self.client.set_session_state(self.session_id, core_pb2.SessionState.DEFINITION) for node_proto in node_protos: - if node_proto.id not in self.created_nodes: + if node_proto.id not in self.created_nodes or True: response = self.client.add_node(self.session_id, node_proto) logging.debug("create node: %s", response) self.created_nodes.add(node_proto.id) @@ -604,6 +597,7 @@ class CoreClient: if ( tuple([link_proto.node_one_id, link_proto.node_two_id]) not in self.created_links + or True ): response = self.client.add_link( self.session_id, diff --git a/coretk/coretk/dialogs/canvassizeandscale.py b/coretk/coretk/dialogs/canvassizeandscale.py index 8d6db86f..624e4246 100644 --- a/coretk/coretk/dialogs/canvassizeandscale.py +++ b/coretk/coretk/dialogs/canvassizeandscale.py @@ -19,6 +19,7 @@ class SizeAndScaleDialog(Dialog): """ super().__init__(master, app, "Canvas Size and Scale", modal=True) self.canvas = self.app.canvas + self.validation = app.validation self.section_font = font.Font(weight="bold") # get current canvas dimensions plot = self.canvas.find_withtag("rectangle") @@ -59,11 +60,23 @@ class SizeAndScaleDialog(Dialog): frame.columnconfigure(3, weight=1) label = ttk.Label(frame, text="Width") label.grid(row=0, column=0, sticky="w", padx=PAD) - entry = ttk.Entry(frame, textvariable=self.pixel_width) + entry = ttk.Entry( + frame, + textvariable=self.pixel_width, + validate="key", + validatecommand=(self.validation.positive_int, "%P"), + ) + entry.bind("", self.validation.focus_out) entry.grid(row=0, column=1, sticky="ew", padx=PAD) label = ttk.Label(frame, text="x Height") label.grid(row=0, column=2, sticky="w", padx=PAD) - entry = ttk.Entry(frame, textvariable=self.pixel_height) + entry = ttk.Entry( + frame, + textvariable=self.pixel_height, + validate="key", + validatecommand=(self.validation.positive_int, "%P"), + ) + entry.bind("", self.validation.focus_out) entry.grid(row=0, column=3, sticky="ew", padx=PAD) label = ttk.Label(frame, text="Pixels") label.grid(row=0, column=4, sticky="w") @@ -75,11 +88,23 @@ class SizeAndScaleDialog(Dialog): frame.columnconfigure(3, weight=1) label = ttk.Label(frame, text="Width") label.grid(row=0, column=0, sticky="w", padx=PAD) - entry = ttk.Entry(frame, textvariable=self.meters_width) + entry = ttk.Entry( + frame, + textvariable=self.meters_width, + validate="key", + validatecommand=(self.validation.positive_float, "%P"), + ) + entry.bind("", self.validation.focus_out) entry.grid(row=0, column=1, sticky="ew", padx=PAD) label = ttk.Label(frame, text="x Height") label.grid(row=0, column=2, sticky="w", padx=PAD) - entry = ttk.Entry(frame, textvariable=self.meters_height) + entry = ttk.Entry( + frame, + textvariable=self.meters_height, + validate="key", + validatecommand=(self.validation.positive_float, "%P"), + ) + entry.bind("", self.validation.focus_out) entry.grid(row=0, column=3, sticky="ew", padx=PAD) label = ttk.Label(frame, text="Meters") label.grid(row=0, column=4, sticky="w") @@ -94,7 +119,13 @@ class SizeAndScaleDialog(Dialog): frame.columnconfigure(1, weight=1) label = ttk.Label(frame, text=f"{PIXEL_SCALE} Pixels =") label.grid(row=0, column=0, sticky="w", padx=PAD) - entry = ttk.Entry(frame, textvariable=self.scale) + entry = ttk.Entry( + frame, + textvariable=self.scale, + validate="key", + validatecommand=(self.validation.positive_float, "%P"), + ) + entry.bind("", self.validation.focus_out) entry.grid(row=0, column=1, sticky="ew", padx=PAD) label = ttk.Label(frame, text="Meters") label.grid(row=0, column=2, sticky="w") @@ -116,12 +147,24 @@ class SizeAndScaleDialog(Dialog): label = ttk.Label(frame, text="X") label.grid(row=0, column=0, sticky="w", padx=PAD) - entry = ttk.Entry(frame, textvariable=self.x) + entry = ttk.Entry( + frame, + textvariable=self.x, + validate="key", + validatecommand=(self.validation.positive_float, "%P"), + ) + entry.bind("", self.validation.focus_out) entry.grid(row=0, column=1, sticky="ew", padx=PAD) label = ttk.Label(frame, text="Y") label.grid(row=0, column=2, sticky="w", padx=PAD) - entry = ttk.Entry(frame, textvariable=self.y) + entry = ttk.Entry( + frame, + textvariable=self.y, + validate="key", + validatecommand=(self.validation.positive_float, "%P"), + ) + entry.bind("", self.validation.focus_out) entry.grid(row=0, column=3, sticky="ew", padx=PAD) label = ttk.Label(label_frame, text="Translates To") @@ -135,17 +178,35 @@ class SizeAndScaleDialog(Dialog): label = ttk.Label(frame, text="Lat") label.grid(row=0, column=0, sticky="w", padx=PAD) - entry = ttk.Entry(frame, textvariable=self.lat) + entry = ttk.Entry( + frame, + textvariable=self.lat, + validate="key", + validatecommand=(self.validation.positive_float, "%P"), + ) + entry.bind("", self.validation.focus_out) entry.grid(row=0, column=1, sticky="ew", padx=PAD) label = ttk.Label(frame, text="Lon") label.grid(row=0, column=2, sticky="w", padx=PAD) - entry = ttk.Entry(frame, textvariable=self.lon) + entry = ttk.Entry( + frame, + textvariable=self.lon, + validate="key", + validatecommand=(self.validation.positive_float, "%P"), + ) + entry.bind("", self.validation.focus_out) entry.grid(row=0, column=3, sticky="ew", padx=PAD) label = ttk.Label(frame, text="Alt") label.grid(row=0, column=4, sticky="w", padx=PAD) - entry = ttk.Entry(frame, textvariable=self.alt) + entry = ttk.Entry( + frame, + textvariable=self.alt, + validate="key", + validatecommand=(self.validation.positive_float, "%P"), + ) + entry.bind("", self.validation.focus_out) entry.grid(row=0, column=5, sticky="ew") def draw_save_as_default(self): diff --git a/coretk/coretk/dialogs/nodeconfig.py b/coretk/coretk/dialogs/nodeconfig.py index f296e4cf..9f7c4d6c 100644 --- a/coretk/coretk/dialogs/nodeconfig.py +++ b/coretk/coretk/dialogs/nodeconfig.py @@ -69,7 +69,13 @@ class NodeConfigDialog(Dialog): # name field label = ttk.Label(frame, text="Name") label.grid(row=row, column=0, sticky="ew", padx=PAD, pady=PAD) - entry = ttk.Entry(frame, textvariable=self.name) + entry = ttk.Entry( + frame, + textvariable=self.name, + validate="key", + validatecommand=(self.app.validation.name, "%P"), + ) + entry.bind("", self.app.validation.name_focus_out) entry.grid(row=row, column=1, sticky="ew") row += 1 @@ -206,5 +212,4 @@ class NodeConfigDialog(Dialog): # redraw self.canvas_node.redraw() - self.destroy() diff --git a/coretk/coretk/dialogs/nodeservice.py b/coretk/coretk/dialogs/nodeservice.py index 35142497..d950371b 100644 --- a/coretk/coretk/dialogs/nodeservice.py +++ b/coretk/coretk/dialogs/nodeservice.py @@ -87,6 +87,7 @@ class NodeService(Dialog): self.current.listbox.delete(0, tk.END) for name in sorted(self.current_services): self.current.listbox.insert(tk.END, name) + self.canvas_node.core_node.services[:] = self.current_services def click_configure(self): current_selection = self.current.listbox.curselection() diff --git a/coretk/coretk/graph/graph.py b/coretk/coretk/graph/graph.py index 612088a0..1091905c 100644 --- a/coretk/coretk/graph/graph.py +++ b/coretk/coretk/graph/graph.py @@ -92,6 +92,7 @@ class CanvasGraph(tk.Canvas): # set the private variables to default value self.mode = GraphMode.SELECT + self.annotation_type = None self.node_draw = None self.selected = None self.nodes.clear() diff --git a/coretk/coretk/graph/node.py b/coretk/coretk/graph/node.py index acd03de3..b34f27c1 100644 --- a/coretk/coretk/graph/node.py +++ b/coretk/coretk/graph/node.py @@ -145,9 +145,6 @@ class CanvasNode: def on_leave(self, event): self.tooltip.on_leave(event) - def click(self, event): - print("click") - def double_click(self, event): if self.app.core.is_runtime(): self.canvas.core.launch_terminal(self.core_node.id) diff --git a/coretk/coretk/menuaction.py b/coretk/coretk/menuaction.py index 246020ba..a9f9ca34 100644 --- a/coretk/coretk/menuaction.py +++ b/coretk/coretk/menuaction.py @@ -149,3 +149,10 @@ class MenuAction: def show_about(self): dialog = AboutDialog(self.app, self.app) dialog.show() + + def throughput(self): + throughput = self.app.core.throughput + if throughput: + self.app.core.throughput = False + else: + self.app.core.throughput = True diff --git a/coretk/coretk/menubar.py b/coretk/coretk/menubar.py index 288a517f..ed5cb2ca 100644 --- a/coretk/coretk/menubar.py +++ b/coretk/coretk/menubar.py @@ -422,7 +422,7 @@ class Menubar(tk.Menu): menu = tk.Menu(self) self.create_observer_widgets_menu(menu) self.create_adjacency_menu(menu) - menu.add_command(label="Throughput", state=tk.DISABLED) + menu.add_checkbutton(label="Throughput", command=self.menuaction.throughput) menu.add_separator() menu.add_command(label="Configure Adjacency...", state=tk.DISABLED) menu.add_command(label="Configure Throughput...", state=tk.DISABLED) diff --git a/coretk/coretk/validation.py b/coretk/coretk/validation.py new file mode 100644 index 00000000..a580c712 --- /dev/null +++ b/coretk/coretk/validation.py @@ -0,0 +1,89 @@ +""" +input validation +""" +import logging +import tkinter as tk + + +class InputValidation: + def __init__(self, app): + self.master = app.master + self.positive_int = None + self.positive_float = None + self.name = None + self.register() + + def register(self): + self.positive_int = self.master.register(self.check_positive_int) + self.positive_float = self.master.register(self.check_positive_float) + self.name = self.master.register(self.check_node_name) + + def focus_out(self, event): + value = event.widget.get() + if value == "": + event.widget.insert(tk.END, 0) + + def name_focus_out(self, event): + logging.debug("name focus out") + value = event.widget.get() + if value == "": + event.widget.insert(tk.END, "empty") + + def check_positive_int(self, s): + logging.debug("int validation...") + if len(s) == 0: + return True + try: + int_value = int(s) + if int_value >= 0: + return True + return False + except ValueError: + return False + + def check_positive_float(self, s): + logging.debug("float validation...") + if len(s) == 0: + return True + try: + float_value = float(s) + if float_value >= 0.0: + return True + return False + except ValueError: + return False + + def check_node_name(self, s): + logging.debug("node name validation...") + if len(s) < 0: + return False + if len(s) == 0: + return True + for char in s: + if not char.isalnum() and char != "_": + return False + return True + + def check_canvas_int(sefl, s): + logging.debug("int validation...") + if len(s) == 0: + return True + try: + int_value = int(s) + if int_value >= 0: + return True + return False + except ValueError: + return False + + def check_canvas_float(self, s): + logging.debug("canvas float validation") + if not s: + return True + try: + float_value = float(s) + if float_value >= 0.0: + return True + return False + except ValueError: + return False diff --git a/coretk/coretk/widgets.py b/coretk/coretk/widgets.py index 486370e0..7d908476 100644 --- a/coretk/coretk/widgets.py +++ b/coretk/coretk/widgets.py @@ -66,6 +66,7 @@ class FrameScroll(ttk.LabelFrame): class ConfigFrame(FrameScroll): def __init__(self, master, app, config, **kw): super().__init__(master, app, ttk.Notebook, **kw) + self.app = app self.config = config self.values = {} @@ -120,11 +121,23 @@ class ConfigFrame(FrameScroll): entry.grid(row=index, column=1, sticky="ew", pady=pady) elif option.type in INT_TYPES: value.set(option.value) - entry = ttk.Entry(frame, textvariable=value) + entry = ttk.Entry( + frame, + textvariable=value, + validate="key", + validatecommand=(self.app.validation.positive_int, "%P"), + ) + entry.bind("", self.app.validation.focus_out) entry.grid(row=index, column=1, sticky="ew", pady=pady) elif option.type == core_pb2.ConfigOptionType.FLOAT: value.set(option.value) - entry = ttk.Entry(frame, textvariable=value) + entry = ttk.Entry( + frame, + textvariable=value, + validate="key", + validatecommand=(self.app.validation.positive_float, "%P"), + ) + entry.bind("", self.app.validation.focus_out) entry.grid(row=index, column=1, sticky="ew", pady=pady) else: logging.error("unhandled config option type: %s", option.type)