PyGTK - gestion d'évènements dans un gtk.ListStore (première partie)
Par Fab le 10/05/2006 20:20
Ma curiosité naturelle prenant le dessus, j'ai voulu découvrir un peu plus en profondeur PyGTK.
Et c'est là que j'ai commencé à rencontrer quelques difficultés à trouver des informations plus précises, comme par exemple, les évenements de la souris dans une TreeView (TreeStore ou ListStore pour mon cas).
J'essaierai de vous montrer dans cette série de tutoriels, une approche pour gérer les évenements de la souris dans ces widgets très spécifiques que sont les gtk.TreeStore et gtk.ListStore.
Cette suite de tutoriels sera composée de 3 parties :
1 - Approche sur la gestion d'évènements dans un gtk.ListStore
2 - Approche avancée sur la gestion d'évenements dans un gtk.TreeStore
3 - Élaboration d'un menu dynamique dans un gtk.TreeStore
Catégories : graphique
Modules python : gtk,gtk.glade,gobject,re,urllib2,signal
Version Python : 2.4
Prérequis
Glade2
Compte tenu du nombre d'articles sur la toile concernant Glade2, je ne vais pas m'étendre sur ce sujet. Donc dans un souci de gain de temps (ou de paresses), vous trouverez içi, le fichier glade à mettre dans votre répertoire source du tutoriel.
Ben oui, vous vous en doutez sûrement, je préfère coder que d'écrire de la documentation. Mais voilà, faut savoir de temps en temps remercier la communauté, et donc contribuer ;-)
Python
Rien de bien spécifique ;-)
On partira du principe que vous avez un dossier "tutorial", dans lequel vous y mettrez vos sources, ainsi que votre fichier glade.
Première approche
Créons un fichier treeview_tuto1.py
Code source de treeview_tuto1.py
# -*- coding: utf-8 -*-
## treeview_tuto1.py
import gtk
import signal
import treeview_window
class Interface:
def __init__(self):
self.treeview_tutorial = treeview_window.Tutorial()
if __name__ == '__main__':
signal.signal(signal.SIGINT, signal.SIG_DFL)
Interface()
gtk.main()
Commentaires
Rien de bien complexe, le code principal vient juste après. En effet, on peut voir qu'on importe un fichier nommé treeview_window.py.
Donc, rajoutons un fichier treeview_window.py à notre répertoire et préparons nous à construire notre interface.
Interface GTK
Code source de treeview_window.py
# -*- coding: utf-8 -*-
## treeview_window.py
import gtk
import gtk.glade
import gobject
TREEVIEW_TUTORIAL = 'treeview_tutorial.glade'
class Tutorial:
'''
Définition de l'interface GTK Tutorial
'''
def __init__(self):
'''
Constructeur avec les attributs de classe décrivant notre interface.
On y retrouve les noms de nos différents widgets déclarés dans Glade2.
'''
self.xml = gtk.glade.XML(TREEVIEW_TUTORIAL, 'window1')
self.window = self.xml.get_widget('window1')
self.xml.signal_autoconnect(self)
self.liststore = self.xml.get_widget("treeview1")
self.treemodel = gtk.ListStore(gobject.TYPE_INT,
gobject.TYPE_STRING,
gobject.TYPE_STRING)
self.liststore.set_model(self.treemodel)
self.liststore.set_headers_visible(True)
renderer2 = gtk.CellRendererText()
column = gtk.TreeViewColumn("ID", renderer2, text=0)
column.set_resizable(True)
self.liststore.append_column(column)
column = gtk.TreeViewColumn("Nom", renderer2, text=1)
column.set_resizable(True)
self.liststore.append_column(column)
column = gtk.TreeViewColumn("Contenu", renderer2, text=2)
column.set_resizable(True)
self.liststore.append_column(column)
def on_window1_destroy(self, widget):
gtk.main_quit()
Commentaires
Heu, j'en ai déjà mis un peu dans le code !
Bon, pour faire court, nous avons :
- une classe Tutorial avec des attributs de classe qui servent à décrire l'interface
- une méthode on_window1_destroy qui sert à sortir de l'interface
Résultats

