comparison pyink/FSM_window.py @ 1492:6616530c4180

Show hint when mouse over a transition
author Thinker K.F. Li <thinker@codemud.net>
date Mon, 02 May 2011 23:23:06 +0800
parents 06c101bba830
children b0e113605382
comparison
equal deleted inserted replaced
1491:06c101bba830 1492:6616530c4180
2 import os 2 import os
3 import math 3 import math
4 import data_monitor 4 import data_monitor
5 import pybInkscape 5 import pybInkscape
6 6
7
8 class _dragger(object):
9 _node = None
10 _start_x = None
11 _start_y = None
12 _state = 0
13
14 def __init__(self):
15 pass
16
17 def mouse_event(self, evtype, button, x, y):
18 raise RuntimeError, 'should not be here'
19
20 def mouse_event_waiting(self, evtype, button, x, y):
21 if evtype == pybInkscape.PYSPItem.PYB_EVENT_BUTTON_PRESS and \
22 button == 1:
23 self._start_x = x
24 self._start_y = y
25 self.mouse_event = self.mouse_event_pressed
26 self.start_drag()
27 pass
28 pass
29
30 def mouse_event_pressed(self, evtype, button, x, y):
31 rx = x - self._start_x
32 ry = y - self._start_y
33
34 if evtype == pybInkscape.PYSPItem.PYB_EVENT_BUTTON_RELEASE:
35 self.mouse_event = self.mouse_event_waiting
36 self.stop_drag(rx, ry)
37 pass
38
39 self.update(rx, ry)
40 pass
41
42 def start(self):
43 self.mouse_event = self.mouse_event_waiting
44 pass
45
46 def stop(self):
47 pass
48
49 def connect(self, node):
50 self.start()
51
52 def handler(item, evtype, button, x, y):
53 self.mouse_event(evtype, button, x, y)
54 pass
55
56 self._node = node
57 hdl_id = node.spitem.connect('mouse-event', handler)
58 self._hdl_id = hdl_id
59 pass
60
61 def disconnect(self):
62 self.stop()
63 node = self._node
64 hdl_id = self._hdl_id
65 node.disconnect(hdl_id)
66 pass
67
68 def start_drag(self):
69 pass
70
71 def stop_drag(self, rx, ry):
72 pass
73
74 def update(self, rx, ry):
75 pass
76 pass
77
78
7 class FSM_window_base(object): 79 class FSM_window_base(object):
8 _add_state_button = None 80 _add_state_button = None
9 _move_state_button = None 81 _move_state_button = None
10 82
11 _state_editor = None 83 _state_editor = None
150 def on_del_state_activate(self, *args): 222 def on_del_state_activate(self, *args):
151 pass 223 pass
152 224
153 def on_edit_state_activate(self, *args): 225 def on_edit_state_activate(self, *args):
154 pass 226 pass
227
228 def on_transition_apply_clicked(self, *args):
229 pass
230
231 def on_transition_cancel_clicked(self, *args):
232 pass
155 pass 233 pass
234
156 235
157 class FSM_transition(object): 236 class FSM_transition(object):
158 _doc = None 237 _doc = None
159 _domview = None 238 _domview = None
160 _fsm_layer = None 239 _fsm_layer = None
163 _states = None 242 _states = None
164 trn_cond = None 243 trn_cond = None
165 trn_g = None 244 trn_g = None
166 _arrow_node = None 245 _arrow_node = None
167 _path_node = None 246 _path_node = None
247 _control_points = None
168 248
169 def __init__(self, trn_cond): 249 def __init__(self, trn_cond):
170 self.trn_cond = trn_cond 250 self.trn_cond = trn_cond
171 pass 251 pass
172 252
269 state_name = self._state.state_name 349 state_name = self._state.state_name
270 trn_cond = self.trn_cond 350 trn_cond = self.trn_cond
271 trn = domview.get_transition(state_name, trn_cond) 351 trn = domview.get_transition(state_name, trn_cond)
272 return trn[1] 352 return trn[1]
273 353
354 @property
355 def state(self):
356 return self._state
357
274 def draw(self, parent): 358 def draw(self, parent):
275 path = self.path 359 path = self.path
276 trn_g, arrow_node, path_node = self._draw_transition_real(parent, path) 360 trn_g, path_node, arrow_node = self._draw_transition_real(parent, path)
277 self.trn_g = trn_g 361 self.trn_g = trn_g
278 self._arrow_node = arrow_node 362 self._arrow_node = arrow_node
279 self._path_node = path_node 363 self._path_node = path_node
280 pass 364 pass
281 365
330 c2y = stop_v[1] * c3c2_len + c3y 414 c2y = stop_v[1] * c3c2_len + c3y
331 new_path = [c0x, c0y, c1x, c1y, c2x, c2y, c3x, c3y] 415 new_path = [c0x, c0y, c1x, c1y, c2x, c2y, c3x, c3y]
332 416
333 domview = self._domview 417 domview = self._domview
334 domview.set_transition_path(state_name, trn_cond, new_path) 418 domview.set_transition_path(state_name, trn_cond, new_path)
419 pass
420
421 def show_control_points(self):
422 if not self._control_points:
423 doc = self._doc
424
425 c1 = doc.createElement('svg:circle')
426 c1.setAttribute('r', '3')
427 c1.setAttribute('style', 'stroke: black; stroke-width: 1; '
428 'fill: white')
429 l01 = doc.createElement('svg:line')
430 l01.setAttribute('style', 'stroke: black; stroke-width: 1; '
431 'stroke-dasharray: 3 2')
432
433 c2 = doc.createElement('svg:circle')
434 c2.setAttribute('r', '3')
435 c2.setAttribute('style', 'stroke: black; stroke-width: 1; '
436 'fill: white')
437 l32 = doc.createElement('svg:line')
438 l32.setAttribute('style', 'stroke: black; stroke-width: 1; '
439 'stroke-dasharray: 3 2')
440
441 control_layer = self._control_layer
442
443 control_layer.appendChild(c1)
444 control_layer.appendChild(l01)
445 control_layer.appendChild(c2)
446 control_layer.appendChild(l32)
447 self._control_points = (c1, l01, c2, l32)
448 pass
449
450 c1, l01, c2, l32 = self._control_points
451 path = self.path
452 c0x, c0y, c1x, c1y, c2x, c2y, c3x, c3y = tuple(path)
453
454 c1.setAttribute('cx', str(c1x))
455 c1.setAttribute('cy', str(c1y))
456 l01.setAttribute('x1', str(c0x))
457 l01.setAttribute('y1', str(c0y))
458 l01.setAttribute('x2', str(c1x))
459 l01.setAttribute('y2', str(c1y))
460
461 c2.setAttribute('cx', str(c2x))
462 c2.setAttribute('cy', str(c2y))
463 l32.setAttribute('x1', str(c3x))
464 l32.setAttribute('y1', str(c3y))
465 l32.setAttribute('x2', str(c2x))
466 l32.setAttribute('y2', str(c2y))
467 pass
468
469 def hide_control_points(self):
470 if not self._control_points:
471 return
472
473 control_layer = self._control_layer
474 for node in self._control_points:
475 control_layer.removeChild(node)
476 pass
477 self._control_points = None
478 pass
479
480 def start_hint(self):
481 path_node = self._path_node
482 arrow_node = self._arrow_node
483 if path_node:
484 path_node.setAttribute('style',
485 'stroke: #404040; stroke-width: 3; '
486 'fill: none')
487 arrow_node.setAttribute('style',
488 'stroke: #404040; stroke-width: 2; '
489 'fill: #404040')
490 pass
491 pass
492
493 def stop_hint(self):
494 path_node = self._path_node
495 arrow_node = self._arrow_node
496 if path_node:
497 path_node.setAttribute('style',
498 'stroke: #000000; stroke-width: 1; ' \
499 'fill: none')
500 arrow_node.setAttribute('style',
501 'stroke: #000000; stroke-width: 1; ' \
502 'fill: #000000')
503 pass
335 pass 504 pass
336 pass 505 pass
337 506
338 class FSM_state(object): 507 class FSM_state(object):
339 _doc = None 508 _doc = None
595 class _FSM_move_state_mode(object): 764 class _FSM_move_state_mode(object):
596 __metaclass__ = data_monitor.data_monitor 765 __metaclass__ = data_monitor.data_monitor
597 __data_monitor_prefix__ = 'on_' 766 __data_monitor_prefix__ = 'on_'
598 767
599 _window = None 768 _window = None
600 _selected_state = None 769 _domview = None
770 _selected_cleaner = None
601 771
602 def __init__(self, window, domview_ui): 772 def __init__(self, window, domview_ui):
603 super(_FSM_move_state_mode, self).__init__() 773 super(_FSM_move_state_mode, self).__init__()
604 774
605 self._window = window 775 self._window = window
606 self._domview = domview_ui 776 self._domview = domview_ui
607 self._locker = domview_ui 777 self._locker = domview_ui
608 pass 778 pass
609 779
610 def on_move_state_background(self, item, evtype, button, x, y): 780 def on_move_state_background(self, item, evtype, button, x, y):
781 if self._selected_cleaner is None:
782 return
783
784 if evtype == pybInkscape.PYSPItem.PYB_EVENT_BUTTON_RELEASE:
785 self._clean_select()
786 pass
611 pass 787 pass
612 788
613 def _select_state(self, state): 789 def _select_state(self, state):
614 if self._selected_state: 790 self._clean_select()
615 self._selected_state.hide_selected() 791 self._selected_cleaner = state.hide_selected
616 pass
617 self._selected_state = state
618 state.show_selected() 792 state.show_selected()
619 pass 793 pass
620 794
621 def _clear_select(self): 795 def _clean_select(self):
622 if self._selected_state: 796 if self._selected_cleaner:
623 self._selected_state.hide_selected() 797 self._selected_cleaner()
624 pass 798 pass
625 self._selected_state = None 799 self._selected_cleaner = None
626 pass 800 pass
627 801
628 def handle_move_state_state(self, state, evtype, button, x, y): 802 def _handle_move_state_state(self, state, evtype, button, x, y):
629 window = self._window 803 window = self._window
630 804
631 def moving_state(item, evtype, button, x, y): 805 def moving_state(item, evtype, button, x, y):
632 if evtype == pybInkscape.PYSPItem.PYB_EVENT_BUTTON_RELEASE: 806 if evtype == pybInkscape.PYSPItem.PYB_EVENT_BUTTON_RELEASE:
633 window.ungrab_mouse() 807 window.ungrab_mouse()
658 button == 1: 832 button == 1:
659 window.ungrab_mouse() 833 window.ungrab_mouse()
660 pass 834 pass
661 pass 835 pass
662 836
837 def _install_transition_mouse_event_handler(self, trn):
838 c1, l01, c2, l32 = trn._control_points
839 path = trn.path
840 c0x, c0y, c1x, c1y, c2x, c2y, c3x, c3y = tuple(path)
841
842 state_src = trn.state
843 target_name = trn.target
844 states = trn._states
845 state_target = states[target_name]
846 domview = self._domview
847 window = self._window
848
849 def c1_update(rx, ry):
850 nc1x = c1x + rx
851 nc1y = c1y + ry
852 cx, cy = state_src.xy
853 r = state_src.r
854
855 cv = nc1x - cx, nc1y - cy
856 cv_len = math.sqrt(cv[0] ** 2 + cv[1] ** 2)
857 nc0x = cx + cv[0] * r / cv_len
858 nc0y = cy + cv[1] * r / cv_len
859
860 path = list(trn.path)
861 path[:4] = [nc0x, nc0y, nc1x, nc1y]
862
863 state_name = state_src.state_name
864 cond = trn.trn_cond
865 domview.set_transition_path(state_name, cond, path)
866
867 trn.show_control_points()
868 trn.update()
869 pass
870
871 def c1_start():
872 def relay_event(item, evtype, button, x, y):
873 c1_dragger.mouse_event(evtype, button, x, y)
874 pass
875
876 window.ungrab_bg()
877 window.grab_bg(relay_event)
878 pass
879
880 def c1_stop(rx, ry):
881 window.ungrab_bg()
882 window.grab_bg(self.on_move_state_background)
883 pass
884
885 def c2_update(rx, ry):
886 nc2x = c2x + rx
887 nc2y = c2y + ry
888 cx, cy = state_target.xy
889 r = state_target.r
890
891 cv = nc2x - cx, nc2y - cy
892 cv_len = math.sqrt(cv[0] ** 2 + cv[1] ** 2)
893 nc3x = cx + cv[0] * r / cv_len
894 nc3y = cy + cv[1] * r / cv_len
895
896 path = list(trn.path)
897 path[4:] = [nc2x, nc2y, nc3x, nc3y]
898
899 state_name = state_src.state_name
900 cond = trn.trn_cond
901 domview.set_transition_path(state_name, cond, path)
902
903 trn.show_control_points()
904 trn.update()
905 pass
906
907 def c2_start():
908 def relay_event(item, evtype, button, x, y):
909 c2_dragger.mouse_event(evtype, button, x, y)
910 pass
911
912 window.ungrab_bg()
913 window.grab_bg(relay_event)
914 pass
915
916 def c2_stop(rx, ry):
917 window.ungrab_bg()
918 window.grab_bg(self.on_move_state_background)
919 pass
920
921 c1_dragger = _dragger()
922 c1_dragger.update = c1_update
923 c1_dragger.start_drag = c1_start
924 c1_dragger.stop_drag = c1_stop
925 c1_dragger.connect(c1)
926
927 c2_dragger = _dragger()
928 c2_dragger.update = c2_update
929 c2_dragger.start_drag = c2_start
930 c2_dragger.stop_drag = c2_stop
931 c2_dragger.connect(c2)
932 pass
933
934 def _select_transition(self, trn):
935 def cleaner():
936 trn.hide_control_points()
937 del self._hint_transition
938 pass
939 self._clean_select()
940 self._selected_cleaner = cleaner
941 trn.show_control_points()
942
943 trn.stop_hint()
944 self._hint_transition = lambda *args: None
945 window = self._window
946 window.ungrab_bg()
947
948 self._install_transition_mouse_event_handler(trn)
949 pass
950
951 def _hint_transition(self, trn):
952 def stop_hint(*args):
953 trn.stop_hint()
954 window.ungrab_bg()
955 window.grab_bg(self.on_move_state_background)
956 pass
957
958 trn.start_hint()
959
960 window = self._window
961 window.ungrab_bg()
962 window.grab_bg(stop_hint)
963 pass
964
965 def _handle_transitoin_mouse_events(self, trn, evtype, button, x, y):
966 if evtype == pybInkscape.PYSPItem.PYB_EVENT_BUTTON_RELEASE and \
967 button == 1:
968 self._select_transition(trn)
969 elif evtype == pybInkscape.PYSPItem.PYB_EVENT_MOUSE_ENTER:
970 self._hint_transition(trn)
971 pass
972 pass
973
663 def activate(self): 974 def activate(self):
664 window = self._window 975 window = self._window
665 window._emit_leave_mode() 976 window._emit_leave_mode()
666 window._clear_leave_mode_cb() 977 window._clear_leave_mode_cb()
667 window.ungrab_all() 978 window.ungrab_all()
668 979
669 window.grab_bg(self.on_move_state_background) 980 window.grab_bg(self.on_move_state_background)
670 window.grab_state(self.handle_move_state_state) 981 window.grab_state(self._handle_move_state_state)
982 window.grab_transition(self._handle_transitoin_mouse_events)
671 pass 983 pass
672 984
673 def deactivate(self): 985 def deactivate(self):
674 if self._selected_state: 986 self._clean_select()
675 self._clear_select()
676 pass
677 pass 987 pass
678 pass 988 pass
679 989
680 990
681 class _FSM_add_state_mode(object): 991 class _FSM_add_state_mode(object):
844 _leave_mode_cb = None 1154 _leave_mode_cb = None
845 _move_state_mode = None 1155 _move_state_mode = None
846 _add_state_mode = None 1156 _add_state_mode = None
847 _state_mouse_event_handler = None 1157 _state_mouse_event_handler = None
848 _add_transition_cb = None 1158 _add_transition_cb = None
1159 _transition_mouse_event_handler = None
849 1160
850 def __init__(self, domview_ui, close_cb, destroy_cb): 1161 def __init__(self, domview_ui, close_cb, destroy_cb):
851 super(FSM_window, self).__init__() 1162 super(FSM_window, self).__init__()
852 1163
853 self._locker = domview_ui 1164 self._locker = domview_ui
948 def ungrab_all(self): 1259 def ungrab_all(self):
949 self.ungrab_bg() 1260 self.ungrab_bg()
950 self.ungrab_mouse() 1261 self.ungrab_mouse()
951 self.ungrab_state() 1262 self.ungrab_state()
952 self.ungrab_add_transition() 1263 self.ungrab_add_transition()
1264 self.ungrab_transition()
953 pass 1265 pass
954 1266
955 def on_state_mouse_event(self, state, evtype, button, x, y): 1267 def on_state_mouse_event(self, state, evtype, button, x, y):
956 if self._state_mouse_event_handler: 1268 if self._state_mouse_event_handler:
957 self._state_mouse_event_handler(state, evtype, button, x, y) 1269 self._state_mouse_event_handler(state, evtype, button, x, y)
963 self.on_state_mouse_event(state, evtype, button, x, y) 1275 self.on_state_mouse_event(state, evtype, button, x, y)
964 pass 1276 pass
965 state.grab(mouse_event_handler) 1277 state.grab(mouse_event_handler)
966 pass 1278 pass
967 1279
1280 def on_transition_mouse_event(self, trn, evtype, button, x, y):
1281 if self._transition_mouse_event_handler:
1282 self._transition_mouse_event_handler(trn, evtype, button, x, y)
1283 pass
1284 pass
1285
1286 def _install_transition_event_handler(self, trn):
1287 def mouse_event_handler(item, evtype, button, x, y):
1288 self.on_transition_mouse_event(trn, evtype, button, x, y)
1289 pass
1290 trn_g = trn.trn_g
1291 trn_g.spitem.connect('mouse-event', mouse_event_handler)
1292 pass
1293
1294 def grab_transition(self, callback):
1295 assert self._transition_mouse_event_handler is None
1296 self._transition_mouse_event_handler = callback
1297 pass
1298
1299 def ungrab_transition(self):
1300 self._transition_mouse_event_handler = None
1301 pass
1302
968 def grab_state(self, callback): 1303 def grab_state(self, callback):
969 assert self._state_mouse_event_handler is None 1304 assert self._state_mouse_event_handler is None
970 self._state_mouse_event_handler = callback 1305 self._state_mouse_event_handler = callback
971 pass 1306 pass
972 1307
987 states = self._states 1322 states = self._states
988 1323
989 self._draw_state_domview(state_name) 1324 self._draw_state_domview(state_name)
990 state = states[state_name] 1325 state = states[state_name]
991 self._install_state_event_handler(state) 1326 self._install_state_event_handler(state)
1327
1328 for trn in state.transitions.values():
1329 self._install_transition_event_handler(trn)
1330 pass
992 pass 1331 pass
993 1332
994 ## \brief Load new state incrementally. 1333 ## \brief Load new state incrementally.
995 # 1334 #
996 def _load_new_state_incr(self, state_name): 1335 def _load_new_state_incr(self, state_name):