|
@@ -22,114 +22,302 @@ init()
|
|
|
login("admin", "admin")
|
|
|
|
|
|
root = Tk()
|
|
|
-canvas = Canvas(root, width=MAX_WIDTH, height=MAX_HEIGHT, bg="white")
|
|
|
-
|
|
|
-selected_model = StringVar(root)
|
|
|
-selected_mapper = StringVar(root)
|
|
|
-
|
|
|
-mm_buttons = []
|
|
|
-available_models = model_list()
|
|
|
-available_mappers = []
|
|
|
-model_options = OptionMenu(root, selected_model, *[i[0] for i in available_models])
|
|
|
-mapper_options = OptionMenu(root, selected_mapper, "", *available_mappers)
|
|
|
-
|
|
|
-
|
|
|
-def visualize(model):
|
|
|
- print("Visualizing model: " + str(model))
|
|
|
- cache = {}
|
|
|
- hierarchy = {}
|
|
|
- parent = {}
|
|
|
-
|
|
|
- def findXY(element_id):
|
|
|
- print(cache[element_id])
|
|
|
- x = cache[element_id]["x"]
|
|
|
- y = cache[element_id]["y"]
|
|
|
-
|
|
|
- while element_id in parent:
|
|
|
- # Has a parent (group), so increment the XY values and keep going
|
|
|
- element_id = parent[element_id]
|
|
|
- x += cache[element_id]["x"]
|
|
|
- y += cache[element_id]["y"]
|
|
|
-
|
|
|
- return x, y
|
|
|
-
|
|
|
- for element in model:
|
|
|
- # Determine type of element
|
|
|
- cache[element["id"]] = element
|
|
|
- t = element["type"]
|
|
|
-
|
|
|
- if t == "contains":
|
|
|
- # Cache the containment links as well
|
|
|
- parent[element["__target"]] = element["__source"]
|
|
|
-
|
|
|
- # Caches filled, so go over all elements, ignoring groups, and draw them relative to their enclosing groups
|
|
|
- for element in model:
|
|
|
- t = element["type"]
|
|
|
- if t in ["Rectangle", "Ellipse", "Line", "Text", "Figure"]:
|
|
|
- x, y = findXY(element["id"])
|
|
|
-
|
|
|
- if t == "Rectangle":
|
|
|
- fillColour = element["fillColour"]
|
|
|
- width = element["width"]
|
|
|
- height = element["height"]
|
|
|
- lineWidth = element["lineWidth"]
|
|
|
- lineColour = element["lineColour"]
|
|
|
- print("Coordinates: " + str((x, y, x+width, y+height)))
|
|
|
- canvas.create_rectangle(x, y, x+width, y+height, fill=fillColour, outline=lineColour)
|
|
|
- elif t == "Ellipse":
|
|
|
- fillColour = element["fillColour"]
|
|
|
- width = element["width"]
|
|
|
- height = element["height"]
|
|
|
- lineWidth = element["lineWidth"]
|
|
|
- lineColour = element["lineColour"]
|
|
|
- canvas.create_ellipse(x, y, x+width, y+height, fill=fillColour, outline=lineColour)
|
|
|
- elif t == "Line":
|
|
|
- targetX = element["targetX"]
|
|
|
- targetY = element["targetY"]
|
|
|
- lineWidth = element["lineWidth"]
|
|
|
- lineColour = element["lineColour"]
|
|
|
- canvas.create_line(x, y, targetX, targetY, fill=lineColour, width=lineWidth)
|
|
|
- elif t == "Text":
|
|
|
- lineWidth = element["lineWidth"]
|
|
|
- lineColour = element["lineColour"]
|
|
|
- text = element["text"]
|
|
|
- canvas.create_text(x, y, fill=lineColour, text=text)
|
|
|
- elif t == "Figure":
|
|
|
- # Fetch associated SVG figure
|
|
|
- raise NotImplementedError()
|
|
|
-
|
|
|
-def reset_optionmenu(optionmenu, options, var):
|
|
|
- menu = optionmenu.children["menu"]
|
|
|
- menu.delete(0, "end")
|
|
|
- var.set("")
|
|
|
- for option in options:
|
|
|
- menu.add_command(label=option, command=lambda value=option: var.set(value))
|
|
|
-
|
|
|
-reset_optionmenu(mapper_options, [], selected_mapper)
|
|
|
-
|
|
|
-def read_available_mappers(instance_model):
|
|
|
- # Get instance model's type first, as transformations are defined on the type
|
|
|
- models = dict(model_list())
|
|
|
- type_model = models[instance_model]
|
|
|
- print("Found type model: " + type_model)
|
|
|
- print("Found MM_Rendered model: " + MM_RENDERED)
|
|
|
-
|
|
|
- # Switch to the core to find all information on megamodel relations
|
|
|
- model_modify("core")
|
|
|
-
|
|
|
- all_elems = [n for n, t in element_list() if t in ["Model", "ModelTransformation"]]
|
|
|
- all_attrs = {i: read_attrs(i) for i in all_elems}
|
|
|
- model_map = {all_attrs[i]["name"]: i for i in all_attrs}
|
|
|
- print("Got model_map: " + str(model_map))
|
|
|
- a = set([read_association_source(i) for i in read_incoming(model_map[MM_RENDERED], "transformOutput")])
|
|
|
- b = set([read_association_source(i) for i in read_incoming(model_map[type_model], "transformInput")])
|
|
|
- model_exit()
|
|
|
-
|
|
|
- print("All mappers for this tool: " + str(a))
|
|
|
- print("All transformations for this model: " + str(b))
|
|
|
- mappers = [all_attrs[i]["name"] for i in a & b]
|
|
|
- print("Overlap: " + str(mappers))
|
|
|
- return mappers
|
|
|
+
|
|
|
+class Window(object):
|
|
|
+ def __init__(self):
|
|
|
+ self.canvas = Canvas(root, width=MAX_WIDTH, height=MAX_HEIGHT, bg="white")
|
|
|
+
|
|
|
+ self.selected_model = StringVar(root)
|
|
|
+ self.selected_mapper = StringVar(root)
|
|
|
+
|
|
|
+ mm_buttons = []
|
|
|
+ available_models = model_list()
|
|
|
+ available_mappers = []
|
|
|
+ self.model_options = OptionMenu(root, self.selected_model, *[i[0] for i in available_models])
|
|
|
+ self.mapper_options = OptionMenu(root, self.selected_mapper, "", *available_mappers)
|
|
|
+ self.reset_optionmenu(self.mapper_options, [], self.selected_mapper)
|
|
|
+
|
|
|
+ bound_functions = {
|
|
|
+ "open": self.open_model,
|
|
|
+ "render": self.render_model,
|
|
|
+ "instantiate": self.instantiate_model,
|
|
|
+ "verify": self.verify_model,
|
|
|
+ }
|
|
|
+
|
|
|
+ buttons = []
|
|
|
+ buttons.append(self.model_options)
|
|
|
+ buttons.append(self.mapper_options)
|
|
|
+
|
|
|
+ self.current_element = None
|
|
|
+
|
|
|
+ for k, v in bound_functions.items():
|
|
|
+ buttons.append(Button(root, text=k, command=v))
|
|
|
+
|
|
|
+ for i, b in enumerate(buttons):
|
|
|
+ b.grid(row=0, column=i, sticky="WENS")
|
|
|
+
|
|
|
+ self.mm_buttons = []
|
|
|
+
|
|
|
+ self.CS_to_AS = {}
|
|
|
+ self.AS_to_CS = {}
|
|
|
+ self.AS_to_TK = {}
|
|
|
+ self.TK_to_CS = {}
|
|
|
+
|
|
|
+ self.canvas.grid(row=2,column=0,columnspan=len(buttons))
|
|
|
+
|
|
|
+ self.canvas.bind("<Button-1>", self.left_clicked)
|
|
|
+ self.canvas.bind("<B1-Motion>", self.left_drag)
|
|
|
+ self.canvas.bind("<ButtonRelease-1>", self.left_released)
|
|
|
+ self.canvas.bind("<Button-2>", self.middle_clicked)
|
|
|
+ self.canvas.bind("<Button-3>", self.right_clicked)
|
|
|
+
|
|
|
+ def find_AS(self, x, y):
|
|
|
+ # Find the group at this location
|
|
|
+ def in_bbox(coord, bbox):
|
|
|
+ if bbox[0] <= coord[0] <= bbox[2] and bbox[1] <= coord[1] <= bbox[3]:
|
|
|
+ return True
|
|
|
+
|
|
|
+ for elem in self.canvas.find_all():
|
|
|
+ if in_bbox((x, y), self.canvas.bbox(elem)):
|
|
|
+ return self.CS_to_AS[self.TK_to_CS[elem]]
|
|
|
+
|
|
|
+ def move_group(self, elem, coords):
|
|
|
+ # Change coordinates of all enclosed elements!
|
|
|
+ # elem is an AS id of the group
|
|
|
+ m_x, m_y = coords
|
|
|
+ o_x, o_y = self.original_coords
|
|
|
+ d_x = m_x - o_x
|
|
|
+ d_y = m_y - o_y
|
|
|
+
|
|
|
+ for tkinter_obj in self.AS_to_TK[elem]:
|
|
|
+ obj_coords = self.canvas.coords(tkinter_obj)
|
|
|
+ if len(obj_coords) == 2:
|
|
|
+ c_x, c_y = obj_coords
|
|
|
+ self.canvas.coords(tkinter_obj, (c_x + d_x, c_y + d_y))
|
|
|
+ elif len(obj_coords) == 4:
|
|
|
+ c_x, c_y, c_x2, c_y2 = obj_coords
|
|
|
+ self.canvas.coords(tkinter_obj, (c_x + d_x, c_y + d_y, c_x2 + d_x, c_y2 + d_y))
|
|
|
+ self.original_coords = coords
|
|
|
+
|
|
|
+ def left_clicked(self, evt):
|
|
|
+ # Left click means we select an element for dragging
|
|
|
+ x, y = evt.x, evt.y
|
|
|
+ self.current_element = self.find_AS(x, y)
|
|
|
+ self.original_coords = x, y
|
|
|
+ print("Left clicked on group with : " + str(self.current_element))
|
|
|
+
|
|
|
+ def left_drag(self, evt):
|
|
|
+ # Left drag means we are dragging something, but only if something is selected
|
|
|
+ x, y = evt.x, evt.y
|
|
|
+ if self.current_element is not None:
|
|
|
+ self.move_group(self.current_element, (x, y))
|
|
|
+
|
|
|
+ def left_released(self, evt):
|
|
|
+ # Left click means we select an element for dragging
|
|
|
+ x, y = evt.x, evt.y
|
|
|
+ if self.current_element is not None:
|
|
|
+ self.move_group(self.current_element, (x, y))
|
|
|
+ # Send new values to Modelverse!
|
|
|
+ # TODO
|
|
|
+
|
|
|
+ def middle_clicked(self, evt):
|
|
|
+ # Middle clicked means we are modifying attributes of something
|
|
|
+ x, y = evt.x, evt.y
|
|
|
+
|
|
|
+ # Read available attrs and their values
|
|
|
+ elem = self.find_AS(x, y)
|
|
|
+ as_id = elem[2]
|
|
|
+ attrs = read_attrs(as_id)
|
|
|
+ print("Got attrs: " + str(attrs))
|
|
|
+
|
|
|
+ # Prompt for new values
|
|
|
+ d = PromptDialog(root, attrs)
|
|
|
+ if d.result is not None:
|
|
|
+ # Change all attributes that have changed and send to Modelverse
|
|
|
+ # TODO
|
|
|
+ #TODO think about type checking
|
|
|
+ print("New attributes: " + str(d.result))
|
|
|
+
|
|
|
+ def right_clicked(self, evt):
|
|
|
+ # Right clicked means we are creating something
|
|
|
+ x, y = evt.x, evt.y
|
|
|
+
|
|
|
+ # Instantiate new element of currently using element
|
|
|
+ pass
|
|
|
+
|
|
|
+ def visualize(self, model):
|
|
|
+ print("Visualizing model: " + str(model))
|
|
|
+ cache = {}
|
|
|
+ hierarchy = {}
|
|
|
+ parent = {}
|
|
|
+
|
|
|
+ # First remove all previous elements in the canvas!
|
|
|
+ for elem in self.canvas.find_all():
|
|
|
+ self.canvas.destroy(elem)
|
|
|
+
|
|
|
+ self.CS_to_AS = {}
|
|
|
+ self.AS_to_CS = {}
|
|
|
+ self.AS_to_TK = {}
|
|
|
+ self.TK_to_CS = {}
|
|
|
+
|
|
|
+ def findXY(element_id):
|
|
|
+ print(cache[element_id])
|
|
|
+ x = cache[element_id]["x"]
|
|
|
+ y = cache[element_id]["y"]
|
|
|
+
|
|
|
+ while element_id in parent:
|
|
|
+ # Has a parent (group), so increment the XY values and keep going
|
|
|
+ element_id = parent[element_id]
|
|
|
+ x += cache[element_id]["x"]
|
|
|
+ y += cache[element_id]["y"]
|
|
|
+
|
|
|
+ return x, y
|
|
|
+
|
|
|
+ def findGroup(element_id):
|
|
|
+ try:
|
|
|
+ while cache[element_id]["__asid"] is None:
|
|
|
+ element_id = parent[element_id]
|
|
|
+ return element_id
|
|
|
+ except KeyError:
|
|
|
+ # No configured __asid found in hierarchy
|
|
|
+ print("ERROR")
|
|
|
+ return None
|
|
|
+
|
|
|
+ def findASID(element_id):
|
|
|
+ return cache[findGroup(element_id)]["__asid"]
|
|
|
+
|
|
|
+ for element in model:
|
|
|
+ # Determine type of element
|
|
|
+ cache[element["id"]] = element
|
|
|
+ t = element["type"]
|
|
|
+
|
|
|
+ if t == "contains":
|
|
|
+ # Cache the containment links as well
|
|
|
+ parent[element["__target"]] = element["__source"]
|
|
|
+ hierarchy.setdefault(element["__source"], []).append(element["__target"])
|
|
|
+
|
|
|
+ # Caches filled, so go over all elements, ignoring groups, and draw them relative to their enclosing groups
|
|
|
+ for element in model:
|
|
|
+ t = element["type"]
|
|
|
+ if t in ["Rectangle", "Ellipse", "Line", "Text", "Figure"]:
|
|
|
+ x, y = findXY(element["id"])
|
|
|
+ as_ID = findASID(element["id"])
|
|
|
+ cs_ID = element["id"]
|
|
|
+ group_ID = findGroup(element["id"])
|
|
|
+
|
|
|
+ if t == "Rectangle":
|
|
|
+ fillColour = element["fillColour"]
|
|
|
+ width = element["width"]
|
|
|
+ height = element["height"]
|
|
|
+ lineWidth = element["lineWidth"]
|
|
|
+ lineColour = element["lineColour"]
|
|
|
+ print("Coordinates: " + str((x, y, x+width, y+height)))
|
|
|
+ tkinter_elem = self.canvas.create_rectangle(x, y, x+width, y+height, fill=fillColour, outline=lineColour)
|
|
|
+ elif t == "Ellipse":
|
|
|
+ fillColour = element["fillColour"]
|
|
|
+ width = element["width"]
|
|
|
+ height = element["height"]
|
|
|
+ lineWidth = element["lineWidth"]
|
|
|
+ lineColour = element["lineColour"]
|
|
|
+ tkinter_elem = self.canvas.create_ellipse(x, y, x+width, y+height, fill=fillColour, outline=lineColour)
|
|
|
+ elif t == "Line":
|
|
|
+ targetX = element["targetX"]
|
|
|
+ targetY = element["targetY"]
|
|
|
+ lineWidth = element["lineWidth"]
|
|
|
+ lineColour = element["lineColour"]
|
|
|
+ tkinter_elem = self.canvas.create_line(x, y, targetX, targetY, fill=lineColour, width=lineWidth)
|
|
|
+ elif t == "Text":
|
|
|
+ lineWidth = element["lineWidth"]
|
|
|
+ lineColour = element["lineColour"]
|
|
|
+ text = element["text"]
|
|
|
+ tkinter_elem = self.canvas.create_text(x, y, fill=lineColour, text=text)
|
|
|
+ elif t == "Figure":
|
|
|
+ # Fetch associated SVG figure
|
|
|
+ raise NotImplementedError()
|
|
|
+
|
|
|
+ self.TK_to_CS[tkinter_elem] = cs_ID
|
|
|
+ self.CS_to_AS[cs_ID] = as_ID
|
|
|
+ self.AS_to_CS[as_ID] = group_ID
|
|
|
+ self.AS_to_TK.setdefault(as_ID, set([])).add(tkinter_elem)
|
|
|
+
|
|
|
+ def reset_optionmenu(self, optionmenu, options, var):
|
|
|
+ menu = optionmenu.children["menu"]
|
|
|
+ menu.delete(0, "end")
|
|
|
+ var.set("")
|
|
|
+ for option in options:
|
|
|
+ menu.add_command(label=option, command=lambda value=option: var.set(value))
|
|
|
+
|
|
|
+ def read_available_mappers(self, instance_model):
|
|
|
+ # Get instance model's type first, as transformations are defined on the type
|
|
|
+ models = dict(model_list())
|
|
|
+ type_model = models[instance_model]
|
|
|
+ print("Found type model: " + type_model)
|
|
|
+ print("Found MM_Rendered model: " + MM_RENDERED)
|
|
|
+
|
|
|
+ mappers = transformation_between(type_model, MM_RENDERED)
|
|
|
+ print("Supported: " + str(mappers))
|
|
|
+ return mappers
|
|
|
+
|
|
|
+ def create_element(self, t):
|
|
|
+ def create_elem():
|
|
|
+ print("Create element of type " + str(t))
|
|
|
+ return create_elem
|
|
|
+
|
|
|
+ def render_model(self):
|
|
|
+ try:
|
|
|
+ model_exit()
|
|
|
+ except InvalidMode:
|
|
|
+ pass
|
|
|
+ rendered = model_render(self.selected_model.get(), self.selected_mapper.get())
|
|
|
+
|
|
|
+ #TODO visualize rendered model
|
|
|
+ print("Rendering model: " + rendered)
|
|
|
+ import json
|
|
|
+ self.visualize(json.loads(rendered))
|
|
|
+ model_modify(self.selected_model.get())
|
|
|
+
|
|
|
+ def open_model(self):
|
|
|
+ try:
|
|
|
+ model_exit()
|
|
|
+ except InvalidMode:
|
|
|
+ pass
|
|
|
+ print("Opening model: " + self.selected_model.get())
|
|
|
+ available_mappers = self.read_available_mappers(self.selected_model.get())
|
|
|
+ self.reset_optionmenu(self.mapper_options, available_mappers, self.selected_mapper)
|
|
|
+
|
|
|
+ model_modify(self.selected_model.get())
|
|
|
+
|
|
|
+ available_types = [i[0] for i in types_full() if i[1] == "Class"]
|
|
|
+
|
|
|
+ for button in self.mm_buttons:
|
|
|
+ button.destroy()
|
|
|
+
|
|
|
+ self.mm_buttons = []
|
|
|
+ for i, t in enumerate(available_types):
|
|
|
+ self.mm_buttons.append(Button(root, text=t, command=lambda : self.create_element(t)))
|
|
|
+ self.mm_buttons[-1].grid(row=1, column=i)
|
|
|
+
|
|
|
+ def instantiate_model():
|
|
|
+ try:
|
|
|
+ model_exit()
|
|
|
+ except InvalidMode:
|
|
|
+ pass
|
|
|
+ d = PromptDialog(root, ["Name:"])
|
|
|
+ if d.result is not None:
|
|
|
+ name = str(d.result["Name:"])
|
|
|
+ print("Creating new model named " + name)
|
|
|
+ model_add(name, self.selected_model.get())
|
|
|
+ self.reset_optionmenu(self.available_models, model_list())
|
|
|
+ self.selected_model.set(name)
|
|
|
+ open_model()
|
|
|
+
|
|
|
+ def verify_model():
|
|
|
+ try:
|
|
|
+ # Exit if necessary
|
|
|
+ model_exit()
|
|
|
+ except InvalidMode:
|
|
|
+ pass
|
|
|
+ print(verify(self.selected_model.get()))
|
|
|
|
|
|
class PromptDialog(tkSimpleDialog.Dialog):
|
|
|
def __init__(self, master, query):
|
|
@@ -147,99 +335,5 @@ class PromptDialog(tkSimpleDialog.Dialog):
|
|
|
def apply(self):
|
|
|
self.result = {self.query[i]: self.entries[i].get() for i in range(len(self.entries))}
|
|
|
|
|
|
-def create_element(t):
|
|
|
- def create_elem():
|
|
|
- print("Create element of type " + str(t))
|
|
|
- return create_elem
|
|
|
-
|
|
|
-def render_model():
|
|
|
- try:
|
|
|
- model_exit()
|
|
|
- except InvalidMode:
|
|
|
- pass
|
|
|
- rendered = model_render(selected_model.get(), selected_mapper.get())
|
|
|
-
|
|
|
- #TODO visualize rendered model
|
|
|
- print("Rendering model: " + rendered)
|
|
|
- import json
|
|
|
- visualize(json.loads(rendered))
|
|
|
-
|
|
|
-def open_model():
|
|
|
- try:
|
|
|
- model_exit()
|
|
|
- except InvalidMode:
|
|
|
- pass
|
|
|
- print("Opening model: " + selected_model.get())
|
|
|
- available_mappers = read_available_mappers(selected_model.get())
|
|
|
- reset_optionmenu(mapper_options, available_mappers, selected_mapper)
|
|
|
-
|
|
|
- model_modify(selected_model.get())
|
|
|
-
|
|
|
- print(types_full())
|
|
|
- available_types = [i[0] for i in types_full() if i[1] == "Class"]
|
|
|
-
|
|
|
- global mm_buttons
|
|
|
- for button in mm_buttons:
|
|
|
- button.destroy()
|
|
|
-
|
|
|
- mm_buttons = []
|
|
|
- for i, t in enumerate(available_types):
|
|
|
- mm_buttons.append(Button(root, text=t, command=lambda : create_element(t)))
|
|
|
- mm_buttons[-1].grid(row=1, column=i)
|
|
|
-
|
|
|
-def instantiate_model():
|
|
|
- try:
|
|
|
- model_exit()
|
|
|
- except InvalidMode:
|
|
|
- pass
|
|
|
- d = PromptDialog(root, ["Name:"])
|
|
|
- if d.result is not None:
|
|
|
- name = str(d.result["Name:"])
|
|
|
- print("Creating new model named " + name)
|
|
|
- model_add(name, selected_model.get())
|
|
|
- reset_optionmenu(available_models, model_list())
|
|
|
- selected_model.set(name)
|
|
|
- open_model()
|
|
|
-
|
|
|
-def verify_model():
|
|
|
- try:
|
|
|
- # Exit if necessary
|
|
|
- model_exit()
|
|
|
- except InvalidMode:
|
|
|
- pass
|
|
|
- print(verify(selected_model.get()))
|
|
|
-
|
|
|
-bound_functions = {
|
|
|
- "open": open_model,
|
|
|
- "render": render_model,
|
|
|
- "instantiate": instantiate_model,
|
|
|
- "verify": verify_model,
|
|
|
- }
|
|
|
-
|
|
|
-buttons = []
|
|
|
-
|
|
|
-buttons.append(model_options)
|
|
|
-buttons.append(mapper_options)
|
|
|
-
|
|
|
-for k, v in bound_functions.items():
|
|
|
- buttons.append(Button(root, text=k, command=v))
|
|
|
-
|
|
|
-for i, b in enumerate(buttons):
|
|
|
- b.grid(row=0, column=i)
|
|
|
-
|
|
|
-canvas.grid(row=2,column=0,columnspan=len(buttons))
|
|
|
-
|
|
|
-def left_clicked():
|
|
|
- pass
|
|
|
-
|
|
|
-def middle_clicked():
|
|
|
- pass
|
|
|
-
|
|
|
-def right_clicked():
|
|
|
- pass
|
|
|
-
|
|
|
-canvas.bind("<Button-1>", left_clicked)
|
|
|
-canvas.bind("<Button-2>", middle_clicked)
|
|
|
-canvas.bind("<Button-3>", right_clicked)
|
|
|
-
|
|
|
+window = Window()
|
|
|
root.mainloop()
|