Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Thread en pyqt

1 réponse
Avatar
Laurent Claessens
Bonjour =E0 toutes et =E0 tous


J'aimerais me familiariser avec le multi-thread en pyqt. (je pr=E9viens
que d=E9j=E0 pyqt ne m'est pas tr=E8s familier). Donc je voudrais commencer
par lancer deux petits boutons ind=E9pendants. (=E0 terme, l'un sera une
fen=EAtre 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
=E9lagu=E9e pour ne garder que le lancement d'un s=E9lecteur de nombre
entier, et ensuite j'ai mix=E9 le r=E9sultat avec une structure threading
qui fonctionne.

Je m'attendais donc =E0 ce que le code suivant me lance un objet de la
classe Dialog dans un thread. Mais au lieu de =E7a, 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 =E0 afficher un bouton, et quand on clique sur le
bouton, une fen=EAtre appara=EEt pour s=E9lectionner un nombre entier.
## Ce qui serait bien, c'est de lancer deux fois cette classe en m=EAme
temps
class Dialog(QtGui.QDialog):
MESSAGE =3D 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=3DNone):
QtGui.QDialog.__init__(self, parent)

self.openFilesPath =3D QtCore.QString()

self.errorMessageDialog =3D QtGui.QErrorMessage(self)

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

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

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

layout =3D 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 =3D 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=E9c=E9dente dans un nouveau
thread
class ThreadDialog (threading.Thread):
def __init__(self):
threading.Thread.__init__(self)

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



print "Bonjour"

app =3D QtGui.QApplication(sys.argv)


## Quand ces deux lignes sont lanc=E9es, 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 =3D ThreadDialog()
x.start()

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

print "Au revoir"

------------------------------------- Fin --------------------

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


En d=E9finitive, ce qui serait bien, c'est d'avoir une boite de dialogue
avec deux boutons. Si on clique sur le premier, =E7a lance une nouvelle
fen=EAtre qui permet de faire des trucs, mais =E7a laisse la porte ouverte
=E0 *en m=EAme temps* cliquer sur l'autre bouton qui ouvre une autre
fen=EAtre 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 =E7a dans la doc ...

Si quelqu'un sait comment proc=E9der, qu'il n'h=E9site pas =E0 se
manifester.
Merci
Laurent

1 réponse

Avatar
Laurent Pointal
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 -