Hlavní navigace

Vytváříme témata pro Screenlety

13. 10. 2009
Doba čtení: 5 minut

Sdílet

V minulém dílu našeho seriálu o tvorbě takzvaných Screenletů jsme se společně naučili připravit archiv určený pro pohodlnou instalaci. Nyní, když již máme Screenlet běžným způsobem nainstalován v našem počítači, můžeme využít některých dalších možností, mezi kterými je například tvorba dalších témat.

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:

  1. 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.
  2. 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):

CS24_early

Screenlet témata

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

Screenlet témata

Příště

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

Byl pro vás článek přínosný?

Autor článku