From 49acac026c0cc71332bbfe0052c050b87bdec423 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Fri, 6 Dec 2019 09:10:50 -0800 Subject: [PATCH 1/5] updates to move node context logic to node class and added check to display options the same as old core --- coretk/coretk/graph/graph.py | 37 +--------------------- coretk/coretk/graph/node.py | 60 +++++++++++++++++++++++++++++++++--- 2 files changed, 56 insertions(+), 41 deletions(-) diff --git a/coretk/coretk/graph/graph.py b/coretk/coretk/graph/graph.py index abf3f952..4abb6e18 100644 --- a/coretk/coretk/graph/graph.py +++ b/coretk/coretk/graph/graph.py @@ -4,7 +4,6 @@ import tkinter as tk from PIL import Image, ImageTk from core.api.grpc import core_pb2 -from core.api.grpc.core_pb2 import NodeType from coretk.dialogs.shapemod import ShapeDialog from coretk.graph.edges import CanvasEdge, CanvasWirelessEdge from coretk.graph.enums import GraphMode, ScaleOption @@ -63,40 +62,6 @@ class CanvasGraph(tk.Canvas): self.show_grid = tk.BooleanVar(value=True) self.adjust_to_dim = tk.BooleanVar(value=False) - def create_node_context(self, canvas_node): - node = canvas_node.core_node - context = tk.Menu(self.master) - context.add_command(label="Configure", command=canvas_node.show_config) - if node.type == NodeType.WIRELESS_LAN: - context.add_command( - label="WLAN Config", command=canvas_node.show_wlan_config - ) - if self.master.core.is_runtime(): - if canvas_node.core_node.id in self.master.core.mobility_players: - context.add_command( - label="Mobility Player", - command=canvas_node.show_mobility_player, - ) - else: - context.add_command( - label="Mobility Config", command=canvas_node.show_mobility_config - ) - if node.type == NodeType.EMANE: - context.add_command( - label="EMANE Config", command=canvas_node.show_emane_config - ) - context.add_command(label="Select adjacent", state=tk.DISABLED) - context.add_command(label="Create link to", state=tk.DISABLED) - context.add_command(label="Assign to", state=tk.DISABLED) - context.add_command(label="Move to", state=tk.DISABLED) - context.add_command(label="Cut", state=tk.DISABLED) - context.add_command(label="Copy", state=tk.DISABLED) - context.add_command(label="Paste", state=tk.DISABLED) - context.add_command(label="Delete", state=tk.DISABLED) - context.add_command(label="Hide", state=tk.DISABLED) - context.add_command(label="Services", state=tk.DISABLED) - return context - def reset_and_redraw(self, session): """ Reset the private variables CanvasGraph object, redraw nodes given the new grpc @@ -512,7 +477,7 @@ class CanvasGraph(tk.Canvas): canvas_node = self.nodes.get(selected) if canvas_node: logging.debug(f"node context: {selected}") - self.context = self.create_node_context(canvas_node) + self.context = canvas_node.create_context() self.context.post(event.x_root, event.y_root) else: self.context.unpost() diff --git a/coretk/coretk/graph/node.py b/coretk/coretk/graph/node.py index 0a46fd77..20e95867 100644 --- a/coretk/coretk/graph/node.py +++ b/coretk/coretk/graph/node.py @@ -2,6 +2,7 @@ import logging import tkinter as tk from tkinter import font +from core.api.grpc.core_pb2 import NodeType from coretk.dialogs.emaneconfig import EmaneConfigDialog from coretk.dialogs.mobilityconfig import MobilityConfigDialog from coretk.dialogs.nodeconfig import NodeConfigDialog @@ -36,6 +37,15 @@ class CanvasNode: fill="#0000CD", ) self.tooltip = CanvasTooltip(self.canvas) + self.edges = set() + self.interfaces = [] + self.wireless_edges = set() + self.moving = None + self.antennae = [] + self.setup_bindings() + + def setup_bindings(self): + # self.canvas.bind("", self.click_context) self.canvas.tag_bind(self.id, "", self.click_press) self.canvas.tag_bind(self.id, "", self.click_release) self.canvas.tag_bind(self.id, "", self.motion) @@ -43,11 +53,6 @@ class CanvasNode: self.canvas.tag_bind(self.id, "", self.select_multiple) self.canvas.tag_bind(self.id, "", self.on_enter) self.canvas.tag_bind(self.id, "", self.on_leave) - self.edges = set() - self.interfaces = [] - self.wireless_edges = set() - self.moving = None - self.antennae = [] def delete(self): self.canvas.delete(self.id) @@ -184,6 +189,51 @@ class CanvasNode: shape = self.canvas.shapes[object_id] shape.motion(None, x - my_x, y - my_y) + def create_context(self): + is_wlan = self.core_node.type == NodeType.WIRELESS_LAN + is_emane = self.core_node.type == NodeType.EMANE + context = tk.Menu(self.canvas) + if self.app.core.is_runtime(): + context.add_command(label="Configure", command=self.show_config) + if is_wlan and self.core_node.id in self.app.core.mobility_players: + context.add_command( + label="Mobility Player", command=self.show_mobility_player + ) + context.add_command(label="Select Adjacent", state=tk.DISABLED) + context.add_command(label="Hide", state=tk.DISABLED) + context.add_command(label="Services", state=tk.DISABLED) + if NodeUtils.is_container_node(self.core_node.type): + context.add_command(label="Shell Window", state=tk.DISABLED) + context.add_command(label="Tcpdump", state=tk.DISABLED) + context.add_command(label="Tshark", state=tk.DISABLED) + context.add_command(label="Wireshark", state=tk.DISABLED) + context.add_command(label="View Log", state=tk.DISABLED) + else: + context.add_command(label="Configure", command=self.show_config) + if is_emane: + context.add_command( + label="EMANE Config", command=self.show_emane_config + ) + if is_wlan: + context.add_command(label="WLAN Config", command=self.show_wlan_config) + context.add_command( + label="Mobility Config", command=self.show_mobility_config + ) + if is_wlan or is_emane: + context.add_command(label="Link To All MDRs", state=tk.DISABLED) + context.add_command(label="Select Members", state=tk.DISABLED) + context.add_command(label="Select Adjacent", state=tk.DISABLED) + context.add_command(label="Create Link To", state=tk.DISABLED) + context.add_command(label="Assign To", state=tk.DISABLED) + context.add_command(label="Move To", state=tk.DISABLED) + context.add_command(label="Cut", state=tk.DISABLED) + context.add_command(label="Copy", state=tk.DISABLED) + context.add_command(label="Paste", state=tk.DISABLED) + context.add_command(label="Delete", state=tk.DISABLED) + context.add_command(label="Hide", state=tk.DISABLED) + context.add_command(label="Services", state=tk.DISABLED) + return context + def select_multiple(self, event): self.canvas.select_object(self.id, choose_multiple=True) From b9bbf397c96f9eaade45e97a093602b6e0d3ce27 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Fri, 6 Dec 2019 09:13:58 -0800 Subject: [PATCH 2/5] updated node context menu option for wireless to use nodeutils --- coretk/coretk/graph/node.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coretk/coretk/graph/node.py b/coretk/coretk/graph/node.py index 20e95867..f726739c 100644 --- a/coretk/coretk/graph/node.py +++ b/coretk/coretk/graph/node.py @@ -219,7 +219,7 @@ class CanvasNode: context.add_command( label="Mobility Config", command=self.show_mobility_config ) - if is_wlan or is_emane: + if NodeUtils.is_wireless_node(self.core_node.type): context.add_command(label="Link To All MDRs", state=tk.DISABLED) context.add_command(label="Select Members", state=tk.DISABLED) context.add_command(label="Select Adjacent", state=tk.DISABLED) From 45a23a6c1417d7dc08af9e824d0f65749251c6be Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Fri, 6 Dec 2019 09:42:41 -0800 Subject: [PATCH 3/5] updated usage of time.time to time.monotonic or time.perf_counter due to time.time possibly rolling backwards --- coretk/coretk/coreclient.py | 8 ++++---- coretk/coretk/menuaction.py | 4 ++-- daemon/core/api/grpc/server.py | 2 +- daemon/core/api/tlv/corehandlers.py | 2 +- daemon/core/emulator/session.py | 8 ++++---- daemon/core/location/event.py | 8 ++++---- daemon/core/location/mobility.py | 10 +++++----- daemon/core/nodes/network.py | 8 ++++---- daemon/core/services/coreservices.py | 4 ++-- daemon/tests/test_grpc.py | 2 +- daemon/tests/test_gui.py | 2 +- 11 files changed, 29 insertions(+), 29 deletions(-) diff --git a/coretk/coretk/coreclient.py b/coretk/coretk/coreclient.py index 05a36c82..e2aef5a2 100644 --- a/coretk/coretk/coreclient.py +++ b/coretk/coretk/coreclient.py @@ -411,7 +411,7 @@ class CoreClient: else: emane_config = None - start = time.time() + start = time.perf_counter() response = self.client.start_session( self.session_id, nodes, @@ -425,7 +425,7 @@ class CoreClient: service_configs, file_configs, ) - process_time = time.time() - start + process_time = time.perf_counter() - start logging.debug("start session(%s), result: %s", self.session_id, response.result) self.app.statusbar.start_session_callback(process_time) @@ -439,9 +439,9 @@ class CoreClient: def stop_session(self, session_id=None): if not session_id: session_id = self.session_id - start = time.time() + start = time.perf_counter() response = self.client.stop_session(session_id) - process_time = time.time() - start + process_time = time.perf_counter() - start self.app.statusbar.stop_session_callback(process_time) logging.debug("stopped session(%s), result: %s", session_id, response.result) diff --git a/coretk/coretk/menuaction.py b/coretk/coretk/menuaction.py index d8dc89cb..1a64800c 100644 --- a/coretk/coretk/menuaction.py +++ b/coretk/coretk/menuaction.py @@ -33,10 +33,10 @@ class MenuAction: def cleanup_old_session(self, quitapp=False): logging.info("cleaning up old session") - start = time.time() + start = time.perf_counter() self.app.core.stop_session() self.app.core.delete_session() - process_time = time.time() - start + process_time = time.perf_counter() - start self.app.statusbar.stop_session_callback(process_time) if quitapp: self.app.quit() diff --git a/daemon/core/api/grpc/server.py b/daemon/core/api/grpc/server.py index a914d617..6a6747f1 100644 --- a/daemon/core/api/grpc/server.py +++ b/daemon/core/api/grpc/server.py @@ -660,7 +660,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): last_check = None last_stats = None while self._is_running(context): - now = time.time() + now = time.monotonic() stats = get_net_stats() # calculate average diff --git a/daemon/core/api/tlv/corehandlers.py b/daemon/core/api/tlv/corehandlers.py index 321306a2..009cb067 100644 --- a/daemon/core/api/tlv/corehandlers.py +++ b/daemon/core/api/tlv/corehandlers.py @@ -1710,7 +1710,7 @@ class CoreHandler(socketserver.BaseRequestHandler): event_type=event_type, name=name, data=fail_data + ";" + unknown_data, - time=str(time.time()), + time=str(time.monotonic()), ) self.session.broadcast_event(event_data) diff --git a/daemon/core/emulator/session.py b/daemon/core/emulator/session.py index fee2bde4..1a01bdaa 100644 --- a/daemon/core/emulator/session.py +++ b/daemon/core/emulator/session.py @@ -103,7 +103,7 @@ class Session: # TODO: should the default state be definition? self.state = EventTypes.NONE.value - self._state_time = time.time() + self._state_time = time.monotonic() self._state_file = os.path.join(self.session_dir, "state") # hooks handlers @@ -1030,7 +1030,7 @@ class Session: return self.state = state_value - self._state_time = time.time() + self._state_time = time.monotonic() logging.info("changing session(%s) to state %s", self.id, state_name) self.write_state(state_value) @@ -1038,7 +1038,7 @@ class Session: self.run_state_hooks(state_value) if send_event: - event_data = EventData(event_type=state_value, time=str(time.time())) + event_data = EventData(event_type=state_value, time=str(time.monotonic())) self.broadcast_event(event_data) def write_state(self, state): @@ -1821,7 +1821,7 @@ class Session: if not in runtime. """ if self.state == EventTypes.RUNTIME_STATE.value: - return time.time() - self._state_time + return time.monotonic() - self._state_time else: return 0.0 diff --git a/daemon/core/location/event.py b/daemon/core/location/event.py index 11e535d3..f930d9b7 100644 --- a/daemon/core/location/event.py +++ b/daemon/core/location/event.py @@ -145,7 +145,7 @@ class EventLoop: with self.lock: if not self.running or not self.queue: break - now = time.time() + now = time.monotonic() if self.queue[0].time > now: schedule = True break @@ -170,7 +170,7 @@ class EventLoop: raise ValueError("scheduling event while not running") if not self.queue: return - delay = self.queue[0].time - time.time() + delay = self.queue[0].time - time.monotonic() if self.timer: raise ValueError("timer was already set") self.timer = Timer(delay, self.__run_events) @@ -187,7 +187,7 @@ class EventLoop: if self.running: return self.running = True - self.start = time.time() + self.start = time.monotonic() for event in self.queue: event.time += self.start self.__schedule_event() @@ -225,7 +225,7 @@ class EventLoop: self.eventnum += 1 evtime = float(delaysec) if self.running: - evtime += time.time() + evtime += time.monotonic() event = Event(eventnum, evtime, func, *args, **kwds) if self.queue: diff --git a/daemon/core/location/mobility.py b/daemon/core/location/mobility.py index 6a796526..b3eb4884 100644 --- a/daemon/core/location/mobility.py +++ b/daemon/core/location/mobility.py @@ -174,7 +174,7 @@ class MobilityManager(ModelManager): event_type=event_type, name=f"mobility:{model.name}", data=data, - time=str(time.time()), + time=str(time.monotonic()), ) self.session.broadcast_event(event_data) @@ -612,7 +612,7 @@ class WayPointMobility(WirelessModel): if self.state != self.STATE_RUNNING: return t = self.lasttime - self.lasttime = time.time() + self.lasttime = time.monotonic() now = self.lasttime - self.timezero dt = self.lasttime - t @@ -664,7 +664,7 @@ class WayPointMobility(WirelessModel): :return: nothing """ logging.info("running mobility scenario") - self.timezero = time.time() + self.timezero = time.monotonic() self.lasttime = self.timezero - (0.001 * self.refresh_ms) self.movenodesinitial() self.runround() @@ -844,7 +844,7 @@ class WayPointMobility(WirelessModel): self.lasttime = 0 self.run() elif laststate == self.STATE_PAUSED: - now = time.time() + now = time.monotonic() self.timezero += now - self.lasttime self.lasttime = now - (0.001 * self.refresh_ms) self.runround() @@ -871,7 +871,7 @@ class WayPointMobility(WirelessModel): :return: nothing """ self.state = self.STATE_PAUSED - self.lasttime = time.time() + self.lasttime = time.monotonic() class Ns2ScriptedMobility(WayPointMobility): diff --git a/daemon/core/nodes/network.py b/daemon/core/nodes/network.py index 6fe291dd..f0639649 100644 --- a/daemon/core/nodes/network.py +++ b/daemon/core/nodes/network.py @@ -57,7 +57,7 @@ class EbtablesQueue: :return: nothing """ with self.updatelock: - self.last_update_time[wlan] = time.time() + self.last_update_time[wlan] = time.monotonic() if self.doupdateloop: return @@ -108,9 +108,9 @@ class EbtablesQueue: :rtype: float """ try: - elapsed = time.time() - self.last_update_time[wlan] + elapsed = time.monotonic() - self.last_update_time[wlan] except KeyError: - self.last_update_time[wlan] = time.time() + self.last_update_time[wlan] = time.monotonic() elapsed = 0.0 return elapsed @@ -122,7 +122,7 @@ class EbtablesQueue: :param wlan: wlan entity :return: nothing """ - self.last_update_time[wlan] = time.time() + self.last_update_time[wlan] = time.monotonic() self.updates.remove(wlan) def updateloop(self): diff --git a/daemon/core/services/coreservices.py b/daemon/core/services/coreservices.py index 80168425..361061be 100644 --- a/daemon/core/services/coreservices.py +++ b/daemon/core/services/coreservices.py @@ -538,13 +538,13 @@ class CoreServices: time.sleep(service.validation_timer) # non-blocking, attempt to validate periodically, up to validation_timer time elif service.validation_mode == ServiceMode.NON_BLOCKING: - start = time.time() + start = time.monotonic() while True: status = self.validate_service(node, service) if not status: break - if time.time() - start > service.validation_timer: + if time.monotonic() - start > service.validation_timer: break time.sleep(service.validation_period) diff --git a/daemon/tests/test_grpc.py b/daemon/tests/test_grpc.py index 28005dbb..56523e81 100644 --- a/daemon/tests/test_grpc.py +++ b/daemon/tests/test_grpc.py @@ -1042,7 +1042,7 @@ class TestGrpc: client.events(session.id, handle_event) time.sleep(0.1) event = EventData( - event_type=EventTypes.RUNTIME_STATE.value, time=str(time.time()) + event_type=EventTypes.RUNTIME_STATE.value, time=str(time.monotonic()) ) session.broadcast_event(event) diff --git a/daemon/tests/test_gui.py b/daemon/tests/test_gui.py index c2a8c9fc..91324d08 100644 --- a/daemon/tests/test_gui.py +++ b/daemon/tests/test_gui.py @@ -523,7 +523,7 @@ class TestGui: MessageFlags.ADD.value, [ (EventTlvs.TYPE, EventTypes.SCHEDULED.value), - (EventTlvs.TIME, str(time.time() + 100)), + (EventTlvs.TIME, str(time.monotonic() + 100)), (EventTlvs.NODE, node.id), (EventTlvs.NAME, "event"), (EventTlvs.DATA, "data"), From 9ef5cdd70be6fa180dc97a8628e6f19761283061 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Fri, 6 Dec 2019 11:13:43 -0800 Subject: [PATCH 4/5] added basic about dialog and created codetext widget for displaying text in terminal like colors using scrolledtext widget --- coretk/coretk/dialogs/about.py | 44 +++++++++++++++++++ coretk/coretk/dialogs/hooks.py | 3 +- coretk/coretk/dialogs/serviceconfiguration.py | 5 +-- coretk/coretk/menuaction.py | 5 +++ coretk/coretk/menubar.py | 2 +- coretk/coretk/widgets.py | 26 ++++++++++- 6 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 coretk/coretk/dialogs/about.py diff --git a/coretk/coretk/dialogs/about.py b/coretk/coretk/dialogs/about.py new file mode 100644 index 00000000..28ddea33 --- /dev/null +++ b/coretk/coretk/dialogs/about.py @@ -0,0 +1,44 @@ +import tkinter as tk + +from coretk.dialogs.dialog import Dialog +from coretk.widgets import CodeText + +LICENSE = """\ +Copyright (c) 2005-2020, the Boeing Company. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE.\ +""" + + +class AboutDialog(Dialog): + def __init__(self, master, app): + super().__init__(master, app, "About CORE", modal=True) + self.draw() + + def draw(self): + self.top.columnconfigure(0, weight=1) + self.top.rowconfigure(0, weight=1) + + text = CodeText(self.top) + text.insert("1.0", LICENSE) + text.config(state=tk.DISABLED) + text.grid(sticky="nsew") diff --git a/coretk/coretk/dialogs/hooks.py b/coretk/coretk/dialogs/hooks.py index dc677e12..d40f1c36 100644 --- a/coretk/coretk/dialogs/hooks.py +++ b/coretk/coretk/dialogs/hooks.py @@ -3,6 +3,7 @@ from tkinter import ttk from core.api.grpc import core_pb2 from coretk.dialogs.dialog import Dialog +from coretk.widgets import CodeText class HookDialog(Dialog): @@ -43,7 +44,7 @@ class HookDialog(Dialog): frame.columnconfigure(0, weight=1) frame.rowconfigure(0, weight=1) frame.grid(row=1, sticky="nsew", pady=2) - self.data = tk.Text(frame) + self.data = CodeText(frame) self.data.insert( 1.0, ( diff --git a/coretk/coretk/dialogs/serviceconfiguration.py b/coretk/coretk/dialogs/serviceconfiguration.py index 1d8059d8..446f9c12 100644 --- a/coretk/coretk/dialogs/serviceconfiguration.py +++ b/coretk/coretk/dialogs/serviceconfiguration.py @@ -2,12 +2,11 @@ import logging import tkinter as tk from tkinter import ttk -from tkinter.scrolledtext import ScrolledText from core.api.grpc import core_pb2 from coretk.dialogs.dialog import Dialog from coretk.images import ImageEnum, Images -from coretk.widgets import ListboxScroll +from coretk.widgets import CodeText, ListboxScroll class ServiceConfiguration(Dialog): @@ -182,7 +181,7 @@ class ServiceConfiguration(Dialog): button.grid(row=0, column=2) frame.grid(row=3, column=0, sticky="nsew") - self.service_file_data = ScrolledText(tab1) + self.service_file_data = CodeText(tab1) self.service_file_data.grid(row=4, column=0, sticky="nsew") if len(self.filenames) > 0: self.filename_combobox.current(0) diff --git a/coretk/coretk/menuaction.py b/coretk/coretk/menuaction.py index 1a64800c..c4cc27bc 100644 --- a/coretk/coretk/menuaction.py +++ b/coretk/coretk/menuaction.py @@ -12,6 +12,7 @@ import grpc from core.api.grpc import core_pb2 from coretk.appconfig import XML_PATH +from coretk.dialogs.about import AboutDialog from coretk.dialogs.canvasbackground import CanvasBackgroundDialog from coretk.dialogs.canvassizeandscale import SizeAndScaleDialog from coretk.dialogs.hooks import HooksDialog @@ -151,3 +152,7 @@ class MenuAction: def edit_observer_widgets(self): dialog = ObserverDialog(self.app, self.app) dialog.show() + + def show_about(self): + dialog = AboutDialog(self.app, self.app) + dialog.show() diff --git a/coretk/coretk/menubar.py b/coretk/coretk/menubar.py index e56c55ec..288a517f 100644 --- a/coretk/coretk/menubar.py +++ b/coretk/coretk/menubar.py @@ -462,5 +462,5 @@ class Menubar(tk.Menu): label="Core Documentation (www)", command=self.menuaction.help_core_documentation, ) - menu.add_command(label="About", state=tk.DISABLED) + menu.add_command(label="About", command=self.menuaction.show_about) self.add_cascade(label="Help", menu=menu) diff --git a/coretk/coretk/widgets.py b/coretk/coretk/widgets.py index bd6904b2..760846a3 100644 --- a/coretk/coretk/widgets.py +++ b/coretk/coretk/widgets.py @@ -1,7 +1,8 @@ import logging import tkinter as tk from functools import partial -from tkinter import ttk +from tkinter import font, ttk +from tkinter.scrolledtext import ScrolledText from core.api.grpc import core_pb2 @@ -155,3 +156,26 @@ class CheckboxList(FrameScroll): func = partial(self.clicked, name, var) checkbox = ttk.Checkbutton(self.frame, text=name, variable=var, command=func) checkbox.grid(sticky="w") + + +class CodeFont(font.Font): + def __init__(self): + super().__init__(font="TkFixedFont", color="green") + + +class CodeText(ScrolledText): + def __init__(self, master, **kwargs): + super().__init__( + master, + bd=0, + bg="black", + cursor="xterm lime lime", + fg="lime", + font=CodeFont(), + highlightbackground="black", + insertbackground="lime", + selectbackground="lime", + selectforeground="black", + relief=tk.FLAT, + **kwargs + ) From c238b5dfc8ac2b5d5bd708c9d7b81a749c147b55 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Fri, 6 Dec 2019 11:17:05 -0800 Subject: [PATCH 5/5] moved node context services option higher for better convenience, added check that context services only shown for container nodes --- coretk/coretk/graph/node.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/coretk/coretk/graph/node.py b/coretk/coretk/graph/node.py index f726739c..70424176 100644 --- a/coretk/coretk/graph/node.py +++ b/coretk/coretk/graph/node.py @@ -195,13 +195,14 @@ class CanvasNode: context = tk.Menu(self.canvas) if self.app.core.is_runtime(): context.add_command(label="Configure", command=self.show_config) + if NodeUtils.is_container_node(self.core_node.type): + context.add_command(label="Services", state=tk.DISABLED) if is_wlan and self.core_node.id in self.app.core.mobility_players: context.add_command( label="Mobility Player", command=self.show_mobility_player ) context.add_command(label="Select Adjacent", state=tk.DISABLED) context.add_command(label="Hide", state=tk.DISABLED) - context.add_command(label="Services", state=tk.DISABLED) if NodeUtils.is_container_node(self.core_node.type): context.add_command(label="Shell Window", state=tk.DISABLED) context.add_command(label="Tcpdump", state=tk.DISABLED) @@ -210,6 +211,8 @@ class CanvasNode: context.add_command(label="View Log", state=tk.DISABLED) else: context.add_command(label="Configure", command=self.show_config) + if NodeUtils.is_container_node(self.core_node.type): + context.add_command(label="Services", state=tk.DISABLED) if is_emane: context.add_command( label="EMANE Config", command=self.show_emane_config @@ -231,7 +234,6 @@ class CanvasNode: context.add_command(label="Paste", state=tk.DISABLED) context.add_command(label="Delete", state=tk.DISABLED) context.add_command(label="Hide", state=tk.DISABLED) - context.add_command(label="Services", state=tk.DISABLED) return context def select_multiple(self, event):