Screenlet a kreslení s buffery

Michal Hořejšek 29. 9. 2009

V minulém díle našeho seriálu o tvorbě takzvaných Screenletů jsme se naučili nakreslit různé tvary a dnes si povíme, jak zařídit, aby se nám některé prvky nevykreslovaly zbytečně často. Tím dokážeme náš Screenlet poměrně dobře optimalizovat a on potom není zbytečně náročný na systémové zdroje.

Něco takového uděláme uděláme jen tak, že použijeme buffery (vyrovnávací paměť). Vyrovnávací paměť pro kreslení ve Screenletech se tvoří tak, že nejprve vytvoříme samotnou proměnnou, ve které budeme kreslení uschovávat. Dále si po inicializaci Screenletu (tj. v metodě on_init) do této proměnné vytvoříme kreslící plochu pomocí metody Pixmap z modulu GTK. Poté si při potřebě vytvoříme novou cairo vrstvu a s tou pracujeme běžně jako s hlavní cairo vrstvou (jako s proměnnou ctx). A nakonec nesmíme zapomenout pokaždé buffer zapsat do hlavní cairo vrstvy, aby se nám obsah zobrazil.

Teď si třeba někteří říkáte, proč dělat něco tak složitého, když můžu mít jednoduše stejný výsledek. Odpověď je taková, že pokud máme naši ukázku s časem a chtěli bychom přidat datum, tak se bude každou vteřinu zbytečně zjišťovat, jaké je datum a ještě k tomu se bude každou vteřinu vykreslovat. Pokud použijeme buffery, tak datum zjistíme jen jednou, vykreslíme ho do vytvořené vrstvy a poté jen tuto vrstvu vykreslujeme do té hlavní (ctx) vrstvy. Tím se náročnost velice sníží. Je pravda, že v tomto případě žádnou změnu defacto nezporozujeme, ale při větších projektech je to už znát.

Ukázka

Vše si předvedeme na názorné ukázce.

#!/usr/bin/env python

import screenlets
from screenlets import DefaultMenuItem
import gobject
import time
import gtk
import cairo

class MyFirstScreenlet( screenlets.Screenlet ):

  __name__ = 'My First Screenlet'
  __version__ = '1.0'
  __author__ = 'Michal Horejsek'
  __desc__ = __doc__

  __timer = None
  __buffers = {}

  firstDrawing = True

  width = 220
  height = 80

  def __init__( self, **keyword_args ):
    screenlets.Screenlet.__init__( self, width=self.width, height=self.height, **keyword_args )

  def on_init( self ):
    self.add_default_menuitems()
    self.initBuffers()

  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
    ctx.set_source_rgba( 1, 1, 1, 0.1 )
    self.draw_rounded_rectangle( ctx, 0, 0, 20, self.width, self.height, fill=True ) # ohraniceni

    # 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 )

Na začátek bych chtěl upozornit, že pro tento příklad je nutné importovat moduly gtk a cairo, které využíváme pro vytváření bufferů.

Když se podíváte do ukázky, základem bufferů je slovník zde nazvaný jako __buffers. Název si však můžete zvolit sami a vůbec nemusíte používat slovník, ale pokud budete používat více než jeden buffer (jakože to je velmi pravděpodobné), tak se toto bude nejspíše hodit více.

Dále si v metodě on_init (volána po úplné inicializaci Screnletů) voláme námi vytvořenou funkci initBuffers, kde si připravíme kreslící plochu. Jak je vidět, tak v příkladu používám rozměry bufferu jako náš samotný Screenlet a poté je vkládám do hlavní vrstvy na souřadnice [0,0]. Můžete také využívat možnosti vytváření menší buffery a vkládat je přímo na určené místo v hlavní vrstvě.

TIP: Tím, že použijete menší buffery a budete je vkládat přímo na určité místo v hlavní vrstvě, ušetříte paměť.

Další, co následuje po vytvoření bufferu, je potřeba ho nějak vyplnit. To v ukázce děláme v podmínce, kde zjišťujeme, jestli je vteřina sudá a pokud ano, tak buffer překreslíme novou hodnotou – tím nám vznikne, že druhý zobrazovaný čas bude „skákat“ po dvou sekundách. Buffer vyplníme cairo vrstvou, kterou následně vyprázdníme od předchozích hodnot (nemusejí to být zrovna předchozí hodnoty, které jsme my používali, ale nějaké „bláboly“, a mohou vznikat opravdová grafická díla, které opravdu nechceme).

ctxLayer = self.__buffers['time2'].cairo_create()
self.clear_cairo_context( ctxLayer ) 