Finition
Afin d'éviter de travailler sur une liste vide, créons un fichier supplémentaire que l'on appelera list.py
Code source de list.py
# -*- coding: utf-8 -*-
## list.py
import re
import urllib2
import gtk
from urllib2 import URLError as url_error
from urllib2 import HTTPError as http_error
class Populate:
def __init__(self, model):
self.model = model
self.url = "http://www.afpy.org/"
self.result = []
self.regex = re.compile( r'href=\"http://www.afpy.org/Members/([\w-]+[/][\w+]+)')
self.insertData()
def insertData(self):
ls = self.request_url()
for content in range(len(ls)):
iter = self.model.append(
[content,
self.split_name(ls[content]),
self.split_content(ls[content])]
)
def request_url(self):
try :
u = urllib2.urlopen(self.url)
r_urls = self.extract_urls(u)
for r_url in r_urls:
self.result.append(r_url)
return self.result
except url_error, e:
print "Echec de la requete:", e.reason
except http_error, e:
print "Echec de la connexion:", e.reason
def extract_urls(self, u):
urls = []
for line in u.readlines() :
if (self.regex.findall(line)):
m = self.regex.findall(line)
for myurl in m :
urls.append(myurl)
u.close()
return urls
def split_name(self, content):
name = re.split("/", content)
return name[0]
def split_content(self, content):
name = re.split("/", content)
return name[1]
Commentaires
Ce qui nous faut retenir dans ce fichier, c'est la façon que la méthode insertData() de la classe Populate :
def insertData(self):
ls = self.request_url()
for content in range(len(ls)):
iter = self.model.append(
[content,
self.split_name(ls[content]),
self.split_content(ls[content])]
)
En effet, c'est elle qui remplit notre tableau, une fois que nous appellerons la classe depuis treeview_window.py.
Rajoutons donc l'appel à notre classe depuis le contructeur de Tutorial, sans oublier d'importer notre nouveau fichier list.py:
# -*- coding: utf-8 -*-
## treeview_window.py
##
import gtk
import gtk.glade
import gobject
import list # <-- IMPORT RAJOUTÉ !!
TREEVIEW_TUTORIAL = 'treeview_tutorial.glade'
class Tutorial:
'''
Définition de l'interface GTK Tutorial
'''
def __init__(self):
'''
Constructeur avec les attributs de classe décrivant notre interface.
On y retrouve les noms de nos différents widgets déclarés dans Glade2.
'''
self.xml = gtk.glade.XML(TREEVIEW_TUTORIAL, 'window1')
self.window = self.xml.get_widget('window1')
self.xml.signal_autoconnect(self)
self.liststore = self.xml.get_widget("treeview1")
self.treemodel = gtk.ListStore(gobject.TYPE_INT,
gobject.TYPE_STRING,
gobject.TYPE_STRING)
self.liststore.set_model(self.treemodel)
self.liststore.set_headers_visible(True)
self.populate = list.Populate(self.treemodel) # <-- APPEL RAJOUTÉ !!
renderer2 = gtk.CellRendererText()
column = gtk.TreeViewColumn("ID", renderer2, text=0)
column.set_resizable(True)
self.liststore.append_column(column)
column = gtk.TreeViewColumn("Nom", renderer2, text=1)
column.set_resizable(True)
self.liststore.append_column(column)
column = gtk.TreeViewColumn("Contenu", renderer2, text=2)
column.set_resizable(True)
self.liststore.append_column(column)
def on_window1_destroy(self, widget):
gtk.main_quit()
Résultat
Il est tout simplement magnifique ;-) :
C'est vrai, j'exagère un peu, mais l'idée est là. Il est temps de passer au vif du sujet, l'association d'un click de la souris ...
Évenements sur le gtk.ListStore
Si vous regardez correctement dans Glade2, vous verrez qu'il y a une action de prédéfinie sur le gtk.TreeView. On va récupérer le nom de cette action et lui associer une méthode dans Tutorial :
def on_treeview1_button_press_event(self, treeview, event):
'''Action pour le bouton droit'''
if event.button == 3:
x = int(event.x)
y = int(event.y)
pthinfo = treeview.get_path_at_pos(x, y)
if pthinfo != None:
path, col, cellx, celly = pthinfo
treeview.grab_focus()
treeview.set_cursor( path, col, 0)
model = treeview.get_model()
iter = model.get_iter(path)
#Je récupère la troisième valeur
content = model[iter][2]
print "Debug clic droit : %s" % content
Une fois ajoutée cette méthode dans la classe Tutorial, vous pouvez relancer le fichier treeview_tuto1.py.
Chaque clic de souris sur le gtk.TreeView va :
- appeler la méthode on_treeview1_button_press_event(),
- récupérer la troisième valeur de la colonne
- et l'afficher dans votre console.
Et voilà !
Conclusion
Nous avons pu voir une première approche sur la récupération d'évenments de la souris dans un gtk.ListStore. Bien entendu, il faut garder à l'esprit que pour un autre widget implémentant l'interface TreeModel, l'approche serait identique. Dans ce cas précis, l'interaction avec Glade2 est quasi-transparente. Nous verrons dans le prochain tutoriel, une approche avec gtk.TreeStore, les exceptions TypeError et une action suivant la valeur de l'iter.
L'archive des sources de ce tutoriel est disponible :







