Témata mají uživatelé přístupná v nastavení Screenletu po kliknutí „pravou myší“, v nabídce Properties, na kartě Themes, nebo také mohou jednoduše témata přepínat po kliknutí „pravou myší“ na Screenlet pod nabídkou Theme.
Pomocí témat se mění vzhled Screenletu, ať už jen drobně či úplně. Drobná úprava Screenletu pomocí témat může být například změna sady ikon, které jsou v našem Screenletu použity, nebo změna pozadí – buďto nějaké pěkné grafické obrázky nebo průhledné. Naopak úplná změna může být změna ikon, pozadí, rozmístění prvků, prostě všeho.
Dobrým příkladem úplné změny Screenletu pomocí témat je třeba zobrazení zaplnění oddílů – v jednom případě může mít Screenlet pěkné barevné ikonky na pěkném pozadí, s grafickým ukazatelem a zobrazení celkového a volného místa a v druhém (tématu) může mít černobíle ikonky na průhledném pozadí, bez grafického zobrazení a zobrazení kompletních informací o zaplnění disku (celkové, volné, využité místo).
Budeme se však držet dvou zásad:
- První zásada je, že nebudeme pomocí témat měnit barvy fontů a různých grafických prvků. Výběr jiné barvy necháme kompletně na uživateli a uděláme mu na to možnost v nastavení (ukážeme si příště). Samozřejmě, pokud používáme nějaké pozadí (myšleno jako obrázek), tak ten si samozřejmě vytvoříme v různých barvách a uděláme potřebná témata.
- Druhá zásada je, že nebudeme pomocí témat měnit funkčnost Screenletu. Myslím tím, že nebudeme v jednom tématu zobrazovat zaplnění disku a v druhém tématu zobrazovat vytížení procesoru. Pokud bychom chtěli v jednom Screenletu sledovat více takových věcí a zobrazovat jen jednu, kterou si uživatel přeje sledovat, jde to udělat jednoduše přes nastavení. Opět samozřejmě nemám namysli, že chci zobrazovat využití disků (oddílů) 3× a pokaždé jinak, jako v příkladu výše.
Ukázka
V ukázce MyFirstScreenlet-1.0.tar.gz se již nespokojíme s jedním souborem. Nyní budeme upravovat soubor se zdrojovým kódem Screenletu a soubor se zdrojovým kódem témata ke Screenletu.
Schválně zde ukazuji jen jednoduchý příklad, abyste to co nejlépe pochopili. Jiný (složitější) případy si vyzkoušejte sami za domácí úkol.
MyFirstScreenlet.py:
#!/usr/bin/env python
import screenlets
from screenlets import DefaultMenuItem
import gobject, gtk, cairo
import time, os, sys
class MyFirstScreenlet( screenlets.Screenlet ):
"""MyFisrstScreenlet"""
__name__ = 'My First Screenlet'
__version__ = '1.0'
__author__ = 'Michal Horejsek'
__desc__ = __doc__
__timer = None
__buffers = {}
__themeModule = None
firstDrawing = True
width = 220
height = 80
def __init__( self, **keyword_args ):
screenlets.Screenlet.__init__( self, width=self.width, height=self.height, **keyword_args )
pridame si do pythonni promenny s adresarema oznacujici moduly nas adresar s tematama tohoto screenletu
# (pro pozdejsi jednoduchy import temata)
os.chdir( self.get_screenlet_dir() )
sys.path.append( 'themes' )
# importujeme si pouzivane tema
self.__themeModule = __import__( self.theme_name )
def on_init( self ):
self.add_default_menuitems()
self.initBuffers()
def on_load_theme( self ):
# tato metoda se vola po inicializaci noveho temata, ale pred prekreslenim (pri zmene temata uzivatelem napriklad)
# zde si naimportujeme nove pouzivane tema
if not self.__themeModule or self.theme_name != self.__themeModule.__name__:
self.__themeModule = __import__( self.theme_name )
def on_map( self ):
if not self.__timer:
self.__timer = gobject.timeout_add( 1000, self.update )
self.update()
def on_unmap( self ):
if self.__timer:
gobject.source_remove( self.__timer )
self.__timer = None
def initBuffers(self):
self.__buffers['time2'] = gtk.gdk.Pixmap( self.window.window, self.width, gtk.gdk.screen_height(), -1 )
def update( self ):
self.redraw_canvas()
return True
def on_draw( self, ctx ):
if self.scale > 5:
self.scale = 5
ctx.save()
ctx.scale( self.scale, self.scale )
# pozadi
self.__themeModule.drawBackground( self, ctx )
# ziskame cas
foo = str( time.localtime()[3] ) + ':' + str( time.localtime()[4] ) + ':' + str( time.localtime()[5] )
ctx.set_source_rgba( 1, 1, 1 )
# vykreslime cas do hlavni vrstvy
self.draw_text( ctx, 'Prave je '+foo, 45, 10, 'FreeSans', 12, 200 )
# druhy cas aktualizujeme pouze, kdyz je vterina suda nebo probiha prvni vykresleni
if time.localtime()[5] % 2 == 0 or self.firstDrawing:
# vytvoreni nove cairo vrstvy
ctxLayer = self.__buffers['time2'].cairo_create()
# vyprazdneni (priprava) vrstvy
self.clear_cairo_context( ctxLayer )
ctxLayer.set_source_rgba( 1, 1, 1 )
# vykreslime cas do nove vytvorene vrstvy (do bufferu)
self.draw_text( ctxLayer, 'Prave je '+foo, 45, 50, 'FreeSans', 12, 200 )
# vzdy vypise buffer time2
ctx.set_source_pixmap( self.__buffers['time2'], 0, 0 )
ctx.paint()
ctx.restore()
self.firstDrawing = False
def on_draw_shape(self, ctx):
self.draw_rectangle(ctx, 0, 0, self.width, self.height)
if __name__ == "__main__":
import screenlets.session
screenlets.session.create_session( MyFirstScreenlet )
themes/default/__init__.py:
def drawBackground( self, ctx ): ctx.save() ctx.set_source_rgba( 0, 0, 0, 0.1 ) self.draw_rounded_rectangle( ctx, 0, 0, 20, self.width, self.height, fill=True ) # ohraniceni ctx.restore()
themes/myNewTheme/__init__.py:
def drawBackground( self, ctx ): ctx.save() ctx.set_source_rgba( 0, 0, 0, 0.1 ) self.draw_rounded_rectangle( ctx, 0, 0, 20, self.width, self.height, fill=False ) # ohraniceni self.draw_circle( ctx, 5, self.height/2-10, 20, 20 ) # leve kolecko self.draw_circle( ctx, self.width-25, self.height/2-10, 20, 20 ) # prave kolecko self.draw_triangle( ctx, self.width/2-10, self.height-12, 20, 10, fill=True ) # trojuhelnik dole uprostred ctx.restore()
Chtěl bych upozornit na to, že téma si importuji v konstruktoru a handleru on_load_theme, který se zavolá po inicializaci témata, ale ještě před jeho vykreslením, příkazem self.__themeModule =
__import__( self.theme_name ). Není to nic složitého, jen si laicky řečeno do připravené proměnné připravím funkce, které v daném tématu jsou. Aby to bylo takto jednoduché, je zařízené v konstruktoru (na řádku 30 a 31), kde si přidáme do pythoní proměnné s adresáři označující moduly náš adresář, který obsahuje všechny témata tohoto Screenletu.
Poté již jen stačí si zavolat určitou funkci, kterou jsme si v našem tématu vytvořili. V tomto příkladě témata využíváme pouze pro pozadí, takže obě témata obsahují funkci drawBackground, která nám pozadí vykreslí a samozřejmě, že každé téma jinak. Ve funkci jsou dva parametry, self a ctx. Parametr self předáváme proto, abychom mohli využívat všechny metody třídy jako v samotném Screenletu a parametr ctx proto, abychom mohli malovat. Jednoduše bez cairo vrstvy a metod, které kreslí, bychom toho moc nenamalovali.
V tématech, resp. ve funkcích drawBackground ještě používám příkazy ctx.save() a ctx.restore(). To je proto, abychom si mohli s vrstvou v určitém úseku dělat co chci a poté nemusel vše ručně vracet do původního stavu. Právě kvůli tomu existují tyto metody a první (tedy save) nám aktuální nastavení cairo vrstvy uloží (barvu, pozici, ..) a druhá (tedy restore) nastavení opět navrátí zpět podle posledně uložených dat.
Výsledek
Dnes máme dva grafické výsledky, jeden s prvním tématem (default):

a druhý s druhým tématem (myNewTheme).

Příště
To je pro dnešek vše a příště se podíváme, jak udělat nastavení.