IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

PyQt Python Discussion :

QThread et moveToObject qui ne fonctionne jamais [QtCore]


Sujet :

PyQt Python

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 832
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 832
    Billets dans le blog
    1
    Par défaut QThread et moveToObject qui ne fonctionne jamais
    Bonjour à tous

    Comme beaucoup ici, je me suis intéressé aux QThreads afin de pouvoir déporter certains traitements longs. Mais comme pour tout, il m'a fallu apprendre. Et dans cette phase d'apprentissage, après différents essais, j'arrive sur un blocage avec la fonction "moveToObject()" que je n'arrive pas à faire fonctionner...

    Tout d'abord, pour vérifier la faisabilité du thread, j'écris mon premier thread, largement inspiré de ce tutoriel.
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
     
    from PyQt4.QtGui import *
    from PyQt4.QtCore import *
    import sys
     
    class MyThread(QThread):
    	def __init__(self, limit, prior, name=None, *args, **kwargs):
    		QThread.__init__(self, *args, **kwargs)
    		self.__prior=prior
    		self.__name=name
    		self.__limit=limit
    	# __init__()
     
    	def run(self):
    		self.setPriority(self.__prior)
    		for i in xrange(self.__limit):
    			qDebug("nom: %s (%x), i=%d" % (self.__name, self.currentThreadId(), i))
    	# run()
    # class MyThread
     
    if __name__ == "__main__":
    	app=QCoreApplication (sys.argv)
    	qDebug("app: %x" % app.thread().currentThreadId())
    	thread=[
    		MyThread(5, prior, name)\
    		for (name, prior) in (
    			("A", QThread.IdlePriority),
    			("B", QThread.NormalPriority),
    			("C", QThread.TimeCriticalPriority),
    		)
    	]
    	for t in thread: t.start()
    	QTimer.singleShot(2000, app, SLOT("quit()"))
    	app.exec_()
    	for t in thread: t.quit()
    # if

    A l'exécution, tout va bien. J'ai bien mes "A", "B" et "C" avec des currentThreadId() tous différents (et différents de celui du QApplication) et je vois que le thread C (le plus prioritaire) se déroule le premier.

    Ensuite, je m'intéresse à la possibilité d'intégrer un QObject. Je trouve à ce propos ce très bel article vous vous y prenez mal que je lis avec attention et où je suis attiré par cette phrase
    Télécharger un fichier, interroger une base de données ou faire tout autre type de traitements ne devrait pas être ajouté dans une sous-classe de QThread. Il devrait être encapsulé dans un objet qui lui est propre... Pour exécuter votre code dans un nouveau thread, vous devriez instancier un QThread et assigner votre objet à ce thread en utilisant la fonction moveToThread()
    .
    J'en conclus qu'il faut créer un QObjet indépendant qui fait le travail, puis envoyer l'objet dans le thread avec moveToObject() pour que ce travail soit fait par le thread.

    Malheureusement, rien de ce que je fais ne fonctionne. En désespoir de cause, je vais jusqu'à aller regarder le code source de l'exemple movedobject qu'on peut trouver dans les exemples du source de la librairie Qt. Je prends ce source et en crée une adaptation Python. Ce qui donne le code suivant
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
     
    from PyQt4.QtGui import *
    from PyQt4.QtCore import *
    import sys
     
    class WorkerObject(QObject):
    	def __init__(self, ms, parent=None, *args, **kwargs):
    		QObject.__init__(self, parent, *args, **kwargs)
    		self.__timer=QTimer()
    		self.connect(self.__timer, SIGNAL("timeout()"), self.__poll)
    		self.__count=0
    		self.__ms=ms
    	# __init__()
     
    	def startPolling(self):
    		qDebug("start polling in thread %x" % self.thread().currentThreadId())
    		self.__count=0
    		self.__timer.start(self.__ms)
    	# startPolling()
     
    	def stopPolling(self, ms):
    		self.__timer.stop()
    	# stopPolling()
     
    	def __poll(self):
    		qDebug("timer hit %d (%x)" % (self.__count, self.thread().currentThreadId()))
    		self.__count+=1
    	# __poll()
    # class WorkerObject
     
    class Thread(QThread):
    	def __init__(self, parent=None, *args, **kwargs):
    		QThread.__init__(self, parent, *args, **kwargs)
    	# __init__()
     
    	def launchWorker(self, worker):
    		qDebug("%x launchWorker()" % self.currentThreadId())
    		worker.moveToThread(self)
    		self.start()
    	# launcherWorkerObject()
     
    	def stopExecutor(self):
    		qDebug("%x stopExecutor()" % self.currentThreadId())
    		self.quit()
    	# stopExecutor()
    # class Thread
     
    if __name__ == "__main__":
    	app=QCoreApplication(sys.argv)
    	qDebug("mainThread=%x" % app.thread().currentThreadId())
    	thread=Thread()
    	worker=WorkerObject(500)
    	QObject.connect(
    		thread,
    		SIGNAL("started()"),
    		worker.startPolling,
    	)
    	QObject.connect(
    		thread,
    		SIGNAL("finished()"),
    		worker.stopPolling,
    	)
    	thread.launchWorker(worker)
    	QTimer.singleShot(3000, app, SLOT("quit()"))
    	app.exec_()
    	thread.stopExecutor()
    # if

    cette adaptation, bien qu'épurée de certains détails présents dans l'exemple C++, me semble quand-même fonctionnelle pour mes tests. Or quand j'exécute ce code, les valeurs affichées aux différents "currentThreadId()" sont pourtant toutes identiques à celles du QApplication
    Arrivé là, je ne vois pas quoi faire de plus pour réussir à déporter mon objet dans son thread...

    Merci de votre attention
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  2. #2
    Expert confirmé
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 486
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 486
    Billets dans le blog
    6
    Par défaut
    Bonjour Sve@r,

    Je n'ai pas de réponse précise à ta question, parce que je n'ai pas rencontré ce genre de problème.

    Pourtant, j'utilise souvent les threads, et quelquefois pour faire des choses compliquées, y compris avec du graphique PyQt, et je trouve que ça marche très bien. L'exemple type est le téléchargement d'un gros fichier en FTP avec barre de progression.

    J'ai déjà lu cette méchante critique sur la manière courante d'écrire les threads sous forme de classe, mais c'est tellement contredit par mon expérience que je n'en tiens aucun compte. Par ailleurs, je n'ai jamais utilisé "moveToThread" ni "moveToObject" et je ne sais même pas à quoi ça sert...

    Dans mes programmes avec PyQt, j'ai souvent un thread à construire avec interaction avec la partie graphique. Comme le graphique PyQt n'est pas "thread-safe", je fais en sorte que les threads puissent envoyer des messages à la partie graphique (y compris avec transmission de données). Pour ça, j'utilise l'une de ces 2 solutions qui fonctionnent correctement:

    - une classe héritant de QThread

    - une classe héritant en même temps de threading et de QObject (il s'agit alors d'un héritage multiple)

    Les codes pour utiliser QThread et threading se ressemblent beaucoup.

    Si tu penses que les principes que j'utilise peuvent t'être utiles, n'hésite pas à me le dire! Comme je suis curieux, peut-être que je découvrirais de nouvelles choses intéressantes...

  3. #3
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 832
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 832
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par tyrtamos Voir le message
    Pourtant, j'utilise souvent les threads, et quelquefois pour faire des choses compliquées, y compris avec du graphique PyQt, et je trouve que ça marche très bien. L'exemple type est le téléchargement d'un gros fichier en FTP avec barre de progression.
    C'est un truc analogue que je veux faire: un gros chargement de datas depuis une bdd (avec barre de progression) puis mise en forme de ces datas pour enfin les afficher dans un objet (QText, QTable, je sais pas trop encore)...

    Citation Envoyé par tyrtamos Voir le message
    Par ailleurs, je n'ai jamais utilisé "moveToThread" ni "moveToObject" et je ne sais même pas à quoi ça sert...
    "moveToObject" je ne l'ai pas vu dans la doc (fonction Qt3 ?) mais d'après ce que j'ai compris, "moveToThread" permet de créer un objet indépendant (destiné à effectuer un travail déterminé et fini) puis envoyer cet objet dans le thread qui le fera exécuter. Ca permet (toujours d'après moi) de conserver une indépendance entre "faire le travail" et "le faire faire par qui". Si par exemple tu as besoin dans un cas X de faire faire le travail par un thread et dans un cas Y de le faire faire directement par l'appli c'est alors facile (dans un cas tu le moveToThread et pas dans l'autre).
    Je pense que ce schéma explique bien la chose.

    Citation Envoyé par tyrtamos Voir le message
    Dans mes programmes avec PyQt, j'ai souvent un thread à construire avec interaction avec la partie graphique. Comme le graphique PyQt n'est pas "thread-safe", je fais en sorte que les threads puissent envoyer des messages à la partie graphique (y compris avec transmission de données). Pour ça, j'utilise l'une de ces 2 solutions qui fonctionnent correctement:

    - une classe héritant de QThread

    - une classe héritant en même temps de threading et de QObject (il s'agit alors d'un héritage multiple)
    Dans le tuto mentionné dans mon premier post, j'ai trouvé cet autre exemple utilisant effectivement la première solution

    Voici sa traduction PyQt
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
     
    import sys
    import time
    from sip import *
    from PyQt4.QtCore import *
    from PyQt4.QtGui import *
     
    class QtDialog(QDialog):
    	def __init__(self, *args, **kwargs):
    		QDialog.__init__(self, *args, **kwargs)
    		self.__t=QtThread(self)
    		self.connect(
    			self.__t,
    			SIGNAL("valueChanged"),
    			self.__slotValueChanged,
    		)
    		self.__l=QLabel(self)
    		btn1=QPushButton("start")
    		self.connect(
    			btn1,
    			SIGNAL("clicked()"),
    			self.__slotStart,
    		)
     
    		btn2=QPushButton("stop")
    		self.connect(
    			btn2,
    			SIGNAL("clicked()"),
    			self.__slotStop,
    		)
     
    		btn3=QPushButton("quit")
    		self.connect(
    			btn3,
    			SIGNAL("clicked()"),
    			self,
    			SLOT("close()"),
    		)
     
    		layout=QHBoxLayout(self)
    		layout.addWidget(self.__l, 1)
    		layout.addWidget(btn1, 0)
    		layout.addWidget(btn2, 0)
    		layout.addWidget(btn3, 0)
    	# __init__()
     
    	def __slotValueChanged(self, n):
    		self.__l.setText("n=%d" % n)
    	# __slotValueChanged()
     
    	def __slotStart(self):
    		print("slotStart")
    		self.__t.start()
    	# __slotStart()
     
    	def __slotStop(self):
    		print("slotStop")
    		self.__t.working=False
    	# __slotStop()
    # class QtDialog
     
    class QtThread(QThread):
    	def __init__(self, *args, **kwargs):
    		QThread.__init__(self, *args, **kwargs)
    		self.working=False
    	# __init__()
     
    	def run(self):
    		qDebug("%x" % self.currentThreadId())
    		self.working=True
    		i=0
    		while self.working:
    			i+=1
    			self.emit(SIGNAL("valueChanged"), i)
    			self.msleep(500)
    		# for
    	# run
    # class QThread
     
    if __name__ == "__main__":
    	a=QApplication(sys.argv)
    	qDebug("%x" % a.thread().currentThreadId())
    	w=QtDialog()
    	w.show()
    	a.exec_()
    # if

    En fait, l'élément qui affiche la valeur est actionné par un signal émis par le thread. Et le thread emet ce signal (avec la valeur à afficher) toutes les demi-secondes. Alors effectivement c'est fonctionnel (je vois bien l'identifiant du thread différent de celui de l'appli) mais je pense que là, le thread est inutile (on obtiendrait la même chose avec un simple QTimer).

    Pour l'héritage multiple, je préfère pour le moment éviter (la première fois que j'ai voulu tenter un héritage multiple entre deux objets Qt ça a été assez catastrophique).

    En tout cas c'est sympa de m'avoir répondu. J'ai pas encore travaillé sur ta réponse de mon autre topic mais je ne l'oublie pas
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  4. #4
    Expert confirmé
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 486
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 486
    Billets dans le blog
    6
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Pour l'héritage multiple, je préfère pour le moment éviter (la première fois que j'ai voulu tenter un héritage multiple entre deux objets Qt ça a été assez catastrophique
    C'est normal: sous PyQt, un héritage multiple avec plus d'un objet Qt est interdit!

    Extrait de la doc:

    It is not possible to define a new Python class that sub-classes from more than one Qt class.
    Mais ce n'est pas ce que je fais (threading + QObject).

  5. #5
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 832
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 832
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par tyrtamos Voir le message
    Si tu penses que les principes que j'utilise peuvent t'être utiles, n'hésite pas à me le dire! Comme je suis curieux, peut-être que je découvrirais de nouvelles choses intéressantes...
    Ben en fait oui parce que pour l'instant, je n'arrive pas à trouver un exemple pertinent qui nécessiterait impérativement un thread avec Qt.

    Par exemple je suis parti de ta phrase "L'exemple type est le téléchargement d'un gros fichier en FTP avec barre de progression." et me suis dit "ok, je vais programmer un petit compteur qui est incrémenté par le thread et qui renvoie l'info à un objet PyQt et que je pourrai interrompre/faire repartir à volonté. Ca me semble tout à fait représentatif de la problématique citée.

    Je me fais rapidement la main sur les Thread avec un exemple trouvé ça et là puis j'y vais. Après tatonnements et essais, j'arrive au code suivant
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
     
    import sys
    import time
    from sip import *
    from PyQt4.QtCore import *
    from PyQt4.QtGui import *
    from threading import *
     
    class QtWork(QObject):
    	def __init__(self, *args, **kwargs):
    		QObject.__init__(self, *args, **kwargs)
    		self.__cpt=0
    	# __init__()
     
    	def work(self):
    		self.__cpt+=1
    		self.emit(SIGNAL("worked"), self.__cpt)
    	# work()
    # class QtWork
     
    class cThread(Thread):
    	def __init__(self, obj):
    		Thread.__init__(self)
    		self.__active=None
    		self.__obj=obj
    	# __init__()
     
    	def run(self):
    		self.__active=False
    		while self.__active is not None:
    			if self.__active: self.__obj.work()
    			time.sleep(0.2)
    		# while
    	# run()
     
    	def on(self): self.__active=True
    	def off(self): self.__active=False
    	def over(self): self.__active=None
    # class cThread()
     
    class QtDialog(QDialog):
    	def __init__(self, *args, **kwargs):
    		QDialog.__init__(self, *args, **kwargs)
    		qDebug("thread=%x" % self.thread().currentThreadId())
    		self.__label=QLabel()
    		self.__work=QtWork()
    		self.connect(
    			self.__work,
    			SIGNAL("worked"),
    			self.__slotWork,
    		)
    		self.__thread=cThread(self.__work)
    		self.__thread.start()
    		btn1=QPushButton("start")
    		self.connect(
    			btn1,
    			SIGNAL("clicked()"),
    			self.__slotStart,
    		)
     
    		btn2=QPushButton("stop")
    		self.connect(
    			btn2,
    			SIGNAL("clicked()"),
    			self.__slotStop,
    		)
     
    		btn3=QPushButton("quit")
    		self.connect(
    			btn3,
    			SIGNAL("clicked()"),
    			self,
    			SLOT("close()"),
    		)
     
    		layout=QHBoxLayout(self)
    		layout.addWidget(self.__label, 1)
    		layout.addWidget(btn1, 0)
    		layout.addWidget(btn2, 0)
    		layout.addWidget(btn3, 0)
    	# __init__()
     
    	def closeEvent(self, e):
    		print("closeEvent(%s, %s)" % (self, e))
    		self.__thread.over()
    		self.__thread.join()
     
    	def __slotStart(self):
    		print("slotStart")
    		self.__thread.on()
    	# __slotStart()
     
    	def __slotStop(self):
    		print("slotStop")
    		self.__thread.off()
    	# __slotStop()
     
    	def __slotWork(self, n):
    		self.__label.setText("n=%s" % n)
    	# __slotWork()
    # class QtDialog
     
    if __name__ == "__main__":
    	a=QApplication(sys.argv)
    	w=QtDialog()
    	w.show()
    	a.exec_()
    # if

    J'ai eu un peu de mal à le mettre au point car on ne peut faire qu'un seul start() par thread d'où cette magouille avec le flag "__active" qui permet aussi de quitter le thread quand il passe à "None" (je n'ai pas trouvé d'autre façon de l'arrêter) mais bon, ça fonctionne.

    Ensuite je regarde ce code et je vois qu'il est très similaire à cet exemple déjà mentionné mais qui, lui, utilise les QThread Qt.

    D'où ce second exemple où je remplace Thread par QThread
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
     
    import sys
    import time
    from sip import *
    from PyQt4.QtCore import *
    from PyQt4.QtGui import *
     
    class QtWork(QObject):
    	def __init__(self, *args, **kwargs):
    		QObject.__init__(self, *args, **kwargs)
    		self.__cpt=0
    	# __init__()
     
    	def work(self):
    		self.__cpt+=1
    		self.emit(SIGNAL("worked"), self.__cpt)
    	# work()
    # class QtWork
     
    class cThread(QThread):
    	def __init__(self, obj):
    		QThread.__init__(self)
    		self.__active=None
    		self.__obj=obj
    	# __init__()
     
    	def run(self):
    		self.__active=False
    		while self.__active is not None:
    			if self.__active: self.__obj.work()
    			time.sleep(0.2)
    		# while
    	# run()
     
    	def on(self): self.__active=True
    	def off(self): self.__active=False
    	def over(self): self.__active=None
    # class cThread()
     
    class QtDialog(QDialog):
    	def __init__(self, *args, **kwargs):
    		QDialog.__init__(self, *args, **kwargs)
    		qDebug("thread=%x" % self.thread().currentThreadId())
    		self.__label=QLabel()
    		self.__work=QtWork()
    		self.connect(
    			self.__work,
    			SIGNAL("worked"),
    			self.__slotWork,
    		)
    		self.__thread=cThread(self.__work)
    		self.__thread.start()
    		btn1=QPushButton("start")
    		self.connect(
    			btn1,
    			SIGNAL("clicked()"),
    			self.__slotStart,
    		)
     
    		btn2=QPushButton("stop")
    		self.connect(
    			btn2,
    			SIGNAL("clicked()"),
    			self.__slotStop,
    		)
     
    		btn3=QPushButton("quit")
    		self.connect(
    			btn3,
    			SIGNAL("clicked()"),
    			self,
    			SLOT("close()"),
    		)
     
    		layout=QHBoxLayout(self)
    		layout.addWidget(self.__label, 1)
    		layout.addWidget(btn1, 0)
    		layout.addWidget(btn2, 0)
    		layout.addWidget(btn3, 0)
    	# __init__()
     
    	def closeEvent(self, e):
    		print("closeEvent(%s, %s)" % (self, e))
    		self.__thread.over()
     
    	def __slotStart(self):
    		print("slotStart")
    		self.__thread.on()
    	# __slotStart()
     
    	def __slotStop(self):
    		print("slotStop")
    		self.__thread.off()
    	# __slotStop()
     
    	def __slotWork(self, n):
    		self.__label.setText("n=%s" % n)
    	# __slotWork()
    # class QtDialog
     
    if __name__ == "__main__":
    	a=QApplication(sys.argv)
    	w=QtDialog()
    	w.show()
    	a.exec_()
    # if

    Et c'est sincèrement que j'ai été étonné de la facilité du portage. En fait, le QThread s'utilise exactement de la même façon que le Thread (remarque en relisant ce topic je vois que tu l'as déjà dit) et en plus je n'ai plus besoin de "join()".

    Mais là, je vois que dans un cas comme dans l'autre, le Thread/QThread ne fait rien d'autre que d'envoyer un "ping" au travailleur. Donc (et je l'ai déjà dit précédemment) je me dis qu'un QTimer pourra faire pareil.

    D'où ce 3° code
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
     
    import sys
    import time
    from sip import *
    from PyQt4.QtCore import *
    from PyQt4.QtGui import *
     
    class QtWork(QObject):
    	def __init__(self, *args, **kwargs):
    		QObject.__init__(self, *args, **kwargs)
    		self.__cpt=0
    		self.__timer=QTimer()
    		self.connect(self.__timer, SIGNAL("timeout()"), self.__work)
    	# __init__()
     
    	def on(self):
    		self.__timer.start(200)
     
    	def off(self):
    		self.__timer.stop()
     
    	def __work(self):
    		self.__cpt+=1
    		self.emit(SIGNAL("worked"), self.__cpt)
    	# work()
    # class QtWork
     
    class QtDialog(QDialog):
    	def __init__(self, *args, **kwargs):
    		QDialog.__init__(self, *args, **kwargs)
    		qDebug("thread=%x" % self.thread().currentThreadId())
    		self.__label=QLabel()
    		self.__work=QtWork()
    		self.connect(
    			self.__work,
    			SIGNAL("worked"),
    			self.__slotWork,
    		)
    		btn1=QPushButton("start")
    		self.connect(
    			btn1,
    			SIGNAL("clicked()"),
    			self.__slotStart,
    		)
     
    		btn2=QPushButton("stop")
    		self.connect(
    			btn2,
    			SIGNAL("clicked()"),
    			self.__slotStop,
    		)
     
    		btn3=QPushButton("quit")
    		self.connect(
    			btn3,
    			SIGNAL("clicked()"),
    			self,
    			SLOT("close()"),
    		)
     
    		layout=QHBoxLayout(self)
    		layout.addWidget(self.__label, 1)
    		layout.addWidget(btn1, 0)
    		layout.addWidget(btn2, 0)
    		layout.addWidget(btn3, 0)
    	# __init__()
     
    	def __slotStart(self):
    		print("slotStart")
    		self.__work.on()
    	# __slotStart()
     
    	def __slotStop(self):
    		print("slotStop")
    		self.__work.off()
    	# __slotStop()
     
    	def __slotWork(self, n):
    		self.__label.setText("n=%s" % n)
    	# __slotWork()
    # class QtDialog
     
    if __name__ == "__main__":
    	a=QApplication(sys.argv)
    	w=QtDialog()
    	w.show()
    	a.exec_()
    # if

    C'est un code bien plus léger (plus besoin de gérer un thread) et tout aussi fonctionnel. Donc si tu voulais bien me dire ce que tu en penses et où à ton avis on devient obligé d'utiliser un Thread ça serait sympa. Parce que là, finalement, je me dis que pour ma problématique de chargement bdd, peut-être qu'un QTimer pourra suffire.

    En ce qui concerne le "moveToThread()", j'ai la bizarre impression que cette fonction ne fonctionne pas sous PyQt. Un indice qui me fait dire ça ; en dehors du fait que mon code PyQt ne fonctionne pas alors que le code Qt/C++ fonctionne (et que j'ai la petite prétention de croire que je n'ai pas fait d'erreur dans la traduction ) ; c'est que le source PyQt ne possède aucun exemple utilisant cette fonction.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  6. #6
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 741
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 741
    Par défaut
    Salut,

    Citation Envoyé par Sve@r Voir le message
    En ce qui concerne le "moveToThread()", j'ai la bizarre impression que cette fonction ne fonctionne pas sous PyQt. Un indice qui me fait dire ça ; en dehors du fait que mon code PyQt ne fonctionne pas alors que le code Qt/C++ fonctionne (et que j'ai la petite prétention de croire que je n'ai pas fait d'erreur dans la traduction ) ; c'est que le source PyQt ne possède aucun exemple utilisant cette fonction.
    Mais si çà fonctionne. Le soucis est de savoir depuis quel thread seront invoquée les méthodes appelées ou qui est le thread courant lorsqu'on fait quelque chose. Dans votre code, le défaut est que le QTimer qui affiche son threadID est lancé dans le main thread.

    Comme vous n'avez pas déclaré le QObject comme parent du QTimer, le moveToThread "bouge" le QObject qui ne fait rien mais laisse le QTimer dans la "main thread".

    Cà c'est l'idée mais votre code est un tel tas de nouille qu'il ne suffit pas de déclarer le parent pour qu'il tombe en marche...
    Simplifions pour montrer les problèmes:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    from PyQt4.QtCore import *
     
    class Worker(QObject):
       def timerEvent(self, *args):
             print('timerEvent: %08X' % QThread.currentThreadId())
     
    if __name__ == '__main__':
     
        app = QCoreApplication([])
        print ('main thread id: %08X' % QThread.currentThreadId())
        thread = QThread()
        worker = Worker()
     
        def on_start():
            print ('on_start, thread: %08X' % QThread.currentThreadId())
            worker.moveToThread(thread)
            worker.startTimer(500)
     
        thread.started.connect(on_start)
        thread.start()
     
        QTimer.singleShot(3000, app, SLOT("quit()"))
        app.exec_()
        thread.quit()
    le callback "on_start" sera appelé depuis le main thread. La séquence:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
            worker.moveToThread(thread)
            worker.startTimer(500)
    se vautrera avec "QObject::startTimer: timers cannot be started from another thread"
    Il faut "inverser", le démarrage du timer puis le move...
    Puisque le callback est exécuté dans le contexte du main thread, pas la peine de s'em... à faire le move lorsque le thread démarre. Simplifions:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    from PyQt4.QtCore import *
     
    class Worker(QObject):
       def timerEvent(self, *args):
             print('timerEvent: %08X' % QThread.currentThreadId())
     
    if __name__ == '__main__':
     
        app = QCoreApplication([])
        print ('main thread id: %08X' % QThread.currentThreadId())
        thread = QThread()
        worker = Worker()
     
        worker.startTimer(500)  
        worker.moveToThread(thread)
        thread.start()
     
        QTimer.singleShot(3000, app, SLOT("quit()"))
        app.exec_()
        thread.quit()
    Maintenant, amusons nous a ajouter un QTimer.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    from PyQt4.QtCore import *
     
    def create_timer(parent=None):
        timer = QTimer(parent=parent)
        def on_timeout():
            print ('timeout, thread=%08X' % QThread.currentThreadId())
        timer.timeout.connect(on_timeout)
        return timer
     
    class Worker(QObject):
        def __init__(self, threads):
            super().__init__()
            self._timer = create_timer(self)
            self._threads = threads
     
        def timerEvent(self, *args):
            print('**timerEvent: %08X' % QThread.currentThreadId())
            ix = (self._threads.index(self.thread()) + 1) % len(self._threads)
            self.moveToThread(self._threads[ix])
     
        def start_timers(self):
            worker.startTimer(500)         
            self._timer.start(200)  
     
    if __name__ == '__main__':
     
        app = QCoreApplication([])
        print ('main thread id: %08X' % QThread.currentThreadId())
        thread = QThread()
        thread.start()
     
        worker = Worker([app.thread(), thread ])
        worker.start_timers()
     
        QTimer.singleShot(3000, app, SLOT("quit()"))
        app.exec_()
        thread.quit()
    Et comme je fais un moveToThread tous les timer's events, vous ne pourrez plus dire que çà ne fonctionne pas.

    edit: mon code est Python3 désolé.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 0
    Dernier message: 22/11/2014, 15h58
  2. Réponses: 2
    Dernier message: 24/09/2010, 12h51
  3. Jointure externe qui ne fonctionne pas
    Par Guizz dans le forum Langage SQL
    Réponses: 3
    Dernier message: 05/02/2004, 12h26
  4. CREATEFILEMAPPING qui ne fonctionne pas???
    Par Jasmine dans le forum MFC
    Réponses: 2
    Dernier message: 06/01/2004, 19h33
  5. UNION qui ne fonctionne pas
    Par r-zo dans le forum Langage SQL
    Réponses: 7
    Dernier message: 21/07/2003, 10h04

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo