Hlavní navigace

KDE 4.2: vývoj plasmoidu pro Twitter

20. 2. 2009
Doba čtení: 8 minut

Sdílet

V pokračování článku o vývoji plasmoidu si vylepšíme jeden z minulých hallo příkladů konkrétně twitter klienta. Přidáme možnost odesílání zpráv a pravidelnou kontrolu nových twittů. Navíc si ukážeme, jak přidat do plasmoidu grafiku, práci se signály a sloty a také kde najít informace k dalším komponentám, které se dají v Plasmě využít.

Úvod

Dnešní článek se bude točit kolem jednoduchého twitter klienta, který již umí odesílat statusy na twitter. První problém, na který jsem narazil, byla neschopnost layoutu ze sebe odstranit umístěné prvky. Metody k tomu určené se chovaly všelijak, ale rozhodně ne tak, jak bych očekával. Dokonce se mi stalo, že z listu, kde byly tři položky, zmizela první a poslední a ta prostřední se z listu prostě neodstranila. Logicky se pak nemohla odstranit ani z layoutu. Další záhada, na kterou jsem narazil, byla u threadů implementovaných v Pythonu. Plasmoid je mnohem jednodušší než normální Qt/KDE program a tudíž bude mít v pozadí více automatického chování. Do příštího a také posledního článku o plasmoidech se pokusím vytvořit multithreadový plasmoid pomocí threadů v Qt a uvidíme, jaký bude výsledek.

Zde popisovaného twitter klienta jsem pojmenoval Rootter. Jak jsem zmínil minule, tak jako příklad používám právě Twitter, protože se v mém okolí i v okolí redakce začal hodně používat. Plasmoid umístěný v SVN repozitáři KDE je na tom stále ve funkčnosti lépe než Rootter, ale má velkou nevýhodu ve své stabilitě. Mě například shodí celou Plasmu, ale stává se také, že rozměrově zabírá celou plochu. Dneska si do klienta přidáme SVG obrázek, budeme pravidelně stahovat nové zprávy z Twitteru a také vytvoříme hodně malý formulář na aktualizaci našeho twitter účtu.

Rootter

Začneme zdrojovým kóde:

#!/usr/bin/python
# -*- coding: utf-8 -*-

import twitter,time,os
from PyQt4.Qt import *
from PyQt4.QtGui import *
from PyKDE4.plasma import Plasma
from PyKDE4.kdecore import KGlobal
from PyKDE4 import plasmascript

class rootter(plasmascript.Applet):
    """
        Hlavní třída Roottera
    """

    def __init__(self, parent, args=None):
        """
            Nastavení základních parametrů plasmoidu
            Vytvoření timeru
            Inicializace twitter API
        """
        self.stack = []
        self.twits = 3
        self.user   = "XXXXX"
        self.passwd = "XXXXX"

        plasmascript.Applet.__init__(self,parent)
        # Timer pro pravidelné získávání twittů
        self.timer = QTimer()
        self.timer.setInterval(60000)

        self.api = twitter.Api(self.user,self.passwd)

    def clean(self):
        """
            Vyčitění labelů s twitty
        """
        for x in self.stack:
            x.setText("")

    def fillTwits(self):
        """
            Získání twittů a jejich zobrazení
        """
        statuses = self.api.GetUserTimeline(self.user)
        i=0
        for x in [s.text for s in statuses][0:self.twits]:
            self.stack[i].setText(x)
            i+=1
        print "refresh"

    def write(self):
        """
            Odeslání statusu na Twitter
        """

        status = self.api.PostUpdate(self.lineedit.text())

        self.fillTwits()
        self.lineedit.setText("")

    def init(self):
        """
            Metoda init, která je čistě v režii Plasmy
        """

        # Vytvoření základních prvků layoutu
        self.layout_main = QGraphicsLinearLayout(Qt.Vertical)
        self.layout_write = QGraphicsLinearLayout(Qt.Horizontal)
        self.layout_twits = QGraphicsLinearLayout(Qt.Vertical)

        # Vytvoření jednotlivých widgetů a nastavení jejich parametrů
        self.lineedit = Plasma.LineEdit()
        self.button   = Plasma.PushButton()
        self.button.setText("Twittnout")
        self.svg      = Plasma.Svg()
        self.svg.setImagePath(os.environ["HOME"]+"/root/rootter/contents/imgs/root.svg")
        self.icon     = Plasma.SvgWidget(self.svg)

        # Spojení slotů a signálů
        self.connect(self.timer, SIGNAL("timeout()"), self.fillTwits)
        self.connect(self.button, SIGNAL("clicked()"), self.write)

        # Vytvoření labelů pro jednotlivé twitty
        for x in xrange(self.twits):
            self.stack.append(Plasma.Label(self.applet))
        for x in self.stack:
            self.layout_twits.addItem(x)

        # Nastavení parametrů plasmoidu
        self.setHasConfigurationInterface(False)
        self.setAspectRatioMode(Plasma.Square)
        self.resize(280, self.twits*130+50)

        # Složení layoutu a widgetů
        self.layout_main.addItem(self.icon)
        self.layout_main.addItem(self.layout_twits)
        self.layout_write.addItem(self.lineedit)
        self.layout_write.addItem(self.button)
        self.layout_main.addItem(self.layout_write)
        self.setLayout(self.layout_main)

        # Odstartování timeru
        self.timer.start()
        # Prvotní vyplněn twittů
        self.fillTwits()

