瀏覽代碼

better bouncing balls example

Simon Van Mierlo 9 年之前
父節點
當前提交
9a2fb97510

+ 369 - 0
examples/bouncingballs/python/bouncing.xml

@@ -0,0 +1,369 @@
+<?xml version="1.0" ?>
+<diagram author="Simon Van Mierlo" name="Bouncing Balls - Tkinter Version ">
+    <description>
+        Tkinter frame with bouncing balls in it.
+    </description>
+    <top>
+        import random
+        import Tkinter as tk
+        from widget import Widget
+    </top>
+    <inport name="input"/>
+    <class name="MainApp" default="true">
+        <relationships>
+            <association name="windows" class="Window" />
+        </relationships>
+        <constructor>
+            <parameter name="root"/>
+            <body>
+                self.nr_of_windows = 0
+                self.root = root
+            </body>
+        </constructor>
+        <scxml initial="main">
+            <parallel id="main">
+                <state id="main_behaviour" initial="initializing">
+                    <state id="initializing">
+                        <onentry>
+                            <raise event="create_window" scope="local" />
+                        </onentry>
+                        <transition target="../running" />
+                    </state>
+                    <state id="running">
+                        <transition event="window_created" target=".">
+                            <script>
+                                self.nr_of_windows += 1
+                            </script>
+                        </transition>
+                        <transition event="window_deleted" target="." cond="self.nr_of_windows > 1">
+                            <script>
+                                self.nr_of_windows -= 1
+                            </script>
+                        </transition>
+                        <transition event="window_deleted" target="." cond="self.nr_of_windows == 1">
+                            <raise event="stop" scope="local" />
+                        </transition>
+                    </state>
+                </state>
+                <state id="creating_behaviour" initial="waiting">
+                    <state id="waiting">
+                        <transition event="create_window" target="../creating">
+                            <raise event="create_instance" scope="cd">
+                                <parameter expr="'windows'" />
+                                <paremeter expr="'Window'" />
+                            </raise>
+                        </transition>
+                    </state>
+                    <state id="creating">
+                        <transition event="instance_created" target="../waiting">
+                            <parameter name="association_name" />
+                            <raise event="start_instance" scope="cd">
+                                <parameter expr="association_name" />
+                            </raise>
+                            <raise event="set_association_name" scope="narrow" target="association_name">
+                                <parameter expr="association_name" />
+                            </raise>
+                            <raise event="window_created" scope="local" />
+                        </transition>
+                    </state>
+                </state>
+                <state id="deleting_behaviour" initial="waiting">
+                    <state id="waiting">
+                        <transition event="delete_window" target="../deleting">
+                            <parameter name="association_name" />
+                            <raise event="delete_instance" scope="cd">
+                                <parameter expr="association_name" />
+                            </raise>
+                        </transition>
+                    </state>
+                    <state id="deleting">
+                        <transition event="instance_deleted" target="../waiting">
+                            <raise event="window_deleted" scope="local" />
+                        </transition>
+                    </state>
+                </state>
+                <transition event="stop" target="../stopped" />
+            </parallel>
+            <state id="stopped">
+                <onentry>
+                    <script>
+                        self.root.quit()
+                    </script>
+                </onentry>
+            </state>
+        </scxml>
+    </class>
+    <class name="Window">
+        <relationships>
+            <association name="parent" class="MainApp" min="1" max="1" />
+            <association name="buttons" class="Button" />
+            <association name="balls" class="Ball" />
+            <inheritance class="tk.Toplevel" priority="1" />
+            <inheritance class="Widget" priority="0" />
+        </relationships>
+        <constructor>
+            <super class="tk.Toplevel" />
+            <super class="Widget">
+                <parameter expr="True" />
+            </super>
+            <body>
+                <![CDATA[
+                self.title('BouncingBalls')
+                
+                CANVAS_SIZE_TUPLE = (0, 0, self.winfo_screenwidth() * 2, self.winfo_screenheight() * 2)
+                self.c = tk.Canvas(self, relief=tk.RIDGE, scrollregion=CANVAS_SIZE_TUPLE)
+                
+                self.set_bindable_and_tagorid(self.c)
+                ]]>
+            </body>
+        </constructor>
+        <destructor>
+            <body>
+                self.destroy()
+            </body>
+        </destructor>
+        <scxml initial="main">
+            <parallel id="main">
+                <state id="main_behaviour" initial="initializing">
+                    <state id="initializing">
+                        <transition event="set_association_name" target="../creating_button">
+                            <parameter name="association_name" />
+                            <script>
+                                self.association_name = association_name
+                            </script>
+                            <raise scope="cd" event="create_instance">
+                                <parameter expr="'buttons'" />
+                                <parameter expr="'Button'" />
+                                <parameter expr="self" />
+                                <parameter expr="'create_window'" />
+                                <parameter expr="'Create Window'" />
+                            </raise>
+                        </transition>
+                    </state>
+                    <state id="creating_button">
+                        <transition event="instance_created" target="../packing_button">
+                            <parameter name="association_name" type="string"/>
+                            <raise scope="cd" event="start_instance">
+                                <parameter expr="association_name" />
+                            </raise>
+                        </transition>
+                    </state>
+                    <state id="packing_button">
+                        <transition event="button_created" target="../running">
+                            <parameter name="button" type="Button"/>
+                            <script>
+                                button.pack(expand=False, fill=tk.X, side=tk.TOP)
+                                self.c.focus_force()
+                                self.c.pack(expand=True, fill=tk.BOTH)
+                            </script>
+                        </transition>
+                    </state>
+                    <state id="running">
+                        <transition event="window-close" port="input" target="." cond="tagorid == id(self)">
+                            <parameter name="tagorid" type="int" default="None" />
+                            <raise event="stop" scope="local" />
+                        </transition>
+                        <transition event="button_pressed" target="." cond="event_name == 'create_window'">
+                            <parameter name="event_name" type="string" />
+                            <raise event="create_window" scope="narrow" target="'parent'" />
+                        </transition>
+                        <transition event="right-click" port="input" target="../creating_ball" cond="tagorid == id(self)">
+                            <parameter name="tagorid" type="int" default="None" />
+                            <raise scope="cd" event="create_instance">
+                                <parameter expr='"balls"' />
+                                <parameter expr='"Ball"' />
+                                <parameter expr="self.c" />
+                                <parameter expr="self.last_x" />
+                                <parameter expr="self.last_y" />
+                            </raise>
+                        </transition>
+                        <transition event="delete_ball" target=".">
+                            <parameter name="association_name" type="string" />
+                            <raise event="delete_instance" scope="cd">
+                                <parameter expr="association_name" />
+                            </raise>
+                        </transition>
+                    </state>
+                    <state id="creating_ball">
+                        <transition event="instance_created" target="../running">
+                            <parameter name="association_name" type="string"/>
+                            <raise event="start_instance" scope="cd">
+                                <parameter expr="association_name" />
+                            </raise>
+                            <raise event="set_association_name" scope="narrow" target="association_name">
+                                <parameter expr="association_name" />
+                            </raise>
+                        </transition>
+                    </state>
+                </state>
+                <transition event="stop" target="../stopped">
+                    <raise event="delete_instance" scope="cd">
+                        <parameter expr="'buttons'" />
+                    </raise>
+                    <raise event="delete_instance" scope="cd">
+                        <parameter expr="'balls'" />
+                    </raise>
+                </transition>
+            </parallel>
+            <state id="stopped">
+                <onentry>                
+                    <raise event="delete_window" scope="narrow" target="'parent'">
+                        <parameter expr="self.association_name" />
+                    </raise>
+                </onentry>
+            </state>
+        </scxml>
+    </class>
+    <class name="Button">
+        <relationships>
+            <association name="parent" class="Field" min="1" max="1" />
+            <inheritance class="tk.Button" priority="1" />
+            <inheritance class="Widget" priority="0" />
+        </relationships>
+        <constructor>
+            <parameter name="parent" type="Field" />
+            <parameter name="event_name" type="str" />
+            <parameter name="button_text" type="str" />
+            <super class="tk.Button">
+                <parameter expr="parent" />
+                <parameter expr="**{'text': button_text}" />
+            </super>
+            <body>
+                self.event_name = event_name
+            </body>
+        </constructor>
+        <destructor>
+            <body>
+                self.destroy()
+            </body>
+        </destructor>
+        <scxml initial="initializing">
+            <state id="initializing">
+                <onentry>                
+                    <raise event="button_created" scope="narrow" target="'parent'">
+                        <parameter expr="self" />
+                    </raise>
+                </onentry>
+                <transition target="../running" />
+            </state>
+            <state id="running">
+                <transition event="left-click" port="input" target="." cond="tagorid == id(self)">
+                    <parameter name="tagorid" type="int" default="None" />
+                    <raise event="button_pressed" scope="narrow" target="'parent'">
+                        <parameter expr="self.event_name" />
+                    </raise>
+                </transition>
+            </state>
+        </scxml>
+    </class>
+    <class name="Ball">
+        <relationships>
+            <association name="parent" class="Window" min="1" max="1" />
+            <inheritance class="Widget" priority="0" />
+        </relationships>
+        <attribute name="canvas" />
+        <constructor>
+            <parameter name="canvas" />
+            <parameter name="x" />
+            <parameter name="y" />
+            <super class="Widget">
+                <parameter expr="True" />
+            </super>
+            <body>
+                self.canvas = canvas
+                self.r = 20.0;
+                self.vel = {'x': random.uniform(-5.0, 5.0), 'y': random.uniform(-5.0, 5.0)};
+                self.smooth = 0.4 # value between 0 and 1
+                self.id = self.canvas.create_oval(x, y, x + (self.r * 2), y + (self.r * 2), fill="black")
+                self.set_bindable_and_tagorid(self.canvas, self.id)
+            </body>
+        </constructor>
+        <destructor>
+            <body>
+                self.canvas.delete(self.id)
+            </body>
+        </destructor>
+        <scxml initial="initializing">
+            <state id="initializing">
+                <transition event="set_association_name" target="../bouncing">
+                    <parameter name="association_name" type="str" />
+                    <script>
+                        self.association_name = association_name
+                    </script>
+                </transition>
+            </state>
+            <state id="bouncing">
+                <transition after="(20 - self.getSimulatedTime() % 20) / 1000.0" target=".">
+                    <script>
+                    <![CDATA[
+                        pos = self.canvas.coords(self.id)
+                        x = self.canvas.canvasx(pos[0])
+                        y = self.canvas.canvasy(pos[1])
+                        if x <= 0 or x + (self.r * 2) >= self.canvas.canvasx(self.canvas.winfo_width()):
+                            self.vel['x'] = -self.vel['x']
+                        if y <= 0 or y + (self.r * 2) >= self.canvas.canvasy(self.canvas.winfo_height()):
+                            self.vel['y'] = -self.vel['y']
+                        self.canvas.move(self.id, self.vel['x'], self.vel['y']);
+                    ]]>                            
+                    </script>
+                </transition>
+                <transition port="input" event="left-click" target="../selected" cond="tagorid == id(self)">
+                    <parameter name="tagorid" type="int" default="None" />
+                    <script>
+                        self.canvas.itemconfig(self.id, fill="yellow")
+                    </script>
+                </transition>
+            </state>
+            <state id="dragging">
+                <transition port="input" event="motion" target=".">
+                    <parameter name="tagorid" type="int" default="None" />
+                    <script>
+                    <![CDATA[
+                        coords = self.canvas.coords(self.id)
+                        dx = self.canvas.canvasx(self.last_x) - self.canvas.canvasx(coords[0])
+                        dy = self.canvas.canvasx(self.last_y) - self.canvas.canvasy(coords[1])
+
+                        self.canvas.move(self.id, dx, dy);
+
+                        # keep ball within boundaries
+                        coords = self.canvas.coords(self.id)
+                        x = self.canvas.canvasx(coords[0])
+                        y = self.canvas.canvasy(coords[1])
+                        if x - self.r <= 0:
+                            x = 1;
+                        elif x + self.r >= self.canvas.winfo_width():
+                            x = self.canvas.winfo_width() - (2 * self.r) - 1
+                        if y - self.r <= 0:
+                            y = 1
+                        elif y + self.r >= self.canvas.winfo_height():
+                            y = self.canvas.winfo_height() - (2 * self.r) - 1;
+                        self.canvas.coords(self.id, x, y, x + (self.r * 2), y + (self.r * 2));
+                        self.vel = {
+                            'x': (1 - self.smooth) * dx + self.smooth * self.vel['x'],
+                            'y': (1 - self.smooth) * dy + self.smooth * self.vel['y']
+                        }
+                    ]]>
+                    </script>
+                </transition>
+                <transition port="input" event="left-release" target="../bouncing">
+                    <parameter name="tagorid" type="int" default="None" />
+                    <script>
+                        self.canvas.itemconfig(self.id, fill="red")
+                    </script>
+                </transition>
+            </state>
+            <state id="selected">
+                <transition port="input" event="left-click" target="../dragging" cond="tagorid == id(self)">
+                    <parameter name="tagorid" type="int" default="None" />
+                </transition>
+                <transition port="input" event="delete" target="../deleted">
+                    <parameter name="tagorid" type="int" default="None" />
+                    <raise event="delete_ball" scope="narrow" target="'parent'">
+                        <parameter expr="self.association_name" />
+                    </raise>
+                </transition>
+            </state>
+            <state id="deleted" />
+        </scxml>
+    </class>
+</diagram>

