Преглед изворни кода

Split of canvas elements and allow them to be dragged around

Yentl Van Tendeloo пре 8 година
родитељ
комит
adba36af0a

BIN
classes/canvas/.canvas_element.xml.swp


+ 34 - 22
classes/canvas/canvas.xml

@@ -3,6 +3,7 @@
         <association name="parent" class="A" min="1" max="1" />
         <inheritance class="SCCDWidget" priority='0'/>
         <inheritance class="tk.Canvas" priority='1'/>
+        <association name="elements" class="CanvasElement"/>
     </relationships>
     <constructor>
         <parameter name="parent" />
@@ -23,13 +24,12 @@
             self.config(background='white', yscrollcommand=vbar.set, xscrollcommand=hbar.set)
             self.focus_set()
 
-            self.elements = {}
-            self.shift = {}
+            self.assoc_links = {}
+            self.element_group = {}
         </body>
     </constructor>
     <destructor>
         <body>
-            self.delete("all")
             self.destroy()
         </body>
     </destructor>
@@ -45,45 +45,57 @@
 
         <state id="ready">
             <transition event="clear_canvas" target=".">
+                <raise event="delete_instance" scope="cd">
+                    <parameter expr="'elements'"/>
+                </raise>
                 <script>
-                    self.delete("all")
-                    self.elements = {}
                     self.shift = {}
                 </script>
             </transition>
 
-            <transition event="define_group" target=".">
+            <transition event="define_group" target="../creating_group">
                 <parameter name="element"/>
                 <script>
-                    self.elements[element["id"]] = element
-                    self.shift[element["id"]] = (element["x"], element["y"])
+                    self.creating_id = element["id"]
                 </script>
+                <raise event="create_instance" scope="cd">
+                    <parameter expr="'elements'"/>
+                    <parameter expr="'CanvasElement'"/>
+                    <parameter expr="self"/>
+                    <parameter expr="(element['x'], element['y'])"/>
+                </raise>
             </transition>
 
             <transition event="define_contains" target=".">
                 <parameter name="element"/>
                 <script>
-                    self.shift[element["__target"]] = self.shift[element["__source"]]
+                    self.element_group[element["__target"]] = element["__source"]
+                    print("Binding element %s to group %s" % (element["__target"], element["__source"]))
                 </script>
             </transition>
 
             <transition event="draw_canvas" target=".">
                 <parameter name="element"/>
                 <script>
-                    print("Render: " + str(element))
-                    self.elements[element["id"]] = element
-                    shift_x, shift_y = self.shift[element["id"]]
-                    elem_x = shift_x + element["x"]
-                    elem_y = shift_y + element["y"]
-                    if element["type"] == "Rectangle":
-                        self.create_rectangle(elem_x, elem_y, elem_x + element["width"], elem_y + element["height"], fill=element["fillColour"], outline=element["lineColour"])
-                    elif element["type"] == "Text":
-                        self.create_text(elem_x, elem_y, fill=element["lineColour"], text=element["text"], anchor=tk.NW)
-                    elif element["type"] == "Line":
-                        self.create_line(elem_x, elem_y, shift_x + element["targetX"], shift_y + element["targetY"], fill=element["lineColour"], width=element["lineWidth"], arrow=tk.LAST if element["arrow"] else tk.NONE)
-                    else:
-                        print("Undefined render format: " + str(element))
+                    print("Drawing element %s" % element["id"])
+                </script>
+                <raise event="draw_element" scope="narrow" target="self.assoc_links[self.element_group[element['id']]]">
+                    <parameter expr="element"/>
+                </raise>
+            </transition>
+        </state>
+
+        <state id="creating_group">
+            <transition event="instance_created" target="../ready">
+                <parameter name="assoc_name"/>
+                <raise event="start_instance" scope="cd">
+                    <parameter expr="assoc_name"/>
+                </raise>
+                <script>
+                    self.assoc_links[self.creating_id] = assoc_name
+                    print("Defining group " + str(self.creating_id))
                 </script>
+                <raise event="group_ready" scope="narrow" target="'parent'"/>
             </transition>
         </state>
     </scxml>

+ 81 - 0
classes/canvas/canvas_element.xml

