Thread en pyqt

Le
Laurent Claessens
Bonjour toutes et tous


J'aimerais me familiariser avec le multi-thread en pyqt. (je prviens
que dj pyqt ne m'est pas trs familier). Donc je voudrais commencer
par lancer deux petits boutons indpendants. ( terme, l'un sera une
fentre 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
lague pour ne garder que le lancement d'un slecteur de nombre
entier, et ensuite j'ai mix le rsultat 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 fentre apparat pour slectionner un nombre entier.
## Ce qui serait bien, c'est de lancer deux fois cette classe en mme
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 prcdente 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 lances, 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
Rsultat : plantage complet de ma machine, la souris ne bougeait mme
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 dfinitive, 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
fentre qui permet de faire des trucs, mais a laisse la porte ouverte
*en mme temps* cliquer sur l'autre bouton qui ouvre une autre
fentre 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 procder, qu'il n'hsite pas se
manifester.
Merci
Laurent
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