+ 19 - 0
examples/bouncingballs/python/runner_bouncing.py

@@ -0,0 +1,19 @@
+'''
+Created on 27-jul.-2014
+
+@author: Simon
+'''
+
+import Tkinter as tk
+import target_py.bouncing as target
+from sccd.runtime.statecharts_core import Event
+from sccd.runtime.tkinter_eventloop import *
+from widget import Widget
+
+if __name__ == '__main__':
+    window = tk.Tk()
+    window.withdraw()
+    controller = target.Controller(window, TkEventLoop(window))
+    Widget.controller = controller
+    controller.start()
+    window.mainloop()

+ 122 - 0
examples/bouncingballs/python/widget.py

@@ -0,0 +1,122 @@
+'''
+Created on 27-jul.-2014
+
+@author: Simon
+'''
+import Tkinter as tk
+from sccd.runtime.statecharts_core import Event
+
+class Widget:
+	controller = None
+
+	def __init__(self, configure_later=False):
+		if not configure_later:
+			self.set_bindable_and_tagorid(None, None)
+
+	def set_bindable_and_tagorid(self, bindable=None, tagorid=None):
+		if bindable is None:
+			bindable = self
+		self.bindable = bindable
+		self.mytagorid = tagorid
+		if isinstance(self, tk.Toplevel):
+			self.protocol("WM_DELETE_WINDOW", self.window_close)
+		if tagorid is not None:
+			if not isinstance(tagorid, list):
+				tagorid = [tagorid]
+			for t in tagorid:
+				self.bindable.tag_bind(t, "<Button>", self.on_click)
+				self.bindable.tag_bind(t, "<ButtonRelease>", self.on_release)
+				self.bindable.tag_bind(t, "<Motion>", self.on_motion)
+				self.bindable.tag_bind(t, "<Enter>", self.on_enter)
+				self.bindable.tag_bind(t, "<Leave>", self.on_leave)
+				self.bindable.tag_bind(t, "<Key>", self.on_key)
+				self.bindable.tag_bind(t, "<KeyRelease>", self.on_key_release)
+		else:
+			self.bindable.bind("<Button>", self.on_click)
+			self.bindable.bind("<ButtonRelease>", self.on_release)
+			self.bindable.bind("<Motion>", self.on_motion)
+			self.bindable.bind("<Enter>", self.on_enter)
+			self.bindable.bind("<Leave>", self.on_leave)
+			self.bindable.bind("<Key>", self.on_key)
+			self.bindable.bind("<KeyRelease>", self.on_key_release)
+		self.last_x = 50
+		self.last_y = 50
+		self.selected_type = None
+
+	def on_click(self, event):
+		event_name = None
+
+		if event.num == 1:
+			event_name = "left-click"
+		elif event.num == 2:
+			event_name = "middle-click"
+		elif event.num == 3:
+			event_name = "right-click"
+
+		if event_name:
+			self.last_x = event.x
+			self.last_y = event.y
+			Widget.controller.addInput(Event(event_name, "input", [id(self)]))
+
+	def on_release(self, event):
+		event_name = None
+
+		if event.num == 1:
+			event_name = "left-release"
+		elif event.num == 2:
+			event_name = "middle-release"
+		elif event.num == 3:
+			event_name = "right-release"
+
+		if event_name:
+			self.last_x = event.x
+			self.last_y = event.y
+			Widget.controller.addInput(Event(event_name, "input", [id(self)]))
+
+	def on_motion(self, event):
+		self.last_x = event.x
+		self.last_y = event.y
+		Widget.controller.addInput(Event("motion", "input", [id(self)]))
+
+	def on_enter(self, event):
+		Widget.controller.addInput(Event("enter", "input", [id(self)]))
+
+	def on_leave(self, event):
+		Widget.controller.addInput(Event("leave", "input", [id(self)]))
+
+	def on_key(self, event):
+		event_name = None
+
+		if event.keysym == 'Escape':
+			event_name = "escape"
+		elif event.keysym == 'Return':
+			event_name = "return"
+		elif event.keysym == 'Delete':
+			event_name = "delete"
+		elif event.keysym == 'Shift_L':
+			event_name = "shift"
+		elif event.keysym == 'Control_L':
+			event_name = "control"
+
+		if event_name:
+			Widget.controller.addInput(Event(event_name, "input", [id(self)]))
+
+	def on_key_release(self, event):
+		event_name = None
+
+		if event.keysym == 'Escape':
+			event_name = "escape-release"
+		elif event.keysym == 'Return':
+			event_name = "return-release"
+		elif event.keysym == 'Delete':
+			event_name = "delete-release"
+		elif event.keysym == 'Shift_L':
+			event_name = "shift-release"
+		elif event.keysym == 'Control_L':
+			event_name = "control-release"
+
+		if event_name:
+			Widget.controller.addInput(Event(event_name, "input", [id(self)]))
+
+	def window_close(self):
+		Widget.controller.addInput(Event("window-close", "input", [id(self)]))