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 :

Passage d'argument signal / slot [QtCore]


Sujet :

PyQt Python

  1. #1
    Rédacteur
    Avatar de Neitsa
    Homme Profil pro
    Chercheur sécurité informatique
    Inscrit en
    Octobre 2003
    Messages
    1 041
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Chercheur sécurité informatique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 1 041
    Points : 1 956
    Points
    1 956
    Par défaut Passage d'argument signal / slot
    Bonjour,

    c'est encore moi :p

    Contexte:



    Résumé:

    • - En résumé il s'agit d'un exemple de producteur / consommateur basique. Le producteur produit un certain nombre d'éléments mis dans un QByteArray (cet objet QByteArray est propriété du producteur) et le consommateur prend des éléments de ce tableau.
    • - Le producteur passe le QByteArray au consommateur via un signal. Le consommateur agit ensuite sur l'objet QByteArray.


    Problème: L'objet reçu par le consommateur (le QByteArray) contient bien les bonnes donnée, mais (c'est là que ce situe le coeur du problème) si l'exemple fonctionne en C++, en python l'objet n'est pas le même, il s'agit d'une copie => résultat, toute action du consommateur sur le QByteArray n'est pas répercutée sur l'objet appartenant au producteur puisque le consommateur reçoit une copie de l'objet et non l'objet lui-même.

    Notez que je parle de copie parce que l'id() de l'objet n'est pas le même entre celui du producteur et celui du consommateur. J'ai légèrement changé le code de façon a voir l'id() du QByteArray pour le producteur et le consommateur.

    question: comment faire pour recevoir exactement le même objet entre le producteur et le consommateur, sans (a priori) recevoir une copie ?

    Output du programme: (notez 'id' ci-dessous qui est différent entre le producteur et le consommateur, alors qu'il "devrait" s'agir du même objet):

    In produce. Data Type: <class 'PyQt4.QtCore.QByteArray'> - id: 41177992 - Size: 0
    Producer: produced 7890 more bytes, 7890 of 123456 total

    In consume. Data Type: <class 'PyQt4.QtCore.QByteArray'> - id: 41178096 - Size: 7890
    In Consume: 0
    Consumer: consumed 7890 more bytes, 7890 of 123456 total

    In produce. Data Type: <class 'PyQt4.QtCore.QByteArray'> - id: 41177992 - Size: 7890

    Producer: Consumer failed to consume!
    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
     
    import sys
    from PyQt4 import QtCore
    from PyQt4.QtCore import pyqtSlot
     
    Limit = 123456
    BlockSize = 7890
     
    class Producer(QtCore.QObject):
     
        produced = QtCore.pyqtSignal(QtCore.QByteArray)
        finished = QtCore.pyqtSignal()
     
        def __init__(self, parent = None):
            super(Producer, self).__init__(parent)
     
            self.bytes = 0
            self.data = QtCore.QByteArray()
     
        @pyqtSlot()
        def produce(self):
            print("In produce. Data Type: {} - id: {} - Size: {}".format(type(self.data), id(self.data), self.data.size()))
     
            remaining = Limit - self.bytes
            if remaining == 0:
                self.finished.emit()
                return
     
            #this will never happen
            if self.data.size() != 0:
                QtCore.qFatal("Producer: Consumer failed to consume!")
     
            size = qMin(BlockSize, remaining)
            self.bytes += size
            remaining -= size
            self.data.fill("Q", size)
     
            print("Producer: produced {} more bytes, {} of {} total".format(size, self.bytes, Limit))
            self.produced.emit(self.data)
     
    class Consumer(QtCore.QObject):
     
        consumed = QtCore.pyqtSignal()
        finished = QtCore.pyqtSignal()
     
        def __init__(self, parent = None):
            super(Consumer, self).__init__(parent)
     
            self.bytes = 0
     
        @pyqtSlot("QByteArray")
        def consume(self, data):
            print("In consume. Data Type: {} - id: {} - Size: {}".format(type(data), id(data), data.size()))
     
            if data.size() == 0:
                QtCore.qFatal("Consumer: Producer failed to produce!")
     
            remaining = Limit - self.bytes
            size = data.size()
            remaining -= size
            self.bytes += size
            data.clear()
            print("In Consume: ", data.size())
     
            print("Consumer: consumed {} more bytes, {} of {} total".format(size, self.bytes, Limit))
            self.consumed.emit()
     
            if remaining == 0:
                self.finished.emit()       
     
    def qMin(a, b):
        return a if a < b else b
     
    def main(argv):
        app = QtCore.QCoreApplication(argv)
     
        # create the producer and consumer and plug them together
        producer = Producer()
        consumer = Consumer()
     
        producer.connect(consumer, QtCore.SIGNAL("consumed()"), producer.produce)
        consumer.connect(producer, QtCore.SIGNAL("produced(QByteArray)"), consumer.consume)
     
        # they both got their own threads
        producerThread = QtCore.QThread()
        producer.moveToThread(producerThread)
     
        consumerThread = QtCore.QThread()
        consumer.moveToThread(consumerThread)
     
        # start producing once the producer's thread has started
        producer.connect(producerThread, QtCore.SIGNAL("started()"), producer.produce)
     
        # when the consumer is done, it stops its thread
        consumerThread.connect(consumer, QtCore.SIGNAL("finished()"), consumerThread.quit)
     
        # when producer thread is done, it quits the application
        app.connect(producerThread, QtCore.SIGNAL("finished()"), app.quit)
     
        # go!
        producerThread.start()
        consumerThread.start()    
     
        app.exec_()    
     
    if __name__ == '__main__':
        main(sys.argv)

    Merci beaucoup pour votre aide !

  2. #2
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 462
    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 462
    Points : 9 249
    Points
    9 249
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    J'ai déjà lu l'article que tu cites et qui dit: "sub-classer QThread, c'est mal". Les arguments sont un peu trop subtils pour moi, et je continue à sub-classer les QThread, tant que ça marche (et ça marche bien) et que ce n'est pas interdit par le manuel. C'est beaucoup plus simple à coder, et la simplicité du codage facilite le déverminage. De plus, la discussion porte sur le fait de sub-classer en C, et je ne sais pas si l'argument vaut aussi quand on est sous Python.

    Ce faisant, j'ai vu ici un exemple de ce que tu cherches à faire, et qui fonctionne chez moi (voir le dernier code de la page):

    http://stackoverflow.com/questions/4...roblem-in-pyqt.
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  3. #3
    Rédacteur
    Avatar de Neitsa
    Homme Profil pro
    Chercheur sécurité informatique
    Inscrit en
    Octobre 2003
    Messages
    1 041
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Chercheur sécurité informatique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 1 041
    Points : 1 956
    Points
    1 956
    Par défaut
    Bonjour Tyrtamos,

    Citation Envoyé par tyrtamos Voir le message
    Bonjour,

    J'ai déjà lu l'article que tu cites et qui dit: "sub-classer QThread, c'est mal". Les arguments sont un peu trop subtils pour moi, et je continue à sub-classer les QThread, tant que ça marche (et ça marche bien) et que ce n'est pas interdit par le manuel. C'est beaucoup plus simple à coder, et la simplicité du codage facilite le déverminage. De plus, la discussion porte sur le fait de sub-classer en C, et je ne sais pas si l'argument vaut aussi quand on est sous Python.
    Effectivement, savoir ce qui est juste ou pas dans ce cadre est assez compliqué... J'ai juste voulu testé la manière dont il était question histoire de voir comment ça fonctionnait. De là à savoir qu'elle est la meilleure c'est une autre histoire...

    Ce faisant, j'ai vu ici un exemple de ce que tu cherches à faire, et qui fonctionne chez moi (voir le dernier code de la page):

    http://stackoverflow.com/questions/4...roblem-in-pyqt.
    Merci tyrtamos, j'avais bien vu ce thread sur stackoverflow mais il me semblait être le même que le mien donc je l'avais expressément rejeté. Après ton message, je l'ai finalement repris et utilisé avec un QByteArray() en lieu et place des entiers.

    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
    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
     
    import sys
    from PyQt4.QtCore import *
    from PyQt4.QtGui import *
    import time
     
    class MyThread(QThread):
        def __init__(self, name):
            super(MyThread, self).__init__()
            self.setObjectName(name)
     
        def run(self):
            print("RUN", QThread.currentThread().objectName(), QApplication.instance().thread().objectName())
            self.exec_()
            print("RUN DONE", QThread.currentThread().objectName())
     
    class Producer(QObject):
        def __init__(self, parent=None):
            super(Producer, self).__init__(parent)
            self.array = QByteArray()
     
        def Start(self):
            for i in range(5):
                #self.array.clear()
                self.array.fill("X", i)
                print("Producer", QThread.currentThread().objectName())
                print("Producer: array type: {}, id: {} , size: {}".format(type(self.array), id(self.array), self.array.size()))            
                self.emit(SIGNAL("testsignal"), self.array)
                #time.sleep(2) # <--- résultat différent si on comment ou décommente 
                print("Producer [after emit()], array type: {}, id: {} , size: {}".format(type(self.array), id(self.array), self.array.size()))
                #time.sleep(2) 
     
            time.sleep(1)
            qApp.quit()
     
    class Consumer(QObject):
        def __init__(self, parent=None):
            super(Consumer, self).__init__(parent)
     
        def Consume(self, array):
            print("Consumer", QThread.currentThread().objectName())
            print("Consumer: array type: {}, id: {} , size: {}\n".format(type(array), id(array), array.size()))        
            array.clear()
            print("Consumer [after clear()]: array type: {}, id: {} , size: {}\n".format(type(array), id(array), array.size()))
     
     
    if __name__ == "__main__":
        app = QApplication([])
        producer = Producer()
        consumer = Consumer()
        QThread.currentThread().setObjectName("MAIN")
        producerThread = MyThread("producer")
        consumerThread = MyThread("consumer")
        producer.moveToThread(producerThread)
        consumer.moveToThread(consumerThread)
        producerThread.started.connect(producer.Start)
        producer.connect(producer, SIGNAL("testsignal"), consumer.Consume)
        def aboutToQuit():
            producerThread.quit()
            consumerThread.quit()
            time.sleep(1)
        qApp.aboutToQuit.connect(aboutToQuit)
        consumerThread.start()
        time.sleep(.1)
        producerThread.start()
        sys.exit(app.exec_())
    - Là, 1er point différent, je reçois effectivement le même objet (même id()). Ceci dit, je ne vois pas pourquoi dans mon code, je reçois une copie de l'objet (id de l'objet différent entre le producteur et le consommateur)... question à creuser à plus avant.

    - Par contre, tel quel, ça ne fonctionne pas avec mon QByteArray(), toute modification de cet objet dans le consumer n'est pas répercutée dans le producer.

    Je cherche donc dans la doc, peut être que c'est un problème au niveau signal/slot et de savoir si ceux-ci sont synchrones ou asynchrones. Et là, révélation. \o/

    http://doc.qt.nokia.com/latest/qt.ht...ctionType-enum

    (default) If the signal is emitted from a different thread than the receiving object, the signal is queued, behaving as Qt::QueuedConnection. Otherwise, the slot is invoked directly, behaving as Qt:irectConnection. The type of connection is determined when the signal is emitted.
    Entre deux threads, par défaut, la transmission signal/slot est asynchrone puisque le signal peut être mis en queue. Donc, si je comprend bien emit() peut retourner avant que le slot ait été effectivement appelé ou que le slot ait fini l'exécution de la totalité de son code. Du coup ça change complètement le fonctionnement du code.

    En résumé donc: il suffit de passer le connect à "BlockingQueuedConnection" pour que ça fonctionne !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
        producer.connect(producer, SIGNAL("testsignal"), consumer.Consume, Qt.BlockingQueuedConnection)
    Voilà, j'ai appris un nouveau truc aujourd'hui!

    Bon ceci dit, je ne sais toujours pas pourquoi dans mon code départ, j'avais des id() différents, mais je vais continuer à creuser. Merci encore à tyrtamos pour son aide, ce qui m'aura permis de creuser un peu plus loin et de finalement trouver une solution

    Merci encore

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

Discussions similaires

  1. Signal et Slot - Passage d'argument(s)
    Par Dokare dans le forum Boost
    Réponses: 5
    Dernier message: 29/10/2014, 20h33
  2. Passage de paramètre signal/slot
    Par Mic75 dans le forum Qt
    Réponses: 20
    Dernier message: 02/10/2008, 11h09
  3. [TASM] Passage d'argument à une macro
    Par sorry60 dans le forum Assembleur
    Réponses: 13
    Dernier message: 23/04/2005, 18h22
  4. [web] passage d'arguments à un CGI
    Par ma2th dans le forum Web
    Réponses: 4
    Dernier message: 20/08/2004, 12h18
  5. passage d'argument à la procédure main ()
    Par Zazeglu dans le forum C
    Réponses: 5
    Dernier message: 01/09/2003, 19h59

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