load shapes from xml

This commit is contained in:
Huy Pham 2019-12-05 13:39:09 -08:00
commit a44fc0b6f9
17 changed files with 296 additions and 331 deletions

View file

@ -5,12 +5,12 @@ from tkinter import ttk
from coretk import appconfig, themes
from coretk.coreclient import CoreClient
from coretk.graph import CanvasGraph
from coretk.graph.graph import CanvasGraph
from coretk.images import ImageEnum, Images
from coretk.menuaction import MenuAction
from coretk.menubar import Menubar
from coretk.nodeutils import NodeUtils
from coretk.status import StatusBar
from coretk.statusbar import StatusBar
from coretk.toolbar import Toolbar

View file

@ -10,9 +10,23 @@ from core.api.grpc import client, core_pb2
from coretk import appconfig
from coretk.dialogs.mobilityplayer import MobilityPlayer
from coretk.dialogs.sessions import SessionsDialog
from coretk.graph.shape import Shape, ShapeData
from coretk.interface import InterfaceManager
from coretk.nodeutils import NodeDraw, NodeUtils
from coretk.shape import ShapeData
LIFT_ORDER = [
"wallpaper",
"shape",
"gridline",
"shapetext",
"text",
"edge",
"antenna",
"nodename",
"linkinfo",
"node",
]
OBSERVERS = {
"processes": "ps",
@ -282,48 +296,7 @@ class CoreClient:
return self.state == core_pb2.SessionState.RUNTIME
def parse_metadata(self, config):
# for key, value in config.items():
# if "global_options" != key:
# canvas_config = parsedata.parse(value)
# print(canvas_config)
# if canvas_config.get("type"):
# config_type = canvas_config["type"]
# if config_type == "rectangle" or config_type == "oval":
# data = ShapeData(
# False,
# canvas_config["label"],
# canvas_config["fontfamily"],
# canvas_config["fontsize"],
# canvas_config["labelcolor"],
# canvas_config["color"],
# canvas_config["border"],
# canvas_config["width"],
# )
# coords = tuple(
# [float(x) for x in canvas_config["iconcoords"].split()]
# )
# shape = Shape(
# self.app,
# self.app.canvas,
# None,
# None,
# coords,
# data,
# config_type,
# )
# self.app.canvas.shapes[shape.id] = shape
# elif canvas_config["type"] == "text":
# print("not implemented")
# else:
# if "wallpaper" in canvas_config:
# logging.info("canvas metadata: %s", canvas_config)
# wallpaper_style = canvas_config["wallpaper-style"]
# self.app.canvas.scale_option.set(wallpaper_style)
# wallpaper = canvas_config["wallpaper"]
# wallpaper = str(appconfig.BACKGROUNDS_PATH.joinpath(wallpaper))
# self.app.canvas.set_wallpaper(wallpaper)
# canvas settings
print(config)
# canvas setting
canvas_config = config.get("canvas")
if canvas_config:
logging.info("canvas metadata: %s", canvas_config)
@ -350,7 +323,14 @@ class CoreClient:
annotation_config["border"],
annotation_config["width"],
)
print(data, coords)
shape = Shape(
self.app, self.app.canvas, None, None, coords, data, config_type
)
self.app.canvas.shapes[shape.id] = shape
else:
logging.debug("not implemented")
for tag in LIFT_ORDER:
self.app.canvas.tag_raise(tag)
def create_new_session(self):
"""

View file

@ -267,7 +267,7 @@ router ospf6
</session_options>
<session_metadata>
<configuration name="annotation a0" value='{"iconcoords": [612.0, 492.0], "type": "text", "label": "wireless network", "labelcolor": "black", "fontfamily": "Arial", "fontsize": 12, "effects": "bold", "canvas": "c1"}'/>
<configuration name="annotation a1" value='{"iconcoords": [142.0, 112.0, 393.0, 291.0], "type": "rectangle", "label": "", "labelcolor": "black", "fontfamily": "Arial", "fontsize": 12, "color": "#ebebde", "width": 1, "border": "#ffffff", "rad": 25, "canvas": "c1"}'/>
<configuration name="annotation a1" value='{"iconcoords": [142.0, 112.0, 393.0, 291.0], "type": "rectangle", "label": "circle", "labelcolor": "black", "fontfamily": "Arial", "fontsize": 12, "color": "#ebebde", "width": 1, "border": "#ffffff", "rad": 25, "canvas": "c1"}'/>
<configuration name="annotation a2" value='{"iconcoords": [492.0, 384.0], "type": "text", "label": "gateway", "labelcolor": "black", "fontfamily": "Arial", "fontsize": 12, "effects": "bold", "canvas": "c1"}'/>
<configuration name="canvas" value='{"name": "Canvas1", "wallpaper-style": 1, "wallpaper": "sample1-bg.gif"}'/>
<configuration name="global_options" value='{"interface_names": false, "ip_addresses": true, "ipv6_addresses": false, "node_labels": true, "link_labels": true, "show_api": false, "background_images": false, "annotations": true, "grid": false, "traffic_start": false}'/>

View file

@ -147,7 +147,7 @@ class ShapeDialog(Dialog):
shape_text = self.shape_text.get()
size = int(self.font_size.get())
x0, y0, x1, y1 = self.canvas.bbox(self.id)
text_y = y0 + 2 * size
text_y = y0 + 1.5 * size
text_x = (x0 + x1) / 2
f = [self.font.get(), size]
if self.bold.get() == 1:

View file

View 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)

View 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

View file

@ -1,51 +1,24 @@
import enum
import logging
import tkinter as tk
from tkinter import font
from PIL import Image, ImageTk
from core.api.grpc import core_pb2
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.wlanconfig import WlanConfigDialog
from coretk.graph_helper import GraphHelper, WlanAntennaManager
from coretk.graph.edges import CanvasEdge, CanvasWirelessEdge
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.linkinfo import LinkInfo, Throughput
from coretk.nodedelete import CanvasComponentManagement
from coretk.nodeutils import NodeUtils
from coretk.shape import Shape
NODE_TEXT_OFFSET = 5
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):
def __init__(self, master, core, width, height, cnf=None, **kwargs):
if cnf is None:
@ -620,222 +593,3 @@ class CanvasGraph(tk.Canvas):
def is_selection_mode(self):
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
View 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()

View file

@ -105,9 +105,11 @@ class Shape:
width=data.border_width,
)
_x = (x0 + x1) / 2
_y = (y0 + y1) / 2
_y = y0 + 1.5 * data.font_size
print("create text with text: ", data.text)
print(data.text_color)
self.text_id = self.canvas.create_text(
_x, _y, text=data.text, fill=data.text_color
_x, _y, tags="shapetext", text=data.text, fill=data.text_color
)
self.shape_data = data
self.cursor_x = None

View file

@ -1,27 +0,0 @@
"""
parse meta data
"""
def parse(meta_string):
parsed = {}
if meta_string[0] == "{" and meta_string[len(meta_string) - 1] == "}":
meta_string = meta_string[1:-1]
for key_value in meta_string.split("} {"):
if key_value[len(key_value) - 1] == "}":
key, value = key_value[:-1].split(" {")
if key == "wallpaper-style":
if value == "upperleft":
parsed[key] = 1
elif value == "centered":
parsed[key] = 2
elif value == "scaled":
parsed[key] = 3
elif value == "tiled":
parsed[key] = 4
else:
parsed[key] = value
else:
key, value = tuple(key_value.split())
parsed[key] = value
return parsed

View file

@ -5,7 +5,7 @@ from functools import partial
from tkinter import ttk
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.nodeutils import NodeUtils
from coretk.tooltip import Tooltip