Un article de Screenlets.
Développeurs
Screenlets API
Une documentation de l'API est disponible en ligne à l'adresse http://screenlets.org//Documentation/index.html here
Soumettre un patch / Amélioration du coeur
Vous pouvez facilementsoumettre une amélioration et aider à corriger des bugs en envoyant un email à helder.fraga@hotmail.com. Une TODO list est disponible si vous ne savez pas où commencer http://bazaar.launchpad.net/~screenlets-dev/screenlets/trunk/files
Où et comment obtenir la version de développement ?
Le code source de screenlets est hebergé par launchpad. Vous pouvez y télécharger et installer la dernière version de développement en utilisant cette comande
mkdir screenlets-trunk cd screenlets-trunk bzr branch http://bazaar.launchpad.net/~screenlets-dev/screenlets/trunk cd trunk python setup.py install Pour mette à jour simplement : cd screenlets-trunk bzr pull
(Il faut au préalable installer le logiciel 'bzr'):
Quelques informations sur screenlets
Controleurs inclus
Les Screenlets peuvent être controlés en utilisant quelques variables définies dans l'API telles que self.scale, self.width, self.x. Par exemple pour déplacer le screenlet à 100 px de la gauche de l'écran, un simple self.x = 100 fera l'affaire.
Dessin
Les Screenlets sont habituellement dessiné en utilisant PyCairo, mais vous pouvez également utiliser des widget GTK. Il faut se méfier parce que les widgets GTK ne se redimensionnent pas de la même façon que les dessins Cairo, Il sera nécessaire d'implémenter un redimensionnement pour ces widgets.
Thèmes
Les thèmes Screenlets sont trés facilement modifiables. Il est possible de développer un thème de trois manières différentes
- En utilisant le controleur de thèmes inclus en utilisant les fonctions comme draw_rectangle , draw_triangle , draw_shadow , draw_line , draw_circle...
- En créant des images pour générer des thèmes.
- En utilisant une combinaison des deux dernières techniques
Est-il facile de créer son propre Screenlet?
L'API est relativement facile à utiliser, mais le moyen le plus rapide pour créer un nouveau screenlet est de se baser surun screenlet existant et de l'éditer. Le screenlet Exemple fournit avec le paquet de base est idéal pour cela. Tous les autres screenlets peuvent vous servir. Ce projet est opensource et les sources sont là aussi pour ça.
Débuter
Structure des fichiers
Your screenlet folder should look like this
Clock/ClockScreenlet.py Clock/icon.png or icon.svg Clock/themes/defaut/ (containing the default skin)
Sarting the python file
First we do the basic imports
import screenlets from screenlets.options import StringOption , BoolOption , IntOption , FileOption , DirectoryOption , ListOption , AccountOption , TimeOption , FontOption, ColorOption , ImageOption
We import some more stuff
import gobject import gtk
Lets start the base class
class ExampleScreenlet (screenlets.Screenlet): """A simple example of how to create a Screenlet"""
Lets set some global vars
__name__ = 'ExampleScreenlet' #Name of the screenlet followed by "Screenlet" __version__ = '0.1' __author__ = 'My name here' __desc__ = __doc__ # set description to docstring of class
Lets set some other global vars that we need
test_text = 'Hi.. im a screenlet'
demo_text = ''
demo_number = ''
int_example = 1
bool_example = True
time_example = (7, 30, 0)
account_example = ('','')
color_example =(0.0, 0.0, 0.0, 1)
font_example = "Sans Medium 5"
image_example = ''
file_example = ''
directory_example = ''
list_example = ('','')
hover = False
Call super
def __init__ (self, **keyword_args): screenlets.Screenlet.__init__(self, width=200, height=200, uses_theme=True, **keyword_args)
Set the default theme
self.theme_name = "default"
Add menu items or configurable settings
self.add_options_group('Example', 'This is an example of ' +\
' editable options within an OptionGroup ...')
# add editable option to the group
self.add_option(StringOption('Example','test_text', # attribute-name
self.test_text, # default-value
'Test-Text', # widget-label
'The Test-Text option for this Screenlet ...' # description
))
self.add_option(BoolOption('Example','bool_example',
self.bool_example, 'Option group bool',
'Example options group using bool'))
self.add_option(TimeOption('Example','time_example', self.time_example,
'Option group time', 'Example options group using time'))
self.add_option(IntOption('Example','int_example',
self.int_example, 'Option group integer',
'Example options group using integer',
min=0, max=5000))
self.add_option(FontOption('Example','font_example',
self.font_example, 'Option group font',
'Example options group using font'))
self.add_option(ColorOption('Example','color_example',
self.color_example, 'Option group color',
'Example options group using color'))
self.add_option(AccountOption('Example','account_example',self.account_example,
'Option group account','Using keyring encryption'))
self.add_option(ImageOption('Example','image_example', self.image_example,
'Option group Image', 'Example options group using Image'))
self.add_option(FileOption('Example','file_example', self.file_example,
'Option group file', 'Example options group using file'))
self.add_option(DirectoryOption('Example','directory_example', self.directory_example,
'Option group directory', 'Example options group using directory'))
self.add_option(ListOption('Example','list_example', self.list_example,
'Option group list', 'Example options group using list'))
The event handlers
def on_after_set_atribute(self,name, value):
"""Called after setting screenlet atributes"""
print name + ' is going to change from ' + str(value)
pass
def on_before_set_atribute(self,name, value):
"""Called before setting screenlet atributes"""
print name + ' has changed to ' + str(value)
pass
def on_create_drag_icon (self):
"""Called when the screenlet's drag-icon is created. You can supply
your own icon and mask by returning them as a 2-tuple."""
return (None, None)
def on_composite_changed(self):
"""Called when composite state has changed"""
pass
def on_drag_begin (self, drag_context):
"""Called when the Screenlet gets dragged."""
pass
def on_drag_enter (self, drag_context, x, y, timestamp):
"""Called when something gets dragged into the Screenlets area."""
pass
def on_drag_leave (self, drag_context, timestamp):
"""Called when something gets dragged out of the Screenlets area."""
pass
def on_drop (self, x, y, sel_data, timestamp):
"""Called when a selection is dropped on this Screenlet."""
return False
def on_focus (self, event):
"""Called when the Screenlet's window receives focus."""
pass
def on_hide (self):
"""Called when the Screenlet gets hidden."""
pass
def on_init (self):
"""Called when the Screenlet's options have been applied and the
screenlet finished its initialization. If you want to have your
Screenlet do things on startup you should use this handler."""
print 'i just got started'
# add menu items from xml file
self.add_default_menuitems(DefaultMenuItem.XML)
# add menu item
self.add_menuitem("at_runtime", "A")
# add default menu items
self.add_default_menuitems()
def on_key_down(self, keycode, keyvalue, event):
"""Called when a keypress-event occured in Screenlet's window."""
key = gtk.gdk.keyval_name(event.keyval)
if key == "Return" or key == "Tab":
screenlets.show_message(self, 'This is the ' + self.__name__ +'\n' + 'It is installed in ' + self.__path__)
def on_load_theme (self):
"""Called when the theme is reloaded (after loading, before redraw)."""
pass
def on_menuitem_select (self, id):
"""Called when a menuitem is selected."""
if id == "at_runtime":
screenlets.show_message(self, 'This is an example on a menu created at runtime')
if id == "at_xml":
screenlets.show_message(self, 'This is an example on a menu created in the menu.xml')
pass
def on_mouse_down (self, event):
"""Called when a buttonpress-event occured in Screenlet's window.
Returning True causes the event to be not further propagated."""
return False
def on_mouse_enter (self, event):
"""Called when the mouse enters the Screenlet's window."""
self.theme.show_tooltip("this is a tooltip , it is set to shows on mouse hover",self.x+self.mousex,self.y+self.mousey)
self.hover = True
print 'mouse is over me'
def on_mouse_leave (self, event):
"""Called when the mouse leaves the Screenlet's window."""
self.theme.hide_tooltip()
self.hover = False
print 'mouse leave'
def on_mouse_move(self, event):
"""Called when the mouse moves in the Screenlet's window."""
self.redraw_canvas()
pass
def on_mouse_up (self, event):
"""Called when a buttonrelease-event occured in Screenlet's window.
Returning True causes the event to be not further propagated."""
return False
def on_quit (self):
"""Callback for handling destroy-event. Perform your cleanup here!"""
screenlets.show_question(self, 'Do you like screenlets?')
return True
def on_realize (self):
""""Callback for handling the realize-event."""
def on_scale (self):
"""Called when Screenlet.scale is changed."""
pass
def on_scroll_up (self):
"""Called when mousewheel is scrolled up (button4)."""
pass
def on_scroll_down (self):
"""Called when mousewheel is scrolled down (button5)."""
pass
def on_show (self):
"""Called when the Screenlet gets shown after being hidden."""
pass
def on_switch_widget_state (self, state):
"""Called when the Screenlet enters/leaves "Widget"-state."""
pass
def on_unfocus (self, event):
"""Called when the Screenlet's window loses focus."""
pass
def on_draw (self, ctx):
"""In here we load the theme"""
# if theme is loaded
if self.theme:
# set scale rel. to scale-attribute
ctx.scale(self.scale, self.scale)
ctx.set_source_rgba(self.color_example[2], self.color_example[1], self.color_example[0],0.4)
if self.hover:
self.theme.draw_rounded_rectangle(ctx,0,0,20,self.width,self.height)
self.draw_circle(ctx,0,0,self.width,self.height)
# TEST: render example-bg into context (either PNG or SVG)
self.theme.render(ctx, 'example-bg')
ctx.set_source_rgba( self.color_example[0], self.color_example[1], self.color_example[2],self.color_example[3])
self.draw_text(ctx, self.test_text, 0, 0, self.font_example , 10,self.width,pango.ALIGN_LEFT)
self.draw_line(ctx,0,40,self.width,0,1)
self.draw_text(ctx, 'timer - ' + str(self.number), 0, 130, self.font_example , 10, self.width,pango.ALIGN_LEFT)
self.draw_text(ctx, self.theme_name, 0, 50, self.font_example , 10, self.width,pango.ALIGN_LEFT)
self.draw_text(ctx, 'mouse x ' + str(self.mousex ) + ' \n mouse y ' + str(self.mousey ) , 0, 170, self.font_example , 10,self.width,pango.ALIGN_LEFT)
# render svg-file
#self.theme['example-bg.svg'].render_cairo(ctx)
# render png-file
#ctx.set_source_surface(self.theme['example-test.png'], 0, 0)
#ctx.paint()
def on_draw_shape (self, ctx):
self.on_draw(ctx)
Lets add the screenlet to session
if __name__ == "__main__": # create new session import screenlets.session screenlets.session.create_session(ExampleScreenlet)
Builtin drawing funtion
ctx.scale(self.scale, self.scale)
ctx.set_source_rgba(self.color_example[2], self.color_example[1], self.color_example[0],0.4)
if self.hover:
self.draw_rounded_rectangle(ctx,0,0,20,self.width,self.height)
self.draw_circle(ctx,0,0,self.width,self.height)
# TEST: render example-bg into context (either PNG or SVG)
self.theme.render(ctx, 'example-bg')
ctx.set_source_rgba( self.color_example[0], self.color_example[1], self.color_example[2],self.color_example[3])
self.draw_text(ctx, self.test_text, 0, 0, self.font_example , 10,self.width,pango.ALIGN_LEFT)
self.draw_line(ctx,0,40,self.width,0,1)
self.draw_text(ctx, 'timer - ' + str(self.number), 0, 130, self.font_example , 10, self.width,pango.ALIGN_LEFT)
self.draw_text(ctx, self.theme_name, 0, 50, self.font_example , 10, self.width,pango.ALIGN_LEFT)
self.draw_text(ctx, 'mouse x ' + str(self.mousex ) + ' \n mouse y ' + str(self.mousey ) , 0, 170, self.font_example , 10,self.width,pango.ALIGN_LEFT)
# render svg-file
#self.theme['example-bg.svg'].render_cairo(ctx)
# render png-file
#ctx.set_source_surface(self.theme['example-test.png'], 0, 0)
#ctx.paint()
So where do i start?
i sugest you get the template screenlet and start coding from there
Codding Rules
*A Screenlet's classname must end on "Screenlet" (e.g. ClockScreenlet) *Not more than 80 chars per line (where possible). *Tabs are 4 char-wide "\t"-characters. *Classes MUST have a documentation-string. *After class-headers's documentation, one separating line. *Internal attributes MUST start with TWO leading underscores. *Editable options NEVER have leading underscores. *All functions that are no inherited event-handlers MUST have a documentation-string. *Constructors of Screenlet-subclasses must implement the **keyword_args parameter as last argument to their __init__-function. *All screenlet-files MUST have the name of the Screenlet-classes they contain (with a .py-extension). *All Screenlets MUST have a head-comment containing license/author note, *Screenlets need to be placed into a directory named like the Screenlet's class (without trailing "Screenlet"). This directory may contain a "themes"-directory where the Screenlet's themes are stored. It may also contain other files (of course). *Screenlets should supply an icon named "icon.svg" or "icon.png" within their directory. *When possible set the width and height of the screenlet matching the background theme image , to avoid confusion *Always call super before anything else ... of course *Never call exit() or sys.exit().But if you need to check the utils.is_manager_running_me() and only call the exit() if that returns false
Avoiding memory leaks
Old versions of screenlets suffered from memory leaks expecilly in the way they draw text , to avoid this use the built in draw_text function. Also remember to clean all your variables to avoid memory leaks
Packaging a screenlet
use the screenlets-packager tool to package your screenlet for release (e.g.):
screenlets-packager ~/.screenlets/Weather
Finding a host for your package
One suggestion ,post your screenlet on www.gnome-look.org under the screenlet section , gnome-look provides you to upload your content there , so it will remain in their servers forever , and your screenlet will be safe and backed up, then you can post it on www.screenlets.org and provide the download link from gnome-look