@@ -0,0 +1,81 @@
+<class name="CanvasElement">
+    <relationships>
+        <association name="parent" class="A" min="1" max="1" />
+        <inheritance class="SCCDWidget" priority='0'/>
+        <association name="elements" class="CanvasElement"/>
+    </relationships>
+    <constructor>
+        <parameter name="parent"/>
+        <parameter name="coordinates"/>
+        <super class="SCCDWidget">
+            <parameter expr="True"/>
+        </super>
+        <body>
+            self.containing_canvas = parent
+            self.coordinates = coordinates
+            self.elements = []
+        </body>
+    </constructor>
+    <destructor>
+        for f in self.elements:
+            self.containing_canvas.delete(f)
+    </destructor>
+
+    <scxml initial="main">
+        <state id="main">
+            <transition event="draw_element" target=".">
+                <parameter name="element"/>
+                <script>
+                    elem_x = self.coordinates[0] + element["x"]
+                    elem_y = self.coordinates[1] + element["y"]
+                    if element["type"] == "Rectangle":
+                        result = self.containing_canvas.create_rectangle(elem_x, elem_y, elem_x + element["width"], elem_y + element["height"], fill=element["fillColour"], outline=element["lineColour"])
+                    elif element["type"] == "Text":
+                        result = self.containing_canvas.create_text(elem_x, elem_y, fill=element["lineColour"], text=element["text"], anchor=tk.NW)
+                    elif element["type"] == "Line":
+                        result = self.containing_canvas.create_line(elem_x, elem_y, self.coordinates[0] + element["targetX"], self.coordinates[1] + element["targetY"], fill=element["lineColour"], width=element["lineWidth"], arrow=tk.LAST if element["arrow"] else tk.NONE)
+                    else:
+                        print("Undefined render format: " + str(element))
+                        result = None
+
+                    if result is not None:
+                        self.elements.append(result)
+                        self.set_bindable_and_tagorid(self.containing_canvas, result)
+                </script>
+            </transition>
+
+            <transition event="left-click" cond="id(self) == ID" target="../dragging">
+                <parameter name="ID"/>
+                <script>
+                    self.original_coords = self.last_x, self.last_y
+                </script>
+            </transition>
+        </state>
+
+        <state id="dragging">
+            <transition event="motion" cond="id(self) == ID" target=".">
+                <parameter name="ID"/>
+                <script>
+                    for f in self.elements:
+                        old_coords = self.containing_canvas.coords(f)
+                        new_x = self.coordinates[0] - self.original_coords[0] + self.last_x
+                        new_y = self.coordinates[1] - self.original_coords[1] + self.last_y
+
+                        if len(old_coords) == 2:
+                            self.containing_canvas.coords(f, (new_x, new_y))
+                        elif len(old_coords) == 4:
+                            height = old_coords[3] - old_coords[1]
+                            width = old_coords[2] - old_coords[0]
+                            self.containing_canvas.coords(f, (new_x, new_y, new_x + width, new_y + height))
+                </script>
+            </transition>
+
+            <transition event="left-release" cond="id(self) == ID" target="../main">
+                <parameter name="ID"/>
+                <script>
+                    self.coordinates = self.coordinates[0] - (self.original_coords[0] - self.last_x), self.coordinates[1] - (self.original_coords[1] - self.last_y)
+                </script>
+            </transition>
+        </state>
+    </scxml>
+</class>

+ 5 - 1
classes/window/main_window.xml

@@ -424,7 +424,7 @@
                     </onentry>
 
                     <state id="allocate_groups">
-                        <transition cond="len(self.groups) > 0" target=".">
+                        <transition cond="len(self.groups) > 0" target="../allocating_group">
                             <raise event="define_group" scope="narrow" target="'canvas'">
                                 <parameter expr="self.groups.pop()"/>
                             </raise>
@@ -432,6 +432,10 @@
                         <transition cond="len(self.groups) == 0" target="../allocate_contains"/>
                     </state>
 
+                    <state id="allocating_group">
+                        <transition event="group_ready" target="../allocate_groups"/>
+                    </state>
+
                     <state id="allocate_contains">
                         <transition cond="len(self.contains) > 0" target=".">
                             <raise event="define_contains" scope="narrow" target="'canvas'">

+ 172 - 22
frontend.py

