Package elisa :: Package plugins :: Package bad :: Package poblenou_frontend :: Package poblenou_widgets :: Module top_level_menu
[hide private]
[frames] | no frames]

Source Code for Module elisa.plugins.bad.poblenou_frontend.poblenou_widgets.top_level_menu

  1  # -*- coding: utf-8 -*- 
  2  # Elisa - Home multimedia server 
  3  # Copyright (C) 2006-2008 Fluendo Embedded S.L. (www.fluendo.com). 
  4  # All rights reserved. 
  5  # 
  6  # This file is available under one of two license agreements. 
  7  # 
  8  # This file is licensed under the GPL version 3. 
  9  # See "LICENSE.GPL" in the root of this distribution including a special 
 10  # exception to use Elisa with Fluendo's plugins. 
 11  # 
 12  # The GPL part of Elisa is also available under a commercial licensing 
 13  # agreement from Fluendo. 
 14  # See "LICENSE.Elisa" in the root directory of this distribution package 
 15  # for details on that license. 
 16  # 
 17  # Author: Mirco Müller <macslow@bangang.de> 
 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  # widget-modes 
 31  CAROUSEL          = 0 
 32  CHAIN             = 1 
 33   
 34  # indices for icon-part 
 35  NORMAL            = 0 
 36  BLURRED           = 1 
 37  REFLECTED         = 2 
 38  TEXT              = 3 
 39   
 40  # indices for attribute-value 
 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   
