Posts Tagged 'Clutter'
2009-12-25
Recently, I've been getting a lot of hits from people searching for "pyclutter examples". Unfortunately, the code that most people find is my hello world example that was written for Clutter 0.6 and since the API for Clutter has change quite a bit since I wrote that code, the code doesn't work with the latest stable release of Clutter.
I've considered updating the code to work with the more modern Clutter, but that wouldn't really be fun would it? Besides, someone has already updated the code. Thanks Ryan. Why don't I write another example? Don't mind if I do.
There is nothing new Clutter-wise in this example to differentiate it from the hello world example except that this code will run with the latest Clutter.
What we have here, is John Conway's Game of Life where each cell is a Clutter Rectangle.
When running the code: press "q" to quit, press "r" to reset.
One issue I have with this code is that when creating the rectangles for the cells, creation slows down significantly after creating about 3000 rectangles. The app runs fine when all of the cells are created, but the slow down during creation really irks me. Perhaps I need more RAM or I should write the code in Vala using SDL?
Now stop reading, and go play with colored rectangles.
I've considered updating the code to work with the more modern Clutter, but that wouldn't really be fun would it? Besides, someone has already updated the code. Thanks Ryan. Why don't I write another example? Don't mind if I do.
There is nothing new Clutter-wise in this example to differentiate it from the hello world example except that this code will run with the latest Clutter.
What we have here, is John Conway's Game of Life where each cell is a Clutter Rectangle.
#!/usr/bin/env python # copyright 2009 jezra lickter #this software is licensed under the GPLv3 http://www.gnu.org/licenses/gpl.txt import clutter import random class Game: def __init__(self,cell_size): self.cells = [] #create a clutter stage self.stage = clutter.Stage() #set the stage size in x,y pixels self.stage.set_size(500,500) self.stage.set_title("The Game of Life") self.stage_width, self.stage_height = self.stage.get_size() #define some clutter colors in rgbo (red,green,blue,opacity) self.color_black =clutter.Color(0,0,0,255) self.color_green =clutter.Color(0,255,0,255) self.color_blue =clutter.Color(0,0,255,255) #set the clutter stages bg color to our black self.stage.set_color(self.color_black) #we will need to check on the key presses from the user self.stage.connect('key-press-event', self.parseKeyPress) self.stage.connect("destroy", self.quit) '''fill the screen with rectangles''' num_cols = int(self.stage_width/cell_size) num_rows = int(self.stage_height/cell_size) cells_to_create = num_cols*num_rows y = 0 for row in range(num_rows): x = 0 self.cells.append( [] ) print "%i cells to create" % (cells_to_create) for column in range(num_cols): #set the column index to a rect that is cell_size by cell_size self.cells[row].append( clutter.Rectangle() ) self.cells[row][column].set_size(cell_size,cell_size) self.cells[row][column].set_position(x,y) #add this cell to the stage self.stage.add(self.cells[row][column]) self.cells[row][column].set_color(self.color_green) self.cells[row][column].set_opacity(0) x+=cell_size cells_to_create-=num_cols y+=cell_size print "cells created" #get some data about the rows self.row_count = len(self.cells) self.col_count = len(self.cells[0]) self.cell_count = self.row_count*self.col_count #make a timer to do stuff for us self.timeline = clutter.Timeline(duration=500) #when the timer finishes running, we want to determine the life self.timeline.connect("completed",self.compute_life) #increase linearly alpha = clutter.Alpha(self.timeline, clutter.LINEAR) #make some opacity behaviours that we will apply to the cells self.dieBehaviour = clutter.BehaviourOpacity(255,0,alpha) self.aliveBehaviour = clutter.BehaviourOpacity(0,255,alpha) def parseKeyPress(self,actor,event): if event.keyval == clutter.keysyms.q: self.quit() elif event.keyval == clutter.keysyms.c: self.compute_life() elif event.keyval == clutter.keysyms.r: self.reseed() def quit(self,widget=None): clutter.main_quit() def run(self): self.stage.show_all() self.seed_life() self.compute_life() clutter.main() def reseed(self): for r in range(self.row_count): for c in range(self.col_count): if self.cells[r][c].get_opacity()>0: self.cells[r][c].set_opacity(0) self.seed_life() def seed_life(self): print "adding random life" #loop through the rows for r in range(self.row_count): #loop through the columns for c in range(self.col_count): #generate a number i = random.randint(0,4) if i==0: # x,y is alive self.cells[r][c].set_opacity(255) def compute_life(self,asset=None): #detach all objects from the behaviors self.dieBehaviour.remove_all() self.aliveBehaviour.remove_all() for r in range(self.row_count): for c in range(self.col_count): if self.cells[r][c].get_opacity()>0: alive=True else: alive=False ln = self.living_neighbors(r,c) if alive and ( ln<2 or ln>3 ): #this cell will die; boohoo self.dieBehaviour.apply(self.cells[r][c] ) elif not alive and ln==3: #this cell is now alive; damn zombies self.aliveBehaviour.apply(self.cells[r][c] ) #start the timeline again self.timeline.start() def living_neighbors(self,r,c): alive = 0 r_min = r-1 r_max = r+1 c_min = c-1 c_max = c+1 if r==0 : r_min=r elif r == self.row_count-1: r_max=r if c==0 : c_min=c elif c == self.col_count-1: c_max=c tr = r_min tc = c_min cells_computed=0 while not tr > r_max: tc = c_min while not tc > c_max: if ( tr!=r or tc!=c ): cells_computed+=1 if self.cells[tr][tc].get_opacity()>0: alive+=1 tc+=1 tr+=1 return alive if __name__=="__main__": game = Game(5) game.run()
When running the code: press "q" to quit, press "r" to reset.
One issue I have with this code is that when creating the rectangles for the cells, creation slows down significantly after creating about 3000 rectangles. The app runs fine when all of the cells are created, but the slow down during creation really irks me. Perhaps I need more RAM or I should write the code in Vala using SDL?
Now stop reading, and go play with colored rectangles.
Comments
2009-05-10
Quite a while ago, I began playing with the clutter library as a way to move and display graphics on a fullscreen application interface. With the 0.8 release of the clutter library, the fullscreen function didn't seem to work properly anymore and I was rather perturbed. Instead of taking up the entire screen, a "fullscreened" app would behave as though one had maximized a window. However, earlier today I revisited some python code that uses pyclutter to access the clutter library and I came up with a hack that will solve the fullscreen problem and actually redefines the problem in my eyes.
First, let me show some code that does not work as expected:
After trying different various possible fixes, it appeared that the bug is actually
"the stage fullscreen function behaves like maximize if used before the clutter main loop".
What does that mean? It means that the fullscreen function will work if the function is called after the clutter main() function. But how does one call a function after a mainloop has been initialized? I'm glad you asked.
Check out this code with the fix in place:
Since the clutter library uses Gobject objects as base classes for most clutter classes, including the mainloop, one can use gobject.timeout_add() to call a function after a set amount of time. In the fixed code, a function that calls fullscreen was set to run a few milliseconds after the clutter.main() was called.
Now I need to port the code to Vala and make sure the fix works with the Vala Clutter bindings as well.
First, let me show some code that does not work as expected:
#!/usr/bin/env python import clutter class test: def __init__(self): #create a stage self.stage = clutter.Stage() #set the stage to be fullscreen self.stage.fullscreen() #show the screen self.stage.show_all() #get the key presses self.stage.connect('key-press-event', self.quit) #start the clutter main loop clutter.main() def quit(self,object,event): #quit the main loop clutter.main_quit() if __name__=="__main__": #make an instance of the test t = test()
After trying different various possible fixes, it appeared that the bug is actually
"the stage fullscreen function behaves like maximize if used before the clutter main loop".
What does that mean? It means that the fullscreen function will work if the function is called after the clutter main() function. But how does one call a function after a mainloop has been initialized? I'm glad you asked.
Check out this code with the fix in place:
#!/usr/bin/env python import clutter import gobject class test: def __init__(self): #create a stage self.stage = clutter.Stage() #show the screen self.stage.show_all() #get the key presses self.stage.connect('key-press-event', self.quit) #add a fullscreen function to the gobject timeout gobject.timeout_add(10,self.go_fullscreen) #start the clutter main loop clutter.main() def go_fullscreen(self): self.stage.fullscreen() def quit(self,object,event): #quit the main loop clutter.main_quit() if __name__=="__main__": #make an instance of the test t = test()
Since the clutter library uses Gobject objects as base classes for most clutter classes, including the mainloop, one can use gobject.timeout_add() to call a function after a set amount of time. In the fixed code, a function that calls fullscreen was set to run a few milliseconds after the clutter.main() was called.
Now I need to port the code to Vala and make sure the fix works with the Vala Clutter bindings as well.
Comments
2008-05-12
This is not a tutorial. This is an example of code and some ramblings of my experience writing the code. What does the code do? What we have here is a basic image viewer using pyclutter textures as the output of the image. The image viewer will run in fullscreen mode and will animate images on and off of the screen when the user pressed either the left or right arrow keys. Here we go.
Problems:
1. I was hoping to automatically change a clutter textures height and width by making sure that the texture's "sync-size" property was true and then changing the textures pixbuf. Unfortunately, this didn't work, so I grabbed the height and width the pixbuf itself and adjusted the size of the texture accordingly.
2. Althought it was a minor problem, I spend too much time trying to figure out how to use gstreamer to get the data from my images files and pass the data to a gstvideotexture. As you can see, I ended up using gtk.gdk for image handling.
3. segfaulting on behaviourPath.append_knots(knot1,knot2)
Nifty Clutter:
behaviourPath knots - a knot is an X,Y coordinate that make a path for an object to follow when the object animates. For example, suppose I have a texture at 0,0 and I want to move the texture to 200,100 and then to 50,400. I would make a knot for each coordinate and add the knot to a behaviourPath using append_knots(list of knots) or insert_knot(knot index, knot).
myBehaviourPath.append_knots(knot1,knot2,knot3)
then apply the behavor to the texture and start the timeline associated with the behaviours alpha
myBehaviourPath.append(myTexture)
myTimeline.start()
Booyah! The texture will move from knot1 to knot2 to knot3
Awesome:
Pydoc. I can't believe it took me so long to learn about pydoc: a python documentation tool. Which I found indispensable for figuring out what methods are available in various python modules.
#!/usr/bin/env python
import sys
import os, os.path
import clutter
import gtk
class imageList:
def __init__(self,dir):
# we need an array and a default index of the array
self.imagelist =[]
self.imagelist_index = 0
#go find the images in the directory
self.find_images(dir)
def get_index_image(self):
return self.imagelist[self.imagelist_index]
def get_left_image(self):
return self.imagelist[ self.get_next_left_index() ]
def get_right_image(self):
return self.imagelist[ self.get_next_right_index() ]
def get_index(self):
return self.imagelist_index
def get_next_left_index(self):
left = self.imagelist_index-1
if left<0:
left = self.get_image_count()-1
return left
def get_next_right_index(self):
right = self.imagelist_index+1
if right>=self.get_image_count():
right = 0
return right
def decrement_index(self):
self.imagelist_index = self.get_next_left_index()
def increment_index(self):
self.imagelist_index = self.get_next_right_index()
def get_image_count(self):
return len(self.imagelist)
def find_images(self,dir):
#loop through the files in the folder
print "searching "+dir+" for images...."
assets = os.listdir(dir)
for i in range(len(assets) ):
asset_path = os.path.join(dir,assets[i] )
if os.path.isdir(asset_path):
#if the asset is a directory, recurse
self.find_images(asset_path)
pass
else:
#how do we know if this is a usable image?
#try to get a pixbuf from the image
try:
"""TODO: assume the majority of files are images and delete
the bad files from the array when they are displayed to the
user, this should vastly improve start up time"""
pixbuf = gtk.gdk.pixbuf_new_from_file(asset_path)
#add this asset to the imagelist
print "Found: "+asset_path
self.imagelist.append(asset_path)
except:
#fail this isn't a usable image
pass
class imageViewer:
def __init__(self):
#is there a sys.argv?
if len(sys.argv)>1:
self.create_image_list(sys.argv[1])
else:
self.print_help("No image directory selected")
#if we made it this far, we can start making the clutter interface
self.stage = clutter.Stage()
#make the stage full screen
self.stage.fullscreen()
#hide the mousey
self.stage.hide_cursor()
#we need to record the stage size
#NOTE: I should try to use the CLUTTER_STAGE_WIDTH, but it may only be part of the c library
(self.stage_w,self.stage_h) = self.stage.get_size()
#define a clutter color in rgbo (red,green,blue,opacity)
color_black =clutter.Color(0,0,0,255)
#set the clutter stages bg color to our black
self.stage.set_color(color_black)
#we will need to check on the key presses from the user
self.stage.connect('key-press-event', self.parseKeyPress)
#make three textures: left,right,and center
self.l_image=clutter.Texture()
self.r_image=clutter.Texture()
self.c_image=clutter.Texture()
#add the textures to the stage
self.stage.add(self.l_image)
self.stage.add(self.r_image)
self.stage.add(self.c_image)
#hide all of the images
self.l_image.set_opacity(0)
self.c_image.set_opacity(0)
self.r_image.set_opacity(0)
#creae a timeline for the texture animations
self.texture_timeline = clutter.Timeline(fps=25,duration=1000)
#create an alpha to describe the movement for behaviours
self.texture_alpha = clutter.Alpha(self.texture_timeline, clutter.ramp_inc_func)
'''we will need a behaviour to handle animation between the left and center(lc),
and between the center and right(cr) '''
self.lc_behaviour = clutter.BehaviourPath(self.texture_alpha)
self.cr_behaviour = clutter.BehaviourPath(self.texture_alpha)
#show all stage items and enter the clutter main loop
self.stage.show_all()
self.set_center_image()
clutter.main()
#we will need to scale the textures to fill the screen
def scale_texture(self,texture):
#NOTE: texture.get_size is not working properly, use the size of the pixbuf
#(twidth,theight) = texture.get_size()
buf = texture.get_pixbuf()
twidth = buf.get_width()
theight = buf.get_height()
if twidth>theight:
scale = ((self.stage_w+0.0)/twidth)
else:
scale = ((self.stage_h+0.0)/theight)
#perform the scaling
print scale
texture.set_property("width",twidth*scale)
texture.set_property("height",theight*scale)
def set_center_image(self):
#hide the texture
self.c_image.set_opacity(0)
#center is the current index
buf = gtk.gdk.pixbuf_new_from_file( self.imagelist.get_index_image() )
self.c_image.set_pixbuf( buf )
#scale the images
self.scale_texture(self.c_image)
#center the center image
(tex_width,tex_height) = self.c_image.get_size()
xloc = self.stage_w/2 - tex_width/2
yloc = self.stage_h/2 - tex_height/2
self.c_image.set_position(xloc,yloc)
#make the center image visible
self.c_image.set_opacity(255)
def set_right_image(self):
#right is right image
buf=gtk.gdk.pixbuf_new_from_file( self.imagelist.get_right_image() )
self.r_image.set_pixbuf( buf )
#scale the image
self.scale_texture(self.r_image)
#move the right image off of the screen and center vertically
(tex_width,tex_height) = self.r_image.get_size()
yloc = self.stage_h/2 - tex_height/2
self.r_image.set_position(self.stage_w,yloc )
self.r_image.set_opacity(255)
def set_left_image(self):
#l_image is left image
buf = gtk.gdk.pixbuf_new_from_file( self.imagelist.get_left_image() )
self.l_image.set_pixbuf( buf )
#scale the texture
self.scale_texture(self.l_image)
#move the left image off of the screen and center vertically
(tex_width,tex_height) = self.l_image.get_size()
yloc = self.stage_h/2 - tex_height/2
self.l_image.set_position(0-tex_width,yloc )
#make the center image visible
self.l_image.set_opacity(255)
def parseKeyPress(self,actor, event):
#do stuff when the user presses a key
#it would be awesome if I could find some documentation regarding clutter.keysyms
if event.keyval == clutter.keysyms.q:
#if the user pressed "q" quit the test
clutter.main_quit()
elif event.keyval == clutter.keysyms.Right:
self.next_image(0)
elif event.keyval == clutter.keysyms.Left:
self.next_image(1)
#direction of 0 means move right, direction 1 means move from right to left
def next_image(self,direction=0):
#clear the behaviour paths
self.lc_behaviour.clear()
self.cr_behaviour.clear()
self.lc_behaviour.remove_all()
self.cr_behaviour.remove_all()
#update the center texture
self.set_center_image()
if direction:
#we need to set the right image
self.set_right_image()
#hide the left image
self.l_image.set_opacity(0)
#adjust the image list index
self.imagelist.increment_index()
#center image moves to the left
(x,y) = self.c_image.get_position()
lc_knot0 = clutter.Knot(x,y)
#what is the destination knot?
lc_knot1 = clutter.Knot(-self.c_image.get_width(),y )
#which texture is moving in the lc?
lc_texture = self.c_image
#right image moves to the left
(x,y) = self.r_image.get_position()
cr_knot0 = clutter.Knot(x,y)
xcenter = self.stage_w/2-self.r_image.get_width()/2
cr_knot1 = clutter.Knot(xcenter,y )
#which texture is moving in the cr?
cr_texture = self.r_image
else:
#we need to set the left image
self.set_left_image()
#hide the right image
self.r_image.set_opacity(0)
#adjust the image list index
self.imagelist.decrement_index()
#left image moves to the center
(x,y) = self.l_image.get_position()
lc_knot0 = clutter.Knot(x,y)
xcenter = self.stage_w/2-self.l_image.get_width()/2
lc_knot1 = clutter.Knot(xcenter,y )
#which texture is moving in the lc?
lc_texture = self.l_image
#center image moves to the right
(x,y) = self.c_image.get_position()
cr_knot0 = clutter.Knot(x,y)
endx = self.stage_w
cr_knot1 = clutter.Knot(endx,y )
#which texture is moving in the cr?
cr_texture = self.c_image
#add the knots to the lc behaviour
self.lc_behaviour.insert_knot(0,lc_knot0)
self.lc_behaviour.insert_knot(1,lc_knot1)
self.lc_behaviour.apply(lc_texture)
#add the knots to the cr behaviour
self.cr_behaviour.insert_knot(0,cr_knot0)
self.cr_behaviour.insert_knot(1,cr_knot1)
self.cr_behaviour.apply(cr_texture)
#start the timeline associated with the behaviours
self.texture_timeline.start()
def create_image_list(self,image_folder):
cwd = os.getcwd()
path_to_image_folder = os.path.join(cwd,image_folder)
#does the folder exist?
if os.path.isdir(path_to_image_folder):
print "image folder: "+path_to_image_folder
self.imagelist = imageList(path_to_image_folder)
#did we find images?
img_count = self.imagelist.get_image_count()
print "Found "+str(img_count)+" images"
if img_count ==0:
self.print_help(image_folder+" does not contain usable images")
else:
self.print_help(image_folder+" is not a directory")
def print_help(self,errormessage=""):
if(errormessage):
print "Error: "+errormessage
print "Usage: "+sys.argv[0]+" /path/to/directory/containing/images"
print "Navigation: use the left and right arrows to view the images"
print ""
sys.exit()
if __name__=="__main__":
#make an imageViewer
imageViewer = imageViewer()
Problems:
1. I was hoping to automatically change a clutter textures height and width by making sure that the texture's "sync-size" property was true and then changing the textures pixbuf. Unfortunately, this didn't work, so I grabbed the height and width the pixbuf itself and adjusted the size of the texture accordingly.
2. Althought it was a minor problem, I spend too much time trying to figure out how to use gstreamer to get the data from my images files and pass the data to a gstvideotexture. As you can see, I ended up using gtk.gdk for image handling.
3. segfaulting on behaviourPath.append_knots(knot1,knot2)
Nifty Clutter:
behaviourPath knots - a knot is an X,Y coordinate that make a path for an object to follow when the object animates. For example, suppose I have a texture at 0,0 and I want to move the texture to 200,100 and then to 50,400. I would make a knot for each coordinate and add the knot to a behaviourPath using append_knots(list of knots) or insert_knot(knot index, knot).
myBehaviourPath.append_knots(knot1,knot2,knot3)
then apply the behavor to the texture and start the timeline associated with the behaviours alpha
myBehaviourPath.append(myTexture)
myTimeline.start()
Booyah! The texture will move from knot1 to knot2 to knot3
Awesome:
Pydoc. I can't believe it took me so long to learn about pydoc: a python documentation tool. Which I found indispensable for figuring out what methods are available in various python modules.
Comments
for clutter-0.8 change:
buf = gtk.gdk.pixbuf_new_from_file( self.imagelist.get_index_image() )
self.c_image.set_pixbuf( buf )
to:
self.c_image.set_from_file( self.imagelist.get_index_image() )
===================================
But self.stage.fullscreen() can only maximize the window.
Do you know how to setup a real fullscreen (borderless) window?
buf = gtk.gdk.pixbuf_new_from_file( self.imagelist.get_index_image() )
self.c_image.set_pixbuf( buf )
to:
self.c_image.set_from_file( self.imagelist.get_index_image() )
===================================
But self.stage.fullscreen() can only maximize the window.
Do you know how to setup a real fullscreen (borderless) window?
Since the fullscreen() function does fill my entire screen when using pyclutter 0.6.x, this may be a bug in the latest branch of clutter.
2008-05-02
What is pyclutter? Pyclutter is the python binding to the clutter library: a high level OpenGL library for creating animated user interfaces.
OK, so this isn't really a tutorial because I'm not going to go through each line of code and explain what it does. However, there are certain parts of the code that I will give a bit more info about, because I had some problems figuring it out and I'm hoping my experience will help other.
This is not a static "hello world". I know, I broke the rules; but I think this is a good thing. Upon running the script, there will be a "hello" on the screen. Pressing "s" on the keyboard will initiate a switching toggle between "hello" and "world" by animating a change in their opacities. Pressing "q" will quit the app. Check it out
Ha ha, just kidding.....
Copy the code and drop it into your favorite text editor.
things I got hung up on....
1. Labels:the pyclutter documentation doesn't reference labels. Fortunately, the pyclutter examples that are included with the pyclutter source code, have a sample of a label
2. Alpha: clutter.alpha is a representation of a value based on a clutter.timeline and a function to determine the value at a given time on the time line. The first function that I used was a sine function that would peak at the midway point and then return to the starting point. So my opacity behavior would go from 0 to 255 and then back to 0, when what I really wanted was to go from 0 to 255.
3. behaviourOpacity: When the behavior transitions the opacity from 255 to 0, the ending opacity is 1, or at least is was the last time I checked. Odd.
4. Determining which key is pressed: I don't know enough about python to be able to print out all of the data in the event object that is sent when there is a "key-press-event". I was trying to print the keys from the event objects dict but nothing was working. Luckily, I stumbles upon the clutter.keysyms doohicky but I haven't found all of the documentation for the keysyms yet. For now, I just use clutter.keysyms.[what key am I looking for] to determine if a specific key is pressed.
That's it for now...
OK, so this isn't really a tutorial because I'm not going to go through each line of code and explain what it does. However, there are certain parts of the code that I will give a bit more info about, because I had some problems figuring it out and I'm hoping my experience will help other.
This is not a static "hello world". I know, I broke the rules; but I think this is a good thing. Upon running the script, there will be a "hello" on the screen. Pressing "s" on the keyboard will initiate a switching toggle between "hello" and "world" by animating a change in their opacities. Pressing "q" will quit the app. Check it out
#!/usr/bin/env python
print "Hello World!"
Ha ha, just kidding.....
#!/usr/bin/env python
import clutter
class clutterTest:
def __init__(self):
#create a clutter stage
self.stage = clutter.Stage()
#set the stage size in x,y pixels
self.stage.set_size(500,200)
#define some clutter colors in rgbo (red,green,blue,opacity)
color_black =clutter.Color(0,0,0,255)
color_green =clutter.Color(0,255,0,255)
color_blue =clutter.Color(0,0,255,255)
#set the clutter stages bg color to our black
self.stage.set_color(color_black)
#we will need to check on the key presses from the user
self.stage.connect('key-press-event', self.parseKeyPress)
#create a clutter label, is there documentation for creating a clutterlabel?
self.label = clutter.Label()
#set the labels font
self.label.set_font_name('Mono 32')
#add some text to the label
self.label.set_text("Hello")
#make the label green
self.label.set_color(color_green )
#put the label in the center of the stage
(label_width, label_height) = self.label.get_size()
label_x = (self.stage.get_width()/2) - label_width/2
label_y = (self.stage.get_height()/2) - label_height/2
self.label.set_position(label_x, label_y)
#make a second label similar to the first label
self.label2 = clutter.Label()
self.label2.set_font_name('Mono 32')
self.label2.set_text("World!")
self.label2.set_color(color_blue )
(label2_width, label2_height) = self.label2.get_size()
label2_x = (self.stage.get_width()/2) - label2_width/2
label2_y = (self.stage.get_height()/2) - label2_height/2
self.label2.set_position(label2_x, label2_y)
#hide the label2
self.label2.set_opacity(0)
#create a timeline for the animations that are going to happen
self.timeline = clutter.Timeline(fps=20,duration=500)
#how will the animation flow? ease in? ease out? or steady?
#ramp_inc_func will make the animation steady
labelalpha = clutter.Alpha(self.timeline, clutter.ramp_inc_func)
#make some opacity behaviours that we will apply to the labels
self.hideBehaviour = clutter.BehaviourOpacity(255,0x00,labelalpha)
self.showBehaviour = clutter.BehaviourOpacity(0x00,255,labelalpha)
#add the items to the stage
self.stage.add(self.label2)
self.stage.add(self.label)
#show all stage items and enter the clutter main loop
self.stage.show_all()
clutter.main()
def parseKeyPress(self,actor, event):
#do stuff when the user presses a key
#it would be awesome if I could find some documentation regarding clutter.keysyms
if event.keyval == clutter.keysyms.q:
#if the user pressed "q" quit the test
clutter.main_quit()
elif event.keyval == clutter.keysyms.s:
#if the user pressed "s" swap the labels
self.swapLabels()
def swapLabels(self):
#which label is at full opacity?, like the highlander, there can be only one
if(self.label.get_opacity()>1 ):
showing = self.label
hidden = self.label2
else:
showing = self.label2
hidden = self.label
#detach all objects from the behaviors
self.hideBehaviour.remove_all()
self.showBehaviour.remove_all()
#apply the behaviors to the labels
self.hideBehaviour.apply(showing)
self.showBehaviour.apply(hidden)
#behaviours do nothing if their timelines are not running
self.timeline.start()
if __name__=="__main__":
test = clutterTest()
Copy the code and drop it into your favorite text editor.
things I got hung up on....
1. Labels:the pyclutter documentation doesn't reference labels. Fortunately, the pyclutter examples that are included with the pyclutter source code, have a sample of a label
2. Alpha: clutter.alpha is a representation of a value based on a clutter.timeline and a function to determine the value at a given time on the time line. The first function that I used was a sine function that would peak at the midway point and then return to the starting point. So my opacity behavior would go from 0 to 255 and then back to 0, when what I really wanted was to go from 0 to 255.
3. behaviourOpacity: When the behavior transitions the opacity from 255 to 0, the ending opacity is 1, or at least is was the last time I checked. Odd.
4. Determining which key is pressed: I don't know enough about python to be able to print out all of the data in the event object that is sent when there is a "key-press-event". I was trying to print the keys from the event objects dict but nothing was working. Luckily, I stumbles upon the clutter.keysyms doohicky but I haven't found all of the documentation for the keysyms yet. For now, I just use clutter.keysyms.[what key am I looking for] to determine if a specific key is pressed.
That's it for now...
Comments
doesn't work with python-clutter1.0.0
This example was written for clutter 0.6. Fortunately, the python backtraces make it fairly easy to determine what has changed from 0.6 to 1.0. In this case, it is probably the change of the "Label" object to a "Text" object.