J’entends par là, que si vous voulez insérez dans votre QTable de nombreuses lignes sans pour autant freezer votre appli, vous ne pourrez pas.
Mais enfait si, elle est pas belle la vie ?
La méthode (générique) est la suivante:
- Démarrer un second processus léger depuis le principal.
- Envoyer via la méthode( thread-safée ) : postEvent(parent,monEvenement ) monEvenement au parent, se trouvant sur le thread principal.
- Catcher cet évènement depuis le parent, et effectuer l’action souhaitée.
Bon, la théorie c’est bien, mais la pratique c’est mieux.
Alors, on va commencer par créer notre Fenêtre,un bouton et notre QTable, ca devrait pas être trop difficile, c’est au moins du niveau CP.
#-*- coding:Utf-8 -*-
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sys,time
class MaFenetre(QWidget):
« »"Ma Fenetre principale »" »
def __init__(self):
« »" à l’initialisation »" »
QWidget.__init__(self)
self.setWindowTitle(‘Tue moi’)#le titre de la fenêtre
self.showMaximized()# met l’appli en taille maximale
##### la QTable #####
self.maTable=QTableWidget()
#on crée notre table
self.maTable.setColumnCount(1)
#la table sera composée de 1 colonne
self.maTable.setHorizontalHeaderLabels(['Element QThread'])
#le nom de la colonne
self.maTable.resizeColumnsToContents()
#on met à la colonne à l’échelle du titre
##### bouton #####
monBouton=QPushButton(« Rafraichir »)
#on crée notre bouton
self.connect(monBouton,SIGNAL(« clicked() »),self.rafraichir)
#quand monBouton sera cliqué, on lancera la méthode rafraichir
##### calque #####
monCalque=QVBoxLayout()
#notre calque
monCalque.addWidget(monBouton)
#on ajoute notre bouton à notre calque
monCalque.addWidget(self.maTable)
#ainsi que la table
self.setLayout(monCalque)
#on fait de monCalque le calque principal de la fenêtre
#### le thread####
self.monThread=None
#on déclare l’attribut qui va contenir le thread comme variable d’instance
def rafraichir(self):
« »"au rafraichissement »" »
pass
if __name__==’__main__’:
monAppli=QApplication(sys.argv)
maFenetre=MaFenetre()
#on instancie notre fenêtre
maFenetre.show()
#on la montre
sys.exit(monAppli.exec_())
Je vais juste expliquer 2 -3 petites choses, vu que c’est pas bien compliqué et qu’il y a pas mal de commentaires:
if __name__==’__main__’:
__name__ est un attribut de notre module (notre fichier).
Si notre fichier est lancé en tant que programme, alors __name__ prendra la valeur : __main__ ,
et le code en dessous s’éxécutera.
self.monThread=None:
On déclare monThread en tant que variable d’instance.
Et là vous êtes censé vous dire » Mais pourquoi donc ne déclare -t-il
pas monThread en tant que locale au sein de la méthode rafraichir() , nom d’une pipe ! «
Et bien si monThread est locale, elle va donc contenir notre thread, que l’on va démarrer. Mais une fois la méthode rafraichir() finie, ses variables lcoales seront détruites dont monThread, en cours.
Et nous aurons alors droit à une belle erreur: QThread: Destroyed while thread is still running .
On va finir notre petit projet:
on va creer notre Thread,notre Evenement,notre méthode qui va catcher l’évènement.
#-*- coding:Utf-8 -*-
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sys,time
class MonThread(QThread):
« »"mon Thread »" »
def
__init__(self,receveur):
QThread.__init__(self)
self.receveur=receveur
def run(self):
maListe=dir(QThread)
#on crée une liste des méthodes et attributs de QThread
for element in maListe:
#pour chaque élément présent dans maListe
QApplication.postEvent(self.receveur,MonEvenement(element) )
#on envoie MonEvenement à notre parent
time.sleep(0.1)
#un sleep de 0.1 seconde
class MonEvenement(QEvent):
« »"MonEvenement qui va envoyer à MaFenetre chaque élément de la liste »" »
def __init__(self,element):
QEvent.__init__(self,QEvent.User)
self.element=element
class MaFenetre(QWidget):
« »"Ma Fenetre principale »" »
def __init__(self):
« »" à l’initialisation »" »
QWidget.__init__(self)
self.setWindowTitle(‘Tue moi’)#le titre de la fenêtre
self.showMaximized()# met l’appli en taille maximale
##### la QTable #####
self.maTable=QTableWidget()
#on crée notre table
self.maTable.setColumnCount(1)
#la table sera composée de 1 colonne
self.maTable.setHorizontalHeaderLabels(['Element QThread'])
#le nom de la colonne
self.maTable.resizeColumnsToContents()
#on met à la colonne à l’échelle du titre
##### bouton #####
monBouton=QPushButton(« Rafraichir »)
#on crée notre bouton
self.connect(monBouton,SIGNAL(« clicked() »),self.rafraichir)
#quand monBouton sera cliqué, on lancera la méthode rafraichir
##### calque #####
monCalque=QVBoxLayout()
#notre calque
monCalque.addWidget(monBouton)
#on ajoute notre bouton à notre calque
monCalque.addWidget(self.maTable)
#ainsi que la table
self.setLayout(monCalque)
#on fait de monCalque le calque principal de la fenêtre
#### le thread####
self.monThread=None
#on déclare l’attribut qui va contenir le thread comme variable d’instance
def rafraichir(self):
« »"au rafraichissement »" »
if self.monThread==None or self.monThread.isFinished():
#condition pour lancer le rafraichissement
self.monThread=MonThread(self)
self.monThread.start()
def customEvent(self,event):
« »"méthode qui catche les évènement utilisateurs »" »
ligne = self.maTable.rowCount()
self.maTable.insertRow(ligne)
self.maTable.setItem(ligne,0,QTableWidgetItem(event.element) )
if __name__==’__main__’:
monAppli=QApplication(sys.argv)
maFenetre=MaFenetre()#on instancie notre fenêtre
maFenetre.show()#on la montre
sys.exit(monAppli.exec_())
Ici,
On crée une classe MonEvement ,laquelle hérite de QEvent.
Elle prend en paramètre element ,qui est la valeur d’un élément de QThread ( cf dir() ).
Ensuite,une nouvelle classe MonThread ,qui hérite de QThread.
Elle prend en paramètre receveur, qui est l’objet auquel va être associé MonEvement .
Pour chaque element présent dans maListe, on va donc utiliser la méthode postEvent(), qui va remonter MonEvement(element) à
notre receveur .
La méthode customEvent() va attraper cet évènement, et ajouter une ligne.
Enfin, j’utilise un time.sleep(0.1) pour, justement éviter de faire freezer le thread principal:
s’il n’y a pas de sleep, le thread principal va ajouter les lignes en boucle donc se bloquer,
or avec le sleep, on va attendre 0,1 seconde avant d’en rajouter.
Voilà,c’est terminé.