Thread en pyqt

Le
Laurent Claessens
Bonjour à toutes et à tous


J'aimerais me familiariser avec le multi-thread en pyqt. (je préviens
que déjà pyqt ne m'est pas très familier). Donc je voudrais commencer
par lancer deux petits boutons indépendants. (à terme, l'un sera une
fenêtre qui note l'avancement d'un apt-get install pendant que
l'utilisateur fera encore des choses dans l'autre).

J'ai donc prit l'exemple dialogs.py de la documentation, je l'ai
élaguée pour ne garder que le lancement d'un sélecteur de nombre
entier, et ensuite j'ai mixé le résultat avec une structure threading
qui fonctionne.

Je m'attendais donc à ce que le code suivant me lance un objet de la
classe Dialog dans un thread. Mais au lieu de ça, il plante.
ASSERT failure in QWidget: "Widgets must be created in the GUI
thread.", file kernel/qwidget.cpp, line 951
Abandon (core dumped)

Ce qui ne fonctionne pas

#! /usr/bin/python
# -*- coding: utf8 -*-


import sys
import threading, time
from PyQt4 import QtCore, QtGui


## Classe qui sert à afficher un bouton, et quand on clique sur le
bouton, une fenêtre apparaît pour sélectionner un nombre entier.
## Ce qui serait bien, c'est de lancer deux fois cette classe en même
temps
class Dialog(QtGui.QDialog):
MESSAGE = QtCore.QT_TR_NOOP("<p>Message boxes have a caption, a
text, and up to "
"three buttons, each with standard or
custom texts.</p>"
"<p>Click a button or press Esc.</p>")

def __init__(self, parent=None):
QtGui.QDialog.__init__(self, parent)

self.openFilesPath = QtCore.QString()

self.errorMessageDialog = QtGui.QErrorMessage(self)

frameStyle = QtGui.QFrame.Sunken | QtGui.QFrame.Panel

self.integerLabel = QtGui.QLabel()
self.integerLabel.setFrameStyle(frameStyle)
self.integerButton =
QtGui.QPushButton(self.tr("QInputDialog.get&Integer()"))

self.connect(self.integerButton, QtCore.SIGNAL("clicked()"),
self.setInteger)

layout = QtGui.QGridLayout()
layout.setColumnStretch(1, 1)
layout.setColumnMinimumWidth(1, 250)

layout.addWidget(self.integerButton, 0, 0)
layout.addWidget(self.integerLabel, 0, 1)

self.setLayout(layout)

self.setWindowTitle(self.tr("Standard Dialogs"))

def setInteger(self):
i, ok = QtGui.QInputDialog.getInteger(self,
self.tr("QInputDialog.getInteger()"),
self.tr("Percentage:"),
25, 0, 100, 1)
if ok:
self.integerLabel.setText(self.tr("%1%").arg(i))

## Cette classe lance un objet de la classe précédente dans un nouveau
thread
class ThreadDialog (threading.Thread):
def __init__(self):
threading.Thread.__init__(self)

def run(self):
self.dialog = Dialog()
sys.exit(dialog.exec_())



print "Bonjour"

app = QtGui.QApplication(sys.argv)


## Quand ces deux lignes sont lancées, j'ai le message d'erreur
#ASSERT failure in QWidget: "Widgets must be created in the GUI
thread.", file kernel/qwidget.cpp, line 951
#Abandon (core dumped)
x = ThreadDialog()
x.start()

## Ces deux lignes-ci fonctionnent parfaitement (mais lance Dialog une
seule fois).
#dialog = Dialog()
#sys.exit(dialog.exec_())

print "Au revoir"

- Fin --

J'ai essayé le code donné ici sur le forum :
http://groups.google.fr/group/fr.comp.lang.python/browse_thread/thread/7ba4b=
216a9ac5b8e/36dbf983b6672b3d?hl=fr&lnk=gst&q=Qthread#36dbf983b6672b3d
Résultat : plantage complet de ma machine, la souris ne bougeait même
plus. Ni ctrl-alt-F1 ni ctrl-alt-backspace n'ont fonctionné. Du coup,
j'avoue ne pas avoir trop essayé de broder autour de cet exemple pour
faire quelque chose de bon pour moi ;)


En définitive, ce qui serait bien, c'est d'avoir une boite de dialogue
avec deux boutons. Si on clique sur le premier, ça lance une nouvelle
fenêtre qui permet de faire des trucs, mais ça laisse la porte ouverte
à *en même temps* cliquer sur l'autre bouton qui ouvre une autre
fenêtre qui fait d'autres trucs. Si il y a moyen de faire sans
Threads, c'est bon aussi.

Je n'ai pas vu d'exemples comme ça dans la doc

Si quelqu'un sait comment procéder, qu'il n'hésite pas à se
manifester.
Merci
Laurent
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Laurent Pointal
Le #661852
Le Mon, 03 Dec 2007 08:31:59 -0800, Laurent Claessens a écrit :
<zip multithreading et GUI>

Bienvenue en enfer.

Je ne connais pas spécialement pyqt (je démarre Qt C++ au boulot). Mais
en général, dans la plus part des systèmes graphiques, tous les appels
relatifs à la GUI doivent se faire dans le thread qui a créé/initialisé
celle-ci - généralement le thread principal.

Pour que la GUI reste répondante lors des longs traitements, tu lance un
thread séparé, et celui-ci envoie périodiquement des messages vers le
thread GUI, avec des informations d'état, d'avancement, et les mises à
jour se font dans ce thread.
Ce qui passe - comme dans l'exemple que tu donnes en lien - par
l'utilisation de la file d'évènements utilisé par le thread GUI
QThread.postEvent() (dans toutes les bonnes crèmeries cette file est
protégée contre les accès concurrents).

Pour info, j'ai copié/collé l'exemple que tu donnes en lien, et chez moi
il fonctionne très bien sans planter (Qt 3.3.8 [*]), il doit y avoir
autre chose chez toi... quelles versions de Python, Qt & Co ?


A+


[*] J'ai essayé de mettre un import direct de PyQt4, mais alors il ne
connaît pas "QMainWindow".


--
Laurent POINTAL -
Publicité
Poster une réponse
Anonyme