ソースを参照

Rendering works and can be dragged

Yentl Van Tendeloo 8 年 前
コミット
a24d90d367

+ 0 - 17
bootstrap/object_operations.alc

@@ -103,23 +103,6 @@ Element function allIncomingAssociationInstances(model : Element, target_name :
 
 	return result!
 
-Element function allIncomingAssociationInstances_slow(model : Element, target_name : String, assoc_name : String):
-	// Read out all outgoing edges of the model and select those that are typed by the specified association
-	Element assocs
-	String assoc
-	Element result
-	Element target
-
-	assocs = allInstances(model, assoc_name)
-	target = model["model"][target_name]
-
-	result = create_node()
-	while (0 < list_len(assocs)):
-		assoc = set_pop(assocs)
-		if (element_eq(target, read_edge_dst(model["model"][assoc]))):
-			set_add(result, assoc)
-	return result!
-
 Element function getAttributeList(model : Element, element : String):
 	Element result
 	Element keys

+ 41 - 1
core/core_algorithm.alc

@@ -23,6 +23,7 @@ String function JSON_print(model : Element):
 	String attr_key
 	Element attr_keys
 	Boolean first
+	Element attr_value
 
 	result = "["
 	keys_m = dict_keys(model["model"])
@@ -49,7 +50,11 @@ String function JSON_print(model : Element):
 			attr_keys = dict_keys(getAttributeList(model, v_m))
 			while (0 < read_nr_out(attr_keys)):
 				attr_key = set_pop(attr_keys)
-				result = ((((result + ", \"") + attr_key) + "\": ") + cast_v2s(read_attribute(model, v_m, attr_key)))
+				attr_value = read_attribute(model, v_m, attr_key)
+				if (element_eq(attr_value, read_root())):
+					result = (((result + ", \"") + attr_key) + "\": null")
+				else:
+					result = ((((result + ", \"") + attr_key) + "\": ") + cast_v2s(attr_value))
 
 			result = result + "}"
 
@@ -806,6 +811,7 @@ Void function user_function_skip_init(user_id : String):
 			output("    transformation_list_full        -- List all model transformations with permissions")
 			output("    transformation_detail           -- List transformation details")
 			output("    transformation_RAMify           -- RAMify a metamodel (again)")
+			output("    transformation_between          -- List all transformations between two metamodels")
 			output("")
 			output("Process operations")
 			output("    process_execute                 -- Execute a process model")
@@ -881,6 +887,40 @@ Void function user_function_skip_init(user_id : String):
 			else:
 				output("No such model")
 
+		elif (cmd == "transformation_between"):
+			String source_name
+			String source_id
+			String target_name
+			String target_id
+			Element onSource
+			Element onTarget
+			Element result
+			String transformation
+
+			output("Source metamodel?")
+			source_name = input()
+			source_id = get_model_id(source_name)
+			if (source_id != ""):
+				output("Target metamodel?")
+				target_name = input()
+				target_id = get_model_id(target_name)
+
+				if (target_id != ""):
+					output("Searching transformations...")
+					onSource = allAssociationOrigins(core, source_id, "transformInput")
+					onTarget = allAssociationOrigins(core, target_id, "transformOutput")
+
+					result = set_overlap(onSource, onTarget)
+
+					while (read_nr_out(result) > 0):
+						transformation = set_pop(result)
+						if (allow_read(user_id, transformation)):
+							output(read_attribute(core, transformation, "name"))
+				else:
+					output("Target unknown!")
+			else:
+				output("Source unknown!")
+
 		elif (cmd == "model_render"):
 			String model_name
 			String model_ID

+ 297 - 203
interface/graphical/main.py

@@ -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()

+ 0 - 4
interface/graphical/upload_mappers.py

@@ -62,10 +62,6 @@ commands = [ "root", "root", "root",
                     "",
                     "render_graphical_CBD",
                     ] + get_model_constructor(open("models/CBD_mapper.mvc", "r").read()) + [
-                "model_list",
-                "model_render",
-                    "my_CBD",
-                    "render_graphical_CBD",
             ]
 
 import urllib2

+ 8 - 0
models/CBD_mapper.mvc

@@ -18,6 +18,10 @@ A B {
                 }
                 Post_MM_rendered_graphical/Group post_block_1 {
                     label = "1"
+                    value___asid = $
+                        String function value(model : Element, name : String, mapping : Element):
+                            return mapping["0"]!
+                        $
                     value_x = $
                         Integer function value(model : Element, name : String, mapping : Element):
                             return 0!
@@ -174,6 +178,10 @@ A B {
 
                 Post_MM_rendered_graphical/Line {
                     label = "7"
+                    value___asid = $
+                        String function value(model : Element, name : String, mapping : Element):
+                            return mapping["2"]!
+                        $
                     value_x = $
                         Integer function value(model : Element, name : String, mapping : Element):
                             return read_attribute(model, mapping["3"], "x")!

+ 1 - 0
models/MM_rendered_graphical.mvc

@@ -7,6 +7,7 @@ SimpleClassDiagram MM_rendered_graphical{
     Class GraphicalElement {
         x : Natural
         y : Natural
+        __asid? : String
     }
 
     Class Group : GraphicalElement {

+ 26 - 2
wrappers/modelverse.py

@@ -22,8 +22,8 @@ def _input(value):
         urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "data": value, "taskname": taskname}))).read()
     else:
         value = json.dumps(value)
+        print("Set input: " + str(value))
         urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": value, "taskname": taskname}))).read()
-        #print("Set input: " + str(value))
 
 def _input_raw(value, taskname):
     # Ugly json encoding of primitives
@@ -66,7 +66,7 @@ def _output(expected=None):
     try:
         global last_output
         last_output = json.loads(urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "get_output", "taskname": taskname}))).read())
-        #print("Got output: " + str(last_output))
+        print("Got output: " + str(last_output))
     except:
         raise UnknownError()
     if expected is not None and last_output != expected:
@@ -375,6 +375,30 @@ def model_render(model, mapper):
     _output("Ready for command...")
     return rendered
 
+def transformation_between(source, target):
+    global mode
+    if mode != 2:
+        raise InvalidMode()
+
+    _input("transformation_between")
+    _output("Source metamodel?")
+    _input(source)
+    if _output() == "Target metamodel?":
+        _input(target)
+        if _output() == "Searching transformations...":
+            result = []
+            while _output() != "Ready for command...":
+                result.append(_last_output())
+            return result
+        elif _last_output() == "Target unknown!":
+            raise UnknownModel()
+        else:
+            raise InterfaceMismatch(_last_output())
+    elif _last_output() == "Source unknown!":
+        raise UnknownModel()
+    else:
+        raise InterfaceMismatch(_last_output())
+
 def transformation_add_MT_language():
     """Create a new Model Transformation language out of a set of metamodels."""
     raise NotImplementedError()