separated out graph code into more file and its own sub package
This commit is contained in:
parent
8c30ad6af5
commit
d970d5ee85
14 changed files with 307 additions and 298 deletions
|
@ -5,12 +5,12 @@ from tkinter import ttk
|
||||||
|
|
||||||
from coretk import appconfig, themes
|
from coretk import appconfig, themes
|
||||||
from coretk.coreclient import CoreClient
|
from coretk.coreclient import CoreClient
|
||||||
from coretk.graph import CanvasGraph
|
from coretk.graph.graph import CanvasGraph
|
||||||
from coretk.images import ImageEnum, Images
|
from coretk.images import ImageEnum, Images
|
||||||
from coretk.menuaction import MenuAction
|
from coretk.menuaction import MenuAction
|
||||||
from coretk.menubar import Menubar
|
from coretk.menubar import Menubar
|
||||||
from coretk.nodeutils import NodeUtils
|
from coretk.nodeutils import NodeUtils
|
||||||
from coretk.status import StatusBar
|
from coretk.statusbar import StatusBar
|
||||||
from coretk.toolbar import Toolbar
|
from coretk.toolbar import Toolbar
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,12 +7,11 @@ import os
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from core.api.grpc import client, core_pb2
|
from core.api.grpc import client, core_pb2
|
||||||
from coretk import appconfig, parsedata
|
from coretk import appconfig
|
||||||
from coretk.dialogs.mobilityplayer import MobilityPlayer
|
from coretk.dialogs.mobilityplayer import MobilityPlayer
|
||||||
from coretk.dialogs.sessions import SessionsDialog
|
from coretk.dialogs.sessions import SessionsDialog
|
||||||
from coretk.interface import InterfaceManager
|
from coretk.interface import InterfaceManager
|
||||||
from coretk.nodeutils import NodeDraw, NodeUtils
|
from coretk.nodeutils import NodeDraw, NodeUtils
|
||||||
from coretk.shape import Shape, ShapeData
|
|
||||||
|
|
||||||
OBSERVERS = {
|
OBSERVERS = {
|
||||||
"processes": "ps",
|
"processes": "ps",
|
||||||
|
@ -282,46 +281,46 @@ class CoreClient:
|
||||||
return self.state == core_pb2.SessionState.RUNTIME
|
return self.state == core_pb2.SessionState.RUNTIME
|
||||||
|
|
||||||
def parse_metadata(self, config):
|
def parse_metadata(self, config):
|
||||||
for key, value in config.items():
|
# for key, value in config.items():
|
||||||
if "global_options" != key:
|
# if "global_options" != key:
|
||||||
canvas_config = parsedata.parse(value)
|
# canvas_config = parsedata.parse(value)
|
||||||
print(canvas_config)
|
# print(canvas_config)
|
||||||
if canvas_config.get("type"):
|
# if canvas_config.get("type"):
|
||||||
config_type = canvas_config["type"]
|
# config_type = canvas_config["type"]
|
||||||
if config_type == "rectangle" or config_type == "oval":
|
# if config_type == "rectangle" or config_type == "oval":
|
||||||
data = ShapeData(
|
# data = ShapeData(
|
||||||
False,
|
# False,
|
||||||
canvas_config["label"],
|
# canvas_config["label"],
|
||||||
canvas_config["fontfamily"],
|
# canvas_config["fontfamily"],
|
||||||
canvas_config["fontsize"],
|
# canvas_config["fontsize"],
|
||||||
canvas_config["labelcolor"],
|
# canvas_config["labelcolor"],
|
||||||
canvas_config["color"],
|
# canvas_config["color"],
|
||||||
canvas_config["border"],
|
# canvas_config["border"],
|
||||||
canvas_config["width"],
|
# canvas_config["width"],
|
||||||
)
|
# )
|
||||||
coords = tuple(
|
# coords = tuple(
|
||||||
[float(x) for x in canvas_config["iconcoords"].split()]
|
# [float(x) for x in canvas_config["iconcoords"].split()]
|
||||||
)
|
# )
|
||||||
shape = Shape(
|
# shape = Shape(
|
||||||
self.app,
|
# self.app,
|
||||||
self.app.canvas,
|
# self.app.canvas,
|
||||||
None,
|
# None,
|
||||||
None,
|
# None,
|
||||||
coords,
|
# coords,
|
||||||
data,
|
# data,
|
||||||
config_type,
|
# config_type,
|
||||||
)
|
# )
|
||||||
self.app.canvas.shapes[shape.id] = shape
|
# self.app.canvas.shapes[shape.id] = shape
|
||||||
elif canvas_config["type"] == "text":
|
# elif canvas_config["type"] == "text":
|
||||||
print("not implemented")
|
# print("not implemented")
|
||||||
else:
|
# else:
|
||||||
if "wallpaper" in canvas_config:
|
# if "wallpaper" in canvas_config:
|
||||||
logging.info("canvas metadata: %s", canvas_config)
|
# logging.info("canvas metadata: %s", canvas_config)
|
||||||
wallpaper_style = canvas_config["wallpaper-style"]
|
# wallpaper_style = canvas_config["wallpaper-style"]
|
||||||
self.app.canvas.scale_option.set(wallpaper_style)
|
# self.app.canvas.scale_option.set(wallpaper_style)
|
||||||
wallpaper = canvas_config["wallpaper"]
|
# wallpaper = canvas_config["wallpaper"]
|
||||||
wallpaper = str(appconfig.BACKGROUNDS_PATH.joinpath(wallpaper))
|
# wallpaper = str(appconfig.BACKGROUNDS_PATH.joinpath(wallpaper))
|
||||||
self.app.canvas.set_wallpaper(wallpaper)
|
# self.app.canvas.set_wallpaper(wallpaper)
|
||||||
# canvas settings
|
# canvas settings
|
||||||
canvas_config = config.get("canvas")
|
canvas_config = config.get("canvas")
|
||||||
if canvas_config:
|
if canvas_config:
|
||||||
|
|
0
coretk/coretk/graph/__init__.py
Normal file
0
coretk/coretk/graph/__init__.py
Normal file
70
coretk/coretk/graph/edges.py
Normal file
70
coretk/coretk/graph/edges.py
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import tkinter as tk
|
||||||
|
|
||||||
|
|
||||||
|
class CanvasWirelessEdge:
|
||||||
|
def __init__(self, token, position, src, dst, canvas):
|
||||||
|
self.token = token
|
||||||
|
self.src = src
|
||||||
|
self.dst = dst
|
||||||
|
self.canvas = canvas
|
||||||
|
self.id = self.canvas.create_line(
|
||||||
|
*position, tags="wireless", width=1.5, fill="#009933"
|
||||||
|
)
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
self.canvas.delete(self.id)
|
||||||
|
|
||||||
|
|
||||||
|
class CanvasEdge:
|
||||||
|
"""
|
||||||
|
Canvas edge class
|
||||||
|
"""
|
||||||
|
|
||||||
|
width = 1.4
|
||||||
|
|
||||||
|
def __init__(self, x1, y1, x2, y2, src, canvas, is_wired=None):
|
||||||
|
"""
|
||||||
|
Create an instance of canvas edge object
|
||||||
|
:param int x1: source x-coord
|
||||||
|
:param int y1: source y-coord
|
||||||
|
:param int x2: destination x-coord
|
||||||
|
:param int y2: destination y-coord
|
||||||
|
:param int src: source id
|
||||||
|
:param tkinter.Canvas canvas: canvas object
|
||||||
|
"""
|
||||||
|
self.src = src
|
||||||
|
self.dst = None
|
||||||
|
self.src_interface = None
|
||||||
|
self.dst_interface = None
|
||||||
|
self.canvas = canvas
|
||||||
|
if is_wired is None or is_wired is True:
|
||||||
|
self.id = self.canvas.create_line(
|
||||||
|
x1, y1, x2, y2, tags="edge", width=self.width, fill="#ff0000"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.id = self.canvas.create_line(
|
||||||
|
x1,
|
||||||
|
y1,
|
||||||
|
x2,
|
||||||
|
y2,
|
||||||
|
tags="edge",
|
||||||
|
width=self.width,
|
||||||
|
fill="#ff0000",
|
||||||
|
state=tk.HIDDEN,
|
||||||
|
)
|
||||||
|
self.token = None
|
||||||
|
self.link_info = None
|
||||||
|
self.throughput = None
|
||||||
|
self.wired = is_wired
|
||||||
|
|
||||||
|
def complete(self, dst, x, y):
|
||||||
|
self.dst = dst
|
||||||
|
self.token = tuple(sorted((self.src, self.dst)))
|
||||||
|
x1, y1, _, _ = self.canvas.coords(self.id)
|
||||||
|
self.canvas.coords(self.id, x1, y1, x, y)
|
||||||
|
self.canvas.helper.draw_wireless_case(self.src, self.dst, self)
|
||||||
|
self.canvas.tag_raise(self.src)
|
||||||
|
self.canvas.tag_raise(self.dst)
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
self.canvas.delete(self.id)
|
23
coretk/coretk/graph/enums.py
Normal file
23
coretk/coretk/graph/enums.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import enum
|
||||||
|
|
||||||
|
|
||||||
|
class GraphMode(enum.Enum):
|
||||||
|
SELECT = 0
|
||||||
|
EDGE = 1
|
||||||
|
PICKNODE = 2
|
||||||
|
NODE = 3
|
||||||
|
ANNOTATION = 4
|
||||||
|
OTHER = 5
|
||||||
|
|
||||||
|
|
||||||
|
class ScaleOption(enum.Enum):
|
||||||
|
NONE = 0
|
||||||
|
UPPER_LEFT = 1
|
||||||
|
CENTERED = 2
|
||||||
|
SCALED = 3
|
||||||
|
TILED = 4
|
||||||
|
|
||||||
|
|
||||||
|
class ShapeType(enum.Enum):
|
||||||
|
OVAL = 0
|
||||||
|
RECTANGLE = 1
|
|
@ -1,51 +1,24 @@
|
||||||
import enum
|
|
||||||
import logging
|
import logging
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import font
|
|
||||||
|
|
||||||
from PIL import Image, ImageTk
|
from PIL import Image, ImageTk
|
||||||
|
|
||||||
from core.api.grpc import core_pb2
|
from core.api.grpc import core_pb2
|
||||||
from core.api.grpc.core_pb2 import NodeType
|
from core.api.grpc.core_pb2 import NodeType
|
||||||
from coretk.canvastooltip import CanvasTooltip
|
|
||||||
from coretk.dialogs.emaneconfig import EmaneConfigDialog
|
|
||||||
from coretk.dialogs.mobilityconfig import MobilityConfigDialog
|
|
||||||
from coretk.dialogs.nodeconfig import NodeConfigDialog
|
|
||||||
from coretk.dialogs.shapemod import ShapeDialog
|
from coretk.dialogs.shapemod import ShapeDialog
|
||||||
from coretk.dialogs.wlanconfig import WlanConfigDialog
|
from coretk.graph.edges import CanvasEdge, CanvasWirelessEdge
|
||||||
from coretk.graph_helper import GraphHelper, WlanAntennaManager
|
from coretk.graph.enums import GraphMode, ScaleOption
|
||||||
|
from coretk.graph.graph_helper import GraphHelper
|
||||||
|
from coretk.graph.linkinfo import LinkInfo, Throughput
|
||||||
|
from coretk.graph.node import CanvasNode
|
||||||
|
from coretk.graph.nodedelete import CanvasComponentManagement
|
||||||
|
from coretk.graph.shape import Shape
|
||||||
from coretk.images import ImageEnum, Images
|
from coretk.images import ImageEnum, Images
|
||||||
from coretk.linkinfo import LinkInfo, Throughput
|
|
||||||
from coretk.nodedelete import CanvasComponentManagement
|
|
||||||
from coretk.nodeutils import NodeUtils
|
from coretk.nodeutils import NodeUtils
|
||||||
from coretk.shape import Shape
|
|
||||||
|
|
||||||
NODE_TEXT_OFFSET = 5
|
|
||||||
ABOVE_WALLPAPER = ["edge", "linkinfo", "wireless", "antenna", "nodename", "node"]
|
ABOVE_WALLPAPER = ["edge", "linkinfo", "wireless", "antenna", "nodename", "node"]
|
||||||
|
|
||||||
|
|
||||||
class GraphMode(enum.Enum):
|
|
||||||
SELECT = 0
|
|
||||||
EDGE = 1
|
|
||||||
PICKNODE = 2
|
|
||||||
NODE = 3
|
|
||||||
ANNOTATION = 4
|
|
||||||
OTHER = 5
|
|
||||||
|
|
||||||
|
|
||||||
class ScaleOption(enum.Enum):
|
|
||||||
NONE = 0
|
|
||||||
UPPER_LEFT = 1
|
|
||||||
CENTERED = 2
|
|
||||||
SCALED = 3
|
|
||||||
TILED = 4
|
|
||||||
|
|
||||||
|
|
||||||
class ShapeType(enum.Enum):
|
|
||||||
OVAL = 0
|
|
||||||
RECTANGLE = 1
|
|
||||||
|
|
||||||
|
|
||||||
class CanvasGraph(tk.Canvas):
|
class CanvasGraph(tk.Canvas):
|
||||||
def __init__(self, master, core, width, height, cnf=None, **kwargs):
|
def __init__(self, master, core, width, height, cnf=None, **kwargs):
|
||||||
if cnf is None:
|
if cnf is None:
|
||||||
|
@ -620,222 +593,3 @@ class CanvasGraph(tk.Canvas):
|
||||||
|
|
||||||
def is_selection_mode(self):
|
def is_selection_mode(self):
|
||||||
return self.mode == GraphMode.SELECT
|
return self.mode == GraphMode.SELECT
|
||||||
|
|
||||||
|
|
||||||
class CanvasWirelessEdge:
|
|
||||||
def __init__(self, token, position, src, dst, canvas):
|
|
||||||
self.token = token
|
|
||||||
self.src = src
|
|
||||||
self.dst = dst
|
|
||||||
self.canvas = canvas
|
|
||||||
self.id = self.canvas.create_line(
|
|
||||||
*position, tags="wireless", width=1.5, fill="#009933"
|
|
||||||
)
|
|
||||||
|
|
||||||
def delete(self):
|
|
||||||
self.canvas.delete(self.id)
|
|
||||||
|
|
||||||
|
|
||||||
class CanvasEdge:
|
|
||||||
"""
|
|
||||||
Canvas edge class
|
|
||||||
"""
|
|
||||||
|
|
||||||
width = 1.4
|
|
||||||
|
|
||||||
def __init__(self, x1, y1, x2, y2, src, canvas, is_wired=None):
|
|
||||||
"""
|
|
||||||
Create an instance of canvas edge object
|
|
||||||
:param int x1: source x-coord
|
|
||||||
:param int y1: source y-coord
|
|
||||||
:param int x2: destination x-coord
|
|
||||||
:param int y2: destination y-coord
|
|
||||||
:param int src: source id
|
|
||||||
:param tkinter.Canvas canvas: canvas object
|
|
||||||
"""
|
|
||||||
self.src = src
|
|
||||||
self.dst = None
|
|
||||||
self.src_interface = None
|
|
||||||
self.dst_interface = None
|
|
||||||
self.canvas = canvas
|
|
||||||
if is_wired is None or is_wired is True:
|
|
||||||
self.id = self.canvas.create_line(
|
|
||||||
x1, y1, x2, y2, tags="edge", width=self.width, fill="#ff0000"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self.id = self.canvas.create_line(
|
|
||||||
x1,
|
|
||||||
y1,
|
|
||||||
x2,
|
|
||||||
y2,
|
|
||||||
tags="edge",
|
|
||||||
width=self.width,
|
|
||||||
fill="#ff0000",
|
|
||||||
state=tk.HIDDEN,
|
|
||||||
)
|
|
||||||
self.token = None
|
|
||||||
self.link_info = None
|
|
||||||
self.throughput = None
|
|
||||||
self.wired = is_wired
|
|
||||||
|
|
||||||
def complete(self, dst, x, y):
|
|
||||||
self.dst = dst
|
|
||||||
self.token = tuple(sorted((self.src, self.dst)))
|
|
||||||
x1, y1, _, _ = self.canvas.coords(self.id)
|
|
||||||
self.canvas.coords(self.id, x1, y1, x, y)
|
|
||||||
self.canvas.helper.draw_wireless_case(self.src, self.dst, self)
|
|
||||||
self.canvas.tag_raise(self.src)
|
|
||||||
self.canvas.tag_raise(self.dst)
|
|
||||||
|
|
||||||
def delete(self):
|
|
||||||
self.canvas.delete(self.id)
|
|
||||||
|
|
||||||
|
|
||||||
class CanvasNode:
|
|
||||||
def __init__(self, app, core_node, image):
|
|
||||||
self.app = app
|
|
||||||
self.canvas = app.canvas
|
|
||||||
self.image = image
|
|
||||||
self.core_node = core_node
|
|
||||||
x = self.core_node.position.x
|
|
||||||
y = self.core_node.position.y
|
|
||||||
self.id = self.canvas.create_image(
|
|
||||||
x, y, anchor=tk.CENTER, image=self.image, tags="node"
|
|
||||||
)
|
|
||||||
image_box = self.canvas.bbox(self.id)
|
|
||||||
y = image_box[3] + NODE_TEXT_OFFSET
|
|
||||||
text_font = font.Font(family="TkIconFont", size=12)
|
|
||||||
self.text_id = self.canvas.create_text(
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
text=self.core_node.name,
|
|
||||||
tags="nodename",
|
|
||||||
font=text_font,
|
|
||||||
fill="#0000CD",
|
|
||||||
)
|
|
||||||
self.antenna_draw = WlanAntennaManager(self.canvas, self.id)
|
|
||||||
self.tooltip = CanvasTooltip(self.canvas)
|
|
||||||
self.canvas.tag_bind(self.id, "<ButtonPress-1>", self.click_press)
|
|
||||||
self.canvas.tag_bind(self.id, "<ButtonRelease-1>", self.click_release)
|
|
||||||
self.canvas.tag_bind(self.id, "<B1-Motion>", self.motion)
|
|
||||||
self.canvas.tag_bind(self.id, "<Double-Button-1>", self.double_click)
|
|
||||||
self.canvas.tag_bind(self.id, "<Control-1>", self.select_multiple)
|
|
||||||
self.canvas.tag_bind(self.id, "<Enter>", self.on_enter)
|
|
||||||
self.canvas.tag_bind(self.id, "<Leave>", self.on_leave)
|
|
||||||
self.edges = set()
|
|
||||||
self.interfaces = []
|
|
||||||
self.wireless_edges = set()
|
|
||||||
self.moving = None
|
|
||||||
|
|
||||||
def redraw(self):
|
|
||||||
self.canvas.itemconfig(self.id, image=self.image)
|
|
||||||
self.canvas.itemconfig(self.text_id, text=self.core_node.name)
|
|
||||||
|
|
||||||
def move(self, x, y, update=True):
|
|
||||||
old_x = self.core_node.position.x
|
|
||||||
old_y = self.core_node.position.y
|
|
||||||
x_offset = x - old_x
|
|
||||||
y_offset = y - old_y
|
|
||||||
self.core_node.position.x = int(x)
|
|
||||||
self.core_node.position.y = int(y)
|
|
||||||
self.canvas.move(self.id, x_offset, y_offset)
|
|
||||||
self.canvas.move(self.text_id, x_offset, y_offset)
|
|
||||||
self.antenna_draw.update_antennas_position(x_offset, y_offset)
|
|
||||||
self.canvas.canvas_management.node_drag(self, x_offset, y_offset)
|
|
||||||
for edge in self.edges:
|
|
||||||
x1, y1, x2, y2 = self.canvas.coords(edge.id)
|
|
||||||
if edge.src == self.id:
|
|
||||||
self.canvas.coords(edge.id, x, y, x2, y2)
|
|
||||||
else:
|
|
||||||
self.canvas.coords(edge.id, x1, y1, x, y)
|
|
||||||
edge.link_info.recalculate_info()
|
|
||||||
for edge in self.wireless_edges:
|
|
||||||
x1, y1, x2, y2 = self.canvas.coords(edge.id)
|
|
||||||
if edge.src == self.id:
|
|
||||||
self.canvas.coords(edge.id, x, y, x2, y2)
|
|
||||||
else:
|
|
||||||
self.canvas.coords(edge.id, x1, y1, x, y)
|
|
||||||
if self.app.core.is_runtime() and update:
|
|
||||||
self.app.core.edit_node(self.core_node.id, int(x), int(y))
|
|
||||||
|
|
||||||
def on_enter(self, event):
|
|
||||||
if self.app.core.is_runtime() and self.app.core.observer:
|
|
||||||
self.tooltip.text.set("waiting...")
|
|
||||||
self.tooltip.on_enter(event)
|
|
||||||
output = self.app.core.run(self.core_node.id)
|
|
||||||
self.tooltip.text.set(output)
|
|
||||||
|
|
||||||
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)
|
|
||||||
else:
|
|
||||||
self.show_config()
|
|
||||||
|
|
||||||
def update_coords(self):
|
|
||||||
x, y = self.canvas.coords(self.id)
|
|
||||||
self.core_node.position.x = int(x)
|
|
||||||
self.core_node.position.y = int(y)
|
|
||||||
|
|
||||||
def click_press(self, event):
|
|
||||||
logging.debug(f"node click press {self.core_node.name}: {event}")
|
|
||||||
self.moving = self.canvas.canvas_xy(event)
|
|
||||||
if self.id not in self.canvas.canvas_management.selected:
|
|
||||||
self.canvas.canvas_management.node_select(self)
|
|
||||||
self.canvas.selected = self.id
|
|
||||||
|
|
||||||
def click_release(self, event):
|
|
||||||
logging.debug(f"node click release {self.core_node.name}: {event}")
|
|
||||||
self.update_coords()
|
|
||||||
self.moving = None
|
|
||||||
|
|
||||||
def motion(self, event):
|
|
||||||
if self.canvas.mode == GraphMode.EDGE:
|
|
||||||
return
|
|
||||||
x, y = self.canvas.canvas_xy(event)
|
|
||||||
my_x = self.core_node.position.x
|
|
||||||
my_y = self.core_node.position.y
|
|
||||||
self.move(x, y)
|
|
||||||
# move other selected components
|
|
||||||
for nid, bboxid in self.canvas.canvas_management.selected.items():
|
|
||||||
if nid != self.id and nid in self.canvas.nodes:
|
|
||||||
other_old_x = self.canvas.nodes[nid].core_node.position.x
|
|
||||||
other_old_y = self.canvas.nodes[nid].core_node.position.y
|
|
||||||
other_new_x = x + other_old_x - my_x
|
|
||||||
other_new_y = y + other_old_y - my_y
|
|
||||||
self.canvas.nodes[nid].move(other_new_x, other_new_y)
|
|
||||||
if nid != self.id and nid in self.canvas.shapes:
|
|
||||||
self.canvas.shapes[nid].motion(None, x - my_x, y - my_y)
|
|
||||||
|
|
||||||
def select_multiple(self, event):
|
|
||||||
self.canvas.canvas_management.node_select(self, True)
|
|
||||||
|
|
||||||
def show_config(self):
|
|
||||||
self.canvas.context = None
|
|
||||||
dialog = NodeConfigDialog(self.app, self.app, self)
|
|
||||||
dialog.show()
|
|
||||||
|
|
||||||
def show_wlan_config(self):
|
|
||||||
self.canvas.context = None
|
|
||||||
dialog = WlanConfigDialog(self.app, self.app, self)
|
|
||||||
dialog.show()
|
|
||||||
|
|
||||||
def show_mobility_config(self):
|
|
||||||
self.canvas.context = None
|
|
||||||
dialog = MobilityConfigDialog(self.app, self.app, self)
|
|
||||||
dialog.show()
|
|
||||||
|
|
||||||
def show_mobility_player(self):
|
|
||||||
self.canvas.context = None
|
|
||||||
mobility_player = self.app.core.mobility_players[self.core_node.id]
|
|
||||||
mobility_player.show()
|
|
||||||
|
|
||||||
def show_emane_config(self):
|
|
||||||
self.canvas.context = None
|
|
||||||
dialog = EmaneConfigDialog(self.app, self.app, self)
|
|
||||||
dialog.show()
|
|
163
coretk/coretk/graph/node.py
Normal file
163
coretk/coretk/graph/node.py
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
import logging
|
||||||
|
import tkinter as tk
|
||||||
|
from tkinter import font
|
||||||
|
|
||||||
|
from coretk.dialogs.emaneconfig import EmaneConfigDialog
|
||||||
|
from coretk.dialogs.mobilityconfig import MobilityConfigDialog
|
||||||
|
from coretk.dialogs.nodeconfig import NodeConfigDialog
|
||||||
|
from coretk.dialogs.wlanconfig import WlanConfigDialog
|
||||||
|
from coretk.graph.canvastooltip import CanvasTooltip
|
||||||
|
from coretk.graph.enums import GraphMode
|
||||||
|
from coretk.graph.graph_helper import WlanAntennaManager
|
||||||
|
|
||||||
|
NODE_TEXT_OFFSET = 5
|
||||||
|
|
||||||
|
|
||||||
|
class CanvasNode:
|
||||||
|
def __init__(self, app, core_node, image):
|
||||||
|
self.app = app
|
||||||
|
self.canvas = app.canvas
|
||||||
|
self.image = image
|
||||||
|
self.core_node = core_node
|
||||||
|
x = self.core_node.position.x
|
||||||
|
y = self.core_node.position.y
|
||||||
|
self.id = self.canvas.create_image(
|
||||||
|
x, y, anchor=tk.CENTER, image=self.image, tags="node"
|
||||||
|
)
|
||||||
|
image_box = self.canvas.bbox(self.id)
|
||||||
|
y = image_box[3] + NODE_TEXT_OFFSET
|
||||||
|
text_font = font.Font(family="TkIconFont", size=12)
|
||||||
|
self.text_id = self.canvas.create_text(
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
text=self.core_node.name,
|
||||||
|
tags="nodename",
|
||||||
|
font=text_font,
|
||||||
|
fill="#0000CD",
|
||||||
|
)
|
||||||
|
self.antenna_draw = WlanAntennaManager(self.canvas, self.id)
|
||||||
|
self.tooltip = CanvasTooltip(self.canvas)
|
||||||
|
self.canvas.tag_bind(self.id, "<ButtonPress-1>", self.click_press)
|
||||||
|
self.canvas.tag_bind(self.id, "<ButtonRelease-1>", self.click_release)
|
||||||
|
self.canvas.tag_bind(self.id, "<B1-Motion>", self.motion)
|
||||||
|
self.canvas.tag_bind(self.id, "<Double-Button-1>", self.double_click)
|
||||||
|
self.canvas.tag_bind(self.id, "<Control-1>", self.select_multiple)
|
||||||
|
self.canvas.tag_bind(self.id, "<Enter>", self.on_enter)
|
||||||
|
self.canvas.tag_bind(self.id, "<Leave>", self.on_leave)
|
||||||
|
self.edges = set()
|
||||||
|
self.interfaces = []
|
||||||
|
self.wireless_edges = set()
|
||||||
|
self.moving = None
|
||||||
|
|
||||||
|
def redraw(self):
|
||||||
|
self.canvas.itemconfig(self.id, image=self.image)
|
||||||
|
self.canvas.itemconfig(self.text_id, text=self.core_node.name)
|
||||||
|
|
||||||
|
def move(self, x, y, update=True):
|
||||||
|
old_x = self.core_node.position.x
|
||||||
|
old_y = self.core_node.position.y
|
||||||
|
x_offset = x - old_x
|
||||||
|
y_offset = y - old_y
|
||||||
|
self.core_node.position.x = int(x)
|
||||||
|
self.core_node.position.y = int(y)
|
||||||
|
self.canvas.move(self.id, x_offset, y_offset)
|
||||||
|
self.canvas.move(self.text_id, x_offset, y_offset)
|
||||||
|
self.antenna_draw.update_antennas_position(x_offset, y_offset)
|
||||||
|
self.canvas.canvas_management.node_drag(self, x_offset, y_offset)
|
||||||
|
for edge in self.edges:
|
||||||
|
x1, y1, x2, y2 = self.canvas.coords(edge.id)
|
||||||
|
if edge.src == self.id:
|
||||||
|
self.canvas.coords(edge.id, x, y, x2, y2)
|
||||||
|
else:
|
||||||
|
self.canvas.coords(edge.id, x1, y1, x, y)
|
||||||
|
edge.link_info.recalculate_info()
|
||||||
|
for edge in self.wireless_edges:
|
||||||
|
x1, y1, x2, y2 = self.canvas.coords(edge.id)
|
||||||
|
if edge.src == self.id:
|
||||||
|
self.canvas.coords(edge.id, x, y, x2, y2)
|
||||||
|
else:
|
||||||
|
self.canvas.coords(edge.id, x1, y1, x, y)
|
||||||
|
if self.app.core.is_runtime() and update:
|
||||||
|
self.app.core.edit_node(self.core_node.id, int(x), int(y))
|
||||||
|
|
||||||
|
def on_enter(self, event):
|
||||||
|
if self.app.core.is_runtime() and self.app.core.observer:
|
||||||
|
self.tooltip.text.set("waiting...")
|
||||||
|
self.tooltip.on_enter(event)
|
||||||
|
output = self.app.core.run(self.core_node.id)
|
||||||
|
self.tooltip.text.set(output)
|
||||||
|
|
||||||
|
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)
|
||||||
|
else:
|
||||||
|
self.show_config()
|
||||||
|
|
||||||
|
def update_coords(self):
|
||||||
|
x, y = self.canvas.coords(self.id)
|
||||||
|
self.core_node.position.x = int(x)
|
||||||
|
self.core_node.position.y = int(y)
|
||||||
|
|
||||||
|
def click_press(self, event):
|
||||||
|
logging.debug(f"node click press {self.core_node.name}: {event}")
|
||||||
|
self.moving = self.canvas.canvas_xy(event)
|
||||||
|
if self.id not in self.canvas.canvas_management.selected:
|
||||||
|
self.canvas.canvas_management.node_select(self)
|
||||||
|
self.canvas.selected = self.id
|
||||||
|
|
||||||
|
def click_release(self, event):
|
||||||
|
logging.debug(f"node click release {self.core_node.name}: {event}")
|
||||||
|
self.update_coords()
|
||||||
|
self.moving = None
|
||||||
|
|
||||||
|
def motion(self, event):
|
||||||
|
if self.canvas.mode == GraphMode.EDGE:
|
||||||
|
return
|
||||||
|
x, y = self.canvas.canvas_xy(event)
|
||||||
|
my_x = self.core_node.position.x
|
||||||
|
my_y = self.core_node.position.y
|
||||||
|
self.move(x, y)
|
||||||
|
# move other selected components
|
||||||
|
for nid, bboxid in self.canvas.canvas_management.selected.items():
|
||||||
|
if nid != self.id and nid in self.canvas.nodes:
|
||||||
|
other_old_x = self.canvas.nodes[nid].core_node.position.x
|
||||||
|
other_old_y = self.canvas.nodes[nid].core_node.position.y
|
||||||
|
other_new_x = x + other_old_x - my_x
|
||||||
|
other_new_y = y + other_old_y - my_y
|
||||||
|
self.canvas.nodes[nid].move(other_new_x, other_new_y)
|
||||||
|
if nid != self.id and nid in self.canvas.shapes:
|
||||||
|
self.canvas.shapes[nid].motion(None, x - my_x, y - my_y)
|
||||||
|
|
||||||
|
def select_multiple(self, event):
|
||||||
|
self.canvas.canvas_management.node_select(self, True)
|
||||||
|
|
||||||
|
def show_config(self):
|
||||||
|
self.canvas.context = None
|
||||||
|
dialog = NodeConfigDialog(self.app, self.app, self)
|
||||||
|
dialog.show()
|
||||||
|
|
||||||
|
def show_wlan_config(self):
|
||||||
|
self.canvas.context = None
|
||||||
|
dialog = WlanConfigDialog(self.app, self.app, self)
|
||||||
|
dialog.show()
|
||||||
|
|
||||||
|
def show_mobility_config(self):
|
||||||
|
self.canvas.context = None
|
||||||
|
dialog = MobilityConfigDialog(self.app, self.app, self)
|
||||||
|
dialog.show()
|
||||||
|
|
||||||
|
def show_mobility_player(self):
|
||||||
|
self.canvas.context = None
|
||||||
|
mobility_player = self.app.core.mobility_players[self.core_node.id]
|
||||||
|
mobility_player.show()
|
||||||
|
|
||||||
|
def show_emane_config(self):
|
||||||
|
self.canvas.context = None
|
||||||
|
dialog = EmaneConfigDialog(self.app, self.app, self)
|
||||||
|
dialog.show()
|
|
@ -5,7 +5,7 @@ from functools import partial
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
|
|
||||||
from coretk.dialogs.customnodes import CustomNodesDialog
|
from coretk.dialogs.customnodes import CustomNodesDialog
|
||||||
from coretk.graph import GraphMode
|
from coretk.graph.enums import GraphMode
|
||||||
from coretk.images import ImageEnum, Images
|
from coretk.images import ImageEnum, Images
|
||||||
from coretk.nodeutils import NodeUtils
|
from coretk.nodeutils import NodeUtils
|
||||||
from coretk.tooltip import Tooltip
|
from coretk.tooltip import Tooltip
|
||||||
|
|
Loading…
Add table
Reference in a new issue