def __init__(self): super(ObjectEdit, self).__init__() self._editor = scripts.editor.getEditor() self.engine = self._editor.getEngine() self.xml_saver = XMLObjectSaver(self.engine) self.default_x, self.default_y = _POSITION self._enabled = False self._showAction = None self._help_dialog = None self.default_settings = _PLUGIN_SETTINGS self.eds = self._editor._settings self.update_settings() self.reset()
class ObjectEdit(plugin.Plugin): """ The B{ObjectEdit} module is a plugin for FIFedit and allows to edit attributes of a selected object fife.Objects can be selected via the B{ObjectSelector}. fife.Instances are instances of fife.Objects - so clicking on a "map object" is actually not selecting an object. Not sure how to handle that - for now this Plugin only reacts on selections in the ObjectSelector There is an InstanceEdit plugin yet to be written which allows the manipulation of instance data. """ def __init__(self): super(ObjectEdit, self).__init__() self._editor = scripts.editor.getEditor() self.engine = self._editor.getEngine() self.xml_saver = XMLObjectSaver(self.engine) self.default_x, self.default_y = _POSITION self._enabled = False self._showAction = None self._help_dialog = None self.default_settings = _PLUGIN_SETTINGS self.eds = self._editor._settings self.update_settings() self.reset() def reset(self): """ reset major ivars """ self.current_angle = None self.current_action = None self._object = None self._instance = None def enable(self): """ plugin method """ if self._enabled: return self._enabled = True self._action_show = Action(unicode(self.getName(), "utf-8"), checkable=True) scripts.gui.action.activated.connect(self.toggle_gui, sender=self._action_show) self._editor._tools_menu.addAction(self._action_show) onObjectSelected.connect(self.update) onInstancesSelected.connect(self.update) preMapClosed.connect(self.hide) postMapShown.connect(self.update) self.create_gui() self.update_gui() if self.settings['docked']: self._editor.dockWidgetTo(self.container, self.settings['dockarea']) def disable(self): """ plugin method """ if not self._enabled: return self._enabled = False self.container.hide() self.reset() onObjectSelected.disconnect(self.update) onInstancesSelected.disconnect(self.update) preMapClosed.disconnect(self.hide) postMapShown.disconnect(self.update) self._editor._tools_menu.removeAction(self._action_show) def isEnabled(self): """ plugin method """ return self._enabled def getName(self): """ plugin method """ return "Object editor" def _show_help(self): """ shows the help dialog """ if self._help_dialog is not None: self._help_dialog.show() return self._help_dialog = pychan.loadXML("gui/help.xml") self._help_dialog.title = u"Help (Object Editor)" self._help_dialog.mapEvents({ "closeButton": self._help_dialog.hide, }) # gimme some more space _SIZE = (320, 400) scrollarea = self._help_dialog.findChildren( __class__=pychan.widgets.ScrollArea)[0] scrollarea.size = _SIZE scrollarea.min_size = _SIZE scrollarea.max_size = _SIZE f = open('lang/help_object_edit.txt', 'r') self._help_dialog.findChild(name="helpText").text = unicode(f.read()) f.close() self._help_dialog.show() def create_gui(self): """ - creates the gui skeleton by loading the xml file - finds some important childs and saves their widget in the object @todo: - move all dynamic widgets to dict """ self.container = pychan.loadXML('gui/objectedit.xml') self.container.position_technique = 'explicit' self.container.position = _POSITION self.container.mapEvents({ 'change_data': self.save, 'show_help': self._show_help, }) self.container.findChild(name="x_offset_up").capture( self.change_offset, "mousePressed") self.container.findChild(name="x_offset_dn").capture( self.change_offset, "mousePressed") self.x_offset = self.container.findChild(name="x_offset") self.x_offset.capture(self.change_offset, "mouseWheelMovedUp") self.x_offset.capture(self.change_offset, "mouseWheelMovedDown") self.container.findChild(name="y_offset_up").capture( self.change_offset, "mousePressed") self.container.findChild(name="y_offset_dn").capture( self.change_offset, "mousePressed") self.y_offset = self.container.findChild(name="y_offset") self.y_offset.capture(self.change_offset, "mouseWheelMovedUp") self.y_offset.capture(self.change_offset, "mouseWheelMovedDown") self.container.findChild(name="object_blocking_toggle").capture( self.object_blocking_toggle, "mousePressed") self.rotations_listbox = self.container.findChild( name="select_rotations") self.rotations_listbox.capture(self.select_rotation, "mouseWheelMovedUp") self.rotations_listbox.capture(self.select_rotation, "mouseWheelMovedDown") self.rotations_listbox.capture(self.select_rotation, "action") self.xoffset_textfield = self.container.findChild(name="x_offset") self.yoffset_textfield = self.container.findChild(name="y_offset") self.actions_wrapper = self.container.findChild(name="actions_wrapper") self.rotations_wrapper = self.container.findChild( name="rotations_wrapper") self.actions_listbox = self.container.findChild(name="select_actions") self.actions_listbox.capture(self.select_action, "mouseWheelMovedUp") self.actions_listbox.capture(self.select_action, "mouseWheelMovedDown") self.actions_listbox.capture(self.select_action, "action") def get_image(self, rotations=None): """ try to get the current image of the object @rtype image: fife.Image @return image: image of the current angle of the current object """ if self._object is None: return image = None if rotations is None: rotations = self.get_rotations() if rotations: if self.current_angle is not None and self.current_angle in rotations: angle = self.current_angle else: angle = rotations[0] visual = self._object.get2dGfxVisual() index = visual.getStaticImageIndexByAngle(angle) if index == -1: action = self._object.getDefaultAction() if action: animation = action.get2dGfxVisual().getAnimationByAngle( angle) image = animation.getFrameByTimestamp(0) else: image = self.engine.getImageManager().get(index) return image def get_rotations(self): """ get either the static rotations or those of the action (if the object has one) """ rotations = [] visual = self._object.get2dGfxVisual() rotations = visual.getStaticImageAngles() if not rotations and self._object.getDefaultAction(): rotations = self._object.getDefaultAction().get2dGfxVisual( ).getActionImageAngles() return rotations def update_gui(self): """ updates the gui widgets with current object data """ x_offset = _DEFAULT_ENTRY y_offset = _DEFAULT_ENTRY rotations = [] actions = [] namespace = _DEFAULT_ENTRY blocking = bool(_DEFAULT_ENTRY) static = _DEFAULT_ENTRY _id = _DEFAULT_ENTRY cost_id = _DEFAULT_ENTRY cost = _DEFAULT_ENTRY cellstack_pos = _DEFAULT_ENTRY if self._object is not None: actions = list(self._object.getActionIds()) rotations = self.get_rotations() image = self.get_image(rotations=rotations) if image is not None: x_offset = unicode(str(image.getXShift()), 'utf-8') y_offset = unicode(str(image.getYShift()), 'utf-8') namespace = unicode(self._object.getNamespace(), 'utf-8') blocking = self._object.isBlocking() static = unicode(str(int(self._object.isStatic())), 'utf-8') _id = unicode(self._object.getId(), 'utf-8') if hasattr(self._object, 'getCostId'): cost_id = unicode(self._object.getCostId(), 'utf-8') if hasattr(self._object, 'getCost'): cost = unicode(str(self._object.getCost()), 'utf-8') if hasattr(self._object, 'getCellStackPosition'): cellstack_pos = unicode( str(self._object.getCellStackPosition()), 'utf-8') self.container.distributeInitialData({ 'object_id': u"\t" + _id, 'x_offset': x_offset, 'y_offset': y_offset, 'object_namespace': u"\t" + namespace, 'object_static': static, 'cost_id': cost_id, 'cost_value': cost, 'object_cellstack_pos': cellstack_pos, }) wdgt = self.container.findChild(name="object_blocking_toggle") wdgt.marked = blocking self.rotations_listbox.items = rotations if rotations: if self.current_angle in rotations: index = rotations.index(self.current_angle) else: index = 0 self.rotations_listbox.selected = index self.rotations_wrapper.show() else: self.rotations_wrapper.hide() self.actions_listbox.items = actions if actions: if self.current_action in actions: index = actions.index(self.current_action) else: index = 0 self.actions_listbox.selected = index self.actions_wrapper.show() else: self.actions_wrapper.hide() if not self.container.isDocked(): self.container.adaptLayout(True) def toggle_gui(self): """ show / hide the gui """ if self.container.isVisible(): self.last_dockarea = self.container.dockarea self.container.hide() self._action_show.setChecked(False) else: if not self.container.isDocked(): self.container.show() self.container.x = self.default_x self.container.y = self.default_y else: self.container.setDocked(True) self.dockWidgetTo(self.container, self.last_dockarea) self._action_show.setChecked(True) def change_offset(self, event, widget): """ widget callback: change the offset of an object @type event: object @param event: FIFE mouseevent or keyevent @type widget: object @param widget: pychan widget """ if self._object is None: return etype = event.getType() image = self.get_image() if image is None: return x = image.getXShift() y = image.getYShift() if etype == fife.MouseEvent.WHEEL_MOVED_UP or widget.name.endswith( "up"): modifier = 1 elif etype == fife.MouseEvent.WHEEL_MOVED_DOWN or widget.name.endswith( "dn"): modifier = -1 if widget.name.startswith("x"): x += modifier elif widget.name.startswith("y"): y += modifier self.set_offset(x, y, image=image) self.update_gui() def object_blocking_toggle(self, event, widget): """ widget callback: change the blocking of an instance @type event: object @param event: FIFE mouseevent or keyevent @type widget: object @param widget: pychan widget """ if self._object is None: return blocking = widget.marked self._object.setBlocking(bool(blocking)) self.update_gui() def save(self): """ saves the current object to its xml file @note: - saves only object data and static image data - no animation data or atlas data is saved """ if self._object is None: return self.xml_saver.save(self._object) self._editor.getStatusBar().setText( u"Saving of object data successful: %s" % self._object.getId()) def select_action(self): """ let an instance act and set the action of the current object """ if self._object is None: return action = str(self.actions_listbox.selected_item) self.current_action = action # @todo: compat layer - trunk/ doesn't have this method exposed # to python yet (cell pathfinding branch has) # @todo: remove this check later if hasattr(self._object, 'setDefaultAction'): self._object.setDefaultAction(action) if self._instance is not None and action is not None: f_loc = self._instance.getFacingLocation() self._instance.actOnce(action, f_loc) def select_rotation(self): """ rotate an instance due to selected angle """ if self._object is None: return angle = self.eval_rotation() self.current_angle = angle if self._instance is not None and angle is not None: self._instance.setRotation(int(angle)) self.update_gui() def eval_rotation(self): """ prepare rotation from gui and apply it to the current selected instance """ selected = self.rotations_listbox.selected_item if selected is None: return selected angle = int(selected) if angle == 360: angle = 0 return angle def set_offset(self, x=None, y=None, image=None): """ set x/y offset of current selected instance """ if self._object is None: return if self._object.isStatic(): if image is None: image = self.get_image() if image is None: return if x is not None: image.setXShift(x) if y is not None: image.setYShift(y) else: self.set_animation_offset(x, y) def set_animation_offset(self, x, y): """ apply the offset to all frames of the default action """ if self.current_action is not None: action = self._object.getAction(self.current_action) else: action = self._object.getDefaultAction() if not action: return if self.current_angle is not None: angle = self.current_angle else: angle = 0 animation = action.get2dGfxVisual().getAnimationByAngle(angle) if not animation: return for index in range(animation.getFrameCount()): image = animation.getFrame(index) image.setXShift(x) image.setYShift(y) def show(self): """ show the plugin gui """ self.container.show() self.container.adaptLayout(False) def hide(self): """ hide the plugin gui - and reset it """ self.container.hide() self.reset() def update(self, object=None, instances=[]): """ called by the editor onObjectSelected or onInstancesSelected (we only use the top instance of the selected cell) @type instances: list @param instances: a list of instances in the selected cell """ self.reset() if object is None and not instances: self._object = None self._instance = None self.update_gui() return if instances: self._instance = instances[0] object = instances[0].getObject() action = object.getDefaultAction() if action: self.current_action = action.getId() self._object = object self.update_gui()
class ObjectEdit(plugin.Plugin): """ The B{ObjectEdit} module is a plugin for FIFedit and allows to edit attributes of a selected object fife.Objects can be selected via the B{ObjectSelector}. fife.Instances are instances of fife.Objects - so clicking on a "map object" is actually not selecting an object. Not sure how to handle that - for now this Plugin only reacts on selections in the ObjectSelector There is an InstanceEdit plugin yet to be written which allows the manipulation of instance data. """ def __init__(self): super(ObjectEdit, self).__init__() self._editor = scripts.editor.getEditor() self.engine = self._editor.getEngine() self.xml_saver = XMLObjectSaver(self.engine) self.default_x, self.default_y = _POSITION self._enabled = False self._showAction = None self._help_dialog = None self.default_settings = _PLUGIN_SETTINGS self.eds = self._editor._settings self.update_settings() self.reset() def reset(self): """ reset major ivars """ self.current_angle = None self.current_action = None self._object = None self._instance = None def enable(self): """ plugin method """ if self._enabled: return self._enabled = True self._action_show = Action(unicode(self.getName(),"utf-8"), checkable=True) scripts.gui.action.activated.connect(self.toggle_gui, sender=self._action_show) self._editor._tools_menu.addAction(self._action_show) onObjectSelected.connect(self.update) onInstancesSelected.connect(self.update) preMapClosed.connect(self.hide) postMapShown.connect(self.update) self.create_gui() self.update_gui() if self.settings['docked']: self._editor.dockWidgetTo(self.container, self.settings['dockarea']) def disable(self): """ plugin method """ if not self._enabled: return self._enabled = False self.container.hide() self.reset() onObjectSelected.disconnect(self.update) onInstancesSelected.disconnect(self.update) preMapClosed.disconnect(self.hide) postMapShown.disconnect(self.update) self._editor._tools_menu.removeAction(self._action_show) def isEnabled(self): """ plugin method """ return self._enabled; def getName(self): """ plugin method """ return "Object editor" def _show_help(self): """ shows the help dialog """ if self._help_dialog is not None: self._help_dialog.show() return self._help_dialog = pychan.loadXML("gui/help.xml") self._help_dialog.title = u"Help (Object Editor)" self._help_dialog.mapEvents({ "closeButton" : self._help_dialog.hide, }) # gimme some more space _SIZE = (320,400) scrollarea = self._help_dialog.findChildren(__class__=pychan.widgets.ScrollArea)[0] scrollarea.size = _SIZE scrollarea.min_size = _SIZE scrollarea.max_size = _SIZE f = open('lang/help_object_edit.txt', 'r') self._help_dialog.findChild(name="helpText").text = unicode(f.read()) f.close() self._help_dialog.show() def create_gui(self): """ - creates the gui skeleton by loading the xml file - finds some important childs and saves their widget in the object @todo: - move all dynamic widgets to dict """ self.container = pychan.loadXML('gui/objectedit.xml') self.container.position_technique = 'explicit' self.container.position = _POSITION self.container.mapEvents({ 'change_data' : self.save, 'show_help' : self._show_help, }) self.container.findChild(name="x_offset_up").capture(self.change_offset, "mousePressed") self.container.findChild(name="x_offset_dn").capture(self.change_offset, "mousePressed") self.x_offset = self.container.findChild(name="x_offset") self.x_offset.capture(self.change_offset, "mouseWheelMovedUp") self.x_offset.capture(self.change_offset, "mouseWheelMovedDown") self.container.findChild(name="y_offset_up").capture(self.change_offset, "mousePressed") self.container.findChild(name="y_offset_dn").capture(self.change_offset, "mousePressed") self.y_offset = self.container.findChild(name="y_offset") self.y_offset.capture(self.change_offset, "mouseWheelMovedUp") self.y_offset.capture(self.change_offset, "mouseWheelMovedDown") self.container.findChild(name="object_blocking_toggle").capture(self.object_blocking_toggle, "mousePressed") self.rotations_listbox = self.container.findChild(name="select_rotations") self.rotations_listbox.capture(self.select_rotation,"mouseWheelMovedUp") self.rotations_listbox.capture(self.select_rotation,"mouseWheelMovedDown") self.rotations_listbox.capture(self.select_rotation,"action") self.xoffset_textfield = self.container.findChild(name="x_offset") self.yoffset_textfield = self.container.findChild(name="y_offset") self.actions_wrapper = self.container.findChild(name="actions_wrapper") self.rotations_wrapper = self.container.findChild(name="rotations_wrapper") self.actions_listbox = self.container.findChild(name="select_actions") self.actions_listbox.capture(self.select_action,"mouseWheelMovedUp") self.actions_listbox.capture(self.select_action,"mouseWheelMovedDown") self.actions_listbox.capture(self.select_action,"action") def get_image(self, rotations=None): """ try to get the current image of the object @rtype image: fife.Image @return image: image of the current angle of the current object """ if self._object is None: return image = None if rotations is None: rotations = self.get_rotations() if rotations: if self.current_angle is not None and self.current_angle in rotations: angle = self.current_angle else: angle = rotations[0] visual = self._object.get2dGfxVisual() index = visual.getStaticImageIndexByAngle(angle) if index == -1: action = self._object.getDefaultAction() if action: animation = action.get2dGfxVisual().getAnimationByAngle(angle) image = animation.getFrameByTimestamp(0) else: image = self.engine.getImageManager().get(index) return image def get_rotations(self): """ get either the static rotations or those of the action (if the object has one) """ rotations = [] visual = self._object.get2dGfxVisual() rotations = visual.getStaticImageAngles() if not rotations and self._object.getDefaultAction(): rotations = self._object.getDefaultAction().get2dGfxVisual().getActionImageAngles() return rotations def update_gui(self): """ updates the gui widgets with current object data """ x_offset = _DEFAULT_ENTRY y_offset = _DEFAULT_ENTRY rotations = [] actions = [] namespace = _DEFAULT_ENTRY blocking = bool(_DEFAULT_ENTRY) static = _DEFAULT_ENTRY _id = _DEFAULT_ENTRY cost_id = _DEFAULT_ENTRY cost = _DEFAULT_ENTRY cellstack_pos = _DEFAULT_ENTRY if self._object is not None: actions = list(self._object.getActionIds()) rotations = self.get_rotations() image = self.get_image(rotations=rotations) if image is not None: x_offset = unicode(str(image.getXShift()), 'utf-8') y_offset = unicode(str(image.getYShift()), 'utf-8') namespace = unicode(self._object.getNamespace(), 'utf-8') blocking = self._object.isBlocking() static = unicode(str(int(self._object.isStatic())), 'utf-8') _id = unicode(self._object.getId(), 'utf-8') if hasattr(self._object, 'getCostId'): cost_id = unicode(self._object.getCostId(), 'utf-8') if hasattr(self._object, 'getCost'): cost = unicode(str(self._object.getCost()), 'utf-8') if hasattr(self._object, 'getCellStackPosition'): cellstack_pos = unicode(str(self._object.getCellStackPosition()), 'utf-8') self.container.distributeInitialData({ 'object_id' : u"\t" + _id, 'x_offset' : x_offset, 'y_offset' : y_offset, 'object_namespace' : u"\t" + namespace, 'object_static' : static, 'cost_id' : cost_id, 'cost_value' : cost, 'object_cellstack_pos' : cellstack_pos, }) wdgt = self.container.findChild(name="object_blocking_toggle") wdgt.marked = blocking self.rotations_listbox.items = rotations if rotations: if self.current_angle in rotations: index = rotations.index(self.current_angle) else: index = 0 self.rotations_listbox.selected = index self.rotations_wrapper.show() else: self.rotations_wrapper.hide() self.actions_listbox.items = actions if actions: if self.current_action in actions: index = actions.index(self.current_action) else: index = 0 self.actions_listbox.selected = index self.actions_wrapper.show() else: self.actions_wrapper.hide() if not self.container.isDocked(): self.container.adaptLayout(True) def toggle_gui(self): """ show / hide the gui """ if self.container.isVisible(): self.last_dockarea = self.container.dockarea self.container.hide() self._action_show.setChecked(False) else: if not self.container.isDocked(): self.container.show() self.container.x = self.default_x self.container.y = self.default_y else: self.container.setDocked(True) self.dockWidgetTo(self.container, self.last_dockarea) self._action_show.setChecked(True) def change_offset(self, event, widget): """ widget callback: change the offset of an object @type event: object @param event: FIFE mouseevent or keyevent @type widget: object @param widget: pychan widget """ if self._object is None: return etype = event.getType() image = self.get_image() if image is None: return x = image.getXShift() y = image.getYShift() if etype == fife.MouseEvent.WHEEL_MOVED_UP or widget.name.endswith("up"): modifier = 1 elif etype == fife.MouseEvent.WHEEL_MOVED_DOWN or widget.name.endswith("dn"): modifier = -1 if widget.name.startswith("x"): x += modifier elif widget.name.startswith("y"): y += modifier self.set_offset(x, y, image=image) self.update_gui() def object_blocking_toggle(self, event, widget): """ widget callback: change the blocking of an instance @type event: object @param event: FIFE mouseevent or keyevent @type widget: object @param widget: pychan widget """ if self._object is None: return blocking = widget.marked self._object.setBlocking(bool(blocking)) self.update_gui() def save(self): """ saves the current object to its xml file @note: - saves only object data and static image data - no animation data or atlas data is saved """ if self._object is None: return self.xml_saver.save(self._object) self._editor.getStatusBar().setText(u"Saving of object data successful: %s" % self._object.getId()) def select_action(self): """ let an instance act and set the action of the current object """ if self._object is None: return action = str(self.actions_listbox.selected_item) self.current_action = action # @todo: compat layer - trunk/ doesn't have this method exposed # to python yet (cell pathfinding branch has) # @todo: remove this check later if hasattr(self._object, 'setDefaultAction'): self._object.setDefaultAction(action) if self._instance is not None and action is not None: f_loc = self._instance.getFacingLocation() self._instance.actOnce(action, f_loc) def select_rotation(self): """ rotate an instance due to selected angle """ if self._object is None: return angle = self.eval_rotation() self.current_angle = angle if self._instance is not None and angle is not None: self._instance.setRotation(int(angle)) self.update_gui() def eval_rotation(self): """ prepare rotation from gui and apply it to the current selected instance """ selected = self.rotations_listbox.selected_item if selected is None: return selected angle = int(selected) if angle == 360: angle = 0 return angle def set_offset(self, x=None, y=None, image=None): """ set x/y offset of current selected instance """ if self._object is None: return if self._object.isStatic(): if image is None: image = self.get_image() if image is None: return if x is not None: image.setXShift(x) if y is not None: image.setYShift(y) else: self.set_animation_offset(x, y) def set_animation_offset(self, x, y): """ apply the offset to all frames of the default action """ if self.current_action is not None: action = self._object.getAction(self.current_action) else: action = self._object.getDefaultAction() if not action: return if self.current_angle is not None: angle = self.current_angle else: angle = 0 animation = action.get2dGfxVisual().getAnimationByAngle(angle) if not animation: return for index in range(animation.getFrameCount()): image = animation.getFrame(index) image.setXShift(x) image.setYShift(y) def show(self): """ show the plugin gui """ self.container.show() self.container.adaptLayout(False) def hide(self): """ hide the plugin gui - and reset it """ self.container.hide() self.reset() def update(self, object=None, instances=[]): """ called by the editor onObjectSelected or onInstancesSelected (we only use the top instance of the selected cell) @type instances: list @param instances: a list of instances in the selected cell """ self.reset() if object is None and not instances: self._object = None self._instance = None self.update_gui() return if instances: self._instance = instances[0] object = instances[0].getObject() action = object.getDefaultAction() if action: self.current_action = action.getId() self._object = object self.update_gui()