Mercurial > MadButterfly
changeset 1478:6fe773e62b2a
Add state to FSM.
- Add a state if user left-click on the background of FSM window.
- pop a dialog that user can specify name and radius of the new state.
author | Thinker K.F. Li <thinker@codemud.net> |
---|---|
date | Mon, 25 Apr 2011 17:52:51 +0800 |
parents | e217c7743905 |
children | 92a8497d0361 |
files | pyink/FSM_window.glade pyink/FSM_window.py |
diffstat | 2 files changed, 370 insertions(+), 8 deletions(-) [+] |
line wrap: on
line diff
--- a/pyink/FSM_window.glade Sun Apr 24 21:09:37 2011 +0800 +++ b/pyink/FSM_window.glade Mon Apr 25 17:52:51 2011 +0800 @@ -130,6 +130,7 @@ <property name="label" translatable="yes">Add State</property> <property name="use_underline">True</property> <property name="stock_id">gtk-add</property> + <signal name="toggled" handler="on_add_state_toggled"/> </object> <packing> <property name="expand">False</property> @@ -144,6 +145,7 @@ <property name="use_underline">True</property> <property name="icon_name">widget-gtk-alignment</property> <property name="group">add_state</property> + <signal name="toggled" handler="on_move_state_toggled"/> </object> <packing> <property name="expand">False</property> @@ -289,4 +291,192 @@ </object> </child> </object> + <object class="GtkDialog" id="state_editor"> + <property name="border_width">5</property> + <property name="title" translatable="yes">State Editor</property> + <property name="modal">True</property> + <property name="window_position">center-on-parent</property> + <property name="default_height">200</property> + <property name="type_hint">normal</property> + <property name="transient_for">FSM_main_win</property> + <signal name="delete_event" handler="gtk_widget_hide"/> + <child internal-child="vbox"> + <object class="GtkVBox" id="dialog-vbox1"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <property name="spacing">2</property> + <child> + <object class="GtkTable" id="table1"> + <property name="visible">True</property> + <property name="n_rows">3</property> + <property name="n_columns">2</property> + <child> + <object class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="label" translatable="yes">Name:</property> + </object> + </child> + <child> + <object class="GtkLabel" id="label3"> + <property name="visible">True</property> + <property name="label" translatable="yes">Radius:</property> + </object> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="state_name"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">●</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="state_radius"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">●</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + </packing> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + <child internal-child="action_area"> + <object class="GtkHButtonBox" id="dialog-action_area1"> + <property name="visible">True</property> + <property name="layout_style">end</property> + <child> + <object class="GtkButton" id="state_apply"> + <property name="label">gtk-apply</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + <signal name="clicked" handler="on_state_apply_clicked"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="state_cancel"> + <property name="label">gtk-cancel</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + <signal name="clicked" handler="on_state_cancel_clicked"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + <action-widgets> + <action-widget response="0">state_apply</action-widget> + <action-widget response="0">state_cancel</action-widget> + </action-widgets> + </object> + <object class="GtkDialog" id="error_dialog"> + <property name="width_request">200</property> + <property name="height_request">200</property> + <property name="border_width">5</property> + <property name="title" translatable="yes">Error</property> + <property name="resizable">False</property> + <property name="modal">True</property> + <property name="type_hint">normal</property> + <property name="transient_for">state_editor</property> + <signal name="delete_event" handler="gtk_widget_hide"/> + <child internal-child="vbox"> + <object class="GtkVBox" id="dialog-vbox2"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <property name="spacing">2</property> + <child> + <object class="GtkVBox" id="vbox3"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkLabel" id="error_dialog_label"> + <property name="visible">True</property> + <property name="label" translatable="yes">Invalid state name. It is a existing state name or invalid in format!</property> + <property name="wrap">True</property> + </object> + <packing> + <property name="position">0</property> + </packing> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + <child internal-child="action_area"> + <object class="GtkHButtonBox" id="dialog-action_area2"> + <property name="visible">True</property> + <property name="layout_style">end</property> + <child> + <placeholder/> + </child> + <child> + <object class="GtkButton" id="error_dialog_ok"> + <property name="label">gtk-ok</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + <signal name="clicked" handler="on_error_dialog_ok_clicked"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + <action-widgets> + <action-widget response="0">error_dialog_ok</action-widget> + </action-widgets> + </object> </interface>
--- a/pyink/FSM_window.py Sun Apr 24 21:09:37 2011 +0800 +++ b/pyink/FSM_window.py Mon Apr 25 17:52:51 2011 +0800 @@ -3,6 +3,13 @@ import data_monitor class FSM_window_base(object): + _state_editor = None + _state_name = None + _state_radius = None + + _error_dialog = None + _error_dialog_label = None + def __init__(self): super(FSM_window_base, self).__init__() @@ -14,12 +21,54 @@ main_win = builder.get_object("FSM_main_win") view_box = builder.get_object("view_box") + + state_editor = builder.get_object("state_editor") + state_name = builder.get_object('state_name') + state_radius = builder.get_object('state_radius') + + error_dialog = builder.get_object('error_dialog') + error_dialog_label = builder.get_object('error_dialog_label') builder.connect_signals(self) self._builder = builder self._main_win = main_win self._view_box = view_box + + self._state_editor = state_editor + self._state_name = state_name + self._state_radius = state_radius + + self._error_dialog = error_dialog + self._error_dialog_label = error_dialog_label + pass + + def show_error(self, msg): + error_dialog = self._error_dialog + error_dialog_label = self._error_dialog_label + + error_dialog_label.set_text(msg) + error_dialog.show() + pass + + def hide_error(self): + error_dialog = self._error_dialog + error_dialog.hide() + pass + + def show_state_editor(self, state_name=''): + state_name_inp = self._state_name + state_radius_inp = self._state_radius + state_editor = self._state_editor + + state_name_inp.set_text(state_name) + state_radius_inp.set_text('30') + state_editor.show() + pass + + def hide_state_editor(self): + state_editor = self._state_editor + state_editor.hide() pass def show(self): @@ -29,6 +78,10 @@ def hide(self): self._main_win.hide() pass + + def gtk_widget_hide(self, widget, event, *data): + widget.hide() + return True def on_start_state_activate(self, *args): pass @@ -56,6 +109,19 @@ def on_FSM_main_win_destroy_event(self, *args): pass + + def on_state_apply_clicked(self, *args): + pass + + def on_state_cancel_clicked(self, *args): + state_editor = self._state_editor + state_editor.hide() + pass + + def on_error_dialog_ok_clicked(self, *args): + error_dialog = self._error_dialog + error_dialog.hide() + pass pass class FSM_transition(object): @@ -282,6 +348,12 @@ _control_layer = None width = 1024 height = 768 + + _grab_hdl = None + _bg_hdl = None + + _saved_x = 0 + _saved_y = 0 def __init__(self, domview_ui, close_cb, destroy_cb): super(FSM_window, self).__init__() @@ -319,6 +391,8 @@ control_layer.setAttribute('inkscape:groupmode', 'layer') root.appendChild(control_layer) self._control_layer = control_layer + + self.grab_bg(self.on_add_state_background) pass def _doc(self): @@ -332,6 +406,9 @@ root = doc.root() return root + def _translate_xy(self, x, y): + return x, y + def _clear_view(self): if not self._background: self._init_layers() @@ -347,20 +424,26 @@ self._states = {} pass - def _update_view(self): - self._clear_view() - + def _draw_state_domview(self, state_name): domview = self._domview doc = self._doc() fsm_layer = self._fsm_layer + state = FSM_state(state_name) + state.init(doc, domview, self._fsm_layer, self._control_layer) + self._states[state_name] = state + + state.draw(fsm_layer) + pass + + def _update_view(self): + self._clear_view() + + domview = self._domview + state_names = domview.all_state_names() for state_name in state_names: - state = FSM_state(state_name) - state.init(doc, domview, self._fsm_layer, self._control_layer) - self._states[state_name] = state - - state.draw(fsm_layer) + self._draw_state_domview(state_name) pass pass @@ -382,6 +465,58 @@ pass def on_add_state_toggled(self, *args): + self.ungrab_bg() + self.grab_bg(self.on_add_state_background) + pass + + def on_move_state_toggled(self, *args): + self.ungrab_bg() + self.grab_bg(self.on_move_state_background) + pass + + def on_add_state_background(self, item, evtype, buttons, x, y): + import pybInkscape + + if evtype == pybInkscape.PYSPItem.PYB_EVENT_BUTTON_RELEASE and \ + buttons == 1: + self._saved_x = x + self._saved_y = y + self.show_state_editor() + pass + pass + + def on_move_state_background(self, item, evtype, buttons, x, y): + pass + + def on_state_apply_clicked(self, *args): + import traceback + + domview = self._domview + x, y = self._translate_xy(self._saved_x, self._saved_y) + + state_name = self._state_name.get_text() + r_txt = self._state_radius.get_text() + try: + r = float(r_txt) + except ValueError: + traceback.print_exc() + self.show_error('Invalid value: "%s" is not a valid value ' + 'for radius.' % (r_txt)) + return + + try: + domview.add_state(state_name) + except: + traceback.print_exc() + self.show_error('Invalid state name: "%s" is existing' % + (state_name)) + return + domview.set_state_xy(state_name, x, y) + domview.set_state_r(state_name, r) + + self._draw_state_domview(state_name) + + self.hide_state_editor() pass def _install_test_data(self): @@ -432,6 +567,43 @@ self._update_view() super(FSM_window, self).show() pass + + def grab_mouse(self, callback): + assert self._grab_hdl is None + + root = self._root() + root.setAttribute('inkscape:groupmode', '') + self._grab_hdl = root.spitem.connect('mouse-event', callback) + pass + + def ungrab_mouse(self): + if not self._grab_hdl: + return + + root = self._root() + root.spitem.disconnect(self._grab_hdl) + self._grab_hdl = None + root.setAttribute('inkscape:groupmode', 'layer') + pass + + def grab_bg(self, callback): + assert self._bg_hdl is None + assert self._background + + background = self._background + bg_hdl = background.spitem.connect('mouse-event', callback) + self._bg_hdl = bg_hdl + pass + + def ungrab_bg(self): + if not self._bg_hdl: + return + + background = self._background + bg_hdl = self._bg_hdl + background.spitem.disconnect(bg_hdl) + self._bg_hdl = None + pass pass if __name__ == '__main__':