3c39a357823074836bd24fc7c97243c9d6ae27d7
1 from collections
import namedtuple
3 from logging
import info
4 from importlib
import import_module
5 from os
.path
import basename
6 from inspect
import isclass
7 from panda3d
.core
import Texture
, TextNode
, LPoint3f
8 from direct
.gui
.OnscreenImage
import OnscreenImage
9 from direct
.gui
.DirectGui
import DirectButton
, DirectFrame
, DirectEntry
, DirectOptionMenu
, OkDialog
10 from direct
.gui
.DirectGuiGlobals
import FLAT
, NORMAL
11 from direct
.gui
.OnscreenText
import OnscreenText
12 from direct
.showbase
.DirectObject
import DirectObject
13 from pmachines
.items
.item
import ItemStrategy
, FixedStrategy
, StillStrategy
14 from pmachines
.items
.box
import HitStrategy
15 from pmachines
.items
.domino
import DownStrategy
, UpStrategy
16 from pmachines
.editor
.augmented_frame
import AugmentedDirectFrame
17 from ya2
.utils
.gui
.base_page
import DirectOptionMenuTestable
18 from ya2
.utils
.gfx
import DirectGuiMixin
21 class Inspector(DirectObject
):
23 def __init__(self
, item
, all_items
, pos_mgr
, strategy_items
):
26 self
.__all
_items
= all_items
27 self
.__pos
_mgr
= pos_mgr
28 self
._font
= base
.loader
.load_font(
29 'assets/fonts/Hanken-Book.ttf')
31 self
._font
.set_pixels_per_unit(60)
32 self
._font
.set_minfilter(Texture
.FTLinearMipmapLinear
)
33 self
._font
.set_outline((0, 0, 0, 1), .8, .2)
36 'text_font': self
._font
,
37 'text_fg': (.9, .9, .9, 1),
39 'frameColor': (.4, .4, .4, .14),
40 'rolloverSound': loader
.load_sfx(
41 'assets/audio/sfx/rollover.ogg'),
42 'clickSound': loader
.load_sfx(
43 'assets/audio/sfx/click.ogg')}
44 tooltip_args
= self
._common
['text_font'], self
._common
['scale'], self
._common
['text_fg']
46 self
._frm
= AugmentedDirectFrame(frameColor
=(.4, .4, .4, .06),
47 frameSize
=(0, w
, -h
, 0),
48 parent
=base
.a2dTopRight
,
50 delta_drag
=LPoint3f(-w
, 0, -h
),
51 collapse_pos
=(w
- .06, 1, -h
+ .06),
53 frame_name
='inspector')
55 p
= self
.__item
._np
.get_pos()
56 r
= self
.__item
._np
.get_r()
57 s
= self
.__item
._np
.get_scale()[0]
59 restitution
= self
.__item
._restitution
60 f
= self
.__item
._friction
62 if 'id' in self
.__item
.json
:
63 _id
= self
.__item
.json
['id']
65 if 'strategy' in self
.__item
.json
:
66 _strategy
= self
.__item
.json
['strategy']
68 if 'strategy_args' in self
.__item
.json
:
69 _strategy_args
= ' '.join(map(str, self
.__item
.json
['strategy_args']))
70 t
, pos_entry
= self
.__add
_row
('position', _('position'), f
'{round(p.x, 3)} {round(p.z, 3)}', self
.on_edit_position
, _('position (e.g. 0.1 2.3 4.5)'))
71 t
, rot_entry
= self
.__add
_row
('roll', _('roll'), f
'{round(r, 3)}', self
.on_edit_roll
, _('roll (e.g. 90)'))
72 t
, scale_entry
= self
.__add
_row
('scale', _('scale'), f
'{round(s, 3)}', self
.on_edit_scale
, _('scale (e.g. 1.2)'))
73 t
, mass_entry
= self
.__add
_row
('mass', _('mass'), f
'{round(m, 3)}', self
.on_edit_mass
, _('mass (default 1; 0 if fixed)'))
74 t
, restitution_entry
= self
.__add
_row
('restitution', _('restitution'), f
'{round(restitution, 3)}', self
.on_edit_restitution
, _('restitution (default 0.5)'))
75 t
, friction_entry
= self
.__add
_row
('friction', _('friction'), f
'{round(f, 3)}', self
.on_edit_friction
, _('friction (default 0.5)'))
76 t
, id_entry
= self
.__add
_row
('id', _('id'), _id
, self
.on_edit_id
, _('id of the item (for the strategies)'))
77 # item_modules = glob('pmachines/items/*.py')
78 # item_modules = [basename(i)[:-3] for i in item_modules]
79 # strategy_items = ['']
80 # for item_module in item_modules:
81 # mod_name = 'pmachines.items.' + item_module
82 # for member in import_module(mod_name).__dict__.values():
83 # if isclass(member) and issubclass(member, ItemStrategy) and \
84 # member != ItemStrategy:
85 # strategy_items = list(set(strategy_items + [member.__name__]))
86 strategy_names
= [s
.__name
__ for s
in strategy_items
]
87 t
, strategy_entry
= self
.__add
_row
_option
(_('strategy'), _strategy
, strategy_names
, self
.on_edit_strategy
, _('the strategy of the item'))
89 def strategy_set(comps
):
90 strategy_labels
= [f
'inspector_strategy_{i.lower()}' for i
in strategy_names
]
91 for i
in strategy_labels
:
92 if i
in self
.__pos
_mgr
:
94 for l
, b
in zip(strategy_labels
, comps
):
95 b
.__class
__ = type('DirectFrameMixed', (DirectFrame
, DirectGuiMixin
), {})
97 self
.__pos
_mgr
[l
] = (p
[0] + 5, p
[1])
98 strategy_entry
._show
_cb
= strategy_set
99 p
= strategy_entry
.pos_pixel()
100 self
.__pos
_mgr
['editor_inspector_strategy'] = (p
[0] + 5, p
[1])
102 t
, strategy_args_entry
= self
.__add
_row
('strategy_args', _('strategy_args'), _strategy_args
, self
.on_edit_strategy_args
, _('the arguments of the strategy'))
103 fields
= ['position', 'roll', 'scale', 'mass', 'restitution', 'friction', 'id', 'strategy', 'strategy_args']
104 Entries
= namedtuple('Entries', fields
)
105 self
.__entries
= Entries(pos_entry
, rot_entry
, scale_entry
, mass_entry
, restitution_entry
, friction_entry
, id_entry
, strategy_entry
, strategy_args_entry
)
106 def load_images_btn(path
, col
):
109 (.6, .6, .6, 1), # ready
110 (1, 1, 1, 1), # press
111 (.8, .8, .8, 1), # rollover
117 (.4, .1, .1, .4)]}[col
]
118 return [self
.__load
_img
_btn
(path
, col
) for col
in colors
]
119 fcols
= (.4, .4, .4, .14), (.3, .3, .3, .05)
121 image
=load_images_btn('exitRight', 'gray'), scale
=.05,
122 pos
=(.06, 1, -h
+ .06),
123 parent
=self
._frm
, command
=self
.destroy
, state
=NORMAL
, relief
=FLAT
,
125 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
126 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
127 b
.__class
__ = type('DirectButtonMixed', (DirectButton
, DirectGuiMixin
), {})
128 pos_mgr
['editor_inspector_close'] = b
.pos_pixel()
129 b
.set_tooltip(_('Close'), *tooltip_args
)
130 self
.accept('item-rototranslated', self
.__on
_item
_rototranslated
)
132 image
=load_images_btn('trashcan', 'gray'), scale
=.05,
133 pos
=(.18, 1, -h
+ .06),
134 parent
=self
._frm
, command
=self
.__delete
_item
, state
=NORMAL
, relief
=FLAT
,
136 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
137 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
138 b
.__class
__ = type('DirectButtonMixed', (DirectButton
, DirectGuiMixin
), {})
139 pos_mgr
['editor_inspector_delete'] = b
.pos_pixel()
140 b
.set_tooltip(_('Delete the item'), *tooltip_args
)
142 def __add_row(self
, id_
, label
, text
, callback
, tooltip
):
144 tooltip_args
= self
._common
['text_font'], self
._common
['scale'], self
._common
['text_fg']
147 pos
=(.03, self
.__z
), parent
=self
._frm
,
148 font
=self
._common
['text_font'],
149 scale
=self
._common
['scale'],
150 fg
=self
._common
['text_fg'],
151 wordwrap
=20, align
=TextNode
.ALeft
)
153 scale
=self
._common
['scale'],
154 pos
=(.30, 1, self
.__z
),
155 entryFont
=self
._font
,
158 frameColor
=self
._common
['frameColor'],
161 text_fg
=self
._common
['text_fg'],
163 e
.__class
__ = type('DirectEntryMixed', (DirectEntry
, DirectGuiMixin
), {})
164 e
.set_tooltip(tooltip
, *tooltip_args
)
165 self
.__pos
_mgr
[f
'editor_inspector_{id_}'] = e
.pos_pixel()
169 def __add_row_option(self
, label
, text
, items
, callback
, tooltip
):
170 tooltip_args
= self
._common
['text_font'], self
._common
['scale'], self
._common
['text_fg']
173 pos
=(.03, self
.__z
), parent
=self
._frm
,
174 font
=self
._common
['text_font'],
175 scale
=self
._common
['scale'],
176 fg
=self
._common
['text_fg'],
177 wordwrap
=20, align
=TextNode
.ALeft
)
178 e
= DirectOptionMenuTestable(
179 scale
=self
._common
['scale'],
181 pos
=(.30, 1, self
.__z
),
188 frameColor
=self
._common
['frameColor'],
189 item_frameColor
=self
._common
['frameColor'],
190 popupMenu_frameColor
=self
._common
['frameColor'],
191 popupMarker_frameColor
=self
._common
['frameColor'],
192 text_font
=self
._font
,
193 text_fg
=self
._common
['text_fg'],
194 highlightColor
=(.9, .9, .9, .9),
195 item_text_font
=self
._font
,
196 item_text_fg
=self
._common
['text_fg'],
197 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
198 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
199 e
.__class
__ = type('DirectOptionMenuMixed', (DirectOptionMenu
, DirectGuiMixin
), {})
200 e
.set_tooltip(tooltip
, *tooltip_args
)
204 def __load_img_btn(self
, path
, col
):
205 img
= OnscreenImage('assets/images/buttons/%s.dds' % path
)
206 img
.set_transparency(True)
211 def __on_item_rototranslated(self
, np
):
214 self
.__entries
.position
.set('%s %s' % (str(round(pos
.x
, 3)), str(round(pos
.z
, 3))))
215 self
.__entries
.roll
.set('%s' % str(round(r
, 3)))
216 self
.__item
.json
['position'] = list(pos
)
217 self
.__item
.json
['roll'] = round(r
, 3)
219 def __delete_item(self
):
220 messenger
.send('editor-inspector-delete', [self
.__item
])
227 def on_edit_position(self
, txt
):
228 x
, z
= map(float, txt
.split())
229 self
.__item
.position
= [x
, 0, z
]
231 def on_edit_roll(self
, txt
):
232 self
.__item
.roll
= float(txt
)
234 def on_edit_scale(self
, txt
):
235 self
.__item
.scale
= float(txt
)
237 def on_edit_mass(self
, txt
):
238 self
.__item
.mass
= float(txt
)
240 def on_edit_restitution(self
, txt
):
241 self
.__item
.restitution
= float(txt
)
243 def on_edit_friction(self
, txt
):
244 self
.__item
.friction
= float(txt
)
246 def on_edit_id(self
, txt
):
249 def on_edit_strategy(self
, txt
):
251 self
.__entries
.strategy_args
.set('')
254 'StillStrategy': StillStrategy
,
255 'UpStrategy': UpStrategy
,
256 'HitStrategy': HitStrategy
,
257 'DownStrategy': DownStrategy
,
258 'FixedStrategy': FixedStrategy
}
259 class_
= name2class
[txt
]
262 if txt
== 'StillStrategy':
263 args
+= [self
.__item
._np
]
264 if txt
in ['UpStrategy', 'DownStrategy']:
265 args
+= [self
.__item
._np
]
267 args
+= [float(self
.__entries
.strategy_args
.get())]
270 if txt
== 'HitStrategy':
271 for item
in self
.__all
_items
:
272 if item
.id == self
.__entries
.strategy_args
.get():
274 args
+= [self
.__item
.node
]
275 args
+= [self
.__item
._world
]
277 self
.__item
.strategy
= class_(*args
)
278 self
.__item
.strategy_json
= txt
280 self
.__show
_error
_popup
()
282 def on_edit_strategy_args(self
, txt
):
283 self
.__item
.strategy_args_json
= txt
285 def __show_error_popup(self
):
286 self
.__dialog
= OkDialog(dialogName
='Strategy args errors',
287 text
=_('There are errors in the strategy args.'),
288 command
=self
.__actually
_close
)
289 self
.__dialog
['frameColor'] = (.4, .4, .4, .14)
290 self
.__dialog
['relief'] = FLAT
291 self
.__dialog
.component('text0')['fg'] = (.9, .9, .9, 1)
292 self
.__dialog
.component('text0')['font'] = self
._font
293 for b
in self
.__dialog
.buttonList
:
294 b
['frameColor'] = (.4, .4, .4, .14)
295 b
.component('text0')['fg'] = (.9, .9, .9, 1)
296 b
.component('text0')['font'] = self
._font
297 b
.component('text1')['fg'] = (.9, .1, .1, 1)
298 b
.component('text1')['font'] = self
._font
299 b
.component('text2')['fg'] = (.9, .9, .1, 1)
300 b
.component('text2')['font'] = self
._font
302 def __actually_close(self
, arg
):
303 self
.__entries
.strategy
.set('')
304 self
.__entries
.strategy_args
.set('')
305 self
.__dialog
.cleanup()
309 self
.ignore('item-rototranslated')
310 messenger
.send('editor-inspector-destroy')
313 class PixelSpaceInspector(DirectObject
):
315 def __init__(self
, item
, all_items
):
316 info('PixelSpaceInspector')
319 self
.__all
_items
= all_items
320 self
._font
= base
.loader
.load_font(
321 'assets/fonts/Hanken-Book.ttf')
323 self
._font
.set_pixels_per_unit(60)
324 self
._font
.set_minfilter(Texture
.FTLinearMipmapLinear
)
325 self
._font
.set_outline((0, 0, 0, 1), .8, .2)
328 'text_font': self
._font
,
329 'text_fg': (.9, .9, .9, 1),
331 'frameColor': (.4, .4, .4, .14),
332 'rolloverSound': loader
.load_sfx(
333 'assets/audio/sfx/rollover.ogg'),
334 'clickSound': loader
.load_sfx(
335 'assets/audio/sfx/click.ogg')}
337 self
._frm
= DirectFrame(frameColor
=(.4, .4, .4, .06),
338 frameSize
=(0, w
, -h
, 0),
339 parent
=base
.a2dTopRight
,
342 p
= self
.__item
._np
.get_pos()
344 if 'id' in self
.__item
.json
:
345 _id
= self
.__item
.json
['id']
346 t
, pos_entry
= self
.__add
_row
(_('position'), f
'{round(p.x, 3)}, {round(p.z, 3)}', self
.on_edit_position
)
347 t
, id_entry
= self
.__add
_row
(_('id'), _id
, self
.on_edit_id
)
348 fields
= ['position', 'id']
349 Entries
= namedtuple('Entries', fields
)
350 self
.__entries
= Entries(pos_entry
, id_entry
)
351 def load_images_btn(path
, col
):
354 (.6, .6, .6, 1), # ready
355 (1, 1, 1, 1), # press
356 (.8, .8, .8, 1), # rollover
362 (.4, .1, .1, .4)]}[col
]
363 return [self
.__load
_img
_btn
(path
, col
) for col
in colors
]
364 fcols
= (.4, .4, .4, .14), (.3, .3, .3, .05)
366 image
=load_images_btn('exitRight', 'gray'), scale
=.05,
367 pos
=(.06, 1, -h
+ .06),
368 parent
=self
._frm
, command
=self
.destroy
, state
=NORMAL
, relief
=FLAT
,
370 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
371 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
372 self
.accept('item-rototranslated', self
.__on
_item
_rototranslated
)
374 image
=load_images_btn('trashcan', 'gray'), scale
=.05,
375 pos
=(.18, 1, -h
+ .06),
376 parent
=self
._frm
, command
=self
.__delete
_item
, state
=NORMAL
, relief
=FLAT
,
378 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
379 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
381 def __add_row(self
, label
, text
, callback
):
385 pos
=(.03, self
.__z
), parent
=self
._frm
,
386 font
=self
._common
['text_font'],
387 scale
=self
._common
['scale'],
388 fg
=self
._common
['text_fg'],
389 wordwrap
=20, align
=TextNode
.ALeft
)
391 scale
=self
._common
['scale'],
392 pos
=(.30, 1, self
.__z
),
393 entryFont
=self
._font
,
396 frameColor
=self
._common
['frameColor'],
399 text_fg
=self
._common
['text_fg'],
404 def __load_img_btn(self
, path
, col
):
405 img
= OnscreenImage('assets/images/buttons/%s.dds' % path
)
406 img
.set_transparency(True)
411 def __on_item_rototranslated(self
, np
):
412 pos
= np
.pos2d_pixel()
413 self
.__entries
.position
.set('%s %s' % (str(round(pos
[0], 3)), str(round(pos
[1], 3))))
414 self
.__item
.json
['position'] = list(pos
)
416 def __delete_item(self
):
417 messenger
.send('editor-inspector-delete', [self
.__item
])
424 def on_edit_position(self
, txt
):
425 x
, z
= map(float, txt
.split())
426 self
.__item
.position
= [x
, 0, z
]
428 def on_edit_id(self
, txt
):
431 def __actually_close(self
, arg
):
432 self
.__entries
.strategy
.set('')
433 self
.__entries
.strategy_args
.set('')
434 self
.__dialog
.cleanup()
438 self
.ignore('item-rototranslated')
439 messenger
.send('editor-inspector-destroy')
442 class WorldSpaceInspector(DirectObject
):
444 def __init__(self
, item
, all_items
, pos_mgr
):
445 info('WorldSpaceInspector')
448 self
.__all
_items
= all_items
449 self
.__pos
_mgr
= pos_mgr
450 self
._font
= base
.loader
.load_font(
451 'assets/fonts/Hanken-Book.ttf')
453 self
._font
.set_pixels_per_unit(60)
454 self
._font
.set_minfilter(Texture
.FTLinearMipmapLinear
)
455 self
._font
.set_outline((0, 0, 0, 1), .8, .2)
458 'text_font': self
._font
,
459 'text_fg': (.9, .9, .9, 1),
461 'frameColor': (.4, .4, .4, .14),
462 'rolloverSound': loader
.load_sfx(
463 'assets/audio/sfx/rollover.ogg'),
464 'clickSound': loader
.load_sfx(
465 'assets/audio/sfx/click.ogg')}
466 tooltip_args
= self
._common
['text_font'], self
._common
['scale'], self
._common
['text_fg']
468 self
._frm
= DirectFrame(frameColor
=(.4, .4, .4, .06),
469 frameSize
=(0, w
, -h
, 0),
470 parent
=base
.a2dTopRight
,
473 p
= self
.__item
._np
.get_pos()
475 if 'id' in self
.__item
.json
:
476 _id
= self
.__item
.json
['id']
477 t
, pos_entry
= self
.__add
_row
('position', _('position'), f
'{round(p.x, 3)} {round(p.z, 3)}', self
.on_edit_position
, _('position (e.g. 0.1 2.3 4.5)'))
478 t
, id_entry
= self
.__add
_row
('id', _('id'), _id
, self
.on_edit_id
, _('id of the item (for the strategies)'))
479 fields
= ['position', 'id']
480 Entries
= namedtuple('Entries', fields
)
481 self
.__entries
= Entries(pos_entry
, id_entry
)
482 def load_images_btn(path
, col
):
485 (.6, .6, .6, 1), # ready
486 (1, 1, 1, 1), # press
487 (.8, .8, .8, 1), # rollover
493 (.4, .1, .1, .4)]}[col
]
494 return [self
.__load
_img
_btn
(path
, col
) for col
in colors
]
495 fcols
= (.4, .4, .4, .14), (.3, .3, .3, .05)
497 image
=load_images_btn('exitRight', 'gray'), scale
=.05,
498 pos
=(.06, 1, -h
+ .06),
499 parent
=self
._frm
, command
=self
.destroy
, state
=NORMAL
, relief
=FLAT
,
501 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
502 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
503 b
.__class
__ = type('DirectButtonMixed', (DirectButton
, DirectGuiMixin
), {})
504 pos_mgr
['editor_inspector_test_close'] = b
.pos_pixel()
505 b
.set_tooltip(_('Close'), *tooltip_args
)
506 self
.accept('item-rototranslated', self
.__on
_item
_rototranslated
)
508 image
=load_images_btn('trashcan', 'gray'), scale
=.05,
509 pos
=(.18, 1, -h
+ .06),
510 parent
=self
._frm
, command
=self
.__delete
_item
, state
=NORMAL
, relief
=FLAT
,
512 rolloverSound
=loader
.load_sfx('assets/audio/sfx/rollover.ogg'),
513 clickSound
=loader
.load_sfx('assets/audio/sfx/click.ogg'))
514 b
.__class
__ = type('DirectButtonMixed', (DirectButton
, DirectGuiMixin
), {})
515 pos_mgr
['editor_inspector_test_delete'] = b
.pos_pixel()
516 b
.set_tooltip(_('Delete the item'), *tooltip_args
)
518 def __add_row(self
, id_
, label
, text
, callback
, tooltip
):
520 tooltip_args
= self
._common
['text_font'], self
._common
['scale'], self
._common
['text_fg']
523 pos
=(.03, self
.__z
), parent
=self
._frm
,
524 font
=self
._common
['text_font'],
525 scale
=self
._common
['scale'],
526 fg
=self
._common
['text_fg'],
527 wordwrap
=20, align
=TextNode
.ALeft
)
529 scale
=self
._common
['scale'],
530 pos
=(.30, 1, self
.__z
),
531 entryFont
=self
._font
,
534 frameColor
=self
._common
['frameColor'],
537 text_fg
=self
._common
['text_fg'],
539 e
.__class
__ = type('DirectEntryMixed', (DirectEntry
, DirectGuiMixin
), {})
540 e
.set_tooltip(tooltip
, *tooltip_args
)
541 self
.__pos
_mgr
[f
'editor_inspector_test_{id_}'] = e
.pos_pixel()
545 def __load_img_btn(self
, path
, col
):
546 img
= OnscreenImage('assets/images/buttons/%s.dds' % path
)
547 img
.set_transparency(True)
552 def __on_item_rototranslated(self
, np
):
554 self
.__entries
.position
.set('%s %s' % (str(round(pos
.x
, 3)), str(round(pos
.z
, 3))))
555 self
.__item
.json
['position'] = list(pos
)
557 def __delete_item(self
):
558 messenger
.send('editor-inspector-delete', [self
.__item
])
565 def on_edit_position(self
, txt
):
566 x
, z
= map(float, txt
.split())
567 self
.__item
.position
= [x
, 0, z
]
569 def on_edit_id(self
, txt
):
572 def __actually_close(self
, arg
):
573 self
.__entries
.strategy
.set('')
574 self
.__entries
.strategy_args
.set('')
575 self
.__dialog
.cleanup()
579 self
.ignore('item-rototranslated')
580 messenger
.send('editor-inspector-destroy')