Преглед на файлове

private ports now work in both directions; added a bouncing balls example where the SCCD model does not contain UI code (completely external)

Simon Van Mierlo преди 6 години
родител
ревизия
37525b9675

+ 2 - 1
examples/bouncingballs/python/make.bat

@@ -1,2 +1,3 @@
 python -m sccd.compiler.sccdc -p eventloop -l python -o target_py\target.py sccd.xml
-python -m sccd.compiler.sccdc -p eventloop -l python -o target_py\multiwindow.py sccd_multiwindow.xml
+python -m sccd.compiler.sccdc -p eventloop -l python -o target_py\multiwindow.py sccd_multiwindow.xml
+python -m sccd.compiler.sccdc -p eventloop -l python sccd_no_ui.xml

+ 70 - 0
examples/bouncingballs/python/runner_no_ui.py

@@ -0,0 +1,70 @@
+'''
+Created on 27-jul.-2014
+
+@author: Simon
+'''
+
+import threading
+
+import Tkinter as tk
+import sccd_no_ui as target
+from sccd.runtime.libs.ui import ui
+from sccd.runtime.statecharts_core import Event
+from sccd.runtime.tkinter_eventloop import *
+from ui_classes import *
+from widget_private_ports import Widget
+
+if __name__ == '__main__':
+    root = tk.Tk()
+    root.withdraw()
+
+#    def add_event(*args):
+#        print "okay adding %s" % args
+#
+#    root.bind("<<ADD_EVENT>>", add_event)
+#
+#    controller = target.Controller(TkEventLoop(root))
+#
+#    def raw_inputter():
+#        while 1:
+#            #controller.addInput(Event(raw_input(), "input", []))
+#            root.event_generate("<<ADD_EVENT>>")
+#    input_thread = threading.Thread(target=raw_inputter)
+#    input_thread.daemon = True
+#    input_thread.start()
+    
+#    output_listener = controller.addOutputListener(["ui_out"])
+#    def outputter():
+#        while 1:
+#            event = output_listener.fetch(-1)
+#            print event
+#    output_thread = threading.Thread(target=outputter)
+#    output_thread.daemon = True
+#    output_thread.start()
+
+    controller = target.Controller(TkEventLoop(root))
+    Widget.controller = controller
+
+    listener = controller.addOutputListener(controller.output_ports["ui_out"])
+    windows = {}
+    def check_output():
+        while True:
+            output_event = listener.fetch(0)
+            if not output_event is None:
+                if output_event.getName() == "create_new_window":
+                    assoc_name = output_event.getParameters()[0]
+                    sccd_object = output_event.getParameters()[1]
+                    windows[assoc_name] = WindowVis(sccd_object)
+                elif output_event.getName() == "delete_window":
+                    assoc_name = output_event.getParameters()[0]
+                    windows[assoc_name].destruct()
+                    del windows[assoc_name]
+                elif output_event.getName() == "stop_ui":
+                    root.quit()
+            else:
+                break
+        root.after(40, check_output) # hmm yeah so this delay is problematic: the visualization is created a bit AFTER the statechart object is, which means that the output listener is created too late, which means events are getting lost
+    root.after(40, check_output)
+
+    controller.start()
+    root.mainloop()

+ 124 - 72
examples/bouncingballs/python/sccd_no_ui.py