@@ -1,7 +1,7 @@
 """
 Generated by Statechart compiler by Glenn De Jonghe, Joeri Exelmans, Simon Van Mierlo, and Yentl Van Tendeloo (for the inspiration)
 
-Date:   Wed Sep 13 11:45:24 2017
+Date:   Wed Sep 13 14:34:26 2017
 
 Model author: Yentl Van Tendeloo and Addis Gebremichael
 Model name:   Modelverse Visual Editor - Tkinter Version 
@@ -3961,14 +3961,17 @@ class MainWindow(RuntimeClassBase, tk.Toplevel, SCCDWidget):
         # state /running/rerender_model/render_model/allocate_groups
         self.states["/running/rerender_model/render_model/allocate_groups"] = State(27, "/running/rerender_model/render_model/allocate_groups", self)
         
+        # state /running/rerender_model/render_model/allocating_group
+        self.states["/running/rerender_model/render_model/allocating_group"] = State(28, "/running/rerender_model/render_model/allocating_group", self)
+        
         # state /running/rerender_model/render_model/allocate_contains
-        self.states["/running/rerender_model/render_model/allocate_contains"] = State(28, "/running/rerender_model/render_model/allocate_contains", self)
+        self.states["/running/rerender_model/render_model/allocate_contains"] = State(29, "/running/rerender_model/render_model/allocate_contains", self)
         
         # state /running/rerender_model/render_model/render_elements
-        self.states["/running/rerender_model/render_model/render_elements"] = State(29, "/running/rerender_model/render_model/render_elements", self)
+        self.states["/running/rerender_model/render_model/render_elements"] = State(30, "/running/rerender_model/render_model/render_elements", self)
         
         # state /close
-        self.states["/close"] = State(30, "/close", self)
+        self.states["/close"] = State(31, "/close", self)
         self.states["/close"].setEnter(self._close_enter)
         
         # add children
@@ -4000,6 +4003,7 @@ class MainWindow(RuntimeClassBase, tk.Toplevel, SCCDWidget):
         self.states["/running/rerender_model"].addChild(self.states["/running/rerender_model/request_render"])
         self.states["/running/rerender_model"].addChild(self.states["/running/rerender_model/render_model"])
         self.states["/running/rerender_model/render_model"].addChild(self.states["/running/rerender_model/render_model/allocate_groups"])
+        self.states["/running/rerender_model/render_model"].addChild(self.states["/running/rerender_model/render_model/allocating_group"])
         self.states["/running/rerender_model/render_model"].addChild(self.states["/running/rerender_model/render_model/allocate_contains"])
         self.states["/running/rerender_model/render_model"].addChild(self.states["/running/rerender_model/render_model/render_elements"])
         self.states[""].fixTree()
@@ -4165,7 +4169,7 @@ class MainWindow(RuntimeClassBase, tk.Toplevel, SCCDWidget):
         self.states["/running/rerender_model/request_render"].addTransition(_running_rerender_model_request_render_1)
         
         # transition /running/rerender_model/render_model/allocate_groups
-        _running_rerender_model_render_model_allocate_groups_0 = Transition(self, self.states["/running/rerender_model/render_model/allocate_groups"], [self.states["/running/rerender_model/render_model/allocate_groups"]])
+        _running_rerender_model_render_model_allocate_groups_0 = Transition(self, self.states["/running/rerender_model/render_model/allocate_groups"], [self.states["/running/rerender_model/render_model/allocating_group"]])
         _running_rerender_model_render_model_allocate_groups_0.setAction(self._running_rerender_model_render_model_allocate_groups_0_exec)
         _running_rerender_model_render_model_allocate_groups_0.setTrigger(None)
         _running_rerender_model_render_model_allocate_groups_0.setGuard(self._running_rerender_model_render_model_allocate_groups_0_guard)
@@ -4175,6 +4179,11 @@ class MainWindow(RuntimeClassBase, tk.Toplevel, SCCDWidget):
         _running_rerender_model_render_model_allocate_groups_1.setGuard(self._running_rerender_model_render_model_allocate_groups_1_guard)
         self.states["/running/rerender_model/render_model/allocate_groups"].addTransition(_running_rerender_model_render_model_allocate_groups_1)
         
+        # transition /running/rerender_model/render_model/allocating_group
+        _running_rerender_model_render_model_allocating_group_0 = Transition(self, self.states["/running/rerender_model/render_model/allocating_group"], [self.states["/running/rerender_model/render_model/allocate_groups"]])
+        _running_rerender_model_render_model_allocating_group_0.setTrigger(Event("group_ready", None))
+        self.states["/running/rerender_model/render_model/allocating_group"].addTransition(_running_rerender_model_render_model_allocating_group_0)
+        
         # transition /running/rerender_model/render_model/allocate_contains
         _running_rerender_model_render_model_allocate_contains_0 = Transition(self, self.states["/running/rerender_model/render_model/allocate_contains"], [self.states["/running/rerender_model/render_model/allocate_contains"]])
         _running_rerender_model_render_model_allocate_contains_0.setAction(self._running_rerender_model_render_model_allocate_contains_0_exec)
@@ -5549,11 +5558,10 @@ class Canvas(RuntimeClassBase, tk.Canvas, SCCDWidget):
         self.config(background='white', yscrollcommand=vbar.set, xscrollcommand=hbar.set)
         self.focus_set()
         
-        self.elements = {}
-        self.shift = {}
+        self.assoc_links = {}
+        self.element_group = {}
     
     def user_defined_destructor(self):
-        self.delete("all")
         self.destroy()
         # call super class destructors
         if hasattr(tk.Canvas, "__del__"):
@@ -5574,9 +5582,13 @@ class Canvas(RuntimeClassBase, tk.Canvas, SCCDWidget):
         # state /ready
         self.states["/ready"] = State(2, "/ready", self)
         
+        # state /creating_group
+        self.states["/creating_group"] = State(3, "/creating_group", self)
+        
         # add children
         self.states[""].addChild(self.states["/main"])
         self.states[""].addChild(self.states["/ready"])
+        self.states[""].addChild(self.states["/creating_group"])
         self.states[""].fixTree()
         self.states[""].default_state = self.states["/main"]
         
@@ -5591,7 +5603,7 @@ class Canvas(RuntimeClassBase, tk.Canvas, SCCDWidget):
         _ready_0.setAction(self._ready_0_exec)
         _ready_0.setTrigger(Event("clear_canvas", None))
         self.states["/ready"].addTransition(_ready_0)
-        _ready_1 = Transition(self, self.states["/ready"], [self.states["/ready"]])
+        _ready_1 = Transition(self, self.states["/ready"], [self.states["/creating_group"]])
         _ready_1.setAction(self._ready_1_exec)
         _ready_1.setTrigger(Event("define_group", None))
         self.states["/ready"].addTransition(_ready_1)
@@ -5603,39 +5615,171 @@ class Canvas(RuntimeClassBase, tk.Canvas, SCCDWidget):
         _ready_3.setAction(self._ready_3_exec)
         _ready_3.setTrigger(Event("draw_canvas", None))
         self.states["/ready"].addTransition(_ready_3)
+        
+        # transition /creating_group
+        _creating_group_0 = Transition(self, self.states["/creating_group"], [self.states["/ready"]])
+        _creating_group_0.setAction(self._creating_group_0_exec)
+        _creating_group_0.setTrigger(Event("instance_created", None))
+        self.states["/creating_group"].addTransition(_creating_group_0)
     
     def _main_0_exec(self, parameters):
         self.big_step.outputEventOM(Event("narrow_cast", None, [self, 'parent', Event("tk_widget", None, [self])]))
     
     def _ready_0_exec(self, parameters):
-        self.delete("all")
-        self.elements = {}
+        self.big_step.outputEventOM(Event("delete_instance", None, [self, 'elements']))
         self.shift = {}
     
     def _ready_1_exec(self, parameters):
         element = parameters[0]
-        self.elements[element["id"]] = element
-        self.shift[element["id"]] = (element["x"], element["y"])
+        self.creating_id = element["id"]
+        self.big_step.outputEventOM(Event("create_instance", None, [self, 'elements', 'CanvasElement', self, (element['x'], element['y'])]))
     
     def _ready_2_exec(self, parameters):
         element = parameters[0]
-        self.shift[element["__target"]] = self.shift[element["__source"]]
+        self.element_group[element["__target"]] = element["__source"]
+        print("Binding element %s to group %s" % (element["__target"], element["__source"]))
     
     def _ready_3_exec(self, parameters):
         element = parameters[0]
-        print("Render: " + str(element))
-        self.elements[element["id"]] = element
-        shift_x, shift_y = self.shift[element["id"]]
-        elem_x = shift_x + element["x"]
-        elem_y = shift_y + element["y"]
+        print("Drawing element %s" % element["id"])
+        self.big_step.outputEventOM(Event("narrow_cast", None, [self, self.assoc_links[self.element_group[element['id']]], Event("draw_element", None, [element])]))
+    
+    def _creating_group_0_exec(self, parameters):
+        assoc_name = parameters[0]
+        self.big_step.outputEventOM(Event("start_instance", None, [self, assoc_name]))
+        self.assoc_links[self.creating_id] = assoc_name
+        print("Defining group " + str(self.creating_id))
+        self.big_step.outputEventOM(Event("narrow_cast", None, [self, 'parent', Event("group_ready", None, [])]))
+    
+    def initializeStatechart(self):
+        # enter default state
+        self.default_targets = self.states["/main"].getEffectiveTargetStates()
+        RuntimeClassBase.initializeStatechart(self)
+
+class CanvasElement(RuntimeClassBase, SCCDWidget):
+    def __init__(self, controller, parent, coordinates):
+        RuntimeClassBase.__init__(self, controller)
+        
+        self.semantics.big_step_maximality = StatechartSemantics.TakeMany
+        self.semantics.internal_event_lifeline = StatechartSemantics.Queue
+        self.semantics.input_event_lifeline = StatechartSemantics.FirstComboStep
+        self.semantics.priority = StatechartSemantics.SourceParent
+        self.semantics.concurrency = StatechartSemantics.Single
+        
+        # build Statechart structure
+        self.build_statechart_structure()
+        
+        # call user defined constructor
+        CanvasElement.user_defined_constructor(self, parent, coordinates)
+    
+    def user_defined_constructor(self, parent, coordinates):
+        SCCDWidget.__init__(self, True)
+        self.containing_canvas = parent
+        self.coordinates = coordinates
+        self.elements = []
+    
+    def user_defined_destructor(self):
+        # call super class destructors
+        if hasattr(SCCDWidget, "__del__"):
+            SCCDWidget.__del__(self)
+    
+    
+    # builds Statechart structure
+    def build_statechart_structure(self):
+        
+        # state <root>
+        self.states[""] = State(0, "", self)
+        
+        # state /main
+        self.states["/main"] = State(1, "/main", self)
+        
+        # state /dragging
+        self.states["/dragging"] = State(2, "/dragging", self)
+        
+        # add children
+        self.states[""].addChild(self.states["/main"])
+        self.states[""].addChild(self.states["/dragging"])
+        self.states[""].fixTree()
+        self.states[""].default_state = self.states["/main"]
+        
+        # transition /main
+        _main_0 = Transition(self, self.states["/main"], [self.states["/main"]])
+        _main_0.setAction(self._main_0_exec)
+        _main_0.setTrigger(Event("draw_element", None))
+        self.states["/main"].addTransition(_main_0)
+        _main_1 = Transition(self, self.states["/main"], [self.states["/dragging"]])
+        _main_1.setAction(self._main_1_exec)
+        _main_1.setTrigger(Event("left-click", None))
+        _main_1.setGuard(self._main_1_guard)
+        self.states["/main"].addTransition(_main_1)
+        
+        # transition /dragging
+        _dragging_0 = Transition(self, self.states["/dragging"], [self.states["/dragging"]])
+        _dragging_0.setAction(self._dragging_0_exec)
+        _dragging_0.setTrigger(Event("motion", None))
+        _dragging_0.setGuard(self._dragging_0_guard)
+        self.states["/dragging"].addTransition(_dragging_0)
+        _dragging_1 = Transition(self, self.states["/dragging"], [self.states["/main"]])
+        _dragging_1.setAction(self._dragging_1_exec)
+        _dragging_1.setTrigger(Event("left-release", None))
+        _dragging_1.setGuard(self._dragging_1_guard)
+        self.states["/dragging"].addTransition(_dragging_1)
+    
+    def _main_0_exec(self, parameters):
+        element = parameters[0]
+        elem_x = self.coordinates[0] + element["x"]
+        elem_y = self.coordinates[1] + element["y"]
         if element["type"] == "Rectangle":
-            self.create_rectangle(elem_x, elem_y, elem_x + element["width"], elem_y + element["height"], fill=element["fillColour"], outline=element["lineColour"])
+            result = self.containing_canvas.create_rectangle(elem_x, elem_y, elem_x + element["width"], elem_y + element["height"], fill=element["fillColour"], outline=element["lineColour"])
         elif element["type"] == "Text":
-            self.create_text(elem_x, elem_y, fill=element["lineColour"], text=element["text"], anchor=tk.NW)
+            result = self.containing_canvas.create_text(elem_x, elem_y, fill=element["lineColour"], text=element["text"], anchor=tk.NW)
         elif element["type"] == "Line":
-            self.create_line(elem_x, elem_y, shift_x + element["targetX"], shift_y + element["targetY"], fill=element["lineColour"], width=element["lineWidth"], arrow=tk.LAST if element["arrow"] else tk.NONE)
+            result = self.containing_canvas.create_line(elem_x, elem_y, self.coordinates[0] + element["targetX"], self.coordinates[1] + element["targetY"], fill=element["lineColour"], width=element["lineWidth"], arrow=tk.LAST if element["arrow"] else tk.NONE)
         else:
             print("Undefined render format: " + str(element))
+            result = None
+        
+        if result is not None:
+            self.elements.append(result)
+            self.set_bindable_and_tagorid(self.containing_canvas, result)
+    
+    def _main_1_exec(self, parameters):
+        ID = parameters[0]
+        print("LEFT CLICK on canvas element at " + str(self.coordinates))
+        self.original_coords = self.last_x, self.last_y
+        print("Store click location: " + str(self.original_coords))
+    
+    def _main_1_guard(self, parameters):
+        ID = parameters[0]
+        return id(self) == ID
+    
+    def _dragging_0_exec(self, parameters):
+        ID = parameters[0]
+        for f in self.elements:
+            old_coords = self.containing_canvas.coords(f)
+            new_x = self.coordinates[0] - self.original_coords[0] + self.last_x
+            new_y = self.coordinates[1] - self.original_coords[1] + self.last_y
+        
+            if len(old_coords) == 2:
+                self.containing_canvas.coords(f, (new_x, new_y))
+            elif len(old_coords) == 4:
+                height = old_coords[3] - old_coords[1]
+                width = old_coords[2] - old_coords[0]
+                self.containing_canvas.coords(f, (new_x, new_y, new_x + width, new_y + height))
+    
+    def _dragging_0_guard(self, parameters):
+        ID = parameters[0]
+        return id(self) == ID
+    
+    def _dragging_1_exec(self, parameters):
+        ID = parameters[0]
+        print("Release at %s, so move coordinates" % str((self.last_x, self.last_y)))
+        self.coordinates = self.coordinates[0] - (self.original_coords[0] - self.last_x), self.coordinates[1] - (self.original_coords[1] - self.last_y)
+        print("New coordinates of element: %s" % str(self.coordinates))
+    
+    def _dragging_1_guard(self, parameters):
+        ID = parameters[0]
+        return id(self) == ID
     
     def initializeStatechart(self):
         # enter default state
@@ -5705,6 +5849,12 @@ class ObjectManager(ObjectManagerBase):
             instance = Canvas(self.controller, construct_params[0])
             instance.associations = {}
             instance.associations["parent"] = Association("A", 1, 1)
+            instance.associations["elements"] = Association("CanvasElement", 0, -1)
+        elif class_name == "CanvasElement":
+            instance = CanvasElement(self.controller, construct_params[0], construct_params[1])
+            instance.associations = {}
+            instance.associations["parent"] = Association("A", 1, 1)
+            instance.associations["elements"] = Association("CanvasElement", 0, -1)
         else:
             raise Exception("Cannot instantiate class " + class_name)
         return instance

+ 1 - 0
frontend.xml

@@ -29,4 +29,5 @@
     <class src="classes/widgets/label.xml"/>
     <class src="classes/widgets/progress_bar.xml"/>
     <class src="classes/canvas/canvas.xml"/>
+    <class src="classes/canvas/canvas_element.xml"/>
 </diagram>