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 :

Python App loop sur la fct rowcount d'un listview PyQt5


Sujet :

PyQt Python

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Inscrit en
    Janvier 2010
    Messages
    29
    Détails du profil
    Informations forums :
    Inscription : Janvier 2010
    Messages : 29
    Par défaut Python App loop sur la fct rowcount d'un listview PyQt5
    Bonjour à tous,

    je suis un peu désespéré après des heures de recherches et de tentatives pour comprendre ce qu'il se passe dans mon code !

    Je développe une app en python 3 avec un gui à base de Pyqt5.

    Mon problème se trouve au niveau d'un QListView que je rempli à l'aide d'un modèle.

    J'ai trouvé des exemples, et tout fonctionnait correctement. Mais depuis peu, lorsque ma fenêtre s'ouvre, le programme boucle sur la fonction rowCount du modèle de mon listView et j'obtiens l'erreur : "maximum recursion depth exceeded while calling a Python object"

    J'ai fini par trouver une ligne qui posait problème dans la fonction data() du modèle du listView :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    serial = index.data().split(" ")[1]
    Le problème se trouve au niveau de index.data() qui lorsque elle commentée permet à l'appli de tourner correctement. Or cette partie est essentielle pour mon code.

    Je vous laisse l'intégralité du code si quelqu'un voit le soucis, merci d'avance !!!

    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
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
     
    fichier 1 : 
     
    from datetime import datetime
    from PyQt5 import QtCore
    from PyQt5.QtWidgets import QMessageBox
    from PyQt5.QtCore import Qt
    import json
    import pandas as pd
     
    class ListPumpModel(QtCore.QAbstractListModel):
        def __init__(self, pumps, types, *args, **kwargs, ):
            super(ListPumpModel, self).__init__(*args, **kwargs)
            self.pumps = pumps
            self.types = types
     
        def data(self, index, role):
            if role == Qt.DisplayRole:
                serial = index.data().split(" ")[1]
                pump = next(pump for pump in self.datas.pumps if pump.serial == serial)           
                return self.types[str(pump.type)] + " " + pump.serial
     
        def rowCount(self, index):
            return len(self.pumps)
     
    class Pump:
     
        def __init__(self, id, loc, type, serial, pms, lochistory):
            """
            init expect : pump type,
            """
            self.id = id
            self.localisation = loc
            self.type = type
            self.serial = serial
            self.pm=[PM(pm) for pm in pms]
            self.lochistory = lochistory
     
        def __str__(self):
            return self.type + " " + self.serial
     
    fichier 2 : 
    from PyQt5.QtWidgets import QMainWindow
    from PyQt5 import QtCore, QtGui
    from PyQt5.uic import loadUi
    from model import ListPumpModel, messageBox, Pump
    from datetime import date, datetime
     
    class PumpWindow(QMainWindow):
        """Main Window."""
        def __init__(self, mainWin, parent=None):
            """Initializer."""
            super(PumpWindow, self).__init__(parent)
            loadUi("pump_manager.ui", self)
            self.datas = mainWin.datas
            self.actionToken = ""
     
            self.initWidgets()       
            self.show()
     
        def initWidgets(self):
            self.entry = QtGui.QStandardItemModel()
            self.loclabel.hide()
            self.loclist.hide()
            self.removelocbutton.hide()
            self.pmlabel.hide()
            self.pmlist.hide()
            self.cancelbutton.hide()
            self.addbutton.clicked.connect(self.addpumpclicked)
            self.cancelbutton.clicked.connect(self.cancelclicked)
            self.validatebutton.clicked.connect(self.validateclicked)
            self.listmodel = ListPumpModel(self.datas.pumps, self.datas.types)
            self.pumplist.setModel(self.listmodel)
            self.pumplist.selectionModel().currentChanged.connect(self.pumpselected)
     
        def pumpselected(self, index):
            if index.data() != None:
                self.showpump(index)       
            else:
                self.deletebutton.setEnabled(False)
                self.validatebutton.setEnabled(False)
                self.comboloc.setCurrentIndex(0)
                self.combotype.setCurrentIndex(0)
                self.lineserial.setText("")

  2. #2
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 801
    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 801
    Billets dans le blog
    1
    Par défaut
    Bonjour
    Citation Envoyé par foxnono06 Voir le message
    Mais depuis peu...
    Ah? Et qu'as tu fait entre "avant" et "après" ce "peu"???

    Citation Envoyé par foxnono06 Voir le message
    Je vous laisse l'intégralité du code
    Non, ce n'est pas l'intégralité du code. Manque les import et le lancement principal (probablement PumpWindow(sys.argv).exec_()) mais aussi l'appel de ListPumpModel. Manque aussi le ".ui". Bref manque tout ce qui permet de tester.
    Accessoirement quand on a une politique de nommage on s'y tient. On peut pas d'un côté spécifier QtCore.QAbstractListModel (ce qui signifie qu'on a importé QtCore via un from PyQt5 import QtCore) et de l'autre spécifier QMainWindow sans préciser son appartenance à QtWidgets ce qui signifie qu'on a importé QtWidgets via un from PyQt5.QtWidgets import *.
    Autres détails: on évite des noms de variable comme "id" ou "type" qui sont aussi des fonctions Python. On n'est pas obligé de spécifier le nom de la classe et "self" dans super(). On peut éviter le for pm in pms: self.pm.append(PM(pm)) via une simple liste en intension (self.pm=[PM(pm) for pm in pms] et nommer ses variables "pm" et "pms" ne rend pas non plus la compréhension aisée et self.type + " " + self.serial s'écrira plus simplement "{0} {1}".format(self.type, self.serial).
    Et on ne teste pas None avec "==" ou "!=".

    Citation Envoyé par foxnono06 Voir le message
    Le problème se trouve au niveau de index.data() qui lorsque elle commentée permet à l'appli de tourner correctement. Or cette partie est essentielle pour mon code.
    Faudrait vérifier type(index) voir ce qui se passe. Car il semblerait que "index" soit de type "ListPumpModel" ce qui fait que l'appel à index.data() appelle la méthode ListPumpModel.data() dans laquelle tu te trouves ce qui amène une récursion infinie.
    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]

  3. #3
    Membre averti
    Inscrit en
    Janvier 2010
    Messages
    29
    Détails du profil
    Informations forums :
    Inscription : Janvier 2010
    Messages : 29
    Par défaut
    Bonjour Sve@r,

    J'imagine qu'il y a certaines incohérences dans mon code je suis développeur à loisir
    Cela ne l'empêche pas de fonctionner, mais je prend note de tes remarques, c'est constructif.

    J'ai fait un print du type de Index :<class 'PyQt5.QtCore.QModelIndex'>

    Pour le lancement, c'est une fenêtre appelée par une autre fenêtre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    def access_windows_pump(self):
            self.pumpwin = PumpWindow(self)
    Pour les import, je vais mettre à jour le code mais à part un problème de cohérence de niveau d'accès, je ne vois pas de problème de ce côté.

    J'ai corrigé de mon côté vos remarques.

    Merci d'avance

  4. #4
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 801
    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 801
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par foxnono06 Voir le message
    J'ai fait un print du type de Index :<class 'PyQt5.QtCore.QModelIndex'>
    Ok. C'est pas ça.
    Autre remarque: la méthode data() existe déjà dans QAbstractListModel donc en la réécrivant, tu en fais une surcharge.
    Or toute surcharge doit obligatoirement appeler la méthode d'origine pour bénéficier de son travail (on appelle ça la "délégation" et celle-ci n'est pas implicite en Python).
    Bref il faut que dans la méthode data() tu appelles quelque part (à priori au début) super().data(index, role). Chaque fois que tu surcharges une méthode existante il faut le faire (comme tu le fais d'ailleurs déjà pour __init__() qui est aussi une surcharge).

    Citation Envoyé par foxnono06 Voir le message
    Pour le lancement, c'est une fenêtre appelée par une autre fenêtre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     def access_windows_pump(self):
            self.pumpwin = PumpWindow(self)
    Mouais. Mais comme tu lui passes en argument un truc que tu stockes dans le paramètre "mainWin" et qu'ensuite tu en extrais ses datas, je peux toujours pas le reproduire (je n'ai pas de mon côté ce mainWin). En apparté, j'espère que ce n'est pas un QMainWindow car passer un QMainWindow en paramètre à un autre QMainWindow...

    Accessoirement j'étais heureux de voir la classe ListPumpModel définir son init ainsi def __init__(self, pumps, types, *args, **kwargs) puis appeler super().__init__(*args, **kwargs). Je me suis dit "tiens, quelqu'un qui a pigé le souci de l'héritage et de son évolution possible". Et ensuite je regarde plus bas et je vois, pour la classe PumpWindow, écrit ceci: def __init__(self, mainWin, parent=None) puis super().__init__(parent) et là je me dis "bof bof"...
    Il n'y a pas que le parent qui est utile pour l'ancètre. La première façon d'écrire était la bonne, elle te permet de lui passer tout ce dont il a besoin sans que tu aies à te préoccuper de ce dont il s'agit. C'est expliqué ici dans mon tuto et tu en as un exemple concret ici où je créer deux objets persos héritant chacun d'un objet Qt existant.
    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]

  5. #5
    Membre averti
    Inscrit en
    Janvier 2010
    Messages
    29
    Détails du profil
    Informations forums :
    Inscription : Janvier 2010
    Messages : 29
    Par défaut
    Je te remercie pour tes remarques. Je vais me pencher dessus, il y a certains concepts que je n'ai pas bien absorber je pense.
    En attendant j'ai créer un github avec le projet, comme ça il y a tout :

    https://github.com/anselmia/pump

    Merci encore.

  6. #6
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 801
    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 801
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par foxnono06 Voir le message
    En attendant j'ai créer un github avec le projet, comme ça il y a tout :

    https://github.com/anselmia/pump
    Ok, je l'ai récupéré.

    Citation Envoyé par foxnono06 Voir le message
    Le problème se trouve au niveau de index.data() qui lorsque elle commentée permet à l'appli de tourner correctement.
    Ce n'est pas tout à fait exact. Quand cette ligne est commentée (et effectivement elle l'est dans ton dernier commit git), la ligne suivante pump = next(pump for pump in self.datas.pumps if pump.serial == 0) est alors en erreur car l'attribut "datas" n'existe pas dans l'objet (et effectivement je ne le vois nulle part). Il faut lire les messages d'erreur de la console.
    A mon avis, c'est pas self.datas.pumps mais self.pumps.

    Pour le reste je sèche devant ta façon de gérer tes fenêtres. Tu as mis PumpWindow en tant que MainWindow sans que je m'en explique la raison. Tu show() ta PumpWindow dès sa création (normalement on laisse l'appelant choisir quand l'afficher). Toutefois il me semble arriver à percevoir "un peu" ce qui se passe.
    Voici ce que j'ai tenté
    • dans le main.py, j'ai déplacé self.pumpwin = PumpWindow(self) dans le __init__() et dans le access_window_pump() j'ai écrit self.pumpwin.show().
    • dans le pumps_window.py j'ai bien évidemment enlevé le self.show() du __init__() puisque c'est l'appelant qui affiche le PumpWindow
    • dans le model.py j'ai commenté toute référence à index.data()

    Hé bien le souci apparait quand-même. J'ai quelque part l'impression que la récursion est due à cette fenêtre PumpWindow qui est appelée sans cesse, probablement via le self.actionManage.triggered.connect(self.access_windows_pump).

    Accessoirement, pour tout ce qui concerne héritage d'objets Qt, voici comment il faut les écrire
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    class monObjet(nom_objet_Qt_hérité):
    	def __init__(self, ... (les éléments dont tu as besoin) ..., *args, **kwargs):
    		super().__init__(*args, **kwargs)
    		... (la suite de ton init) ...
    Et de supprimer ce parent=None qui est alors inclus dans *args et **kwargs lequel inclus aussi tous les autres éléments éventuellement nécessaires à l'objet Qt dont tu hérites.

    Je te recommande d'aller chercher et regarder mes exemples PyQt qui commencent ici ; et quand tu auras acquis une certaine aisance, de regarder plus particulièrement cet exemple qui montre comment ouvrir une sous-fenêtre sur un menu. Parce que quelque part j'ai l'impression que c'est là que ça pêche, ton PumpWindow étant réappelé à l'infini.
    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]

  7. #7
    Membre averti
    Inscrit en
    Janvier 2010
    Messages
    29
    Détails du profil
    Informations forums :
    Inscription : Janvier 2010
    Messages : 29
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Mouais. Mais comme tu lui passes en argument un truc que tu stockes dans le paramètre "mainWin" et qu'ensuite tu en extrais ses datas, je peux toujours pas le reproduire (je n'ai pas de mon côté ce mainWin). En apparté, j'espère que ce n'est pas un QMainWindow car passer un QMainWindow en paramètre à un autre QMainWindow...
    Et si c'est bien un QMainWindow ! Mais je ne savais pas comment mettre à jour une instance de classe que j'avais créé dans le premier et dont j'ai besoin dans la deuxième, et que je dois retourner à la première. :/

  8. #8
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 801
    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 801
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par foxnono06 Voir le message
    Et si c'est bien un QMainWindow ! Mais je ne savais pas comment mettre à jour une instance de classe que j'avais créé dans le premier et dont j'ai besoin dans la deuxième, et que je dois retourner à la première. :/
    Tu crées ton QMainWindow en dehors et tu passes simplement son instance aux deux objets
    Comme un objet n'est copié que par référence, les objets auront tous deux accès au QMainWindow originel
    Exemple
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class myToto:
    	def __init__(self, mainWid): self.__mainWid=mainWid
     
    class myTiti:
    	def __init__(self, mainWid): self.__mainWid=mainWid
     
    mainWid=QMainWindow()
    toto=myToto(mainWid)
    titi=myTiti(mainWid)

    Un exemple plus complet ici où je montre comment faire en sorte que les widgets enfant connaissent le mainWindow.
    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]

Discussions similaires

  1. Loop sur existence d'un fichier
    Par fabienfoo dans le forum ASP.NET
    Réponses: 9
    Dernier message: 02/10/2008, 15h08
  2. Migrer une app desktop sur mobile
    Par 123quatre dans le forum Développement Mobile en Java
    Réponses: 0
    Dernier message: 21/09/2008, 19h06
  3. [VBA-E] - Loop sur userform
    Par Qatari dans le forum Macros et VBA Excel
    Réponses: 12
    Dernier message: 26/03/2007, 14h00
  4. Pb sur une fct qui marche sous mozilla mais pas ie
    Par chpog dans le forum Général JavaScript
    Réponses: 2
    Dernier message: 21/09/2005, 11h26
  5. loop sur tableau associatif
    Par Plawi dans le forum Général JavaScript
    Réponses: 6
    Dernier message: 28/02/2005, 11h29

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