Un lecteur de News (RSS/RDF/Atom) avec PyQt

Un article de Mangue.org, l'encyclopéde libre.

L'objectif


L' objectif des prochains articles va être de développer un lecteur de News de type RSS, RDF et Atom. Dans un premier temps nous allons créer une simple fenêtre qui vous permettra de rentrer une URL puis d'afficher dans une table les entrées contenues dans le résultat de la requête. Vous pourrez aussi sauvegarder vos URLs favorites afin de les retrouver à chaque lancement de l'application.


Nous n'allons pas réaliser la requête ni le parsing des résultats nous même mais nous allons nous baser sur l'excellent feedparser de Mark Pilgrim (http://diveintomark.org/projects/feed_parser/). En effet ce module nous donne la possibilité de gérer plusieurs formats et différentes subtilités comme l'encoding, les différentes erreurs, etc.


Une fenêtre principale


Il est évident que cette fois nous n'allons pas nous contenter d'un label ou d'un bouton pour notre application. Nous allons nous baser sur la classe QMainWindow de Qt. Cette classe nous permet de définir une fenêtre principale qui sera reconnue en tant que telle par le gestionnaire de fenêtre et apparaîtra donc dans votre bar de tâches.


Image:Rssfetcher1.png


Nous allons créer une classe qui dérive de QMainWindow afin de l'adapter à nos besoins.


import sys
from qt import *
 
import feedparser
 
class rssfetcher(QMainWindow):
    def __init__(self,parent = None,name = None,fl = 0):
        # Appel au constructeur de base pour initialiser la fenetre
        QMainWindow.__init__(self,parent,name,fl)
 
        # Definition dy layout principal en grille
        rssfetcherLayout = QGridLayout(self,1,1,11,6,"rssfetcherLayout")
 
        #Ce label ne contiendra simplement le texte URL
        self.textLabel1 = QLabel(self,"textLabel1")
 
        # Ajout du label dans le layout
        rssfetcherLayout.addWidget(self.textLabel1,0,0)
 
        # Ajout de la table (enfin QListView) qui va afficher les résultats
        self.itemList = QListView(self,"itemList")
        # Ajout des colonnes de cette table 
        self.itemList.addColumn(self.__tr("Titre"))
        self.itemList.addColumn(self.__tr("Date"))
        self.itemList.addColumn(self.__tr("Description"))
        # On indique que lorsqu'une ligne de la table est selectionne, toutes les colonnes de cette ligne le sont
        self.itemList.setAllColumnsShowFocus(1)
 
        # Ajout au layout
        rssfetcherLayout.addMultiCellWidget(self.itemList,1,1,0,4)
 
        # Creation du bouton Quitter
        self.quitButton = QPushButton(self,"quitButton")
        # On evite que ce boutton soit celui par defaut
        self.quitButton.setAutoDefault(0)
        rssfetcherLayout.addMultiCellWidget(self.quitButton,2,2,0,1)
 
        self.saveButton = QPushButton(self,"saveButton")
        rssfetcherLayout.addWidget(self.saveButton,2,2)
 
        self.refreshButton = QPushButton(self,"refreshButton")
        rssfetcherLayout.addWidget(self.refreshButton,2,3)
 
        # Creation d'un element QSpacer qui permet d'eviter que les boutons s'etendent sur toute la largeur de la fenetre.
        spacer3 = QSpacerItem(91,20,QSizePolicy.Expanding,QSizePolicy.Minimum)
        rssfetcherLayout.addItem(spacer3,2,4)
 
        # Creation de la liste deroulante presentant les URLs, le 1 signifie qu'elle est editable
        self.urlCombo = QComboBox(1,self,"urlCombo")
 
        # Voir plus bas
        settings = QSettings()
        settings.setPath('Mangue.org','LecteurRSS')
        self.urls,state = settings.readListEntry('/URL/urls')
 
        if not state:
            self.urls = QStringList()
            
        self.urlCombo.insertStringList(self.urls)
 
        rssfetcherLayout.addMultiCellWidget(self.urlCombo,0,0,1,4)
 
        # On ajoute le texte des elements
        self.languageChange()
 
        # On redefinie la taille par defaut de la fenetre
        self.resize(QSize(512,531).expandedTo(self.minimumSizeHint()))
        self.clearWState(Qt.WState_Polished)
 
        # On definie les evenement
        # Methode attachee a la liste deroulante lorsqu'on tape une nouvelle URL
        self.connect(self.urlCombo.listBox(),SIGNAL("returnPressed(QListBoxItem *)"),self.enteredUrl)
        # Methode attachee a la liste deroulante lorsqu'on active une URL
        self.connect(self.urlCombo,SIGNAL("activated(const QString &)"),self.activateUrl)
        self.connect(self.refreshButton,SIGNAL("clicked()"),self.refreshUrl)
        self.connect(self.saveButton,SIGNAL("clicked()"),self.saveUrls)
        self.connect(self.quitButton,SIGNAL("clicked()"),qApp,SLOT("quit()"))
 
    def __parseURL(self,url):
        # Methode qui va realiser le travail de requete et d'affichage dans la table
 
        # D'abord on efface toutes les entrees de la table
        self.itemList.clear()
 
        # Requete sur l'URL
        data = feedparser.parse(str(url))
        for entry in data['entries']:
            # Ajout entree par entree dans la table
            QListViewItem(self.itemList,entry['title'],entry['date'],entry['description'])
 
    def languageChange(self):
        self.setCaption(self.__tr("Lecteur RSS/RDF"))
        self.textLabel1.setText(self.__tr("URL :"))
        self.itemList.header().setLabel(0,self.__tr("Titre"))
        self.itemList.header().setLabel(1,self.__tr("Date"))
        self.itemList.header().setLabel(2,self.__tr("Description"))
        self.itemList.clear()
 
        self.quitButton.setText(self.__tr("Quitter"))
        self.saveButton.setText(self.__tr("Conserver l'URL"))
        self.refreshButton.setText(self.__tr("Rafraichir"))
 
    def enteredUrl(self,item):
        self.__parseURL(item.text())
 
    def activateUrl(self,url):
        self.__parseURL(url)
 
    def refreshUrl(self):
        self.__parseURL(self.urlCombo.currentText())
 
    def saveUrls(self):
        settings = QSettings()
        settings.setPath('Mangue.org','LecteurRSS')
        self.urls.append(self.urlCombo.currentText())
        settings.writeEntry('/URL/urls',self.urls)
 
    def __tr(self,s,c = None):
        return qApp.translate("rssfetcher",s,c)
 
if __name__ == "__main__":
    a = QApplication(sys.argv)
    QObject.connect(a,SIGNAL("lastWindowClosed()"),a,SLOT("quit()"))
    w = rssfetcher()
    a.setMainWidget(w)
    w.show()
    a.exec_loop()


Nous utilisons la classe QSettings afin de sauvegarder de manière transparente et simple les différentes URLs ajoutées. QSettings va sauvegarder :


  • Dans un fichier de config dans le répertoire .qt sous Linux
  • Dans la base de registre sous Windows
  • En utilisant l'API Carbon sous Mac OSX


Son utilisation est simple. On définie un chemin unique en utilisant un domaine, puis le nom de l'application. Puis par la suite on ajoute des sections en utilisant les méthodes beginGroup() et endGroup(). On ajoute des entrées aux sections en utilisant writeEntry*(). Bien entendu la relecture se fait via readEntry*(). Par conséquent, dasn le constructeur de la fenêtre principale, on tente de lire les entrées pour les ajouter a la liste déroulante.


Un mot sur les layouts


Lorsque vous créez une application, vous souhaitez qu'elle possède une apparence cohérente peu importe la résolution de l'utilisateur final. la solution est donc d'utiliser des layouts offerts par Qt afin de laisser l'API positionner au mieux les éléments. Il existe plusieurs layouts de bases :

  • Vertical
  • Horizontal
  • Grille


Le grille vous permet de coupler simplement les deux premiers. D'une manière générale plus vos applications deviendront complexes, plus vos layouts le seront aussi.


Ceci étant il est important de passer un temps de réflexion afin de décider du positionnement de vos éléments afin de le faire une fois pour toute puis de laisser Qt réaliser le travail ingrat. Lorsque vous souhaitez séparer deux widgets sans pour autant créer deux layouts. Par exemple un menu de gauche et une zone d'édition à droite comme dans un explorateur classique, une bonne alternative est le widget QSplitter. Nous verrons son utilisation la prochaine fois.


Lors du prochain article, nous améliorerons notre lecteur de News afin de le rendre plus utilisable. Nous verrons aussi comment afficher le résultats des liens HTML dans un QTextBrowser.

Outils personels