# HG changeset patch # User Thinker K.F. Li # Date 1304349786 -28800 # Node ID 6616530c41802ef5b7ccfed26f8f9c4e66714416 # Parent 06c101bba830d9dabe69e7a3322e0a0f54491ebc Show hint when mouse over a transition diff -r 06c101bba830 -r 6616530c4180 pyink/FSM_window.glade --- a/pyink/FSM_window.glade Sun May 01 00:09:56 2011 +0800 +++ b/pyink/FSM_window.glade Mon May 02 23:23:06 2011 +0800 @@ -306,12 +306,14 @@ True Name: + right True Radius: + right 1 @@ -343,10 +345,28 @@ - + + True + Entry Action: + right + + + 2 + 3 + - + + True + True + + + + 1 + 2 + 2 + 3 + @@ -501,4 +521,117 @@ + + 5 + True + center-always + 200 + normal + FSM_main_win + + + True + vertical + 2 + + + True + 2 + 2 + + + True + Condition: + right + + + + + True + Action: + right + + + 1 + 2 + + + + + True + True + + + + 1 + 2 + + + + + True + True + + + + 1 + 2 + 1 + 2 + + + + + False + 1 + + + + + True + end + + + gtk-apply + True + True + True + True + + + + False + False + 0 + + + + + gtk-cancel + True + True + True + True + + + + False + False + 1 + + + + + False + end + 0 + + + + + + transition_apply + transition_cancel + + diff -r 06c101bba830 -r 6616530c4180 pyink/FSM_window.py --- a/pyink/FSM_window.py Sun May 01 00:09:56 2011 +0800 +++ b/pyink/FSM_window.py Mon May 02 23:23:06 2011 +0800 @@ -4,6 +4,78 @@ import data_monitor import pybInkscape + +class _dragger(object): + _node = None + _start_x = None + _start_y = None + _state = 0 + + def __init__(self): + pass + + def mouse_event(self, evtype, button, x, y): + raise RuntimeError, 'should not be here' + + def mouse_event_waiting(self, evtype, button, x, y): + if evtype == pybInkscape.PYSPItem.PYB_EVENT_BUTTON_PRESS and \ + button == 1: + self._start_x = x + self._start_y = y + self.mouse_event = self.mouse_event_pressed + self.start_drag() + pass + pass + + def mouse_event_pressed(self, evtype, button, x, y): + rx = x - self._start_x + ry = y - self._start_y + + if evtype == pybInkscape.PYSPItem.PYB_EVENT_BUTTON_RELEASE: + self.mouse_event = self.mouse_event_waiting + self.stop_drag(rx, ry) + pass + + self.update(rx, ry) + pass + + def start(self): + self.mouse_event = self.mouse_event_waiting + pass + + def stop(self): + pass + + def connect(self, node): + self.start() + + def handler(item, evtype, button, x, y): + self.mouse_event(evtype, button, x, y) + pass + + self._node = node + hdl_id = node.spitem.connect('mouse-event', handler) + self._hdl_id = hdl_id + pass + + def disconnect(self): + self.stop() + node = self._node + hdl_id = self._hdl_id + node.disconnect(hdl_id) + pass + + def start_drag(self): + pass + + def stop_drag(self, rx, ry): + pass + + def update(self, rx, ry): + pass + pass + + class FSM_window_base(object): _add_state_button = None _move_state_button = None @@ -152,8 +224,15 @@ def on_edit_state_activate(self, *args): pass + + def on_transition_apply_clicked(self, *args): + pass + + def on_transition_cancel_clicked(self, *args): + pass pass + class FSM_transition(object): _doc = None _domview = None @@ -165,6 +244,7 @@ trn_g = None _arrow_node = None _path_node = None + _control_points = None def __init__(self, trn_cond): self.trn_cond = trn_cond @@ -271,9 +351,13 @@ trn = domview.get_transition(state_name, trn_cond) return trn[1] + @property + def state(self): + return self._state + def draw(self, parent): path = self.path - trn_g, arrow_node, path_node = self._draw_transition_real(parent, path) + trn_g, path_node, arrow_node = self._draw_transition_real(parent, path) self.trn_g = trn_g self._arrow_node = arrow_node self._path_node = path_node @@ -333,6 +417,91 @@ domview = self._domview domview.set_transition_path(state_name, trn_cond, new_path) pass + + def show_control_points(self): + if not self._control_points: + doc = self._doc + + c1 = doc.createElement('svg:circle') + c1.setAttribute('r', '3') + c1.setAttribute('style', 'stroke: black; stroke-width: 1; ' + 'fill: white') + l01 = doc.createElement('svg:line') + l01.setAttribute('style', 'stroke: black; stroke-width: 1; ' + 'stroke-dasharray: 3 2') + + c2 = doc.createElement('svg:circle') + c2.setAttribute('r', '3') + c2.setAttribute('style', 'stroke: black; stroke-width: 1; ' + 'fill: white') + l32 = doc.createElement('svg:line') + l32.setAttribute('style', 'stroke: black; stroke-width: 1; ' + 'stroke-dasharray: 3 2') + + control_layer = self._control_layer + + control_layer.appendChild(c1) + control_layer.appendChild(l01) + control_layer.appendChild(c2) + control_layer.appendChild(l32) + self._control_points = (c1, l01, c2, l32) + pass + + c1, l01, c2, l32 = self._control_points + path = self.path + c0x, c0y, c1x, c1y, c2x, c2y, c3x, c3y = tuple(path) + + c1.setAttribute('cx', str(c1x)) + c1.setAttribute('cy', str(c1y)) + l01.setAttribute('x1', str(c0x)) + l01.setAttribute('y1', str(c0y)) + l01.setAttribute('x2', str(c1x)) + l01.setAttribute('y2', str(c1y)) + + c2.setAttribute('cx', str(c2x)) + c2.setAttribute('cy', str(c2y)) + l32.setAttribute('x1', str(c3x)) + l32.setAttribute('y1', str(c3y)) + l32.setAttribute('x2', str(c2x)) + l32.setAttribute('y2', str(c2y)) + pass + + def hide_control_points(self): + if not self._control_points: + return + + control_layer = self._control_layer + for node in self._control_points: + control_layer.removeChild(node) + pass + self._control_points = None + pass + + def start_hint(self): + path_node = self._path_node + arrow_node = self._arrow_node + if path_node: + path_node.setAttribute('style', + 'stroke: #404040; stroke-width: 3; ' + 'fill: none') + arrow_node.setAttribute('style', + 'stroke: #404040; stroke-width: 2; ' + 'fill: #404040') + pass + pass + + def stop_hint(self): + path_node = self._path_node + arrow_node = self._arrow_node + if path_node: + path_node.setAttribute('style', + 'stroke: #000000; stroke-width: 1; ' \ + 'fill: none') + arrow_node.setAttribute('style', + 'stroke: #000000; stroke-width: 1; ' \ + 'fill: #000000') + pass + pass pass class FSM_state(object): @@ -597,7 +766,8 @@ __data_monitor_prefix__ = 'on_' _window = None - _selected_state = None + _domview = None + _selected_cleaner = None def __init__(self, window, domview_ui): super(_FSM_move_state_mode, self).__init__() @@ -608,24 +778,28 @@ pass def on_move_state_background(self, item, evtype, button, x, y): + if self._selected_cleaner is None: + return + + if evtype == pybInkscape.PYSPItem.PYB_EVENT_BUTTON_RELEASE: + self._clean_select() + pass pass def _select_state(self, state): - if self._selected_state: - self._selected_state.hide_selected() - pass - self._selected_state = state + self._clean_select() + self._selected_cleaner = state.hide_selected state.show_selected() pass - def _clear_select(self): - if self._selected_state: - self._selected_state.hide_selected() + def _clean_select(self): + if self._selected_cleaner: + self._selected_cleaner() pass - self._selected_state = None + self._selected_cleaner = None pass - def handle_move_state_state(self, state, evtype, button, x, y): + def _handle_move_state_state(self, state, evtype, button, x, y): window = self._window def moving_state(item, evtype, button, x, y): @@ -660,6 +834,143 @@ pass pass + def _install_transition_mouse_event_handler(self, trn): + c1, l01, c2, l32 = trn._control_points + path = trn.path + c0x, c0y, c1x, c1y, c2x, c2y, c3x, c3y = tuple(path) + + state_src = trn.state + target_name = trn.target + states = trn._states + state_target = states[target_name] + domview = self._domview + window = self._window + + def c1_update(rx, ry): + nc1x = c1x + rx + nc1y = c1y + ry + cx, cy = state_src.xy + r = state_src.r + + cv = nc1x - cx, nc1y - cy + cv_len = math.sqrt(cv[0] ** 2 + cv[1] ** 2) + nc0x = cx + cv[0] * r / cv_len + nc0y = cy + cv[1] * r / cv_len + + path = list(trn.path) + path[:4] = [nc0x, nc0y, nc1x, nc1y] + + state_name = state_src.state_name + cond = trn.trn_cond + domview.set_transition_path(state_name, cond, path) + + trn.show_control_points() + trn.update() + pass + + def c1_start(): + def relay_event(item, evtype, button, x, y): + c1_dragger.mouse_event(evtype, button, x, y) + pass + + window.ungrab_bg() + window.grab_bg(relay_event) + pass + + def c1_stop(rx, ry): + window.ungrab_bg() + window.grab_bg(self.on_move_state_background) + pass + + def c2_update(rx, ry): + nc2x = c2x + rx + nc2y = c2y + ry + cx, cy = state_target.xy + r = state_target.r + + cv = nc2x - cx, nc2y - cy + cv_len = math.sqrt(cv[0] ** 2 + cv[1] ** 2) + nc3x = cx + cv[0] * r / cv_len + nc3y = cy + cv[1] * r / cv_len + + path = list(trn.path) + path[4:] = [nc2x, nc2y, nc3x, nc3y] + + state_name = state_src.state_name + cond = trn.trn_cond + domview.set_transition_path(state_name, cond, path) + + trn.show_control_points() + trn.update() + pass + + def c2_start(): + def relay_event(item, evtype, button, x, y): + c2_dragger.mouse_event(evtype, button, x, y) + pass + + window.ungrab_bg() + window.grab_bg(relay_event) + pass + + def c2_stop(rx, ry): + window.ungrab_bg() + window.grab_bg(self.on_move_state_background) + pass + + c1_dragger = _dragger() + c1_dragger.update = c1_update + c1_dragger.start_drag = c1_start + c1_dragger.stop_drag = c1_stop + c1_dragger.connect(c1) + + c2_dragger = _dragger() + c2_dragger.update = c2_update + c2_dragger.start_drag = c2_start + c2_dragger.stop_drag = c2_stop + c2_dragger.connect(c2) + pass + + def _select_transition(self, trn): + def cleaner(): + trn.hide_control_points() + del self._hint_transition + pass + self._clean_select() + self._selected_cleaner = cleaner + trn.show_control_points() + + trn.stop_hint() + self._hint_transition = lambda *args: None + window = self._window + window.ungrab_bg() + + self._install_transition_mouse_event_handler(trn) + pass + + def _hint_transition(self, trn): + def stop_hint(*args): + trn.stop_hint() + window.ungrab_bg() + window.grab_bg(self.on_move_state_background) + pass + + trn.start_hint() + + window = self._window + window.ungrab_bg() + window.grab_bg(stop_hint) + pass + + def _handle_transitoin_mouse_events(self, trn, evtype, button, x, y): + if evtype == pybInkscape.PYSPItem.PYB_EVENT_BUTTON_RELEASE and \ + button == 1: + self._select_transition(trn) + elif evtype == pybInkscape.PYSPItem.PYB_EVENT_MOUSE_ENTER: + self._hint_transition(trn) + pass + pass + def activate(self): window = self._window window._emit_leave_mode() @@ -667,13 +978,12 @@ window.ungrab_all() window.grab_bg(self.on_move_state_background) - window.grab_state(self.handle_move_state_state) + window.grab_state(self._handle_move_state_state) + window.grab_transition(self._handle_transitoin_mouse_events) pass def deactivate(self): - if self._selected_state: - self._clear_select() - pass + self._clean_select() pass pass @@ -846,6 +1156,7 @@ _add_state_mode = None _state_mouse_event_handler = None _add_transition_cb = None + _transition_mouse_event_handler = None def __init__(self, domview_ui, close_cb, destroy_cb): super(FSM_window, self).__init__() @@ -950,6 +1261,7 @@ self.ungrab_mouse() self.ungrab_state() self.ungrab_add_transition() + self.ungrab_transition() pass def on_state_mouse_event(self, state, evtype, button, x, y): @@ -965,6 +1277,29 @@ state.grab(mouse_event_handler) pass + def on_transition_mouse_event(self, trn, evtype, button, x, y): + if self._transition_mouse_event_handler: + self._transition_mouse_event_handler(trn, evtype, button, x, y) + pass + pass + + def _install_transition_event_handler(self, trn): + def mouse_event_handler(item, evtype, button, x, y): + self.on_transition_mouse_event(trn, evtype, button, x, y) + pass + trn_g = trn.trn_g + trn_g.spitem.connect('mouse-event', mouse_event_handler) + pass + + def grab_transition(self, callback): + assert self._transition_mouse_event_handler is None + self._transition_mouse_event_handler = callback + pass + + def ungrab_transition(self): + self._transition_mouse_event_handler = None + pass + def grab_state(self, callback): assert self._state_mouse_event_handler is None self._state_mouse_event_handler = callback @@ -989,6 +1324,10 @@ self._draw_state_domview(state_name) state = states[state_name] self._install_state_event_handler(state) + + for trn in state.transitions.values(): + self._install_transition_event_handler(trn) + pass pass ## \brief Load new state incrementally.