create grids

This commit is contained in:
Huy Pham 2019-09-30 10:11:29 -07:00
parent 130c2a9b8d
commit 269d7f8f92
3 changed files with 162 additions and 13 deletions

View file

@ -21,7 +21,7 @@ class Application(tk.Frame):
def setup_app(self): def setup_app(self):
self.master.title("CORE") self.master.title("CORE")
self.master.geometry("800x600") self.master.geometry("1000x800")
image = Images.get("core") image = Images.get("core")
self.master.tk.call("wm", "iconphoto", self.master._w, image) self.master.tk.call("wm", "iconphoto", self.master._w, image)
self.pack(fill=tk.BOTH, expand=True) self.pack(fill=tk.BOTH, expand=True)
@ -40,11 +40,11 @@ class Application(tk.Frame):
core_editbar.create_toolbar() core_editbar.create_toolbar()
self.canvas = CanvasGraph( self.canvas = CanvasGraph(
self, background="#cccccc", scrollregion=(0, 0, 1000, 1000) master=self, background="#cccccc", scrollregion=(0, 0, 1000, 1000)
) )
self.canvas.pack(fill=tk.BOTH, expand=True) self.canvas.pack(fill=tk.BOTH, expand=True)
# self.canvas.create_line(0, 0, 10, 10) # self.canvas.create_rectangle(0, 0, 1000, 750, outline="#000000", fill="#ffffff", width=1)
scroll_x = tk.Scrollbar( scroll_x = tk.Scrollbar(
self.canvas, orient=tk.HORIZONTAL, command=self.canvas.xview self.canvas, orient=tk.HORIZONTAL, command=self.canvas.xview

View file

@ -1,6 +1,7 @@
import logging import logging
import tkinter as tk import tkinter as tk
from coretk.graph import GraphMode
from coretk.images import Images from coretk.images import Images
from coretk.tooltip import CreateToolTip from coretk.tooltip import CreateToolTip
@ -34,6 +35,9 @@ class CoreToolbar(object):
self.marker_option_menu = None self.marker_option_menu = None
self.network_layer_option_menu = None self.network_layer_option_menu = None
# variables used by canvas graph
self.mode = GraphMode.SELECT
def load_toolbar_images(self): def load_toolbar_images(self):
""" """
Load the images that appear in core toolbar Load the images that appear in core toolbar
@ -66,6 +70,24 @@ class CoreToolbar(object):
Images.load("stop", "stop.gif") Images.load("stop", "stop.gif")
Images.load("observe", "observe.gif") Images.load("observe", "observe.gif")
def get_graph_mode(self):
"""
Retrieve current graph mode
:rtype: int
:return: current graph mode
"""
return self.mode
def set_graph_mode(self, mode):
"""
Set graph mode
:param int mode: graph mode
:return: nothing
"""
self.mode = mode
def destroy_previous_frame(self): def destroy_previous_frame(self):
""" """
Destroy any extra frame from previous before drawing a new one Destroy any extra frame from previous before drawing a new one
@ -94,7 +116,7 @@ class CoreToolbar(object):
if i.winfo_name() != "!frame": if i.winfo_name() != "!frame":
i.destroy() i.destroy()
def create_button(self, img, func, frame, main_button): def create_button(self, img, func, frame, main_button, btt_message):
""" """
Create button and put it on the frame Create button and put it on the frame
@ -106,6 +128,7 @@ class CoreToolbar(object):
""" """
button = tk.Button(frame, width=self.width, height=self.height, image=img) button = tk.Button(frame, width=self.width, height=self.height, image=img)
button.pack(side=tk.LEFT, pady=1) button.pack(side=tk.LEFT, pady=1)
CreateToolTip(button, btt_message)
button.bind("<Button-1>", lambda mb: func(main_button)) button.bind("<Button-1>", lambda mb: func(main_button))
def create_radio_button(self, frame, image, func, variable, value, tooltip_msg): def create_radio_button(self, frame, image, func, variable, value, tooltip_msg):
@ -122,11 +145,12 @@ class CoreToolbar(object):
button.pack(side=tk.TOP, pady=1) button.pack(side=tk.TOP, pady=1)
CreateToolTip(button, tooltip_msg) CreateToolTip(button, tooltip_msg)
def create_regular_button(self, frame, image, func): def create_regular_button(self, frame, image, func, btt_message):
button = tk.Button( button = tk.Button(
frame, width=self.width, height=self.height, image=image, command=func frame, width=self.width, height=self.height, image=image, command=func
) )
button.pack(side=tk.TOP, pady=1) button.pack(side=tk.TOP, pady=1)
CreateToolTip(button, btt_message)
def draw_button_menu_frame(self, edit_frame, option_frame, main_button): def draw_button_menu_frame(self, edit_frame, option_frame, main_button):
""" """
@ -165,6 +189,7 @@ class CoreToolbar(object):
def click_selection_tool(self): def click_selection_tool(self):
logging.debug("Click SELECTION TOOL") logging.debug("Click SELECTION TOOL")
self.set_graph_mode(GraphMode.SELECT)
def click_start_stop_session_tool(self): def click_start_stop_session_tool(self):
logging.debug("Click START STOP SESSION button") logging.debug("Click START STOP SESSION button")
@ -173,6 +198,7 @@ class CoreToolbar(object):
def click_link_tool(self): def click_link_tool(self):
logging.debug("Click LINK button") logging.debug("Click LINK button")
self.set_graph_mode(GraphMode.EDGE)
def pick_router(self, main_button): def pick_router(self, main_button):
self.network_layer_option_menu.destroy() self.network_layer_option_menu.destroy()
@ -237,9 +263,22 @@ class CoreToolbar(object):
self.pick_ovs, self.pick_ovs,
self.pick_editnode, self.pick_editnode,
] ]
tooltip_list = [
"router",
"host",
"PC",
"mdr",
"prouter",
"OVS",
"edit node types",
]
for i in range(len(img_list)): for i in range(len(img_list)):
self.create_button( self.create_button(
img_list[i], func_list[i], option_frame, network_layer_button img_list[i],
func_list[i],
option_frame,
network_layer_button,
tooltip_list[i],
) )
# place frame at a calculated position as well as keep a reference of that frame # place frame at a calculated position as well as keep a reference of that frame
@ -320,9 +359,20 @@ class CoreToolbar(object):
self.pick_rj45, self.pick_rj45,
self.pick_tunnel, self.pick_tunnel,
] ]
tooltip_list = [
"ethernet hub",
"ethernet switch",
"wireless LAN",
"rj45 physical interface tool",
"tunnel tool",
]
for i in range(len(img_list)): for i in range(len(img_list)):
self.create_button( self.create_button(
img_list[i], func_list[i], option_frame, link_layer_button img_list[i],
func_list[i],
option_frame,
link_layer_button,
tooltip_list[i],
) )
# place frame at a calculated position as well as keep a reference of the frame # place frame at a calculated position as well as keep a reference of the frame
@ -358,7 +408,6 @@ class CoreToolbar(object):
self.marker_option_menu.destroy() self.marker_option_menu.destroy()
main_button.configure(image=Images.get("marker")) main_button.configure(image=Images.get("marker"))
logging.debug("Pick MARKER") logging.debug("Pick MARKER")
return "break"
def pick_oval(self, main_button): def pick_oval(self, main_button):
self.marker_option_menu.destroy() self.marker_option_menu.destroy()
@ -397,8 +446,11 @@ class CoreToolbar(object):
self.pick_rectangle, self.pick_rectangle,
self.pick_text, self.pick_text,
] ]
tooltip_list = ["marker", "oval", "rectangle", "text"]
for i in range(len(img_list)): for i in range(len(img_list)):
self.create_button(img_list[i], func_list[i], option_frame, main_button) self.create_button(
img_list[i], func_list[i], option_frame, main_button, tooltip_list[i]
)
# place the frame at a calculated position as well as keep a reference of that frame # place the frame at a calculated position as well as keep a reference of that frame
self.draw_button_menu_frame(self.edit_frame, option_frame, main_button) self.draw_button_menu_frame(self.edit_frame, option_frame, main_button)
@ -432,7 +484,10 @@ class CoreToolbar(object):
def create_toolbar(self): def create_toolbar(self):
self.load_toolbar_images() self.load_toolbar_images()
self.create_regular_button( self.create_regular_button(
self.edit_frame, Images.get("start"), self.click_start_stop_session_tool self.edit_frame,
Images.get("start"),
self.click_start_stop_session_tool,
"start the session",
) )
self.create_radio_button( self.create_radio_button(
self.edit_frame, self.edit_frame,
@ -503,7 +558,10 @@ class CoreToolbar(object):
def create_runtime_toolbar(self): def create_runtime_toolbar(self):
self.create_regular_button( self.create_regular_button(
self.edit_frame, Images.get("stop"), self.click_stop_button self.edit_frame,
Images.get("stop"),
self.click_stop_button,
"stop the session",
) )
self.create_radio_button( self.create_radio_button(
self.edit_frame, self.edit_frame,
@ -539,5 +597,5 @@ class CoreToolbar(object):
"run command from one node to another", "run command from one node to another",
) )
self.create_regular_button( self.create_regular_button(
self.edit_frame, Images.get("run"), self.click_run_button self.edit_frame, Images.get("run"), self.click_run_button, "run"
) )

View file

@ -17,14 +17,42 @@ class CanvasGraph(tk.Canvas):
cnf = {} cnf = {}
kwargs["highlightthickness"] = 0 kwargs["highlightthickness"] = 0
super().__init__(master, cnf, **kwargs) super().__init__(master, cnf, **kwargs)
self.mode = GraphMode.SELECT self.mode = GraphMode.SELECT
self.selected = None self.selected = None
self.node_context = None self.node_context = None
self.nodes = {} self.nodes = {}
self.edges = {} self.edges = {}
self.drawing_edge = None self.drawing_edge = None
self.setup_menus() self.setup_menus()
self.setup_bindings() self.setup_bindings()
self.draw_grid()
def draw_grid(self, width=1000, height=750):
"""
Create grid
:param int width: the width
:param int height: the height
:return: nothing
"""
rectangle_id = self.create_rectangle(
0,
0,
width,
height,
outline="#000000",
fill="#ffffff",
width=1,
tags="rectangle",
)
self.tag_lower(rectangle_id)
for i in range(0, width, 27):
self.create_line(i, 0, i, height, dash=(2, 4), tags="grid line")
for i in range(0, height, 27):
self.create_line(0, i, width, i, dash=(2, 4), tags="grid line")
def setup_menus(self): def setup_menus(self):
self.node_context = tk.Menu(self.master) self.node_context = tk.Menu(self.master)
@ -33,6 +61,11 @@ class CanvasGraph(tk.Canvas):
self.node_context.add_command(label="Three") self.node_context.add_command(label="Three")
def setup_bindings(self): def setup_bindings(self):
"""
Bind any mouse events or hot keys to the matching action
:return: nothing
"""
self.bind("<ButtonPress-1>", self.click_press) self.bind("<ButtonPress-1>", self.click_press)
self.bind("<ButtonRelease-1>", self.click_release) self.bind("<ButtonRelease-1>", self.click_release)
self.bind("<B1-Motion>", self.click_motion) self.bind("<B1-Motion>", self.click_motion)
@ -42,11 +75,25 @@ class CanvasGraph(tk.Canvas):
self.bind("n", self.set_mode) self.bind("n", self.set_mode)
def canvas_xy(self, event): def canvas_xy(self, event):
"""
Convert window coordinate to canvas coordinate
:param event:
:rtype: (int, int)
:return: x, y canvas coordinate
"""
x = self.canvasx(event.x) x = self.canvasx(event.x)
y = self.canvasy(event.y) y = self.canvasy(event.y)
return x, y return x, y
def get_selected(self, event): def get_selected(self, event):
"""
Retrieve the item id that is on the mouse position
:param event: mouse event
:rtype: int
:return: the item that the mouse point to
"""
overlapping = self.find_overlapping(event.x, event.y, event.x, event.y) overlapping = self.find_overlapping(event.x, event.y, event.x, event.y)
nodes = set(self.find_withtag("node")) nodes = set(self.find_withtag("node"))
selected = None selected = None
@ -64,6 +111,12 @@ class CanvasGraph(tk.Canvas):
return selected return selected
def click_release(self, event): def click_release(self, event):
"""
Draw a node or finish drawing an edge according to the current graph mode
:param event: mouse event
:return: nothing
"""
self.focus_set() self.focus_set()
self.selected = self.get_selected(event) self.selected = self.get_selected(event)
logging.debug(f"click release selected: {self.selected}") logging.debug(f"click release selected: {self.selected}")
@ -109,6 +162,12 @@ class CanvasGraph(tk.Canvas):
logging.debug(f"edges: {self.find_withtag('edge')}") logging.debug(f"edges: {self.find_withtag('edge')}")
def click_press(self, event): def click_press(self, event):
"""
Start drawing an edge if mouse click is on a node
:param event: mouse event
:return: nothing
"""
logging.debug(f"click press: {event}") logging.debug(f"click press: {event}")
selected = self.get_selected(event) selected = self.get_selected(event)
is_node = selected in self.find_withtag("node") is_node = selected in self.find_withtag("node")
@ -117,6 +176,12 @@ class CanvasGraph(tk.Canvas):
self.drawing_edge = CanvasEdge(x, y, x, y, selected, self) self.drawing_edge = CanvasEdge(x, y, x, y, selected, self)
def click_motion(self, event): def click_motion(self, event):
"""
Redraw drawing edge according to the current position of the mouse
:param event: mouse event
:return: nothing
"""
if self.mode == GraphMode.EDGE and self.drawing_edge is not None: if self.mode == GraphMode.EDGE and self.drawing_edge is not None:
x2, y2 = self.canvas_xy(event) x2, y2 = self.canvas_xy(event)
x1, y1, _, _ = self.coords(self.drawing_edge.id) x1, y1, _, _ = self.coords(self.drawing_edge.id)
@ -130,6 +195,12 @@ class CanvasGraph(tk.Canvas):
self.node_context.post(event.x_root, event.y_root) self.node_context.post(event.x_root, event.y_root)
def set_mode(self, event): def set_mode(self, event):
"""
Set canvas mode according to the hot key that has been pressed
:param event: key event
:return: nothing
"""
logging.debug(f"mode event: {event}") logging.debug(f"mode event: {event}")
if event.char == "e": if event.char == "e":
self.mode = GraphMode.EDGE self.mode = GraphMode.EDGE
@ -147,21 +218,41 @@ class CanvasGraph(tk.Canvas):
class CanvasEdge: class CanvasEdge:
"""
Canvas edge class
"""
width = 3 width = 3
def __init__(self, x1, y1, x2, y2, src, canvas): def __init__(self, x1, y1, x2, y2, src, canvas):
"""
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.src = src
self.dst = None self.dst = None
self.canvas = canvas self.canvas = canvas
self.id = self.canvas.create_line(x1, y1, x2, y2, tags="edge", width=self.width) self.id = self.canvas.create_line(x1, y1, x2, y2, tags="edge", width=self.width)
self.token = None self.token = None
self.canvas.tag_lower(self.id)
# TODO resolve this
# self.canvas.tag_lower(self.id)
def complete(self, dst, x, y): def complete(self, dst, x, y):
self.dst = dst self.dst = dst
self.token = tuple(sorted((self.src, self.dst))) self.token = tuple(sorted((self.src, self.dst)))
x1, y1, _, _ = self.canvas.coords(self.id) x1, y1, _, _ = self.canvas.coords(self.id)
self.canvas.coords(self.id, x1, y1, x, y) self.canvas.coords(self.id, x1, y1, x, y)
self.canvas.lift(self.src)
self.canvas.lift(self.dst)
# self.canvas.create_line(0,0,10,10)
# print(x1,y1,x,y)
# self.canvas.create_line(x1+1, y1+1, x+1, y+1)
def delete(self): def delete(self):
self.canvas.delete(self.id) self.canvas.delete(self.id)