A po usilovné práci máme vytvořenou novou vrstvu (v příkladu označená jako ctxLayer), s kterou již pracujeme stejně jako s hlavní vrstvou (v příkladu označená jako ctx).

Upozornění: Každá vrstva má vlastní nastavení. Pokud jsme v příkladu v hlavní vrstvě (ctx) nastavili nějakou barvu, pro novou vrstvu (ctxLayer) budeme muset opět barvu nastavit.

Nesmíme nakonec zapomenout buffer pokaždé přidat do hlavní cairo vrstvy.

ctx.set_source_pixmap( self.__buffers['time2'], 0, 0 )
ctx.paint() 

Výsledek

A tím je hotovo. Nyní už jede druhý výpis času přes buffer a na výsledek se můžete podívat. Zobrazení 2× času jsem použil proto, aby bylo vidět, že buffer opravdu funguje a můžete zkusit vytvořit cokoli podle vaší fantazie.

Screenlet 3

Tipy, kde buffery použít

Buffery se dají použít u Screenletů, kde je potřeba provést refresh jen části Screenletu, což je vlastně skoro všude. Můžete si vytvořit pozadí a různé grafické prvky a poté už jen dělat refresh částí, které se mění. Například v minulém příkladu jsme vždy vykreslovali složité pozadí a do toho čas, nyní to můžete předělat tak, že pozadí se vykreslí pouze při startu Screenletu do bufferu a poté už jen vykreslovat ten čas. Buffery se také hodí u projektů, kde se provádí nějaká změna při přejíždění myši. Například mějme Screenlet, který zobrazuje seznam připojených oddílů a při přejetí myší nad určitým oddílem se pozadí toho oddílu změní – to by bylo bez bufferu pro procesor náročné, s buffery to je mnohem lepší.

Závěr

Buffery jsou opravdu dobrá věc, mně snížily až 3× zatížení procesoru u projektu InfoPanel a opravdu je doporučuju používat, pokud kódujete trošku vetší Screenlet, který už něco „umí“. Příště se podíváme, jak na nastavení.

Našli jste v článku chybu?
Vitalia.cz: Syfilis: To není „nemoc z lásky“

Syfilis: To není „nemoc z lásky“

Měšec.cz: TEST: Vyzkoušeli jsme pražské taxikáře

TEST: Vyzkoušeli jsme pražské taxikáře

Podnikatel.cz: Infolinka k EET? Poskytne až 100 úředníků

Infolinka k EET? Poskytne až 100 úředníků

Měšec.cz: Test: Výběry z bankomatů v cizině a kurzy

Test: Výběry z bankomatů v cizině a kurzy

120na80.cz: Víte, co je svobodná menstruace?

Víte, co je svobodná menstruace?

Podnikatel.cz: Česká pošta vycouvala ze služby ČP Cloud

Česká pošta vycouvala ze služby ČP Cloud

Podnikatel.cz: OSA zdraží, ale taky přidá nový poplatek

OSA zdraží, ale taky přidá nový poplatek

DigiZone.cz: Elektrická Formule E. Práva má Arena Sport

Elektrická Formule E. Práva má Arena Sport

Podnikatel.cz: Novela zákoníku práce. Řeší homeworking

Novela zákoníku práce. Řeší homeworking

120na80.cz: Lepší poporodní sexuální život? Žádný problém

Lepší poporodní sexuální život? Žádný problém

DigiZone.cz: Sky Deutschland: dvakrát fotbal ve 4K

Sky Deutschland: dvakrát fotbal ve 4K

Měšec.cz: Co když na dovolené přijdete o kartu?

Co když na dovolené přijdete o kartu?

DigiZone.cz: Fotbal na O2 TV Sport posiluje

Fotbal na O2 TV Sport posiluje

Vitalia.cz: Co potřebujete vědět o zubech moudrosti?

Co potřebujete vědět o zubech moudrosti?

Lupa.cz: Měřičům síly hesla se nedá věřit. Víte proč?

Měřičům síly hesla se nedá věřit. Víte proč?

Root.cz: Čína má nejvýkonnější počítač světa

Čína má nejvýkonnější počítač světa

Podnikatel.cz: Zajímavý paradox: Daří se vedle konkurence

Zajímavý paradox: Daří se vedle konkurence

Vitalia.cz: Vakcína Cervarix je oficiálně i pro chlapce

Vakcína Cervarix je oficiálně i pro chlapce

Lupa.cz: Co najdete uvnitř kosmické sondy?

Co najdete uvnitř kosmické sondy?

Vitalia.cz: Ženy, které milují příliš, jsou neštěstí

Ženy, které milují příliš, jsou neštěstí