1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 from pgm.graph.group import Group
20 from pgm.graph.image import Image
21 from pgm.graph.text import Text
22 from pgm.timing import implicit
23 from pgm.utils import classinit
24 import math
25 import gobject
26 import gst
27 import os
28 import pgm
29
30
31 CAROUSEL = 0
32 CHAIN = 1
33
34
35 NORMAL = 0
36 BLURRED = 1
37 REFLECTED = 2
38 TEXT = 3
39
40
41 X = 0
42 Y = 1
43 WIDTH = 2
44 HEIGHT = 3
45 NORMAL_OPACITY = 4
46 BLURRED_OPACITY = 5
47 REFLECTED_OPACITY = 6
48 TEXT_OPACITY = 7
49
51 """
52 """
53
67 """
68 """
69
70 Group.__init__(self, canvas, layer)
71
72 self._canvas = canvas
73 self._layer = layer
74 self._width = width
75 self._height = height
76 self._font_height = 0.3
77 self._font_family = font_family
78 self._selected_position = selected_position
79 self._selected_size = selected_size
80 self._selected_opacity = selected_opacity
81 self._mode = mode
82 self._path_steps = path_steps
83 self._duration = duration
84 self._transformation = transformation
85 self._number_of_items = 0
86 self._selected = False
87 self._focused_item = 0
88 self._item_handles = []
89 self._items = []
90 self._values = []
91
92
95
98
100 return (self._width, self._height)
101
104
107
109 self._width = size[0]
110 self._height = size[1]
111
113 """
114 Internal function to make a bounds-check against the current limits of
115 the item-list.
116 """
117 if index > -1 and index < self._number_of_items:
118 return True
119 else:
120 return False
121
123 """
124 Update self._items[index] with the values for the selected state
125 """
126 item = self._items[index]
127
128
129 item[NORMAL].opacity = self._selected_opacity
130 item[NORMAL].position = self._selected_position
131 item[NORMAL].size = self._selected_size
132
133
134 item[BLURRED].opacity = 0
135 item[BLURRED].position = self._selected_position
136 item[BLURRED].size = self._selected_size
137
138 reflected_y = self._selected_position[1] + self._selected_size[1]
139
140
141 item[REFLECTED].opacity = 0
142 item[REFLECTED].position = (self._selected_position[0], \
143 reflected_y, \
144 self._selected_position[2])
145 item[REFLECTED].size = self._selected_size
146
147
148 item[TEXT].opacity = 0
149 item[TEXT].position = (self._selected_position[0], \
150 reflected_y, \
151 self._selected_position[2])
152 item[TEXT].size = self._selected_size
153
154
192
194 """
195 """
196 item = self._items[item_index]
197
198 start = value_index
199
200 if self._path_steps > 1:
201 normal_opacity = []
202 blurred_opacity = []
203 reflected_opacity = []
204 text_opacity = []
205 position = []
206 reflected_position = []
207 text_position = []
208 size = []
209
210
211 for index in xrange(self._path_steps):
212 normal_opacity.append(self._values[start + index][NORMAL_OPACITY])
213 blurred_opacity.append(self._values[start + index][BLURRED_OPACITY])
214 reflected_opacity.append(self._values[start + index][REFLECTED_OPACITY])
215 text_opacity.append(self._values[start + index][TEXT_OPACITY])
216 position.append((self._values[start + index][X],
217 self._values[start + index][Y],
218 item[NORMAL].z))
219
220
221 reflected_position.append((self._values[start + index][X], \
222 self._values[start + index][Y] + \
223 self._values[start + index][HEIGHT], \
224 item[REFLECTED].z))
225
226 text_position.append((self._values[start + index][X], \
227 self._values[start + index][Y] + \
228 self._values[start + index][HEIGHT], \
229 item[TEXT].z))
230
231 size.append((self._values[start + index][WIDTH], \
232 self._values[start + index][HEIGHT]))
233 else:
234 normal_opacity = self._values[start][NORMAL_OPACITY]
235 blurred_opacity = self._values[start][BLURRED_OPACITY]
236 reflected_opacity = self._values[start][REFLECTED_OPACITY]
237 text_opacity = self._values[start][TEXT_OPACITY]
238 position = (self._values[start][X], \
239 self._values[start][Y], \
240 item[NORMAL].z)
241 reflected_position = (self._values[start][X], \
242 self._values[start][Y] + \
243 self._values[start][HEIGHT], \
244 item[REFLECTED].z)
245 text_position = (self._values[start][X], \
246 self._values[start][Y] + \
247 self._values[start][HEIGHT],
248 item[TEXT].z)
249 size = (self._values[start][WIDTH], \
250 self._values[start][HEIGHT])
251
252
253
254 item[NORMAL].opacity = normal_opacity
255 item[NORMAL].position = position
256 item[NORMAL].size = size
257
258
259 item[BLURRED].opacity = blurred_opacity
260 item[BLURRED].position = position
261 item[BLURRED].size = size
262
263
264 item[REFLECTED].opacity = reflected_opacity
265 item[REFLECTED].position = reflected_position
266 item[REFLECTED].size = size
267
268
269 item[TEXT].opacity = text_opacity
270 item[TEXT].position = text_position
271 item[TEXT].size = size
272
274 value_list = []
275
276 x = 0
277 y = 0
278 width = 0
279 height = 0
280 normal_opacity = 0
281 blurred_opacity = 0
282 reflected_opacity = 0
283 text_opacity = 0
284
285 if self._mode == CAROUSEL:
286 coef = 1.6
287 tmp_x = (1.0 + math.sin(value))/2.0
288 tmp_y = (1.0 + math.cos(value))/2.0
289 width = self._width/3.0 * (coef-tmp_y)/coef
290 height = self._height/3.0 * (coef-tmp_y)/coef
291 x = (self._width - width) * tmp_x
292 y = (self._height - height) * tmp_y
293 y = abs(y + 2.0*height - self._height)
294 normal_opacity = 255 * (1 - tmp_y)
295 blurred_opacity = 128 - 128 * (1 - tmp_y)
296 reflected_opacity = 64 * (1 - tmp_y)
297 text_opacity = 255 * math.exp(40.0 * (-tmp_y))
298 elif self._mode == CHAIN:
299
300 x = self._width / 2.0 * math.cos(value)
301 y = self._height * math.sin(value)
302 width = y
303 height = y
304 normal_opacity = 255
305 blurred_opacity = 128
306 reflected_opacity = 1
307 text_opacity = 255
308
309 value_list.append(x)
310 value_list.append(y)
311 value_list.append(width)
312 value_list.append(height)
313 value_list.append(normal_opacity)
314 value_list.append(blurred_opacity)
315 value_list.append(reflected_opacity)
316 value_list.append(text_opacity)
317
318 return value_list
319
321 """
322 Compute the positions, sizes and opacities for all the items in the
323 widget.
324 """
325 if self._number_of_items == 0:
326 return
327
328 self._values = []
329
330 if self._mode == CAROUSEL:
331 max_index = self._number_of_items * self._path_steps
332 elif self._mode == CHAIN:
333 max_index = self._number_of_items
334
335 start = -math.pi
336 end = math.pi
337 value = start
338 value_step = (end - start) / max_index
339 for index in xrange(max_index):
340 self._values.append(self._calculate_value_list(value))
341 value += value_step
342
343 self.regenerate_text()
344
345
347
348 max_values = self._calculate_value_list(math.pi)
349
350 for i in self._items:
351 i[TEXT].stop_animations()
352
353 for i in self._item_handles:
354
355 prev_size = i[TEXT].size
356
357 i[TEXT].visible = False
358
359 i[TEXT].size = (max_values[WIDTH], max_values[HEIGHT])
360
361 self._font_height = max_values[HEIGHT] / 3.0
362
363 i[TEXT].font_height = self._font_height + 0.1
364 i[TEXT].font_height = self._font_height
365
366
367 i[TEXT].size = prev_size
368 i[TEXT].visible = True
369
371 """
372 Sets the correct positions, sizes and opacities for all
373 items in the widget depending on the currently select state or currently
374 focused item.
375 """
376 if self._number_of_items == 0:
377 return
378
379 if self._selected:
380 self._apply_selected_values(self._focused_item)
381 start = self._focused_item + 1
382 index = 0
383 value_index = 1
384 while index < (self._number_of_items - 1):
385 real_index = (start + index) % self._number_of_items
386 self._apply_hidden_values(real_index, value_index)
387 index += 1
388 value_index -= self._path_steps
389 else:
390 start = self._focused_item
391 index = 0
392 value_index = 0
393 while index < self._number_of_items:
394 real_index = (start + index) % self._number_of_items
395 self._apply_visible_values(real_index, value_index)
396 index += 1
397 value_index -= self._path_steps
398
399
401 if index >= 0 and index < len(self._item_handles):
402 normal_image = self._item_handles[index][0]
403 blurred_image = self._item_handles[index][1]
404 reflected_image = self._item_handles[index][2]
405 normal_image.set_from_file(normal_file)
406 blurred_image.set_from_file(blurred_file)
407 reflected_image.set_from_file(reflected_file)
408
409
411 """
412 This call has no effect if the widget is in state "selected"!
413 """
414 if self._selected:
415 return
416
417
418 normal_image = Image()
419 blurred_image = Image()
420 reflected_image = Image()
421 text_image = Text()
422
423
424 normal_image.set_from_file(normal_file)
425 blurred_image.set_from_file(blurred_file)
426 reflected_image.set_from_file(reflected_file)
427
428
429
430 text_image.label = ""
431 text_image.font_height = self._font_height
432 text_image.font_family = self._font_family
433 text_image.alignment = pgm.TEXT_ALIGN_CENTER
434 text_image.ellipsize = pgm.TEXT_ELLIPSIZE_MIDDLE
435
436
437 normal_image.opacity = 1
438 blurred_image.opacity = 1
439 reflected_image.opacity = 1
440 text_image.opacity = 1
441
442
443 normal_image.fg_color = (255, 255, 255, 255)
444 blurred_image.fg_color = (255, 255, 255, 255)
445 reflected_image.fg_color = (255, 255, 255, 255)
446 text_image.fg_color = (255, 255, 255, 255)
447
448
449 normal_image.bg_color = (0, 0, 0, 0)
450 blurred_image.bg_color = (0, 0, 0, 0)
451 reflected_image.bg_color = (0, 0, 0, 0)
452 text_image.bg_color = (0, 0, 0, 0)
453
454
455 normal_image.visible = True
456 blurred_image.visible = True
457 reflected_image.visible = True
458 text_image.visible = True
459
460
461 normal_image.layout = pgm.IMAGE_SCALED
462 blurred_image.layout = pgm.IMAGE_SCALED
463 reflected_image.layout = pgm.IMAGE_SCALED
464
465
466
467 self.add(normal_image)
468 self.add(blurred_image)
469 self.add(reflected_image)
470 self.add(text_image)
471
472
473
474 item = []
475 item.append(normal_image)
476 item.append(blurred_image)
477 item.append(reflected_image)
478 item.append(text_image)
479 self._item_handles.insert(index, item)
480
481
482 animated_normal_image = implicit.AnimatedObject(normal_image)
483 animated_blurred_image = implicit.AnimatedObject(blurred_image)
484 animated_reflected_image = implicit.AnimatedObject(reflected_image)
485 animated_text_image = implicit.AnimatedObject(text_image)
486
487
488 animated_normal_image.setup_next_animations(duration = self._duration,
489 transformation = self._transformation)
490 animated_normal_image.mode = implicit.REPLACE
491 animated_blurred_image.setup_next_animations(duration = self._duration,
492 transformation = self._transformation)
493 animated_blurred_image.mode = implicit.REPLACE
494 animated_reflected_image.setup_next_animations(duration = self._duration,
495 transformation = self._transformation)
496 animated_reflected_image.mode = implicit.REPLACE
497 animated_text_image.setup_next_animations(duration = self._duration,
498 transformation = self._transformation)
499 animated_text_image.mode = implicit.REPLACE
500
501
502 item = []
503 item.append(animated_normal_image)
504 item.append(animated_blurred_image)
505 item.append(animated_reflected_image)
506 item.append(animated_text_image)
507 self._items.insert(index, item)
508
509 self._number_of_items = len(self._items)
510
512 """
513 This call has no effect if the widget is in state "selected"!
514 """
515 if self._selected:
516 return
517
518 self.insert(self._number_of_items,
519 normal_file,
520 blurred_file,
521 reflected_file,
522 label)
523
525 """
526 This call has no effect if the widget is in state "selected"!
527 """
528 if self._number_of_items == 0 or self._selected:
529 return
530
531
532 item = self._item_handles.pop(index)
533 self.remove(item[NORMAL])
534 self.remove(item[BLURRED])
535 self.remove(item[REFLECTED])
536 self.remove(item[TEXT])
537 item = self._items.pop(index)
538
539 self._number_of_items = len(self._items)
540
542 """
543 Make the previous item in the widget the currently focused item. This
544 function will automatically set the selected state to false and also
545 call update() implicitly for you.
546 """
547 if self._number_of_items == 0:
548 return
549
550 if self._selected:
551 self._selected = False
552 else:
553 focused_item = self._focused_item
554 self._focused_item = (focused_item - 1) % self._number_of_items
555 self.update()
556
558 """
559 Make the next item in the widget the currently focused item. This
560 function will automatically set the selected state to false and also
561 call update() implicitly for you.
562 """
563 if self._number_of_items == 0:
564 return
565
566 if self._selected:
567 self._selected = False
568 else:
569 focused_item = self._focused_item
570 self._focused_item = (focused_item + 1) % self._number_of_items
571 self.update()
572
574 """
575 This toggles the selected state of the widget. update() is called
576 implicitly for you.
577 """
578 if self._number_of_items == 0:
579 return
580
581 if self._selected:
582 self._selected = False
583 elif not self._selected:
584 self._selected = True
585 self.update()
586
588 """
589 Returns if widget is in selected state or not.
590 """
591 return self._selected
592
594 """
595 Get the index of the currently focused item. This does not imply any
596 selected state! If you want to know the selected state call
597 is_selected()
598 """
599 return self._focused_item
600
602 """
603 Directly set the currently focused item. This function has no effect if
604 widget is in selected state. If widget is not in selected state update()
605 is called implicitly for you.
606 """
607 if self._selected:
608 return
609
610 if self._focused_item != index and self._within_bounds(index):
611 self._focused_item = index
612 self.update()
613
614 if __name__ == "__main__":
615
616 import os, os.path
617 import sys
618 import random
619 import pgm
620 import gobject
621 import gst
622 from pgm.graph.group import Group
623 from pgm.graph.text import Text
624 from pgm.graph.image import Image
625 from pgm.utils import classinit
626 from pgm.timing.implicit import AnimatedObject
627
628 - def on_delete(viewport,
629 event,
630 shade):
631 shade.position = (0.0, 0.0, 0.0)
632 gobject.timeout_add(1600, pgm.main_quit)
633
634 - def on_key_press(viewport,
635 event,
636 widget,
637 shade):
638 if event.type == pgm.KEY_PRESS:
639 if event.keyval == pgm.keysyms.q or \
640 event.keyval == pgm.keysyms.Escape:
641 shade.position = (0.0, 0.0, 0.0)
642 gobject.timeout_add(1600, pgm.main_quit)
643 elif event.keyval == pgm.keysyms.space or \
644 event.keyval == pgm.keysyms.Return:
645 widget.select()
646 elif event.keyval == pgm.keysyms.n or \
647 event.keyval == pgm.keysyms.Right:
648 widget.next()
649 elif event.keyval == pgm.keysyms.p or \
650 event.keyval == pgm.keysyms.Left:
651 widget.prev()
652 elif event.keyval == pgm.keysyms.a:
653 widget.append("./examples/pictures/music-normal_256x256.png",
654 "./examples/pictures/music-blurred_256x256.png",
655 "./examples/pictures/music-reflected_256x256.png",
656 "New")
657 widget.layout()
658 widget.update()
659 elif event.keyval == pgm.keysyms.r:
660 widget.delete(1)
661 widget.layout()
662 widget.update()
663 elif event.keyval == pgm.keysyms.f:
664 viewport.fullscreen = False
665 viewport.update ()
666 elif event.keyval == pgm.keysyms.v:
667 viewport.fullscreen = True
668 viewport.update ()
669
670
671 factory = pgm.ViewportFactory('opengl')
672 gl = factory.create()
673 gl.title = 'TopLevelMenu widget-test'
674 size = gl.get_screen_resolution()
675 aspect_ratio = float(size[0]) / float(size[1])
676 gl.size = (aspect_ratio * 400, 400)
677 window_height = 3.0
678 window_width = aspect_ratio * window_height
679 gl.fullscreen = True
680 gl.show()
681
682
683 canvas = pgm.Canvas()
684 canvas.size = (window_width, window_height)
685
686
687 gl.set_canvas(canvas)
688
689 bg_image = Image()
690 bg_image.set_from_file("./examples/pictures/bg.png")
691 bg_image.opacity = 255
692 bg_image.visible = True
693 bg_image.fg_color = (255, 255, 255, 255)
694 bg_image.bg_color = (0, 0, 0, 0)
695 bg_image.position = (0.0, 0.0, 0.0)
696 bg_image.width = window_width
697 bg_image.height = window_height
698 bg_image.layout = pgm.IMAGE_FILLED
699
700 bg_group = Group(canvas,
701 pgm.DRAWABLE_FAR)
702 bg_group.add(bg_image)
703 bg_group.opacity = 255
704 bg_group.visible = True
705
706 shade_image = Image()
707 shade_image.set_from_file("./examples/pictures/shade.png")
708 shade_image.opacity = 255
709 shade_image.visible = True
710 shade_image.fg_color = (255, 255, 255, 255)
711 shade_image.bg_color = (0, 0, 0, 0)
712 shade_image.position = (0.0, .0, 0.0)
713 shade_image.width = window_width
714 shade_image.height = window_height
715 shade_image.layout = pgm.IMAGE_FILLED
716 animated_shade_image = AnimatedObject(shade_image)
717 animated_shade_image.setup_next_animations(duration = 1500)
718 animated_shade_image.position = (0.0, -6.0, 0.0)
719
720 shade_group = Group(canvas, pgm.DRAWABLE_NEAR)
721 shade_group.add(shade_image)
722 shade_group.opacity = 255
723 shade_group.visible = True
724
725 widget = TopLevelMenu(canvas,
726 pgm.DRAWABLE_MIDDLE,
727 width = window_width,
728 height = window_height,
729 selected_position = (0.1, -0.75, 0.0),
730 selected_size = (4.5, 4.5),
731 selected_opacity = 32,
732 mode = CAROUSEL,
733 path_steps = 1,
734 duration = 500,
735 transformation = implicit.DECELERATE)
736
737 widget.position = ((window_width-widget.width)/2.0,
738 (window_height-widget.height)/2.0,
739 0.0)
740 widget.opacity = 255
741 widget.visible = True
742
743 """
744 frame = Image()
745 frame.opacity = 255
746 frame.bg_color = (0, 0, 255, 255)
747 frame.position = widget.position
748 frame.size = widget.size
749 frame.visible = True
750 canvas.add(pgm.DRAWABLE_MIDDLE, frame)
751 """
752
753 widget.append("./examples/pictures/music-normal_256x256.png",
754 "./examples/pictures/music-blurred_256x256.png",
755 "./examples/pictures/music-reflected_256x256.png",
756 "音樂")
757
758 widget.append("./examples/pictures/video-normal_256x256.png",
759 "./examples/pictures/video-blurred_256x256.png",
760 "./examples/pictures/video-reflected_256x256.png",
761 "視頻")
762
763 widget.append("./examples/pictures/dvd-normal_256x256.png",
764 "./examples/pictures/dvd-blurred_256x256.png",
765 "./examples/pictures/dvd-reflected_256x256.png",
766 "DVD")
767
768 widget.append("./examples/pictures/photo-normal_256x256.png",
769 "./examples/pictures/photo-blurred_256x256.png",
770 "./examples/pictures/photo-reflected_256x256.png",
771 "圖片")
772
773 widget.append("./examples/pictures/service-normal_256x256.png",
774 "./examples/pictures/service-blurred_256x256.png",
775 "./examples/pictures/service-reflected_256x256.png",
776 "服務")
777
778 widget.layout()
779 widget.update()
780
781 gl.connect('key-press-event',
782 on_key_press,
783 widget,
784 animated_shade_image)
785 gl.connect('delete-event',
786 on_delete,
787 animated_shade_image)
788 gobject.timeout_add(15,
789 gl.update)
790
791 pgm.main()
792