@@ -8,6 +8,7 @@ Bouncing Balls - no UI code.
 """
 
 from sccd.runtime.statecharts_core import *
+from ui_classes import *
 
 # package "Bouncing_Balls_Python_Version"
 
@@ -15,6 +16,7 @@ class MainApp(RuntimeClassBase):
     def __init__(self, controller):
         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
@@ -29,8 +31,6 @@ class MainApp(RuntimeClassBase):
     
     def user_defined_constructor(self):
         self.nr_of_windows = 0
-        self.WINDOW_SIZE_W = 800
-        self.WINDOW_SIZE_H = 600
     
     def user_defined_destructor(self):
         pass
@@ -149,7 +149,7 @@ class MainApp(RuntimeClassBase):
         self.raiseInternalEvent(Event("create_window", None, []))
     
     def _stopped_enter(self):
-        self.big_step.outputEvent(Event("stop_ui", "ui_out", []))
+        self.big_step.outputEvent(Event("stop_ui", self.getOutPortName("ui_out"), []))
     
     def _main_main_behaviour_running_0_exec(self, parameters):
         self.nr_of_windows += 1
@@ -167,16 +167,18 @@ class MainApp(RuntimeClassBase):
         return self.nr_of_windows == 1
     
     def _main_creating_behaviour_waiting_0_exec(self, parameters):
-        self.big_step.outputEventOM(Event("create_instance", None, [self, 'windows', 'Window', self.WINDOW_SIZE_H, self.WINDOW_SIZE_W]))
+        self.big_step.outputEventOM(Event("create_instance", None, [self, 'windows', 'Window']))
     
     def _main_creating_behaviour_creating_0_exec(self, parameters):
         association_name = parameters[0]
+        self.big_step.outputEvent(Event("create_new_window", self.getOutPortName("ui_out"), [association_name, self.getSingleChild(association_name)]))
         self.big_step.outputEventOM(Event("start_instance", None, [self, association_name]))
         self.big_step.outputEventOM(Event("narrow_cast", None, [self, association_name, Event("set_association_name", None, [association_name])]))
         self.raiseInternalEvent(Event("window_created", None, []))
     
     def _main_deleting_behaviour_waiting_0_exec(self, parameters):
         association_name = parameters[0]
+        self.big_step.outputEvent(Event("delete_window", self.getOutPortName("ui_out"), [association_name]))
         self.big_step.outputEventOM(Event("delete_instance", None, [self, association_name]))
     
     def _main_deleting_behaviour_deleting_0_exec(self, parameters):
@@ -188,9 +190,12 @@ class MainApp(RuntimeClassBase):
         RuntimeClassBase.initializeStatechart(self)
 
 class Window(RuntimeClassBase):
-    def __init__(self, controller, height, width):
+    def __init__(self, controller):
         RuntimeClassBase.__init__(self, controller)
         
+        self.inports["window_ui_in"] = controller.addInputPort("window_ui_in", self)
+        self.outports["window_ui_out"] = controller.addOutputPort("window_ui_out", self)
+        
         self.semantics.big_step_maximality = StatechartSemantics.TakeMany
         self.semantics.internal_event_lifeline = StatechartSemantics.Queue
         self.semantics.input_event_lifeline = StatechartSemantics.FirstComboStep
@@ -199,14 +204,12 @@ class Window(RuntimeClassBase):
         
         # build Statechart structure
         self.build_statechart_structure()
-        self.inports["window_ui_in"] = controller.addInputPort("window_ui_in", self)
         
         # call user defined constructor
-        Window.user_defined_constructor(self, height, width)
+        Window.user_defined_constructor(self)
     
-    def user_defined_constructor(self, height, width):
-        self.height = height
-        self.width = width
+    def user_defined_constructor(self):
+        pass
     
     def user_defined_destructor(self):
         pass
@@ -227,23 +230,32 @@ class Window(RuntimeClassBase):
         # state /main/main_behaviour/initializing
         self.states["/main/main_behaviour/initializing"] = State(3, "/main/main_behaviour/initializing", self)
         
+        # state /main/main_behaviour/waiting_for_ui
+        self.states["/main/main_behaviour/waiting_for_ui"] = State(4, "/main/main_behaviour/waiting_for_ui", self)
+        
         # state /main/main_behaviour/creating_button
-        self.states["/main/main_behaviour/creating_button"] = State(4, "/main/main_behaviour/creating_button", self)
+        self.states["/main/main_behaviour/creating_button"] = State(5, "/main/main_behaviour/creating_button", self)
         
         # state /main/main_behaviour/running
-        self.states["/main/main_behaviour/running"] = State(5, "/main/main_behaviour/running", self)
+        self.states["/main/main_behaviour/running"] = State(6, "/main/main_behaviour/running", self)
         
         # state /main/main_behaviour/creating_ball
-        self.states["/main/main_behaviour/creating_ball"] = State(6, "/main/main_behaviour/creating_ball", self)
+        self.states["/main/main_behaviour/creating_ball"] = State(7, "/main/main_behaviour/creating_ball", self)
         
         # state /main/size_change_listener
-        self.states["/main/size_change_listener"] = State(7, "/main/size_change_listener", self)
+        self.states["/main/size_change_listener"] = State(8, "/main/size_change_listener", self)
         
         # state /main/size_change_listener/listening
-        self.states["/main/size_change_listener/listening"] = State(8, "/main/size_change_listener/listening", self)
+        self.states["/main/size_change_listener/listening"] = State(9, "/main/size_change_listener/listening", self)
+        
+        # state /main/delete_listener
+        self.states["/main/delete_listener"] = State(10, "/main/delete_listener", self)
+        
+        # state /main/delete_listener/listening
+        self.states["/main/delete_listener/listening"] = State(11, "/main/delete_listener/listening", self)
         
         # state /stopped
-        self.states["/stopped"] = State(9, "/stopped", self)
+        self.states["/stopped"] = State(12, "/stopped", self)
         self.states["/stopped"].setEnter(self._stopped_enter)
         
         # add children
@@ -251,22 +263,32 @@ class Window(RuntimeClassBase):
         self.states[""].addChild(self.states["/stopped"])
         self.states["/main"].addChild(self.states["/main/main_behaviour"])
         self.states["/main"].addChild(self.states["/main/size_change_listener"])
+        self.states["/main"].addChild(self.states["/main/delete_listener"])
         self.states["/main/main_behaviour"].addChild(self.states["/main/main_behaviour/initializing"])
+        self.states["/main/main_behaviour"].addChild(self.states["/main/main_behaviour/waiting_for_ui"])
         self.states["/main/main_behaviour"].addChild(self.states["/main/main_behaviour/creating_button"])
         self.states["/main/main_behaviour"].addChild(self.states["/main/main_behaviour/running"])
         self.states["/main/main_behaviour"].addChild(self.states["/main/main_behaviour/creating_ball"])
         self.states["/main/size_change_listener"].addChild(self.states["/main/size_change_listener/listening"])
+        self.states["/main/delete_listener"].addChild(self.states["/main/delete_listener/listening"])
         self.states[""].fixTree()
         self.states[""].default_state = self.states["/main"]
         self.states["/main/main_behaviour"].default_state = self.states["/main/main_behaviour/initializing"]
         self.states["/main/size_change_listener"].default_state = self.states["/main/size_change_listener/listening"]
+        self.states["/main/delete_listener"].default_state = self.states["/main/delete_listener/listening"]
         
         # transition /main/main_behaviour/initializing
-        _main_main_behaviour_initializing_0 = Transition(self, self.states["/main/main_behaviour/initializing"], [self.states["/main/main_behaviour/creating_button"]])
+        _main_main_behaviour_initializing_0 = Transition(self, self.states["/main/main_behaviour/initializing"], [self.states["/main/main_behaviour/waiting_for_ui"]])
         _main_main_behaviour_initializing_0.setAction(self._main_main_behaviour_initializing_0_exec)
         _main_main_behaviour_initializing_0.setTrigger(Event("set_association_name", None))
         self.states["/main/main_behaviour/initializing"].addTransition(_main_main_behaviour_initializing_0)
         
+        # transition /main/main_behaviour/waiting_for_ui
+        _main_main_behaviour_waiting_for_ui_0 = Transition(self, self.states["/main/main_behaviour/waiting_for_ui"], [self.states["/main/main_behaviour/creating_button"]])
+        _main_main_behaviour_waiting_for_ui_0.setAction(self._main_main_behaviour_waiting_for_ui_0_exec)
+        _main_main_behaviour_waiting_for_ui_0.setTrigger(Event("ui_initialized", self.getInPortName("window_ui_in")))
+        self.states["/main/main_behaviour/waiting_for_ui"].addTransition(_main_main_behaviour_waiting_for_ui_0)
+        
         # transition /main/main_behaviour/creating_button
         _main_main_behaviour_creating_button_0 = Transition(self, self.states["/main/main_behaviour/creating_button"], [self.states["/main/main_behaviour/running"]])
         _main_main_behaviour_creating_button_0.setAction(self._main_main_behaviour_creating_button_0_exec)
@@ -281,11 +303,11 @@ class Window(RuntimeClassBase):
         self.states["/main/main_behaviour/running"].addTransition(_main_main_behaviour_running_0)
         _main_main_behaviour_running_1 = Transition(self, self.states["/main/main_behaviour/running"], [self.states["/main/main_behaviour/running"]])
         _main_main_behaviour_running_1.setAction(self._main_main_behaviour_running_1_exec)
-        _main_main_behaviour_running_1.setTrigger(Event("close_window", "window_ui_in"))
+        _main_main_behaviour_running_1.setTrigger(Event("close_window", self.getInPortName("window_ui_in")))
         self.states["/main/main_behaviour/running"].addTransition(_main_main_behaviour_running_1)
         _main_main_behaviour_running_2 = Transition(self, self.states["/main/main_behaviour/running"], [self.states["/main/main_behaviour/creating_ball"]])
         _main_main_behaviour_running_2.setAction(self._main_main_behaviour_running_2_exec)
-        _main_main_behaviour_running_2.setTrigger(Event("create_ball", "window_ui_in"))
+        _main_main_behaviour_running_2.setTrigger(Event("create_ball", self.getInPortName("window_ui_in")))
         self.states["/main/main_behaviour/running"].addTransition(_main_main_behaviour_running_2)
         _main_main_behaviour_running_3 = Transition(self, self.states["/main/main_behaviour/running"], [self.states["/main/main_behaviour/running"]])
         _main_main_behaviour_running_3.setAction(self._main_main_behaviour_running_3_exec)
@@ -301,9 +323,15 @@ class Window(RuntimeClassBase):
         # transition /main/size_change_listener/listening
         _main_size_change_listener_listening_0 = Transition(self, self.states["/main/size_change_listener/listening"], [self.states["/main/size_change_listener/listening"]])
         _main_size_change_listener_listening_0.setAction(self._main_size_change_listener_listening_0_exec)
-        _main_size_change_listener_listening_0.setTrigger(Event("size_changed", "window_ui_in"))
+        _main_size_change_listener_listening_0.setTrigger(Event("size_changed", self.getInPortName("window_ui_in")))
         self.states["/main/size_change_listener/listening"].addTransition(_main_size_change_listener_listening_0)
         
+        # transition /main/delete_listener/listening
+        _main_delete_listener_listening_0 = Transition(self, self.states["/main/delete_listener/listening"], [self.states["/main/delete_listener/listening"]])
+        _main_delete_listener_listening_0.setAction(self._main_delete_listener_listening_0_exec)
+        _main_delete_listener_listening_0.setTrigger(Event("delete", self.getInPortName("window_ui_in")))
+        self.states["/main/delete_listener/listening"].addTransition(_main_delete_listener_listening_0)
+        
         # transition /main
         _main_0 = Transition(self, self.states["/main"], [self.states["/stopped"]])
         _main_0.setAction(self._main_0_exec)
@@ -320,11 +348,17 @@ class Window(RuntimeClassBase):
     def _main_main_behaviour_initializing_0_exec(self, parameters):
         association_name = parameters[0]
         self.association_name = association_name
+    
+    def _main_main_behaviour_waiting_for_ui_0_exec(self, parameters):
+        width = parameters[0]
+        height = parameters[1]
+        self.width = width
+        self.height = height
         self.big_step.outputEventOM(Event("create_instance", None, [self, "buttons", "Button", 'create_window']))
     
     def _main_main_behaviour_creating_button_0_exec(self, parameters):
         association_name = parameters[0]
-        self.big_step.outputEvent(Event("create_new_button", "window_ui_out", [self.association_name, association_name]))
+        self.big_step.outputEvent(Event("create_new_button", self.getOutPortName("window_ui_out"), [association_name, self.getSingleChild(association_name)]))
         self.big_step.outputEventOM(Event("start_instance", None, [self, association_name]))
     
     def _main_main_behaviour_running_0_exec(self, parameters):
@@ -341,25 +375,28 @@ class Window(RuntimeClassBase):
     def _main_main_behaviour_running_2_exec(self, parameters):
         x = parameters[0]
         y = parameters[1]
-        self.big_step.outputEventOM(Event("create_instance", None, [self, "balls", "Ball"]))
+        self.big_step.outputEventOM(Event("create_instance", None, [self, "balls", "Ball", x, y, self.width, self.height]))
     
     def _main_main_behaviour_running_3_exec(self, parameters):
         association_name = parameters[0]
         self.big_step.outputEventOM(Event("delete_instance", None, [self, association_name]))
-        self.big_step.outputEvent(Event("delete_ball", "window_ui_out", [self.association_name, association_name]))
+        self.big_step.outputEvent(Event("delete_ball", self.getOutPortName("window_ui_out"), [association_name]))
     
     def _main_main_behaviour_creating_ball_0_exec(self, parameters):
         association_name = parameters[0]
-        self.big_step.outputEvent(Event("create_new_ball", "window_ui_out", [self.association_name, association_name]))
+        self.big_step.outputEvent(Event("create_new_ball", self.getOutPortName("window_ui_out"), [association_name, self.getSingleChild(association_name)]))
         self.big_step.outputEventOM(Event("start_instance", None, [self, association_name]))
         self.big_step.outputEventOM(Event("narrow_cast", None, [self, association_name, Event("set_association_name", None, [association_name])]))
     
     def _main_size_change_listener_listening_0_exec(self, parameters):
-        new_height = parameters[0]
-        new_width = parameters[1]
-        self.height = height
-        self.width = width
-        self.big_step.outputEventOM(Event("narrow_cast", None, [self, balls, Event("window_size_changed", None, [new_height, new_width])]))
+        new_width = parameters[0]
+        new_height = parameters[1]
+        self.width = new_width
+        self.height = new_height
+        self.big_step.outputEventOM(Event("narrow_cast", None, [self, 'balls', Event("window_size_changed", None, [new_width, new_height])]))
+    
+    def _main_delete_listener_listening_0_exec(self, parameters):
+        self.big_step.outputEventOM(Event("narrow_cast", None, [self, 'balls', Event("delete", None, [])]))
     
     def initializeStatechart(self):
         # enter default state
@@ -370,6 +407,9 @@ class Button(RuntimeClassBase):
     def __init__(self, controller, event_name):
         RuntimeClassBase.__init__(self, controller)
         
+        self.inports["button_ui_in"] = controller.addInputPort("button_ui_in", self)
+        self.outports["button_ui_out"] = controller.addOutputPort("button_ui_out", self)
+        
         self.semantics.big_step_maximality = StatechartSemantics.TakeMany
         self.semantics.internal_event_lifeline = StatechartSemantics.Queue
         self.semantics.input_event_lifeline = StatechartSemantics.FirstComboStep
@@ -378,7 +418,6 @@ class Button(RuntimeClassBase):
         
         # build Statechart structure
         self.build_statechart_structure()
-        self.inports["button_ui_in"] = controller.addInputPort("button_ui_in", self)
         
         # call user defined constructor
         Button.user_defined_constructor(self, event_name)
@@ -396,45 +435,48 @@ class Button(RuntimeClassBase):
         # state <root>
         self.states[""] = State(0, "", self)
         
-        # state /initializing
-        self.states["/initializing"] = State(1, "/initializing", self)
-        self.states["/initializing"].setEnter(self._initializing_enter)
+        # state /waiting
+        self.states["/waiting"] = State(1, "/waiting", self)
         
         # state /running
         self.states["/running"] = State(2, "/running", self)
         
         # add children
-        self.states[""].addChild(self.states["/initializing"])
+        self.states[""].addChild(self.states["/waiting"])
         self.states[""].addChild(self.states["/running"])
         self.states[""].fixTree()
-        self.states[""].default_state = self.states["/initializing"]
+        self.states[""].default_state = self.states["/waiting"]
         
-        # transition /initializing
-        _initializing_0 = Transition(self, self.states["/initializing"], [self.states["/running"]])
-        _initializing_0.setTrigger(None)
-        self.states["/initializing"].addTransition(_initializing_0)
+        # transition /waiting
+        _waiting_0 = Transition(self, self.states["/waiting"], [self.states["/running"]])
+        _waiting_0.setAction(self._waiting_0_exec)
+        _waiting_0.setTrigger(Event("ui_initialized", self.getInPortName("button_ui_in")))
+        self.states["/waiting"].addTransition(_waiting_0)
         
         # transition /running
         _running_0 = Transition(self, self.states["/running"], [self.states["/running"]])
         _running_0.setAction(self._running_0_exec)
-        _running_0.setTrigger(Event("clicked", "button_ui_in"))
+        _running_0.setTrigger(Event("clicked", self.getInPortName("button_ui_in")))
         self.states["/running"].addTransition(_running_0)
     
-    def _initializing_enter(self):
-        self.big_step.outputEventOM(Event("narrow_cast", None, [self, 'parent', Event("button_created", None, [self])]))
+    def _waiting_0_exec(self, parameters):
+        self.big_step.outputEvent(Event("set_text", self.getOutPortName("button_ui_out"), [self.event_name]))
     
     def _running_0_exec(self, parameters):
         self.big_step.outputEventOM(Event("narrow_cast", None, [self, 'parent', Event("button_pressed", None, [self.event_name])]))
     
     def initializeStatechart(self):
         # enter default state
-        self.default_targets = self.states["/initializing"].getEffectiveTargetStates()
+        self.default_targets = self.states["/waiting"].getEffectiveTargetStates()
         RuntimeClassBase.initializeStatechart(self)
 
 class Ball(RuntimeClassBase):
-    def __init__(self, controller, x, y, window_height, window_width):
+    def __init__(self, controller, x, y, window_width, window_height):
         RuntimeClassBase.__init__(self, controller)
         
+        self.inports["ball_ui_in"] = controller.addInputPort("ball_ui_in", self)
+        self.outports["ball_ui_out"] = controller.addOutputPort("ball_ui_out", self)
+        
         self.semantics.big_step_maximality = StatechartSemantics.TakeMany
         self.semantics.internal_event_lifeline = StatechartSemantics.Queue
         self.semantics.input_event_lifeline = StatechartSemantics.FirstComboStep
@@ -443,17 +485,15 @@ class Ball(RuntimeClassBase):
         
         # build Statechart structure
         self.build_statechart_structure()
-        self.inports["ball_ui_in"] = controller.addInputPort("ball_ui_in", self)
-        self.inports["ball_ui_out"] = controller.addInputPort("ball_ui_out", self)
         
         # call user defined constructor
-        Ball.user_defined_constructor(self, x, y, window_height, window_width)
+        Ball.user_defined_constructor(self, x, y, window_width, window_height)
     
-    def user_defined_constructor(self, x, y, window_height, window_width):
+    def user_defined_constructor(self, x, y, window_width, window_height):
         self.x = x
         self.y = y
-        self.window_height = window_height
         self.window_width = window_width
+        self.window_height = window_height
         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
@@ -477,25 +517,28 @@ class Ball(RuntimeClassBase):
         # state /main/main_behaviour/initializing
         self.states["/main/main_behaviour/initializing"] = State(3, "/main/main_behaviour/initializing", self)
         
+        # state /main/main_behaviour/waiting_for_ui
+        self.states["/main/main_behaviour/waiting_for_ui"] = State(4, "/main/main_behaviour/waiting_for_ui", self)
+        
         # state /main/main_behaviour/bouncing
-        self.states["/main/main_behaviour/bouncing"] = State(4, "/main/main_behaviour/bouncing", self)
+        self.states["/main/main_behaviour/bouncing"] = State(5, "/main/main_behaviour/bouncing", self)
         self.states["/main/main_behaviour/bouncing"].setEnter(self._main_main_behaviour_bouncing_enter)
         self.states["/main/main_behaviour/bouncing"].setExit(self._main_main_behaviour_bouncing_exit)
         
         # state /main/main_behaviour/dragging
-        self.states["/main/main_behaviour/dragging"] = State(5, "/main/main_behaviour/dragging", self)
+        self.states["/main/main_behaviour/dragging"] = State(6, "/main/main_behaviour/dragging", self)
         
         # state /main/main_behaviour/selected
-        self.states["/main/main_behaviour/selected"] = State(6, "/main/main_behaviour/selected", self)
+        self.states["/main/main_behaviour/selected"] = State(7, "/main/main_behaviour/selected", self)
         
         # state /main/size_change_listener
-        self.states["/main/size_change_listener"] = State(7, "/main/size_change_listener", self)
+        self.states["/main/size_change_listener"] = State(8, "/main/size_change_listener", self)
         
         # state /main/size_change_listener/listening
-        self.states["/main/size_change_listener/listening"] = State(8, "/main/size_change_listener/listening", self)
+        self.states["/main/size_change_listener/listening"] = State(9, "/main/size_change_listener/listening", self)
         
         # state /deleted
-        self.states["/deleted"] = State(9, "/deleted", self)
+        self.states["/deleted"] = State(10, "/deleted", self)
         
         # add children
         self.states[""].addChild(self.states["/main"])
@@ -503,6 +546,7 @@ class Ball(RuntimeClassBase):
         self.states["/main"].addChild(self.states["/main/main_behaviour"])
         self.states["/main"].addChild(self.states["/main/size_change_listener"])
         self.states["/main/main_behaviour"].addChild(self.states["/main/main_behaviour/initializing"])
+        self.states["/main/main_behaviour"].addChild(self.states["/main/main_behaviour/waiting_for_ui"])
         self.states["/main/main_behaviour"].addChild(self.states["/main/main_behaviour/bouncing"])
         self.states["/main/main_behaviour"].addChild(self.states["/main/main_behaviour/dragging"])
         self.states["/main/main_behaviour"].addChild(self.states["/main/main_behaviour/selected"])
@@ -513,11 +557,17 @@ class Ball(RuntimeClassBase):
         self.states["/main/size_change_listener"].default_state = self.states["/main/size_change_listener/listening"]
         
         # transition /main/main_behaviour/initializing
-        _main_main_behaviour_initializing_0 = Transition(self, self.states["/main/main_behaviour/initializing"], [self.states["/main/main_behaviour/bouncing"]])
+        _main_main_behaviour_initializing_0 = Transition(self, self.states["/main/main_behaviour/initializing"], [self.states["/main/main_behaviour/waiting_for_ui"]])
         _main_main_behaviour_initializing_0.setAction(self._main_main_behaviour_initializing_0_exec)
         _main_main_behaviour_initializing_0.setTrigger(Event("set_association_name", None))
         self.states["/main/main_behaviour/initializing"].addTransition(_main_main_behaviour_initializing_0)
         
+        # transition /main/main_behaviour/waiting_for_ui
+        _main_main_behaviour_waiting_for_ui_0 = Transition(self, self.states["/main/main_behaviour/waiting_for_ui"], [self.states["/main/main_behaviour/bouncing"]])
+        _main_main_behaviour_waiting_for_ui_0.setAction(self._main_main_behaviour_waiting_for_ui_0_exec)
+        _main_main_behaviour_waiting_for_ui_0.setTrigger(Event("ui_initialized", self.getInPortName("ball_ui_in")))
+        self.states["/main/main_behaviour/waiting_for_ui"].addTransition(_main_main_behaviour_waiting_for_ui_0)
+        
         # transition /main/main_behaviour/bouncing
         _main_main_behaviour_bouncing_0 = Transition(self, self.states["/main/main_behaviour/bouncing"], [self.states["/main/main_behaviour/bouncing"]])
         _main_main_behaviour_bouncing_0.setAction(self._main_main_behaviour_bouncing_0_exec)
@@ -525,26 +575,26 @@ class Ball(RuntimeClassBase):
         self.states["/main/main_behaviour/bouncing"].addTransition(_main_main_behaviour_bouncing_0)
         _main_main_behaviour_bouncing_1 = Transition(self, self.states["/main/main_behaviour/bouncing"], [self.states["/main/main_behaviour/selected"]])
         _main_main_behaviour_bouncing_1.setAction(self._main_main_behaviour_bouncing_1_exec)
-        _main_main_behaviour_bouncing_1.setTrigger(Event("select_ball", "ball_ui_in"))
+        _main_main_behaviour_bouncing_1.setTrigger(Event("select_ball", self.getInPortName("ball_ui_in")))
         self.states["/main/main_behaviour/bouncing"].addTransition(_main_main_behaviour_bouncing_1)
         
         # transition /main/main_behaviour/dragging
         _main_main_behaviour_dragging_0 = Transition(self, self.states["/main/main_behaviour/dragging"], [self.states["/main/main_behaviour/dragging"]])
         _main_main_behaviour_dragging_0.setAction(self._main_main_behaviour_dragging_0_exec)
-        _main_main_behaviour_dragging_0.setTrigger(Event("motion", "ball_ui_in"))
+        _main_main_behaviour_dragging_0.setTrigger(Event("motion", self.getInPortName("ball_ui_in")))
         self.states["/main/main_behaviour/dragging"].addTransition(_main_main_behaviour_dragging_0)
         _main_main_behaviour_dragging_1 = Transition(self, self.states["/main/main_behaviour/dragging"], [self.states["/main/main_behaviour/bouncing"]])
         _main_main_behaviour_dragging_1.setAction(self._main_main_behaviour_dragging_1_exec)
-        _main_main_behaviour_dragging_1.setTrigger(Event("left-release", "input"))
+        _main_main_behaviour_dragging_1.setTrigger(Event("unselect_ball", self.getInPortName("ball_ui_in")))
         self.states["/main/main_behaviour/dragging"].addTransition(_main_main_behaviour_dragging_1)
         
         # transition /main/main_behaviour/selected
         _main_main_behaviour_selected_0 = Transition(self, self.states["/main/main_behaviour/selected"], [self.states["/main/main_behaviour/dragging"]])
-        _main_main_behaviour_selected_0.setTrigger(Event("clicked", "ball_ui_in"))
+        _main_main_behaviour_selected_0.setTrigger(Event("select_ball", self.getInPortName("ball_ui_in")))
         self.states["/main/main_behaviour/selected"].addTransition(_main_main_behaviour_selected_0)
         _main_main_behaviour_selected_1 = Transition(self, self.states["/main/main_behaviour/selected"], [self.states["/main/main_behaviour/selected"]])
         _main_main_behaviour_selected_1.setAction(self._main_main_behaviour_selected_1_exec)
-        _main_main_behaviour_selected_1.setTrigger(Event("delete", "ball_ui_in"))
+        _main_main_behaviour_selected_1.setTrigger(Event("delete", None))
         self.states["/main/main_behaviour/selected"].addTransition(_main_main_behaviour_selected_1)
         
         # transition /main/size_change_listener/listening
@@ -568,17 +618,20 @@ class Ball(RuntimeClassBase):
         association_name = parameters[0]
         self.association_name = association_name
     
+    def _main_main_behaviour_waiting_for_ui_0_exec(self, parameters):
+        self.big_step.outputEvent(Event("set_initial_params", self.getOutPortName("ball_ui_out"), [self.x, self.y, self.r]))
+    
     def _main_main_behaviour_bouncing_0_exec(self, parameters):
-        if x <= 0 or x + (self.r * 2) >= self.window_width:
+        if self.x <= 0 or self.x + (self.r * 2) >= self.window_width:
             self.vel['x'] = -self.vel['x']
-        if y <= 0 or y + (self.r * 2) >= self.window_height:
+        if self.y <= 0 or self.y + (self.r * 2) >= self.window_height:
             self.vel['y'] = -self.vel['y']
         self.x += self.vel['x']
         self.y += self.vel['y']
-        self.big_step.outputEvent(Event("change_position", "ball_ui_out", [self.x, self.y]))
+        self.big_step.outputEvent(Event("change_position", self.getOutPortName("ball_ui_out"), [self.x, self.y]))
     
     def _main_main_behaviour_bouncing_1_exec(self, parameters):
-        self.big_step.outputEvent(Event("change_color", "ball_ui_out", [yellow]))
+        self.big_step.outputEvent(Event("change_color", self.getOutPortName("ball_ui_out"), ['yellow']))
     
     def _main_main_behaviour_dragging_0_exec(self, parameters):
         dx = parameters[0]
@@ -597,21 +650,20 @@ class Ball(RuntimeClassBase):
             'x': (1 - self.smooth) * dx + self.smooth * self.vel['x'],
             'y': (1 - self.smooth) * dy + self.smooth * self.vel['y']
         }
-        self.big_step.outputEvent(Event("change_position", "ball_ui_out", [self.x, self.y]))
+        self.big_step.outputEvent(Event("change_position", self.getOutPortName("ball_ui_out"), [self.x, self.y]))
     
     def _main_main_behaviour_dragging_1_exec(self, parameters):
-        tagorid = parameters[0]
-        self.canvas.itemconfig(self.id, fill="red")
+        self.big_step.outputEvent(Event("change_color", self.getOutPortName("ball_ui_out"), ['red']))
     
     def _main_main_behaviour_selected_1_exec(self, parameters):
         self.big_step.outputEventOM(Event("narrow_cast", None, [self, 'parent', Event("delete_ball", None, [self.association_name])]))
         self.raiseInternalEvent(Event("delete_local", None, []))
     
     def _main_size_change_listener_listening_0_exec(self, parameters):
-        new_height = parameters[0]
-        new_width = parameters[1]
-        self.window_height = new_height
+        new_width = parameters[0]
+        new_height = parameters[1]
         self.window_width = new_width
+        self.window_height = new_height
     
     def initializeStatechart(self):
         # enter default state
@@ -628,7 +680,7 @@ class ObjectManager(ObjectManagerBase):
             instance.associations = {}
             instance.associations["windows"] = Association("Window", 0, -1)
         elif class_name == "Window":
-            instance = Window(self.controller, construct_params[0], construct_params[1])
+            instance = Window(self.controller)
             instance.associations = {}
             instance.associations["balls"] = Association("Ball", 0, -1)
             instance.associations["buttons"] = Association("Button", 0, -1)

+ 70 - 44
examples/bouncingballs/python/sccd_no_ui.xml

@@ -3,6 +3,9 @@
     <description>
         Bouncing Balls - no UI code.
     </description>
+    <top>
+        from ui_classes import *
+    </top>
     <inport name="ui_in"/>
     <outport name="ui_out"/>
     <class name="MainApp" default="true">
@@ -13,8 +16,6 @@
             <body>
                 <![CDATA[
                 self.nr_of_windows = 0
-                self.WINDOW_SIZE_W = 800
-                self.WINDOW_SIZE_H = 600
                 ]]>
             </body>
         </constructor>
@@ -49,14 +50,16 @@
                             <raise event="create_instance" scope="cd">
                                 <parameter expr="'windows'" />
                                 <parameter expr="'Window'" />
-                                <parameter expr="self.WINDOW_SIZE_H" />
-                                <parameter expr="self.WINDOW_SIZE_W" />
                             </raise>
                         </transition>
                     </state>
                     <state id="creating">
                         <transition event="instance_created" target="../waiting">
                             <parameter name="association_name" />
+                            <raise event="create_new_window" port="ui_out">
+                                <parameter expr="association_name" />
+                                <parameter expr="self.getSingleChild(association_name)" />
+                            </raise>
                             <raise event="start_instance" scope="cd">
                                 <parameter expr="association_name" />
                             </raise>
@@ -71,6 +74,9 @@
                     <state id="waiting">
                         <transition event="delete_window" target="../deleting">
                             <parameter name="association_name" />
+                            <raise event="delete_window" port="ui_out">
+                                <parameter expr="association_name" />
+                            </raise>
                             <raise event="delete_instance" scope="cd">
                                 <parameter expr="association_name" />
                             </raise>
@@ -100,23 +106,25 @@
             <association name="buttons" class="Button" />
             <association name="parent" class="MainApp" min="1" max="1" />
         </relationships>
-        <constructor>
-            <parameter name="height" />
-            <parameter name="width" />
-            <body>
-                self.height = height
-                self.width = width
-            </body>
-        </constructor>
         <scxml initial="main">
             <parallel id="main">
                 <state id="main_behaviour" initial="initializing">
                     <state id="initializing">
-                        <transition event="set_association_name" target="../creating_button">
+                        <transition event="set_association_name" target="../waiting_for_ui">
                             <parameter name="association_name" />
                             <script>
                                 self.association_name = association_name
                             </script>
+                        </transition>
+                    </state>
+                    <state id="waiting_for_ui">
+                        <transition event="ui_initialized" target="../creating_button" port="window_ui_in">                      
+                            <parameter name="width" />
+                            <parameter name="height" />
+                            <script>
+                                self.width = width
+                                self.height = height
+                            </script>
                             <raise scope="cd" event="create_instance">
                                 <parameter expr='"buttons"' />
                                 <parameter expr='"Button"' />
@@ -128,8 +136,8 @@
                         <transition event="instance_created" target="../running">
                             <parameter name="association_name" type="string"/>
                             <raise event="create_new_button" port="window_ui_out">
-                                <parameter expr="self.association_name" />
                                 <parameter expr="association_name" />
+                                <parameter expr="self.getSingleChild(association_name)" />
                             </raise>
                             <raise scope="cd" event="start_instance">
                                 <parameter expr="association_name" />
@@ -150,6 +158,10 @@
                             <raise scope="cd" event="create_instance">
                                 <parameter expr='"balls"' />
                                 <parameter expr='"Ball"' />
+                                <parameter expr="x" />
+                                <parameter expr="y" />
+                                <parameter expr="self.width" />
+                                <parameter expr="self.height" />
                             </raise>
                         </transition>
                         <transition event="delete_ball" target=".">
@@ -158,7 +170,6 @@
                                 <parameter expr="association_name" />
                             </raise>
                             <raise event="delete_ball" port="window_ui_out">
-                                <parameter expr="self.association_name" />
                                 <parameter expr="association_name" />
                             </raise>
                         </transition>
@@ -167,8 +178,8 @@
                         <transition event="instance_created" target="../running">
                             <parameter name="association_name" type="string"/>
                             <raise event="create_new_ball" port="window_ui_out">
-                                <parameter expr="self.association_name" />
                                 <parameter expr="association_name" />
+                                <parameter expr="self.getSingleChild(association_name)" />
                             </raise>
                             <raise event="start_instance" scope="cd">
                                 <parameter expr="association_name" />
@@ -182,19 +193,26 @@
                 <state id="size_change_listener" initial="listening">
                     <state id="listening">
                         <transition target="." event="size_changed" port="window_ui_in">
-                            <parameter name="new_height" />
                             <parameter name="new_width" />
+                            <parameter name="new_height" />
                             <script>
-                                self.height = height
-                                self.width = width
+                                self.width = new_width
+                                self.height = new_height
                             </script>
-                            <raise event="window_size_changed" scope="narrow" target="balls">
-                                <parameter expr="new_height" />
+                            <raise event="window_size_changed" scope="narrow" target="'balls'">
                                 <parameter expr="new_width" />
+                                <parameter expr="new_height" />
                             </raise>
                         </transition>
                     </state>
                 </state>
+                <state id="delete_listener" initial="listening">
+                    <state id="listening">
+                        <transition event="delete" target="." port="window_ui_in">
+                            <raise event="delete" target="'balls'" />
+                        </transition>
+                    </state>
+                </state>
                 <transition event="stop" target="../stopped">
                     <raise event="delete_instance" scope="cd">
                         <parameter expr="'buttons'" />
@@ -215,6 +233,7 @@
     </class>
     <class name="Button">
         <inport name="button_ui_in" />
+        <outport name="button_ui_out" />
         <relationships>
             <association name="parent" class="Field" min="1" max="1" />
         </relationships>
@@ -224,14 +243,13 @@
                 self.event_name = event_name
             </body>
         </constructor>
-        <scxml initial="initializing">
-            <state id="initializing">
-                <onentry>                
-                    <raise event="button_created" scope="narrow" target="'parent'">
-                        <parameter expr="self" />
+        <scxml initial="waiting">
+            <state id="waiting">
+                <transition event="ui_initialized" target="../running" port="button_ui_in">
+                    <raise event="set_text" port="button_ui_out">
+                        <parameter expr="self.event_name" />
                     </raise>
-                </onentry>
-                <transition target="../running" />
+                </transition>
             </state>
             <state id="running">
                 <transition event="clicked" port="button_ui_in" target=".">
@@ -244,20 +262,20 @@
     </class>
     <class name="Ball">
         <inport name="ball_ui_in" />
-        <inport name="ball_ui_out" />
+        <outport name="ball_ui_out" />
         <relationships>
             <association name="parent" class="Window" min="1" max="1" />
         </relationships>
         <constructor>
             <parameter name="x" />
             <parameter name="y" />
-            <parameter name="window_height" />
             <parameter name="window_width" />
+            <parameter name="window_height" />
             <body>
                 self.x = x
                 self.y = y
-                self.window_height = window_height
                 self.window_width = window_width
+                self.window_height = window_height
                 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
@@ -267,20 +285,29 @@
             <parallel id="main">
                 <state id="main_behaviour" initial="initializing">
                     <state id="initializing">
-                        <transition event="set_association_name" target="../bouncing">
+                        <transition event="set_association_name" target="../waiting_for_ui">
                             <parameter name="association_name" type="str" />
                             <script>
                                 self.association_name = association_name
                             </script>
                         </transition>
                     </state>
+                    <state id="waiting_for_ui">
+                        <transition event="ui_initialized" target="../bouncing" port="ball_ui_in">                      
+                            <raise event="set_initial_params" port="ball_ui_out">
+                                <parameter expr="self.x" />
+                                <parameter expr="self.y" />
+                                <parameter expr="self.r" />
+                            </raise>
+                        </transition>
+                    </state>
                     <state id="bouncing">
                         <transition after="(20 - self.getSimulatedTime() % 20) / 1000.0" target=".">
                             <script>
                             <![CDATA[
-                                if x <= 0 or x + (self.r * 2) >= self.window_width:
+                                if self.x <= 0 or self.x + (self.r * 2) >= self.window_width:
                                     self.vel['x'] = -self.vel['x']
-                                if y <= 0 or y + (self.r * 2) >= self.window_height:
+                                if self.y <= 0 or self.y + (self.r * 2) >= self.window_height:
                                     self.vel['y'] = -self.vel['y']
                                 self.x += self.vel['x']
                                 self.y += self.vel['y']
@@ -293,7 +320,7 @@
                         </transition>
                         <transition port="ball_ui_in" event="select_ball" target="../selected">
                             <raise event="change_color" port="ball_ui_out">
-                                <parameter expr="yellow" />
+                                <parameter expr="'yellow'" />
                             </raise>
                         </transition>
                     </state>
@@ -324,16 +351,15 @@
                                 <parameter expr="self.y" />
                             </raise>
                         </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 port="ball_ui_in" event="unselect_ball" target="../bouncing">
+                            <raise event="change_color" port="ball_ui_out">
+                                <parameter expr="'red'" />
+                            </raise>
                         </transition>
                     </state>
                     <state id="selected">
-                        <transition port="ball_ui_in" event="clicked" target="../dragging" />
-                        <transition port="ball_ui_in" event="delete" target=".">
+                        <transition port="ball_ui_in" event="select_ball" target="../dragging" />
+                        <transition event="delete" target=".">
                             <raise event="delete_ball" scope="narrow" target="'parent'">
                                 <parameter expr="self.association_name" />
                             </raise>
@@ -344,11 +370,11 @@
                 <state id="size_change_listener" initial="listening">
                     <state id="listening">
                         <transition target="." event="window_size_changed">
-                            <parameter name="new_height" />
                             <parameter name="new_width" />
+                            <parameter name="new_height" />
                             <script>
-                                self.window_height = new_height
                                 self.window_width = new_width
+                                self.window_height = new_height
                             </script>
                         </transition>
                     </state>

+ 210 - 0
examples/bouncingballs/python/ui_classes.py

@@ -0,0 +1,210 @@
+from widget_private_ports import Widget
+from sccd.runtime.statecharts_core import Event
+import Tkinter as tk
+import random
+
+class WindowVis(tk.Toplevel, Widget):
+    def __init__(self, sccd_object):
+        tk.Toplevel.__init__(self)
+        self.sccd_object = sccd_object
+        self.inport = self.sccd_object.inports["window_ui_in"]
+        self.outport = self.sccd_object.outports["window_ui_out"]
+        Widget.__init__(self, True, self.inport, self.outport)
+
+        self.buttons = {}
+        self.balls = {}
+
+        self.title('BouncingBalls')
+        self.c = tk.Canvas(self, relief=tk.RIDGE)
+        self.repack()
+        self.set_bindable_and_tagorid(self.c)
+        self.c.bind("<Configure>", self.window_resize_handler)
+
+        self.listener = self.sccd_object.controller.addOutputListener(self.outport)
+        self.after(40, self.handle_output_events)
+        Widget.controller.addInput(Event("ui_initialized", self.inport, [self.winfo_width(), self.winfo_height()]))
+
+    def destruct(self):
+        self.destroy()
+
+    def repack(self):
+        self.c.pack_forget()
+        for b in self.buttons.itervalues():
+            b.pack_forget()
+            b.pack(expand=False, fill=tk.X, side=tk.TOP)
+        self.c.focus_force()
+        self.c.pack(expand=True, fill=tk.BOTH)
+        self.update()
+
+    def window_resize_handler(self, event):
+        Widget.controller.addInput(Event("size_changed", self.inport, [event.width, event.height]))
+
+    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 == "right-click":
+            self.last_x = event.x
+            self.last_y = event.y
+            Widget.controller.addInput(Event("create_ball", self.inport, [self.last_x, self.last_y]))
+
+    def handle_output_events(self):
+        while True:
+            output_event = self.listener.fetch(0)
+            if not output_event is None:
+                if output_event.getName() == "create_new_button":
+                    self.on_create_new_button(output_event)
+                elif output_event.getName() == "delete_ball":
+                    self.on_delete_ball(output_event)
+                elif output_event.getName() == "create_new_ball":
+                    self.on_create_new_ball(output_event)
+                elif output_event.getName() == "resize_window":
+                    self.on_resize_window(output_event)
+            else:
+                break
+        for b in self.buttons.itervalues():
+            b.handle_output_events()
+        for b in self.balls.itervalues():
+            b.handle_output_events()
+
+        self.after(40, self.handle_output_events)
+
+    def on_create_new_button(self, event):
+        assoc_name = event.getParameters()[0]
+        sccd_object = event.getParameters()[1]
+        self.buttons[assoc_name] = ButtonVis(sccd_object, self)
+        self.repack()
+
+    def on_delete_ball(self, event):
+        self.balls[event.getParameters()[0]].destruct()
+        del self.balls[event.getParameters()[0]]
+
+    def on_create_new_ball(self, event):
+        assoc_name = event.getParameters()[0]
+        sccd_object = event.getParameters()[1]
+        self.balls[assoc_name] = BallVis(sccd_object, self.c)
+
+    def on_resize_window(self, event):
+        #self.geometry("%sx%s" % tuple(event.getParameters()))
+        pass
+
+class ButtonVis(tk.Button, Widget):
+    def __init__(self, sccd_object, window):
+        tk.Button.__init__(self, window)
+        self.sccd_object = sccd_object
+        self.inport = self.sccd_object.inports["button_ui_in"]
+        self.outport = self.sccd_object.outports["button_ui_out"]
+        Widget.__init__(self, False, self.inport, self.outport)
+
+        self.listener = self.sccd_object.controller.addOutputListener(self.outport)
+
+        Widget.controller.addInput(Event("ui_initialized", self.inport))
+
+    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 == "left-click":
+            Widget.controller.addInput(Event("clicked", self.inport))
+
+    def handle_output_events(self):
+        while True:
+            output_event = self.listener.fetch(0)
+            if not output_event is None:
+                if output_event.getName() == "set_text":
+                    self.on_set_text(output_event)
+            else:
+                break
+
+    def on_set_text(self, event):
+        self.config(text=event.getParameters()[0])
+
+    def mymethod(self):
+        pass
+
+class BallVis(Widget):
+    def __init__(self, sccd_object, canvas):
+        self.sccd_object = sccd_object
+        self.inport = self.sccd_object.inports["ball_ui_in"]
+        self.outport = self.sccd_object.outports["ball_ui_out"]
+        Widget.__init__(self, True, self.inport, self.outport)
+        self.canvas = canvas
+
+        self.listener = self.sccd_object.controller.addOutputListener(self.outport)
+
+        Widget.controller.addInput(Event("ui_initialized", self.inport))
+
+    def destruct(self):
+        self.canvas.delete(self.id)
+
+    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 == "left-click":
+            self.last_x = event.x
+            self.last_y = event.y
+            Widget.controller.addInput(Event("select_ball", self.inport))
+
+    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 == "left-release":
+            self.last_x = event.x
+            self.last_y = event.y
+            Widget.controller.addInput(Event("unselect_ball", self.inport))
+
+    def on_motion(self, event):
+        Widget.controller.addInput(Event("motion", self.inport, [self.canvas.canvasx(event.x) - self.canvas.canvasx(self.last_x), self.canvas.canvasy(event.y) - self.canvas.canvasy(self.last_y)]))
+        self.last_x = event.x
+        self.last_y = event.y
+
+    def handle_output_events(self):
+        while True:
+            output_event = self.listener.fetch(0)
+            if not output_event is None:
+                if output_event.getName() == "set_initial_params":
+                    self.on_set_initial_params(output_event)
+                if output_event.getName() == "change_position":
+                    self.on_change_position(output_event)
+                if output_event.getName() == "change_color":
+                    self.on_change_color(output_event)
+            else:
+                break
+
+    def on_set_initial_params(self, event):
+        self.x, self.y, self.r = event.getParameters()
+        self.id = self.canvas.create_oval(self.x, self.y, self.x + (self.r * 2), self.y + (self.r * 2), fill="black")
+        self.set_bindable_and_tagorid(self.canvas, self.id)
+
+    def on_change_position(self, event):
+        self.x, self.y = event.getParameters()
+        self.canvas.coords(self.id, self.x, self.y, self.x + (self.r * 2), self.y + (self.r * 2))
+
+    def on_change_color(self, event):
+        self.canvas.itemconfig(self.id, fill=event.getParameters()[0])

+ 125 - 0
examples/bouncingballs/python/widget_private_ports.py

@@ -0,0 +1,125 @@
+'''
+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, inport="input", outport="output"):
+		if not configure_later:
+			self.set_bindable_and_tagorid(None, None)
+		self.inport = inport
+		self.outport = outport
+
+	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, self.inport))
+
+	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, self.inport))
+
+	def on_motion(self, event):
+		self.last_x = event.x
+		self.last_y = event.y
+		Widget.controller.addInput(Event("motion", self.inport))
+
+	def on_enter(self, event):
+		Widget.controller.addInput(Event("enter", self.inport))
+
+	def on_leave(self, event):
+		Widget.controller.addInput(Event("leave", self.inport))
+
+	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, self.inport))
+
+	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, self.inport))
+
+	def window_close(self):
+		event = Event("close_window", self.inport)
+		Widget.controller.addInput(event)

+ 27 - 7
src/python_sccd/python_sccd_compiler/generic_generator.py

@@ -284,6 +284,18 @@ class GenericGenerator(Visitor):
             self.writer.endSuperClassConstructorCall()
 
             self.writer.addVSpace()
+            
+            for p in constructor.parent_class.inports:
+                self.writer.addAssignment(
+                    GLC.MapIndexedExpression(GLC.SelfProperty("inports"), GLC.String(p)),
+                    GLC.FunctionCall(GLC.Property("controller", "addInputPort"), [GLC.String(p), GLC.SelfExpression()]))
+
+            for p in constructor.parent_class.outports:
+                self.writer.addAssignment(
+                    GLC.MapIndexedExpression(GLC.SelfProperty("outports"), GLC.String(p)),
+                    GLC.FunctionCall(GLC.Property("controller", "addOutputPort"), [GLC.String(p), GLC.SelfExpression()]))
+
+            self.writer.addVSpace()
 
             if constructor.parent_class.statechart.big_step_maximality == "take_one":
                 self.writer.addAssignment(GLC.Property(GLC.SelfProperty("semantics"), "big_step_maximality"), GLC.Property("StatechartSemantics", "TakeOne"))
@@ -319,11 +331,6 @@ class GenericGenerator(Visitor):
             self.writer.addComment("build Statechart structure")
             self.writer.add(GLC.FunctionCall(GLC.SelfProperty("build_statechart_structure"), []))
 
-        for p in constructor.parent_class.inports:
-            self.writer.addAssignment(
-                GLC.MapIndexedExpression(GLC.SelfProperty("inports"), GLC.String(p)),
-                GLC.FunctionCall(GLC.Property("controller", "addInputPort"), [GLC.String(p), GLC.SelfExpression()]))
-
         if constructor.parent_class.attributes:
             self.writer.addVSpace()
             self.writer.addComment("user defined attributes")
@@ -555,7 +562,15 @@ class GenericGenerator(Visitor):
                 if t.trigger.is_after:
                     trigger = GLC.NewExpression("Event", [GLC.String("_%iafter" % (t.trigger.getAfterIndex()))])
                 elif t.trigger.event:
-                    trigger = GLC.NewExpression("Event", [GLC.String(t.trigger.event), GLC.NoneExpression() if t.trigger.port is None else GLC.String(t.trigger.port)])
+                    trigger = GLC.NewExpression("Event",
+                                                    [
+                                                        GLC.String(t.trigger.event),
+                                                        GLC.NoneExpression() if t.trigger.port is None else GLC.FunctionCall(
+                                                                                                                GLC.SelfProperty("getInPortName"),
+                                                                                                                [GLC.String(t.trigger.port)]
+                                                                                                            )
+                                                    ]
+                                                )
                 else:
                     trigger = GLC.NoneExpression()
                 if trigger:
@@ -795,7 +810,12 @@ class GenericGenerator(Visitor):
 
         self.writer.addActualParameter(GLC.String(raise_event.getEventName()))
         if raise_event.isOutput():
-            self.writer.addActualParameter(GLC.String(raise_event.getPort()))
+            self.writer.addActualParameter(
+                                            GLC.FunctionCall(
+                                                                GLC.SelfProperty("getOutPortName"),
+                                                                [GLC.String(raise_event.getPort())]
+                                                            )
+                                            )
         else:
             self.writer.addActualParameter(GLC.NoneExpression())
 

+ 2 - 2
src/python_sccd/python_sccd_runtime/libs/ui.py

@@ -13,8 +13,8 @@ try:
 except ImportError:
     import tkinter as tk
 
-from src.python_sccd.python_sccd_runtime.libs.drawing import drawing
-from src.python_sccd.python_sccd_runtime.libs.utils import utils
+from sccd.runtime.libs.drawing import drawing
+from sccd.runtime.libs.utils import utils
 
 from sccd.runtime.statecharts_core import Event
 

+ 32 - 5
src/python_sccd/python_sccd_runtime/statecharts_core.py

@@ -433,7 +433,7 @@ class OutputListener(object):
     def __init__(self, port_names):
         if not isinstance(port_names, list):
             port_names = [port_names]
-        self.port_names = port_names
+        self.port_names = [port_name.port_name if isinstance(port_name, OutputPortEntry) else port_name for port_name in port_names]
         self.queue = Queue()
 
     def add(self, event):
@@ -459,6 +459,12 @@ class InputPortEntry(object):
     def __init__(self, virtual_name, instance):
         self.virtual_name = virtual_name
         self.instance = instance
+
+class OutputPortEntry(object):
+    def __init__(self, port_name, virtual_name, instance):
+        self.port_name = port_name
+        self.virtual_name = virtual_name
+        self.instance = instance
         
 class ControllerBase(object):
     def __init__(self, object_manager):
@@ -472,7 +478,7 @@ class ControllerBase(object):
         self.input_queue = EventQueue()
 
         # keep track of output ports
-        self.output_ports = []
+        self.output_ports = {}
         self.output_listeners = []
         
         self.simulated_time = None
@@ -496,8 +502,14 @@ class ControllerBase(object):
         self.input_ports[port_name] = InputPortEntry(virtual_name, instance)
         return port_name
         
-    def addOutputPort(self, port_name):
-        self.output_ports.append(port_name)
+    def addOutputPort(self, virtual_name, instance = None):
+        if instance == None:
+            port_name = virtual_name
+        else:
+            port_name = "private_" + str(self.private_port_counter) + "_" + virtual_name
+            self.private_port_counter += 1
+        self.output_ports[port_name] = OutputPortEntry(port_name, virtual_name, instance)
+        return port_name
 
     def broadcast(self, new_event, time_offset = 0):
         self.object_manager.broadcast(None, new_event, time_offset)
@@ -535,7 +547,7 @@ class ControllerBase(object):
             event_time = self.input_queue.getEarliestTime()
             e = self.input_queue.pop()
             input_port = self.input_ports[e.getPort()]
-            e.port = input_port.virtual_name
+            # e.port = input_port.virtual_name
             target_instance = input_port.instance
             if target_instance == None:
                 self.broadcast(e, event_time - self.simulated_time)
@@ -621,6 +633,7 @@ class EventLoopControllerBase(ControllerBase):
         self.main_thread = thread.get_ident()
 
     def addInput(self, input_event, time_offset = 0, force_internal=False):
+        # import pdb; pdb.set_trace()
         if self.main_thread == thread.get_ident():
             # Running on the main thread, so just execute what we want
             self.simulated_time = self.accurate_time.get_wct()
@@ -1000,6 +1013,7 @@ class RuntimeClassBase(object):
         self.controller = controller
         self.__set_stable(True)
         self.inports = {}
+        self.outports = {}
         self.timers = {}
         self.states = {}
         self.eventless_states = 0
@@ -1016,6 +1030,19 @@ class RuntimeClassBase(object):
     def __lt__(self, other):
         return len(self.events.event_list) < len(other.events.event_list)
 
+    def getChildren(self, link_name):
+        traversal_list = self.controller.object_manager.processAssociationReference(link_name)
+        return [i["instance"] for i in self.controller.object_manager.getInstances(self, traversal_list)]
+
+    def getSingleChild(self, link_name):
+        return self.getChildren(link_name)[0] # assume this will return a single child...
+
+    def getOutPortName(self, port_name):
+        return self.outports[port_name] if port_name in self.outports else port_name
+
+    def getInPortName(self, port_name):
+        return self.inports[port_name] if port_name in self.inports else port_name            
+
     def start(self):
         self.configuration = []