ya2 · news · projects · code · about

refactoring of assets/images
[pmachines.git] / pmachines / scene.py
1 from os.path import exists
2 from glob import glob
3 from importlib import import_module
4 from inspect import isclass
5 from panda3d.core import AmbientLight, DirectionalLight, Point3, Texture, \
6 TextPropertiesManager, TextNode, Spotlight, PerspectiveLens, BitMask32
7 from panda3d.bullet import BulletPlaneShape, BulletGhostNode
8 from direct.gui.OnscreenImage import OnscreenImage
9 from direct.gui.OnscreenText import OnscreenText
10 from direct.gui.DirectGui import DirectButton, DirectFrame
11 from direct.gui.DirectGuiGlobals import FLAT, DISABLED, NORMAL
12 from direct.showbase.DirectObject import DirectObject
13 from pmachines.items.background import Background
14 from pmachines.sidepanel import SidePanel
15 from lib.engine.gui.cursor import MouseCursor
16 from lib.lib.p3d.gfx import P3dGfxMgr
17
18
19 class Scene(DirectObject):
20
21 def __init__(self, world, exit_cb, auto_close_instr, dbg_items, reload_cb):
22 super().__init__()
23 self._world = world
24 self._exit_cb = exit_cb
25 self._dbg_items = dbg_items
26 self._reload_cb = reload_cb
27 self._set_camera()
28 self._cursor = MouseCursor(
29 'assets/images/buttons/arrowUpLeft.dds', (.04, 1, .04), (.5, .5, .5, 1),
30 (.01, .01))
31 self._set_gui()
32 self._set_lights()
33 self._set_input()
34 self._set_mouse_plane()
35 self.items = []
36 self.reset()
37 self._state = 'init'
38 self._paused = False
39 self._item_active = None
40 if auto_close_instr:
41 self.__store_state()
42 self.__restore_state()
43 else:
44 self._set_instructions()
45 self._bg = Background()
46 self._side_panel = SidePanel(world, self._mouse_plane_node, (-5, 4), (-3, 1), 1, self.items)
47 self._scene_tsk = taskMgr.add(self.on_frame, 'on_frame')
48
49 @staticmethod
50 def name():
51 return ''
52
53 def _instr_txt(self):
54 return ''
55
56 def _set_items(self):
57 self.items = []
58
59 def screenshot(self, task=None):
60 tex = Texture('screenshot')
61 buffer = base.win.make_texture_buffer('screenshot', 512, 512, tex, True )
62 cam = base.make_camera(buffer)
63 cam.reparent_to(render)
64 cam.node().get_lens().set_fov(base.camLens.get_fov())
65 cam.set_pos(0, -20, 0)
66 cam.look_at(0, 0, 0)
67 import simplepbr
68 simplepbr.init(
69 window=buffer,
70 camera_node=cam,
71 use_normal_maps=True,
72 use_emission_maps=False,
73 use_occlusion_maps=True,
74 msaa_samples=4,
75 enable_shadows=True)
76 base.graphicsEngine.renderFrame()
77 base.graphicsEngine.renderFrame()
78 fname = self.__class__.__name__
79 buffer.save_screenshot('assets/images/scenes/%s.png' % fname)
80 # img = DirectButton(
81 # frameTexture=buffer.get_texture(), relief=FLAT,
82 # frameSize=(-.2, .2, -.2, .2))
83 return buffer.get_texture()
84
85 def current_bottom(self):
86 curr_bottom = 1
87 for item in self.items:
88 if item.repos_done:
89 curr_bottom = min(curr_bottom, item.get_bottom())
90 return curr_bottom
91
92 def reset(self):
93 [itm.destroy() for itm in self.items]
94 self._set_items()
95 self._state = 'init'
96 self._commands = []
97 self._command_idx = 0
98 if hasattr(self, '_success_txt'):
99 self._success_txt.destroy()
100 del self._success_txt
101 self.__right_btn['state'] = NORMAL
102
103 def destroy(self):
104 self._unset_gui()
105 self._unset_lights()
106 self._unset_input()
107 self._unset_mouse_plane()
108 [itm.destroy() for itm in self.items]
109 self._bg.destroy()
110 self._side_panel.destroy()
111 self._cursor.destroy()
112 taskMgr.remove(self._scene_tsk)
113 if hasattr(self, '_success_txt'):
114 self._success_txt.destroy()
115
116 def _set_camera(self):
117 base.camera.set_pos(0, -20, 0)
118 base.camera.look_at(0, 0, 0)
119
120 def __load_img_btn(self, path, col):
121 img = OnscreenImage('assets/images/buttons/%s.dds' % path)
122 img.set_transparency(True)
123 img.set_color(col)
124 img.detach_node()
125 return img
126
127 def _set_gui(self):
128 def load_images_btn(path, col):
129 colors = {
130 'gray': [
131 (.6, .6, .6, 1), # ready
132 (1, 1, 1, 1), # press
133 (.8, .8, .8, 1), # rollover
134 (.4, .4, .4, .4)],
135 'green': [
136 (.1, .68, .1, 1),
137 (.1, 1, .1, 1),
138 (.1, .84, .1, 1),
139 (.4, .1, .1, .4)]}[col]
140 return [self.__load_img_btn(path, col) for col in colors]
141 abl, abr = base.a2dBottomLeft, base.a2dBottomRight
142 btn_info = [
143 ('home', self.on_home, NORMAL, abl, 'gray'),
144 ('information', self._set_instructions, NORMAL, abl, 'gray'),
145 ('right', self.on_play, NORMAL, abr, 'green'),
146 ('next', self.on_next, DISABLED, abr, 'gray'),
147 ('previous', self.on_prev, DISABLED, abr, 'gray'),
148 ('rewind', self.reset, NORMAL, abr, 'gray')]
149 num_l = num_r = 0
150 btns = []
151 for binfo in btn_info:
152 imgs = load_images_btn(binfo[0], binfo[4])
153 if binfo[3] == base.a2dBottomLeft:
154 sign, num = 1, num_l
155 num_l += 1
156 else:
157 sign, num = -1, num_r
158 num_r += 1
159 fcols = (.4, .4, .4, .14), (.3, .3, .3, .05)
160 btn = DirectButton(
161 image=imgs, scale=.05, pos=(sign * (.06 + .11 * num), 1, .06),
162 parent=binfo[3], command=binfo[1], state=binfo[2], relief=FLAT,
163 frameColor=fcols[0] if binfo[2] == NORMAL else fcols[1],
164 rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
165 clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
166 btn.set_transparency(True)
167 btns += [btn]
168 self.__home_btn, self.__info_btn, self.__right_btn, self.__next_btn, \
169 self.__prev_btn, self.__rewind_btn = btns
170 if self._dbg_items:
171 self._info_txt = OnscreenText(
172 '', parent=base.a2dTopRight, scale=0.04,
173 pos=(-.03, -.06), fg=(.9, .9, .9, 1), align=TextNode.A_right)
174
175 def _unset_gui(self):
176 btns = [
177 self.__home_btn, self.__info_btn, self.__right_btn,
178 self.__next_btn, self.__prev_btn, self.__rewind_btn]
179 [btn.destroy() for btn in btns]
180 if self._dbg_items:
181 self._info_txt.destroy()
182
183 def _set_spotlight(self, name, pos, look_at, color, shadows=False):
184 light = Spotlight(name)
185 if shadows:
186 light.setLens(PerspectiveLens())
187 light_np = render.attach_new_node(light)
188 light_np.set_pos(pos)
189 light_np.look_at(look_at)
190 light.set_color(color)
191 render.set_light(light_np)
192 return light_np
193
194 def _set_lights(self):
195 alight = AmbientLight('alight') # for ao
196 alight.set_color((.15, .15, .15, 1))
197 self._alnp = render.attach_new_node(alight)
198 render.set_light(self._alnp)
199 self._key_light = self._set_spotlight(
200 'key light', (-5, -80, 5), (0, 0, 0), (2.8, 2.8, 2.8, 1))
201 self._shadow_light = self._set_spotlight(
202 'key light', (-5, -80, 5), (0, 0, 0), (.58, .58, .58, 1), True)
203 self._shadow_light.node().set_shadow_caster(True, 2048, 2048)
204 self._shadow_light.node().get_lens().set_film_size(2048, 2048)
205 self._shadow_light.node().get_lens().set_near_far(1, 256)
206 self._shadow_light.node().set_camera_mask(BitMask32(0x01))
207
208 def _unset_lights(self):
209 for light in [self._alnp, self._key_light, self._shadow_light]:
210 render.clear_light(light)
211 light.remove_node()
212
213 def _set_input(self):
214 self.accept('mouse1', self.on_click_l)
215 self.accept('mouse1-up', self.on_release)
216 self.accept('mouse3', self.on_click_r)
217 self.accept('mouse3-up', self.on_release)
218
219 def _unset_input(self):
220 for evt in ['mouse1', 'mouse1-up', 'mouse3', 'mouse3-up']:
221 self.ignore(evt)
222
223 def _set_mouse_plane(self):
224 shape = BulletPlaneShape((0, -1, 0), 0)
225 #self._mouse_plane_node = BulletRigidBodyNode('mouse plane')
226 self._mouse_plane_node = BulletGhostNode('mouse plane')
227 self._mouse_plane_node.addShape(shape)
228 #np = render.attachNewNode(self._mouse_plane_node)
229 #self._world.attachRigidBody(self._mouse_plane_node)
230 self._world.attach_ghost(self._mouse_plane_node)
231
232 def _unset_mouse_plane(self):
233 self._world.remove_ghost(self._mouse_plane_node)
234
235 def _get_hits(self):
236 if not base.mouseWatcherNode.has_mouse(): return []
237 p_from, p_to = P3dGfxMgr.world_from_to(base.mouseWatcherNode.get_mouse())
238 return self._world.ray_test_all(p_from, p_to).get_hits()
239
240 def _update_info(self, item):
241 txt = ''
242 if item:
243 txt = '%.3f %.3f\n%.3f°' % (
244 item._np.get_x(), item._np.get_z(), item._np.get_r())
245 self._info_txt['text'] = txt
246
247 def _on_click(self, method):
248 if self._paused:
249 return
250 for hit in self._get_hits():
251 if hit.get_node() == self._mouse_plane_node:
252 pos = hit.get_hit_pos()
253 for hit in self._get_hits():
254 for item in [i for i in self.items if hit.get_node() == i.node and i.interactable]:
255 if not self._item_active:
256 self._item_active = item
257 getattr(item, method)(pos)
258 img = 'move' if method == 'on_click_l' else 'rotate'
259 if not (img == 'rotate' and not item._instantiated):
260 self._cursor.set_image('assets/images/buttons/%s.dds' % img)
261
262 def on_click_l(self):
263 self._on_click('on_click_l')
264
265 def on_click_r(self):
266 self._on_click('on_click_r')
267
268 def on_release(self):
269 if self._item_active and not self._item_active._first_command:
270 self._commands = self._commands[:self._command_idx]
271 self._commands += [self._item_active]
272 self._command_idx += 1
273 self.__prev_btn['state'] = NORMAL
274 fcols = (.4, .4, .4, .14), (.3, .3, .3, .05)
275 self.__prev_btn['frameColor'] = fcols[0]
276 if self._item_active._command_idx == len(self._item_active._commands) - 1:
277 self.__next_btn['state'] = DISABLED
278 self.__next_btn['frameColor'] = fcols[1]
279 self._item_active = None
280 [item.on_release() for item in self.items]
281 self._cursor.set_image('assets/images/buttons/arrowUpLeft.dds')
282
283 def repos(self):
284 for item in self.items:
285 item.repos_done = False
286 self.items = sorted(self.items, key=lambda itm: itm.__class__.__name__)
287 [item.on_aspect_ratio_changed() for item in self.items]
288 self._side_panel.update(self.items)
289 max_x = -float('inf')
290 for item in self.items:
291 if not item._instantiated:
292 max_x = max(item._np.get_x(), max_x)
293 for item in self.items:
294 if not item._instantiated:
295 item.repos_x(max_x)
296
297 def on_aspect_ratio_changed(self):
298 self.repos()
299
300 def _win_condition(self):
301 pass
302
303 def _fail_condition(self):
304 return all(itm.fail_condition() for itm in self.items) and not self._paused and self._state == 'playing'
305
306 def on_frame(self, task):
307 hits = self._get_hits()
308 pos = None
309 for hit in self._get_hits():
310 if hit.get_node() == self._mouse_plane_node:
311 pos = hit.get_hit_pos()
312 hit_nodes = [hit.get_node() for hit in hits]
313 if self._item_active:
314 items_hit = [self._item_active]
315 else:
316 items_hit = [itm for itm in self.items if itm.node in hit_nodes]
317 items_no_hit = [itm for itm in self.items if itm not in items_hit]
318 [itm.on_mouse_on() for itm in items_hit]
319 [itm.on_mouse_off() for itm in items_no_hit]
320 if pos and self._item_active:
321 self._item_active.on_mouse_move(pos)
322 if self._dbg_items:
323 self._update_info(items_hit[0] if items_hit else None)
324 if self._win_condition():
325 self._set_win()
326 elif self._state == 'playing' and self._fail_condition():
327 self._set_fail()
328 if any(itm._overlapping for itm in self.items):
329 self._cursor.cursor_img.img.set_color(.9, .1, .1, 1)
330 else:
331 self._cursor.cursor_img.img.set_color(.9, .9, .9, 1)
332 return task.cont
333
334 def cb_inst(self, item):
335 self.items += [item]
336
337 def on_play(self):
338 self._state = 'playing'
339 self.__prev_btn['state'] = DISABLED
340 self.__next_btn['state'] = DISABLED
341 self.__right_btn['state'] = DISABLED
342 [itm.play() for itm in self.items]
343
344 def on_next(self):
345 self._commands[self._command_idx].redo()
346 self._command_idx += 1
347 fcols = (.4, .4, .4, .14), (.3, .3, .3, .05)
348 self.__prev_btn['state'] = NORMAL
349 self.__prev_btn['frameColor'] = fcols[0]
350 more_commands = self._command_idx < len(self._commands)
351 self.__next_btn['state'] = NORMAL if more_commands else DISABLED
352 self.__next_btn['frameColor'] = fcols[0] if more_commands else fcols[1]
353
354 def on_prev(self):
355 self._command_idx -= 1
356 self._commands[self._command_idx].undo()
357 fcols = (.4, .4, .4, .14), (.3, .3, .3, .05)
358 self.__next_btn['state'] = NORMAL
359 self.__next_btn['frameColor'] = fcols[0]
360 self.__prev_btn['state'] = NORMAL if self._command_idx else DISABLED
361 self.__prev_btn['frameColor'] = fcols[0] if self._command_idx else fcols[1]
362
363 def on_home(self):
364 self._exit_cb()
365
366 def _set_instructions(self):
367 self._paused = True
368 self.__store_state()
369 mgr = TextPropertiesManager.get_global_ptr()
370 for name in ['mouse_l', 'mouse_r']:
371 graphic = OnscreenImage('assets/images/buttons/%s.dds' % name)
372 graphic.set_scale(.5)
373 graphic.get_texture().set_minfilter(Texture.FTLinearMipmapLinear)
374 graphic.get_texture().set_anisotropic_degree(2)
375 mgr.set_graphic(name, graphic)
376 graphic.set_z(-.2)
377 graphic.set_transparency(True)
378 graphic.detach_node()
379 frm = DirectFrame(frameColor=(.4, .4, .4, .06),
380 frameSize=(-.6, .6, -.3, .3))
381 font = base.loader.load_font('assets/fonts/Hanken-Book.ttf')
382 font.clear()
383 font.set_pixels_per_unit(60)
384 font.set_minfilter(Texture.FTLinearMipmapLinear)
385 font.set_outline((0, 0, 0, 1), .8, .2)
386 self._txt = OnscreenText(
387 self._instr_txt(), parent=frm, font=font, scale=0.06,
388 fg=(.9, .9, .9, 1), align=TextNode.A_left)
389 u_l = self._txt.textNode.get_upper_left_3d()
390 l_r = self._txt.textNode.get_lower_right_3d()
391 w, h = l_r[0] - u_l[0], u_l[2] - l_r[2]
392 btn_scale = .05
393 mar = .06 # margin
394 z = h / 2 - font.get_line_height() * self._txt['scale'][1]
395 z += (btn_scale + 2 * mar) / 2
396 self._txt['pos'] = -w / 2, z
397 u_l = self._txt.textNode.get_upper_left_3d()
398 l_r = self._txt.textNode.get_lower_right_3d()
399 c_l_r = l_r[0], l_r[1], l_r[2] - 2 * mar - btn_scale
400 fsz = u_l[0] - mar, l_r[0] + mar, c_l_r[2] - mar, u_l[2] + mar
401 frm['frameSize'] = fsz
402 colors = [
403 (.6, .6, .6, 1), # ready
404 (1, 1, 1, 1), # press
405 (.8, .8, .8, 1), # rollover
406 (.4, .4, .4, .4)]
407 imgs = [self.__load_img_btn('exitRight', col) for col in colors]
408 btn = DirectButton(
409 image=imgs, scale=btn_scale,
410 pos=(l_r[0] - btn_scale, 1, l_r[2] - mar - btn_scale),
411 parent=frm, command=self.__on_close_instructions, extraArgs=[frm],
412 relief=FLAT, frameColor=(.6, .6, .6, .08),
413 rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
414 clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
415 btn.set_transparency(True)
416
417 def _set_win(self):
418 loader.load_sfx('assets/audio/sfx/success.ogg').play()
419 self._paused = True
420 self.__store_state()
421 frm = DirectFrame(frameColor=(.4, .4, .4, .06),
422 frameSize=(-.6, .6, -.3, .3))
423 font = base.loader.load_font('assets/fonts/Hanken-Book.ttf')
424 font.clear()
425 font.set_pixels_per_unit(60)
426 font.set_minfilter(Texture.FTLinearMipmapLinear)
427 font.set_outline((0, 0, 0, 1), .8, .2)
428 self._txt = OnscreenText(
429 _('You win!'),
430 parent=frm,
431 font=font, scale=0.2,
432 fg=(.9, .9, .9, 1))
433 u_l = self._txt.textNode.get_upper_left_3d()
434 l_r = self._txt.textNode.get_lower_right_3d()
435 w, h = l_r[0] - u_l[0], u_l[2] - l_r[2]
436 btn_scale = .05
437 mar = .06 # margin
438 z = h / 2 - font.get_line_height() * self._txt['scale'][1]
439 z += (btn_scale + 2 * mar) / 2
440 self._txt['pos'] = 0, z
441 u_l = self._txt.textNode.get_upper_left_3d()
442 l_r = self._txt.textNode.get_lower_right_3d()
443 c_l_r = l_r[0], l_r[1], l_r[2] - 2 * mar - btn_scale
444 fsz = u_l[0] - mar, l_r[0] + mar, c_l_r[2] - mar, u_l[2] + mar
445 frm['frameSize'] = fsz
446 colors = [
447 (.6, .6, .6, 1), # ready
448 (1, 1, 1, 1), # press
449 (.8, .8, .8, 1), # rollover
450 (.4, .4, .4, .4)]
451 imgs = [self.__load_img_btn('home', col) for col in colors]
452 btn = DirectButton(
453 image=imgs, scale=btn_scale,
454 pos=(-2.8 * btn_scale, 1, l_r[2] - mar - btn_scale),
455 parent=frm, command=self._on_end_home, extraArgs=[frm],
456 relief=FLAT, frameColor=(.6, .6, .6, .08),
457 rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
458 clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
459 btn.set_transparency(True)
460 imgs = [self.__load_img_btn('rewind', col) for col in colors]
461 btn = DirectButton(
462 image=imgs, scale=btn_scale,
463 pos=(0, 1, l_r[2] - mar - btn_scale),
464 parent=frm, command=self._on_restart, extraArgs=[frm],
465 relief=FLAT, frameColor=(.6, .6, .6, .08),
466 rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
467 clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
468 btn.set_transparency(True)
469 scenes = []
470 for _file in glob('pmachines/scenes/*.py'):
471 _fn = _file.replace('.py', '').replace('/', '.')
472 for member in import_module(_fn).__dict__.values():
473 if isclass(member) and issubclass(member, Scene) and \
474 member != Scene:
475 scenes += [member]
476 scenes = sorted(scenes, key=lambda elm: elm.sorting)
477 enabled = scenes.index(self.__class__) < len(scenes) - 1
478 if enabled:
479 next_scene = scenes[scenes.index(self.__class__) + 1]
480 else:
481 next_scene = None
482 imgs = [self.__load_img_btn('right', col) for col in colors]
483 btn = DirectButton(
484 image=imgs, scale=btn_scale,
485 pos=(2.8 * btn_scale, 1, l_r[2] - mar - btn_scale),
486 parent=frm, command=self._on_next_scene,
487 extraArgs=[frm, next_scene], relief=FLAT,
488 frameColor=(.6, .6, .6, .08),
489 rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
490 clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
491 btn['state'] = NORMAL if enabled else DISABLED
492 btn.set_transparency(True)
493
494 def _set_fail(self):
495 loader.load_sfx('assets/audio/sfx/success.ogg').play()
496 self._paused = True
497 self.__store_state()
498 frm = DirectFrame(frameColor=(.4, .4, .4, .06),
499 frameSize=(-.6, .6, -.3, .3))
500 font = base.loader.load_font('assets/fonts/Hanken-Book.ttf')
501 font.clear()
502 font.set_pixels_per_unit(60)
503 font.set_minfilter(Texture.FTLinearMipmapLinear)
504 font.set_outline((0, 0, 0, 1), .8, .2)
505 self._txt = OnscreenText(
506 _('You have failed!'),
507 parent=frm,
508 font=font, scale=0.2,
509 fg=(.9, .9, .9, 1))
510 u_l = self._txt.textNode.get_upper_left_3d()
511 l_r = self._txt.textNode.get_lower_right_3d()
512 w, h = l_r[0] - u_l[0], u_l[2] - l_r[2]
513 btn_scale = .05
514 mar = .06 # margin
515 z = h / 2 - font.get_line_height() * self._txt['scale'][1]
516 z += (btn_scale + 2 * mar) / 2
517 self._txt['pos'] = 0, z
518 u_l = self._txt.textNode.get_upper_left_3d()
519 l_r = self._txt.textNode.get_lower_right_3d()
520 c_l_r = l_r[0], l_r[1], l_r[2] - 2 * mar - btn_scale
521 fsz = u_l[0] - mar, l_r[0] + mar, c_l_r[2] - mar, u_l[2] + mar
522 frm['frameSize'] = fsz
523 colors = [
524 (.6, .6, .6, 1), # ready
525 (1, 1, 1, 1), # press
526 (.8, .8, .8, 1), # rollover
527 (.4, .4, .4, .4)]
528 imgs = [self.__load_img_btn('home', col) for col in colors]
529 btn = DirectButton(
530 image=imgs, scale=btn_scale,
531 pos=(-2.8 * btn_scale, 1, l_r[2] - mar - btn_scale),
532 parent=frm, command=self._on_end_home, extraArgs=[frm],
533 relief=FLAT, frameColor=(.6, .6, .6, .08),
534 rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
535 clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
536 btn.set_transparency(True)
537 imgs = [self.__load_img_btn('rewind', col) for col in colors]
538 btn = DirectButton(
539 image=imgs, scale=btn_scale,
540 pos=(0, 1, l_r[2] - mar - btn_scale),
541 parent=frm, command=self._on_restart, extraArgs=[frm],
542 relief=FLAT, frameColor=(.6, .6, .6, .08),
543 rolloverSound=loader.load_sfx('assets/audio/sfx/rollover.ogg'),
544 clickSound=loader.load_sfx('assets/audio/sfx/click.ogg'))
545 btn.set_transparency(True)
546
547 def _on_restart(self, frm):
548 self.__on_close_instructions(frm)
549 self.reset()
550
551 def _on_end_home(self, frm):
552 self.__on_close_instructions(frm)
553 self.on_home()
554
555 def _on_next_scene(self, frm, scene):
556 self.__on_close_instructions(frm)
557 self._reload_cb(scene)
558
559 def __store_state(self):
560 btns = [
561 self.__home_btn, self.__info_btn, self.__right_btn,
562 self.__next_btn, self.__prev_btn, self.__rewind_btn]
563 self.__btn_state = [btn['state'] for btn in btns]
564 for btn in btns:
565 btn['state'] = DISABLED
566 [itm.store_state() for itm in self.items]
567
568 def __restore_state(self):
569 btns = [
570 self.__home_btn, self.__info_btn, self.__right_btn,
571 self.__next_btn, self.__prev_btn, self.__rewind_btn]
572 for btn, state in zip(btns, self.__btn_state):
573 btn['state'] = state
574 [itm.restore_state() for itm in self.items]
575 self._paused = False
576
577 def __on_close_instructions(self, frm):
578 frm.remove_node()
579 self.__restore_state()