50 -class TopLevelMenu(Group):
51 """ 52 """ 53
54 - def __init__(self, 55 canvas = None, 56 layer = pgm.DRAWABLE_MIDDLE, 57 width = 1.0, 58 height = 1.0, 59 font_family = "DejaVu Sans", 60 selected_position = (0.0, 0.0, 0.0), 61 selected_size = (0.0, 0.0), 62 selected_opacity = 255, 63 mode = CAROUSEL, 64 path_steps = 1, 65 duration = 500, 66 transformation = implicit.DECELERATE):
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
93 - def width__get(self):
94 return self._width
95
96 - def height__get(self):
97 return self._height
98
99 - def size__get(self):
100 return (self._width, self._height)
101
102 - def width__set(self, width):
103 self._width = width
104
105 - def height__set(self, height):
106 self._height = height
107
108 - def size__set(self, size):
109 self._width = size[0] 110 self._height = size[1]
111
112 - def _within_bounds(self, index):
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
122 - def _apply_selected_values(self, index):
123 """ 124 Update self._items[index] with the values for the selected state 125 """ 126 item = self._items[index] 127 128 # set values for normal-image 129 item[NORMAL].opacity = self._selected_opacity 130 item[NORMAL].position = self._selected_position 131 item[NORMAL].size = self._selected_size 132 133 # set values for blurred-image 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 # set values for reflected-image 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 # set values for text-image 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
155 - def _apply_hidden_values(self, item_index, value_index):
156 """ 157 """ 158 item = self._items[item_index] 159 160 start = value_index 161 index = 0 162 opacity = [] 163 position = [] 164 reflected_position = [] 165 size = [] 166 167 for index in xrange(self._path_steps): 168 169 opacity.append(1) 170 x = self._values[start + index][X] + \ 171 0.45 * self._values[start + index][WIDTH] 172 y = self._values[start + index][Y] + \ 173 0.45 * self._values[start + index][HEIGHT] 174 reflected_y = self._values[start + index][Y] + \ 175 0.55 * self._values[start + index][HEIGHT] 176 position.append((x, y, item[NORMAL].z)) 177 reflected_position.append((x, reflected_y, item[NORMAL].z)) 178 size.append((0.1 * self._values[start + index][WIDTH], \ 179 0.1 * self._values[start + index][HEIGHT])) 180 181 # set values for normal-image 182 item[NORMAL].opacity = opacity 183 184 # set values for blurred-image 185 item[BLURRED].opacity = opacity 186 187 # set values for reflected-image 188 item[REFLECTED].opacity = opacity 189 190 # set values for text-image 191 item[TEXT].opacity = opacity
192
193 - def _apply_visible_values(self, item_index, value_index):
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 # fill the value-lists 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 # set values for normal-image 254 item[NORMAL].opacity = normal_opacity 255 item[NORMAL].position = position 256 item[NORMAL].size = size 257 258 # set values for blurred-image 259 item[BLURRED].opacity = blurred_opacity 260 item[BLURRED].position = position 261 item[BLURRED].size = size 262 263 # set values for reflected-image 264 item[REFLECTED].opacity = reflected_opacity 265 item[REFLECTED].position = reflected_position 266 item[REFLECTED].size = size 267 268 # set values for text-image 269 item[TEXT].opacity = text_opacity 270 item[TEXT].position = text_position 271 item[TEXT].size = size
272
273 - def _calculate_value_list(self, value):
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 # FIXME: values for chain need to computed here 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
320 - def layout(self):
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
346 - def regenerate_text(self):
347 # compute the maximum size of the text drawables 348 max_values = self._calculate_value_list(math.pi) 349 350 for i in self._items: 351 i[TEXT].stop_animations() 352 # apply it to all the text drawables 353 for i in self._item_handles: 354 # store the original size 355 prev_size = i[TEXT].size 356 357 i[TEXT].visible = False 358 # apply the maximum size 359 i[TEXT].size = (max_values[WIDTH], max_values[HEIGHT]) 360 361 self._font_height = max_values[HEIGHT] / 3.0 362 # FIXME: hack to regenerate the text drawable 363 i[TEXT].font_height = self._font_height + 0.1 364 i[TEXT].font_height = self._font_height 365 366 # restore original size 367 i[TEXT].size = prev_size 368 i[TEXT].visible = True
369
370 - def update(self):
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
400 - def update_images(self, index, normal_file, blurred_file, reflected_file):
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
410 - def insert(self, index, normal_file, blurred_file, reflected_file, label):
411 """ 412 This call has no effect if the widget is in state "selected"! 413 """ 414 if self._selected: 415 return 416 417 # create images/drawables 418 normal_image = Image() 419 blurred_image = Image() 420 reflected_image = Image() 421 text_image = Text() 422 423 # read images from disk 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 # set up the text-label 429 #text_image.label = label 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 # set initial opacity 437 normal_image.opacity = 1 438 blurred_image.opacity = 1 439 reflected_image.opacity = 1 440 text_image.opacity = 1 441 442 # set initial foreground-color to full opaque white 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 # set initial background-color to transparent black 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 # set inital state of visible-flag 455 normal_image.visible = True 456 blurred_image.visible = True 457 reflected_image.visible = True 458 text_image.visible = True 459 460 # make sure the image is scaled 461 normal_image.layout = pgm.IMAGE_SCALED 462 blurred_image.layout = pgm.IMAGE_SCALED 463 reflected_image.layout = pgm.IMAGE_SCALED 464 465 # add each image of the icon to the widgets group so it actually gets 466 # added to the canvas 467 self.add(normal_image) 468 self.add(blurred_image) 469 self.add(reflected_image) 470 self.add(text_image) 471 472 # create a list of the four images and their "handles" in order to 473 # be able to delete it later (in remove()) 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 # create animated version of the images 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 # set animation attributes fo the images 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 # create and add an item of the animated images 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
511 - def append(self, normal_file, blurred_file, reflected_file, label):
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
524 - def delete(self, index):
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 # retrieve handles from the group 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
541 - def prev(self):
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
557 - def next(self):
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
573 - def select(self):
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
587 - def is_selected(self):
588 """ 589 Returns if widget is in selected state or not. 590 """ 591 return self._selected
592
593 - def focused_item__get(self):
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
601 - def focused_item__set(self, index):
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 # OpenGL viewport creation 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 # Canvas and image drawable creation 683 canvas = pgm.Canvas() 684 canvas.size = (window_width, window_height) 685 686 # Bind the canvas to the OpenGL viewport 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