comparison clients/editor/plugins/ObjectSelector.py @ 255:51cc05d862f2

Merged editor_rewrite branch to trunk. This contains changes that may break compatibility against existing clients. For a list of changes that may affect your client, see: http://wiki.fifengine.de/Changes_to_pychan_and_FIFE_in_editor_rewrite_branch
author cheesesucker@33b003aa-7bff-0310-803a-e67f0ece8222
date Mon, 08 Jun 2009 16:00:02 +0000
parents
children 6add14ebe9f5
comparison
equal deleted inserted replaced
254:10b5f7f36dd4 255:51cc05d862f2
1 # coding: utf-8
2
3 import pychan
4 from pychan import widgets, tools, attrs, internal
5 from pychan.tools import callbackWithArguments
6 import scripts
7 import scripts.plugin as plugin
8 from scripts.events import *
9 from scripts.gui.action import Action
10 import fife
11 from fife import Color
12
13 # TODO:
14 # - Better event handling
15
16 _DEFAULT_BASE_COLOR = internal.DEFAULT_STYLE['default']['base_color']
17 _DEFAULT_SELECTION_COLOR = internal.DEFAULT_STYLE['default']['selection_color']
18 _DEFAULT_COLOR_STEP = Color(10, 10, 10)
19
20 class ObjectIcon(widgets.VBox):
21 """ The ObjectIcon is used to represent the object in the object selector.
22 """
23 ATTRIBUTES = widgets.VBox.ATTRIBUTES + [ attrs.Attr("text"), attrs.Attr("image"), attrs.BoolAttr("selected") ]
24
25 def __init__(self,callback,**kwargs):
26 super(ObjectIcon,self).__init__(**kwargs)
27
28 self.callback = callback
29
30 self.capture(self._mouseEntered, "mouseEntered")
31 self.capture(self._mouseExited, "mouseExited")
32 self.capture(self._mouseClicked, "mouseClicked")
33
34 vbox = widgets.VBox(padding=3)
35
36 # Icon
37 self.icon = widgets.Icon(**kwargs)
38 self.addChild(self.icon)
39
40 # Label
41 hbox = widgets.HBox(padding=1)
42 self.addChild(hbox)
43 self.label = widgets.Label(**kwargs)
44 hbox.addChild(self.label)
45
46 def _setText(self, text):
47 self.label.text = text
48
49 def _getText(self):
50 return self.label.text
51 text = property(_getText, _setText)
52
53 def _setImage(self, image):
54 self.icon.image = image
55
56 def _getImage(self):
57 return self.icon.image
58 image = property(_getImage, _setImage)
59
60 def _setSelected(self, enabled):
61 if isinstance(self.parent, ObjectIconList):
62 if enabled == True:
63 self.parent.selected_item = self
64 else:
65 if self.selected:
66 self.parent.selected_item = None
67
68 if self.selected:
69 self.base_color = _DEFAULT_SELECTION_COLOR
70 else:
71 self.base_color = _DEFAULT_BASE_COLOR
72
73 def _isSelected(self):
74 if isinstance(self.parent, ObjectIconList):
75 return self == self.parent.selected_item
76 return False
77 selected = property(_isSelected, _setSelected)
78
79 #--- Event handling ---#
80 def _mouseEntered(self, event):
81 self.base_color += _DEFAULT_COLOR_STEP
82
83 def _mouseExited(self, event):
84 self.base_color -= _DEFAULT_COLOR_STEP
85
86 def _mouseClicked(self, event):
87 self.selected = True
88 self.callback()
89
90 class ObjectIconList(widgets.VBox):
91 ATTRIBUTES = widgets.VBox.ATTRIBUTES
92
93 def __init__(self,**kwargs):
94 super(ObjectIconList, self).__init__(max_size=(5000,500000), **kwargs)
95 self.base_color = self.background_color
96
97 self.capture(self._keyPressed, "keyPressed")
98 #self.capture(self._keyPressed, "keyReleased")
99 self._selectedItem = None
100 self.is_focusable = True
101
102 def _keyPressed(self, event):
103 print "KeyEvent", event
104
105 def clear(self):
106 for c in reversed(self.children):
107 self.removeChild(c)
108
109 def _setSelectedItem(self, item):
110 if isinstance(item, ObjectIcon) or item is None:
111 if self._selectedItem is not None:
112 tmp = self._selectedItem
113 self._selectedItem = item
114 tmp.selected = False
115 else:
116 self._selectedItem = item
117
118 def _getSelectedItem(self):
119 return self._selectedItem
120 selected_item = property(_getSelectedItem, _setSelectedItem)
121
122 class ObjectSelector(plugin.Plugin):
123 """The ObjectSelector class offers a gui Widget that let's you select the object you
124 wish to use to in the editor.
125 @param engine: fife instance
126 @param map: fife.Map instance containing your map
127 @param selectNotify: callback function used to tell the editor you selected an object.
128 """
129 def __init__(self):
130 self.editor = None
131 self.engine = None
132 self.mode = 'list' # Other mode is 'preview'
133
134 self._enabled = False
135
136 def enable(self):
137 if self._enabled is True:
138 return
139
140 self.editor = scripts.editor.getEditor()
141 self.engine = self.editor.getEngine()
142
143 self._showAction = Action(u"Object selector", checkable=True)
144 scripts.gui.action.activated.connect(self.toggle, sender=self._showAction)
145
146 self.editor._toolsMenu.addAction(self._showAction)
147
148 events.postMapShown.connect(self.update_namespace)
149 events.onObjectSelected.connect(self.setPreview)
150
151 self.buildGui()
152
153 def disable(self):
154 if self._enabled is False:
155 return
156
157 self.gui.hide()
158 self.removeAllChildren()
159
160 events.postMapShown.disconnect(self.update_namespace)
161 events.onObjectSelected.disconnect(self.setPreview)
162
163 self.editor._toolsMenu.removeAction(self._showAction)
164
165 def isEnabled(self):
166 return self._enabled;
167
168 def getName(self):
169 return "Object selector"
170
171
172 def buildGui(self):
173 self.gui = pychan.loadXML('gui/objectselector.xml')
174
175 # Add search field
176 self._searchfield = self.gui.findChild(name="searchField")
177 self._searchfield.capture(self._search)
178 self._searchfield.capture(self._search, "keyPressed")
179 self.gui.findChild(name="searchButton").capture(self._search)
180
181 # Add the drop down with list of namespaces
182 self.namespaces = self.gui.findChild(name="namespaceDropdown")
183 self.namespaces.items = self.engine.getModel().getNamespaces()
184 self.namespaces.selected = 0
185
186 # TODO: Replace with SelectionEvent, once pychan supports it
187 self.namespaces.capture(self.update_namespace, "action")
188 self.namespaces.capture(self.update_namespace, "mouseWheelMovedUp")
189 self.namespaces.capture(self.update_namespace, "mouseWheelMovedDown")
190 self.namespaces.capture(self.update_namespace, "keyReleased")
191
192 # Object list
193 self.mainScrollArea = self.gui.findChild(name="mainScrollArea")
194 self.objects = None
195 if self.mode == 'list':
196 self.setTextList()
197 else: # Assuming self.mode is 'preview'
198 self.setImageList()
199
200 # Action buttons
201 self.gui.findChild(name="toggleModeButton").capture(self.toggleMode)
202 self.gui.findChild(name="closeButton").capture(self.hide)
203
204 # Preview area
205 self.gui.findChild(name="previewScrollArea").background_color = self.gui.base_color
206 self.preview = self.gui.findChild(name="previewIcon")
207
208
209 def toggleMode(self):
210 if self.mode == 'list':
211 self.setImageList()
212 self.mode = 'preview'
213 elif self.mode == 'preview':
214 self.setTextList()
215 self.mode = 'list'
216 self.update()
217
218
219 def setImageList(self):
220 """Sets the mainScrollArea to contain a Vbox that can be used to fill in
221 preview Images"""
222 if self.objects is not None:
223 self.mainScrollArea.removeChild(self.objects)
224 self.objects = ObjectIconList(name='list', size=(200,1000))
225 self.objects.base_color = self.mainScrollArea.background_color
226 self.mainScrollArea.addChild(self.objects)
227
228 def setTextList(self):
229 """Sets the mainScrollArea to contain a List that can be used to fill in
230 Object names/paths"""
231 if self.objects is not None:
232 self.mainScrollArea.removeChild(self.objects)
233 self.objects = widgets.ListBox(name='list')
234 self.objects.capture(self.listEntrySelected)
235 self.mainScrollArea.addChild(self.objects)
236
237 def _search(self):
238 self.search(self._searchfield.text)
239
240 def search(self, str):
241 results = []
242
243 # Format search terms
244 terms = [term.lower() for term in str.split()]
245
246 # Search
247 if len(terms) > 0:
248 namespaces = self.engine.getModel().getNamespaces()
249 for namesp in namespaces:
250 objects = self.engine.getModel().getObjects(namesp)
251 for obj in objects:
252 doAppend = True
253 for term in terms:
254 if obj.getId().lower().find(term) < 0:
255 doAppend = False
256 break
257 if doAppend:
258 results.append(obj)
259 else:
260 results = None
261
262 if self.mode == 'list':
263 self.fillTextList(results)
264 elif self.mode == 'preview':
265 self.fillPreviewList(results)
266
267 def fillTextList(self, objects=None):
268 if objects is None:
269 if self.namespaces.selected_item is None:
270 return
271 objects = self.engine.getModel().getObjects(self.namespaces.selected_item)
272
273 class _ListItem:
274 def __init__( self, name, namespace ):
275 self.name = name
276 self.namespace = namespace
277 def __str__( self ):
278 return self.name
279
280 if self.namespaces.selected_item:
281 self.objects.items = [_ListItem(obj.getId(), obj.getNamespace()) for obj in objects]
282 if not self.objects.selected_item:
283 self.objects.selected = 0
284 self.listEntrySelected()
285
286 def listEntrySelected(self):
287 """This function is used as callback for the TextList."""
288 if self.objects.selected_item:
289 object_id = self.objects.selected_item.name
290 namespace = self.objects.selected_item.namespace
291 obj = self.engine.getModel().getObject(object_id, namespace)
292 self.objectSelected(obj)
293
294 def fillPreviewList(self, objects=None):
295 self.objects.clear()
296
297 if objects is None:
298 if self.namespaces.selected_item is None:
299 return
300 objects = self.engine.getModel().getObjects(self.namespaces.selected_item)
301
302 for obj in objects:
303 image = self._getImage(obj)
304 if image is None:
305 print 'No image available for selected object'
306 image = ""
307
308 callback = tools.callbackWithArguments(self.objectSelected, obj)
309 icon = ObjectIcon(callback=callback, image=image, text=unicode(obj.getId()))
310 self.objects.addChild(icon)
311
312 if len(objects)>0:
313 objects[0].selected = True
314 self.objectSelected(objects[0])
315
316
317 def objectSelected(self, obj):
318 """This is used as callback function to notify the editor that a new object has
319 been selected.
320 @param obj: fife.Object instance"""
321
322 self.setPreview(obj)
323
324 self.gui.adaptLayout()
325 events.onObjectSelected.send(sender=self, object=obj)
326
327 self.objects.adaptLayout()
328 self.gui.adaptLayout()
329
330 # Set preview image
331 def setPreview(self, object):
332 self.preview.image = self._getImage(object)
333 height = self.preview.image.getHeight();
334 if height > 200: height = 200
335 self.preview.parent.max_height = height
336
337 def update_namespace(self):
338
339 self.namespaces.items = self.engine.getModel().getNamespaces()
340 if not self.namespaces.selected_item:
341 self.namespaces.selected = 0
342 if self.mode == 'list':
343 self.setTextList()
344 elif self.mode == 'preview':
345 self.setImageList()
346 self.update()
347
348 def update(self):
349 if self.mode == 'list':
350 self.fillTextList()
351 elif self.mode == 'preview':
352 self.fillPreviewList()
353
354 self.gui.adaptLayout()
355
356 def _getImage(self, obj):
357 """ Returns an image for the given object.
358 @param: fife.Object for which an image is to be returned
359 @return: fife.GuiImage"""
360 visual = None
361 try:
362 visual = obj.get2dGfxVisual()
363 except:
364 print 'Visual Selection created for type without a visual?'
365 raise
366
367 # Try to find a usable image
368 index = visual.getStaticImageIndexByAngle(0)
369 image = None
370 # if no static image available, try default action
371 if index == -1:
372 action = obj.getDefaultAction()
373 if action:
374 animation_id = action.get2dGfxVisual().getAnimationIndexByAngle(0)
375 animation = self.engine.getAnimationPool().getAnimation(animation_id)
376 image = animation.getFrameByTimestamp(0)
377 index = image.getPoolId()
378
379 # Construct the new GuiImage that is to be returned
380 if index != -1:
381 image = fife.GuiImage(index, self.engine.getImagePool())
382
383 return image
384
385
386 def show(self):
387 self.update_namespace()
388 self.gui.show()
389 self._showAction.setChecked(True)
390
391 def hide(self):
392 self.gui.setDocked(False)
393 self.gui.hide()
394 self._showAction.setChecked(False)
395
396 def toggle(self):
397 if self.gui.isVisible() or self.gui.isDocked():
398 self.hide()
399 else:
400 self.show()