def CreateApplet(parent):
    return rootter(parent) 

Příklad není dlouhý a ukazuje pouze to, co je nezbytné. K dokonalosti programu ještě hodně chybí, ale to si necháme na příště.

Rootter

První a jediná třída představuje vlastnosti celého plasmoidu. Najdeme v ní dvě init metody. Jedna je klasická pythoňácká (init()) a druhá se váže na Plasmu (init()). Jak jsme si minule řekli, preferovat bychom měli tu druhou variantu a většinu kódu umísťovat tam. Další metody případně části metod init si rozebereme níže.

Signály a sloty

Na rozdíl od klasických Qt aplikací zde nejsme na slotech a signálech tak závislí a pokud nepožadujeme vstup od uživatele, nemusíme je ani použít. Pro úplnost popíšu vlastnosti signálů a slotů podrobněji.

V každé aplikaci si musí objekty mezi sebou předávat informace. Většinou nám stačí jen zpráva o tom, že tlačítko bylo zmáčknuté nebo že myš najela tam, kam potřebujeme. Někdy to jsou složitější informace a s kliknutím může třeba přicházet i poloha myši nebo vstupní text. Tato potřeba se v Qt, resp. KDE světě řeší pomocí tzv. signálů a slotů. Signál je informace, kterou nějaký objekt emituje a slot je zase něco jako díra, do které signál zapadne. Na nás vývojářích je, abychom definovali, kam má který signál dorazit. Vytvoříme něco jako potrubí, kterým letí signál do místa, které na něj bude reagovat. Pro příklad si můžeme slot představit jako metodu, která zavře okno a signál jako kliknutí myší na tlačítko. Pokud vytvoříme mezi signálem tlačítka a vypnutím okna pouto, tak se každé kliknutí na tlačítko spustí metoda, která představuje slot. V našem případě se zavře okno.

Signály a sloty přinesly do prostředí Linuxu jednoduchý a přehledný způsob na předávání zpráv. Na podobném principu funguje předávání zpráv i v GTK. Jiné operační systémy používají složitější variantu, kde hlavní smyčkou programu prolétává mnoho pevně definovaných zpráv a programátor si je v případě potřeby odchytne a zareaguje na ni. Nevýhodou je poté složitější kód a malá flexibilita samotných zpráv.

V našem plasmoidu se sloty a signály párují stejně jako v Qt:

self.connect(self.button, SIGNAL("clicked()"), self.write) 

Jako první argument použijeme objekt, ze kterého signál přichází. Druhý argument definuje samotný signál. Ten je v tomto případě relativně jednoduchý, ale může obsahovat třeba i parametry. Třetím argumentem je samotný slot. Ten je v tomto případě metodou write třídy rootter. Existuje ale i složitější zápis, o kterém si povíme něco příště. V tomto případu by nám byl k ničemu. Sloty i signály každého objektu najdeme v dokumentaci. Jeden signál může být napojen na více slotů a také více různých signálů může být napojeno na jeden slot.

Cyklické obnovování obsahu

V době vícejaderných procesorů by bylo možná vhodnější použít více threadů při stahování a odesílání twittů, ale jak jsem již psal výše, zatím nemám moc úspěch. Další možnost, kterou můžeme použít, je třída QTimer. Ta odpočítává nastavený čas a periodicky emituje signál. Na ten si můžeme nastavit jakýkoli slot. Pravděpodobně si kvůli tomu vytvoříme vlastní, jako je to v našem příkladu. Jedná se o metodu fillTwits(), která stáhne a zobrazí aktuální twitty. Nejedná se o multithreadové řešení a tudíž Plasmoid na dobu, po kterou získává twitty jakoby zamrzne. Nejvíce patrné to je při spouštění Plasmoidu, kdy mu chvíli trvá, než naváže spojení a do té doby se nic neobjeví.

