bouncing_tkinter.sccd 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. Diagram(name = 'BouncingBalls',
  2. author = 'Simon Van Mierlo',
  3. description = 'Tkinter frame with bouncing balls in it.'):
  4. Top {
  5. import time
  6. import random
  7. import Tkinter as tk
  8. from mvk_widget import MvKWidget
  9. }
  10. Inport(name = 'input')
  11. Class(name = 'MainApp', default = True):
  12. Association(name = 'fields', class = 'Field')
  13. #Inheritance(class = 'RuntimeClassBase', priority = 1)
  14. Inheritance(class = 'tk.Tk', priority = 0)
  15. Constructor {
  16. tk.Tk->__init__(self)
  17. self.fixed_update_time = 20
  18. self->update_self()
  19. self->withdraw()
  20. self.nr_of_fields = 0
  21. }
  22. Method update_self() {
  23. self.controller->update(self.fixed_update_time / 1000.0)
  24. self.schedule_time = time->time()
  25. self.scheduled_update_id = self->after(self.fixed_update_time, self.update_self)
  26. }
  27. StateMachine:
  28. initial = 'running'
  29. State('running'):
  30. initial = 'root'
  31. Orthogonal('root'):
  32. State('main_behaviour'):
  33. initial = 'initializing'
  34. State('initializing'):
  35. Transition(target='../running'):
  36. raise += { create_field() }
  37. State('running'):
  38. Transition(target='.'):
  39. event = button_pressed(event_name: String)
  40. guard = {event_name == 'create_new_field'}
  41. raise += { create_field() }
  42. State('cd_behaviour'):
  43. initial = 'waiting'
  44. State('waiting'):
  45. Transition(target='../creating'):
  46. event = create_field()
  47. raise += { scope=cd, create_field('fields') }
  48. Transition(target='../check_nr_of_fields'):
  49. event = delete_field(association_name: String)
  50. raise += { scope=cd, delete_instance(association_name) }
  51. Actions {
  52. self.nr_of_fields -= 1
  53. }
  54. State('creating'):
  55. Transition(target='../waiting'):
  56. event = instance_created(association_name: String)
  57. raise += { scope=cd, start_instance(association_name) }
  58. raise += { scope=narrow, target=association_name, set_association_name(association_name) }
  59. Actions {
  60. self.nr_of_fields += 1
  61. }
  62. State('check_nr_of_fields'):
  63. Transition(target='../../../stopped'):
  64. guard = {self.nr_of_fields == 0}
  65. Actions {
  66. self->destroy()
  67. }
  68. Transition(target='../waiting'):
  69. guard = {self.nr_of_fields != 0}
  70. State('stopped')
  71. Class(name = 'Field'):
  72. Association(name = 'balls', class = 'Ball')
  73. Association(name = 'buttons', class = 'Button')
  74. Association(name = 'parent', class = 'MainApp', min = 1, max = 1)
  75. #Inheritance(class = 'RuntimeClassBase', priority = 1)
  76. Inheritance(class = 'tk.Toplevel', priority = 0)
  77. Inheritance(class = 'MvKWidget', priority = -1)
  78. Constructor {
  79. tk.Toplevel->__init__(self)
  80. self->title('BouncingBalls')
  81. CANVAS_SIZE_TUPLE = (0, 0, self->winfo_screenwidth() * 2, self->winfo_screenheight() * 2)
  82. self.c = tk->Canvas(self, relief=tk.RIDGE, scrollregion=CANVAS_SIZE_TUPLE)
  83. MvKWidget->__init__(self, self.controller, self.c)
  84. }
  85. Destructor {
  86. self->destroy()
  87. }
  88. StateMachine:
  89. initial = 'root'
  90. State('root'):
  91. initial = 'waiting'
  92. State('waiting'):
  93. Transition(target='../initializing'):
  94. event = set_association_name(association_name: String)
  95. Actions {
  96. self.association_name = association_name
  97. }
  98. State('initializing'):
  99. Transition(target='../creating'):
  100. raise += { scope=cd, create_instance('buttons', self, 'create_new_field', 'Spawn New Window') }
  101. State('creating'):
  102. Transition(target='../packing'):
  103. event = instance_created(association_name: String)
  104. raise += { scope=cd, start_instance(association_name) }
  105. State('packing'):
  106. Transition(target='../running'):
  107. event = button_created(button: Button)
  108. Actions {
  109. button->pack(expand=False, fill=tk.X, side=tk.TOP)
  110. self.c->focus_force()
  111. self.c->pack(expand=True, fill=tk.BOTH)
  112. }
  113. Orthogonal('running'):
  114. Transition(port = 'input', target='../deleting'):
  115. event = window-close(tagorid: Integer='None')
  116. raise += { scope=narrow, target='balls',delete_self() }
  117. State('main_behaviour'):
  118. initial = 'running'
  119. State('running'):
  120. Transition(port = 'input', target='../creating'):
  121. event = right-click(tagorid: Integer='None')
  122. raise += { scope=cd, create_instance('balls', self.c, self.last_x, self.last_y) }
  123. State('creating'):
  124. Transition(target='../running'):
  125. event = instance_created(association_name: String)
  126. raise += { scope=cd, start_instance(association_name) }
  127. raise += { scope=narrow, target=association_name, set_association_name(association_name) }
  128. State('deleting_behaviour'):
  129. initial = 'running'
  130. State('running'):
  131. Transition(target='.'):
  132. event = delete_ball(association_name: String)
  133. raise += { scope=cd, delete_instance(association_name) }
  134. State('child_behaviour'):
  135. initial = 'listening'
  136. State('listening'):
  137. Transition(target='.'):
  138. event = button_pressed(event_name: String)
  139. raise += { scope=narrow, target='parent', button_pressed(event_name) }
  140. State('deleting'):
  141. Transition(after=0.05, target='../deleted'):
  142. raise += { scope=narrow, target='parent', delete_field(self.association_name) }
  143. State('deleted')
  144. Class(name = 'Button'):
  145. Association(name = 'parent', class = 'Field', min = 1, max = 1)
  146. #Inheritance(class = 'RuntimeClassBase', priority = 1)
  147. Inheritance(class = 'MvKWidget', priority = 0)
  148. Inheritance(class = 'tk.Button', priority = -1)
  149. Constructor(parent: Field, event_name: String, button_text: String) {
  150. tk.Button->__init__(self, parent, text=button_text)
  151. MvKWidget->__init__(self, self.controller)
  152. self.event_name = event_name
  153. }
  154. StateMachine:
  155. initial = 'initializing'
  156. State('initializing'):
  157. Transition(target='../running'):
  158. raise += { scope=narrow, target='parent', button_created(self) }
  159. State('running'):
  160. Transition(port = 'input', target='.'):
  161. event = left-click(tagorid: Integer='None')
  162. guard = {tagorid == id(self)}
  163. raise += { scope=narrow, target='parent', button_pressed(self.event_name) }
  164. Class(name = 'Ball'):
  165. Association(name = 'parent', class = 'Field', min = 1, max = 1)
  166. #Inheritance(class = 'RuntimeClassBase', priority = 1)
  167. Inheritance(class = 'MvKWidget', priority = 0)
  168. Attribute(name = 'canvas')
  169. Constructor(canvas, x, y) {
  170. self.canvas = canvas
  171. self.r = 15.0
  172. self.smooth = 0.4 # value between 0 and 1
  173. self.vel = ['x': random->random() * 2.0 - 1.0, 'y': random->random() * 2.0 - 1.0]
  174. self.id = self.canvas->create_oval(x, y, x + (self.r * 2), y + (self.r * 2), fill="black")
  175. MvKWidget->__init__(self, self.controller, self.canvas, self.id)
  176. }
  177. Destructor {
  178. self.canvas->delete(self.id)
  179. }
  180. StateMachine:
  181. initial = 'main_behaviour'
  182. State('main_behaviour'):
  183. initial = 'initializing'
  184. State('initializing'):
  185. Transition(target='../bouncing'):
  186. event = set_association_name(association_name: String)
  187. Actions {
  188. self.association_name = association_name
  189. }
  190. State('bouncing'):
  191. Transition(after=0.01, target='.'):
  192. Actions {
  193. pos = self.canvas->coords(self.id)
  194. x = self.canvas->canvasx(pos[0])
  195. y = self.canvas->canvasy(pos[1])
  196. if x <= 0 or x + (self.r * 2) >= self.canvas->canvasx(self.canvas->winfo_width()):
  197. self.vel['x'] = -self.vel['x']
  198. end
  199. if y <= 0 or y + (self.r * 2) >= self.canvas->canvasy(self.canvas->winfo_height()):
  200. self.vel['y'] = -self.vel['y']
  201. end
  202. self.canvas->move(self.id, self.vel['x'], self.vel['y'])
  203. }
  204. Transition(port = 'input', target='../selected'):
  205. event = left-click(tagorid: Integer='None')
  206. guard = {tagorid == id(self)}
  207. Actions {
  208. self.canvas->itemconfig(self.id, fill="yellow")
  209. }
  210. State('dragging'):
  211. Transition(port = 'input', target='.'):
  212. event = motion(tagorid: Integer='None')
  213. Actions {
  214. coords = self.canvas->coords(self.id)
  215. dx = self.canvas->canvasx(self.last_x) - self.canvas->canvasx(coords[0])
  216. dy = self.canvas->canvasx(self.last_y) - self.canvas->canvasy(coords[1])
  217. self.canvas->move(self.id, dx, dy)
  218. coords = self.canvas->coords(self.id) # keep ball within boundaries
  219. x = self.canvas->canvasx(coords[0])
  220. y = self.canvas->canvasy(coords[1])
  221. if x - self.r <= 0:
  222. x = 1
  223. else:
  224. if x + self.r >= self.canvas->winfo_width():
  225. x = self.canvas->winfo_width() - (2 * self.r) - 1
  226. end
  227. end
  228. if y - self.r <= 0:
  229. y = 1
  230. else:
  231. if y + self.r >= self.canvas->winfo_height():
  232. y = self.canvas->winfo_height() - (2 * self.r) - 1
  233. end
  234. end
  235. self.canvas->coords(self.id, x, y, x + (self.r * 2), y + (self.r * 2))
  236. self.vel = [
  237. 'x': (1 - self.smooth) * dx + self.smooth * self.vel['x'],
  238. 'y': (1 - self.smooth) * dy + self.smooth * self.vel['y']
  239. ]
  240. }
  241. Transition(port = 'input', target='../bouncing'):
  242. event = left-release(tagorid: Integer='None')
  243. Actions {
  244. self.canvas->itemconfig(self.id, fill="red")
  245. }
  246. State('selected'):
  247. Transition(port = 'input', target='../dragging'):
  248. event = left-click(tagorid: Integer='None')
  249. guard = {tagorid == id(self)}
  250. Transition(port = 'input', target='.'):
  251. event = delete(tagorid: Integer='None')
  252. raise += { scope=local, delete_self() }
  253. Transition(target='../deleted'):
  254. event = delete_self(tagorid: Integer='None')
  255. raise += { scope=narrow, target='parent', delete_ball(self.association_name) }
  256. State('deleted')