V Rootteru je timer vytvářen v metodě init() a je mu nastaven interval pro emitování signálu na 60 vteřin. QTimer může být nastaven také tak, aby emitoval signál pouze jednou a pak se zastavil. Další pokračování kódu týkajícího se timeru najdeme na konci metody init(), kde se použije metoda start() na spuštění odpočítávání.

Na signál timeru je napojená metoda pro stáhnutí a zobrazení twittů.

Přidání SVG grafiky

Jelikož je KDE 4 založeno na vektorové grafice, nemůže v našem příkladu chybět ani alespoň jednoduchá ukázka umístění SVG obrázku do plasmoidu. SVG obrázek se dá vytvořit třeba v Inkscapu, o kterém jsme již psali, a můžeme si tam nakreslit některé části našeho plasmoidu pro lepší vzhledový efekt. Vytvoření obrázku provedeme pomocí následujících třech řádků:

self.svg      = Plasma.Svg()
self.svg.setImagePath(os.environ["HOME"]+"/root/rootter/contents/imgs/root.svg")
self.icon     = Plasma.SvgWidget(self.svg) 

Na první si vytvoříme objekt Plasma.Svg. Ten reprezentuje samotný obrázek. Na druhém řádku předáme adresu SVG souboru, ze kterého má zmíněný objekt vycházet. Poslední řádek vezme Svg objekt a vytvoří widget, který vložíme do layoutu.

Jednoduchý formulář

Vstup od uživatele se dá řešit různě. Nejjednodušší je použít klasické vstupní pole. V našem případě jde o jedno pole, ale není problém jich udělat více. Princip je takový, že na signál kliknutí na tlačítko navážeme slot ve formě metody, která získá z našeho vstupního pole zapsaná data a zpracuje je. Nakonec vstupní pole vymaže a formulář se vrátí do původního stavu.

Vytvoření vstupního pole je reprezentováno tímto řádkem:

self.lineedit = Plasma.LineEdit() 

V rámci Plasmy jsou k dispozici i další formulářové prvky jako ComboBox, RadioButton, TextEdit atd.

Layout

Přidávání do layoutu jsme si v jednoduché formě představili minule. V dnešním příkladu se nám to trochu zkomplikovalo, protože máme prvků několik. Jsou to:

  • SVG obrázek
  • Labely s twitty
  • Vstupní pole
  • Tlačítko

Vstupní pole a tlačítko jsou uzavřeny v horizontálním layoutu. Labely s twitty pro změnu ve vertikálním. Tyto dva layouty jsou umístěny do dalšího vertikálního layoutu společně s obrázkem.

Vytvoření jednotlivých layoutů najdeme na řádcích:

self.layout_main = QGraphicsLinearLayout(Qt.Vertical)
self.layout_write = QGraphicsLinearLayout(Qt.Horizontal)
self.layout_twits = QGraphicsLinearLayout(Qt.Vertical) 

A kousek níže pak jejich spojení a naplnění:

self.layout_main.addItem(self.icon)
self.layout_main.addItem(self.layout_twits)
self.layout_write.addItem(self.lineedit)
self.layout_write.addItem(self.button)
self.layout_main.addItem(self.layout_write)
self.setLayout(self.layout_main) 

Instalace

Nebudeme příklad komplikovat kopírováním a můžeme si rovnou stáhnout kompletní projekt. Pro otestování stačí rozbalit archiv, opravit cestu k obrázku, nastavit přístupové údaje a spustit:

sh install.sh 

Plasmoid se nainstaluje do vašeho domovského adresáře a rovnou se spustí v prohlížeči plasmoidů, Lze ho pak také umístit na plochu.

Tip do článku - TOP100

Závěr

Většina článku čerpala informace z dokumentace k Plasmě. Trochu mi vadilo a také mě překvapilo, že k ní nevede cesta přes domovskou stránku plasmy, musel jsem ji složitě vyhledávat googlem. Oproti dokumentaci ke Qt se mi ta plasmovská, resp. KDEčkovská, zdá mnohem méně přehledná, ale nakonec jsem se k cíli dostal. Rozhodně to je dobrý pomocník do budoucna.

Příští článek si doděláme Rootter, přidáme hlášení chyb, thready, podíváme se na slibované DataEngines a nezapomeneme si představit trochu podrobněji také python API pro přístup k Twitteru.

Autor článku

Adam Štrauch je redaktorem serveru Root.cz a svobodný software nasazuje jak na desktopech tak i na routerech a serverech. Ve svém volném čase se stará o komunitní síť, ve které je již přes 100 členů.