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

wxPython Discussion :

wx.ListCtrl virtuelle et base de données


Sujet :

wxPython

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Février 2008
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 45
    Par défaut wx.ListCtrl virtuelle et base de données
    Bonjour (ou plutôt bonsoir ^^)

    J'ai créé une wx.ListCtrl virtuelle de manière à libérer de la mémoire, et que je souhaite synchroniser avec une base de données sous Elixir (donc sous SQLAlchemy). Le tout fonctionne, c'est-à-dire que les résultats s'affichent proprement dans ma ListCtrl, mais mon problème, c'est que pour seulement 4 résultats, la liste n'est déjà plus fluide (c'est très léger, mais je constate quand même ce léger ralentissement et cela peut être génant pour une liste qui peut contenir quelques milliers de résultats, à terme).

    Voici tout d'abord à quoi ressemble ma classe de base de données :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    class Mouvement(Entity):
        nombre = Field(Float(1))
        date = Field(Integer()) #J'utilise un timestamp mais je ne veux pas
                                       #utiliser des objets datetime puisque j'utilise
                                       # les wx.DateTime fournis avec wxPython
        description = Field(Unicode())
        action = ManyToOne('Action')
     
    class Action(Entity):
        nom = Field(Unicode())
        mouvements = OneToMany('Mouvement')

    Et voici à quoi ressemble ma liste virtuelle :
    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
     
    class HistoriqueMouvement(wx.ListCtrl):
        def __init__(self, parent, id):
     
            wx.ListCtrl.__init__(self, parent, id, size=parent.GetClientSize(), style=wx.LC_REPORT|wx.LC_VIRTUAL|wx.LC_SINGLE_SEL)
            self.InsertColumn(0, u"Nom de l'action")
            self.InsertColumn(1, u"Nombre")
            self.InsertColumn(2, u"Date du mouvement")
            self.InsertColumn(3, u"Description")
     
            self.itemid = [mvmt.id for mvmt in Mouvement.query.all()]
            self.SetItemCount(len(self.itemid))
     
     
        def OnGetItemText(self, row, column):
     
            id = self.itemid[row]
            mvmt = Mouvement.query.filter_by(id=id).one()
            if column==0:
                itm = mvmt.action.nom
            elif column==1:
                itm = mvmt.nombre
            elif column==2:
                itm = wx.DateTime().SetTimeT(mvmt.date).FormatDate()
            elif column==3:
                itm = mvmt.description
     
            return itm
    Je précise que la liste self.itemid permet de retrouver les mouvements par leur id. On saura ainsi qu'à la ligne <row>, l'ID du mouvement à récupérer sera donnée par self.itemid[row].

    Le tout est peut-être un peu maladroit, mais j'espère que c'est quand même correct. Ce n'est jamais évident d'apprendre en autodidacte sans avoir jamais reçu de cours de programmation
    L'utilisation de self.itemid est peut être aussi un peu ambigue, mais je n'ai pas trouvé mieux pour pouvoir établir une correspondance entre les lignes de la ListCtrl et les objets de la BDD.

    Y aurait-il un moyen d'optimiser ce fonctionnement de synchronisation entre la ListCtrl et la BDD ?

    # -----------------------------------------------------------------------#

    J'aurai également d'autres questions S.V.P, si vous avez le temps, merci.
    1) Dans quel cas utiliser une ListCtrl virtuelle plutôt qu'une liste réelle ? Par exemple, dans ce cas-là, mon choix d'utiliser une liste virtuelle est-il bon ?

    2) Lorsque je fais une requête : Mouvement.query.all(), le programme éxécute un SELECT et me renvoie une liste d'instances. Maintenant, lorsque je récupère l'attribut "nom" d'une instance, le programme éxécute-t-il aussi une requête "SELECT nom" ? Ou est-ce qu'il le récupère simplement auprès de l'instance ?


    Merci beaucoup d'avance

    Lotendan

  2. #2
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 746
    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 746
    Par défaut questions simples
    Salut

    La question simple:

    2) Lorsque je fais une requête : Mouvement.query.all(), le programme éxécute un SELECT et me renvoie une liste d'instances. Maintenant, lorsque je récupère l'attribut "nom" d'une instance, le programme éxécute-t-il aussi une requête "SELECT nom" ? Ou est-ce qu'il le récupère simplement auprès de l'instance ?
    Lorsque vous avez des questions de ce genre le plus simple est de regarder les requetes SQL qui sont expédiées:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    metadata.bind.echo = True
    On verra plus tard pour:
    1) Dans quel cas utiliser une ListCtrl virtuelle plutôt qu'une liste réelle ? Par exemple, dans ce cas-là, mon choix d'utiliser une liste virtuelle est-il bon ?
    Le soucis étant: est on sur de ce qu'on veut faire...
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  3. #3
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 746
    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 746
    Par défaut problème de fond
    Re.
    D'après ce que j'ai compris:
    - l'écran ne peut afficher qu'un nombre fini N de lignes/row "mouvements",
    - il y a une règle (pas très nette) pour associer le numéro de ligne à la row correspondante de la table,

    Techniquement, c'est un problème de pagination l'écran doit afficher l'intervalle de lignes [X, X+N], et on peut récupérer les rows correspondantes via Mouvement.query.offset(X).limit(N).all().

    Entre ce qui est/sera affiché sur l'écran et les lectures faites en base, on peut faire ce qu'on veut (ou peut) mais il faut bien séparer ces deux points de vues (quitte à fusionner côté réalisation).

    Dans la pratique, le "OnGetItemText" - l'utlisateur veut voir les infos associées à la ligne - ne doit pas travailler directement avec Mouvement - la table - mais avec un "Proxy" qui anticipe et décorèlle l'affichage des E/S disque.

    En gros: mvmt = proxy.get_id(id)
    • retourne l'information immédiatement si elle a été chargée
    • maintient (par exemple dans un dict) une "page" de rows lues par des requêtes paginées .offset().limit()
    Note: Dans un premier temps X et X+N peuvent simplement correspondre aux lignes affichées.

    Après avoir lu, compris et vérifié que ce que je raconte correspond à peut près à ce que vous voulez faire...
    Il faudra trouver comment réaliser cela avec VxWidgets. Les GUI apportent (en général) une sorte de MVC permettant d'emboiter facilement les deux extrémités (collées par le proxy).

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

  4. #4
    Membre averti
    Profil pro
    Inscrit en
    Février 2008
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 45
    Par défaut
    Salut wiztricks,

    2) Lorsque je fais une requête : Mouvement.query.all(), le programme éxécute un SELECT et me renvoie une liste d'instances. Maintenant, lorsque je récupère l'attribut "nom" d'une instance, le programme éxécute-t-il aussi une requête "SELECT nom" ? Ou est-ce qu'il le récupère simplement auprès de l'instance ?
    Lorsque vous avez des questions de ce genre le plus simple est de regarder les requetes SQL qui sont expédiées:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    metadata.bind.echo = True
    Vous avez raison, j'aurai du y penser !
    J'ai donc réalise mes tests et la réponse est que le programme ne fait qu'une requête SQL et renvoie l'instance de la classe, auprès de laquelle on peut récupérer autant d'attributs que l'on souhaite sans avoir besoin d'effectuer d'autres requêtes SQL.

    Il faudra trouver comment réaliser cela avec VxWidgets. Les GUI apportent (en général) une sorte de MVC permettant d'emboiter facilement les deux extrémités (collées par le proxy).
    Si j'ai bien compris, il faut mettre en mémoire les lignes de lintrrvalle [X, X+N+1] (je met X+N+1 pour anticiper l'action de l'utilisateur lorsqu'il descendra ou remontera l'ascenseur de la ListCtrl)
    J'ai donc recherché sur la documentation en ligne de wxWidgets un moyen de le faire et je pense que ceci est possible en dérivant la classe wxStreamBase (http://docs.wxwidgets.org/stable/wx_...l#wxstreambase) et en surchargeant les trois méthodes suivantes :
    size_t OnSysRead(void* buffer, size_t bufsize)
    Internal function. It is called when the stream wants to read data of the specified size. It should return the size that was actually read.

    wxStreamBase::OnSysSeek
    off_t OnSysSeek(off_t pos, wxSeekMode mode)
    Internal function. It is called when the stream needs to change the current position.

    wxStreamBase::OnSysTell
    off_t OnSysTell() const
    Internal function. Is is called when the stream needs to know the real position.
    Je pense pouvoir arriver a surcharger les deux dernières fonctions sans trop de problèmes. Par contre, je ne comprends pas la première fonction :
    - buffer est de type void*, mais que signifie cette variable ?
    - bufsize est une taille. Cela fonctionnera-t-il si je réalise ma fonction de telle façon que bufsize = N (nombre de lignes a afficher) ? Ou est ce forcément une taille en octets voire en bits ?

    Par la suite, il me suffira d'encapsuler wxStreamBase dans un wxStreamBuffer, et le reste se fera tout seul.

    Qu'en pensez-vous ?

    Merci,*

    Lotendan

  5. #5
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 746
    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 746
    Par défaut
    Salut,
    Je ne connais pas WxWidgets mais comme tout GUI normal il doit savoir faire du MVC d'une façon ou d'une autre.
    Quelques vagues recherches sur Google semblent dire que Wx promeut un style View/Document, qui me semblerait faire l'affaire. Mais je n'ai pas le temps de jouer avec pour me faire une opinion.
    Désolé,
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  6. #6
    Membre averti
    Profil pro
    Inscrit en
    Février 2008
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 45
    Par défaut
    Salut,

    Tout d'abord merci de vos réponses rapides.

    Quelques vagues recherches sur Google semblent dire que Wx promeut un style View/Document, qui me semblerait faire l'affaire.
    Je me suis intéressé à ces deux styles mais, malheureusement, ils ne conviennent pas pour ce que je veux faire. Ensemble, ils servent à automatiser la création/édition de documents quelconques en fournissant un menu avec les commandes "Nouveau", "Ouvrir ...", "Enregistrer", "Enregistrer sous", etc ..., de détecter les changements et de proposer une sauvegarde, et pour le style wxView à créer une nouvelle fenêtre d'édition comme le ferait Word, Excel, ou autres ...

    Je ne connais pas WxWidgets mais comme tout GUI normal il doit savoir faire du MVC d'une façon ou d'une autre.
    J'ai fait des recherches à propos du MVC dont vous me dites du bien J'ai du mal à comprendre son fonctionnement, et dans ce que j'ai compris, je ne crois pas qu'il pourrait être utile pour optimiser la synchronisation de ma liste avec la base de données. Ceci dit, merci quand même !

    J'ai donc recherché sur la documentation en ligne de wxWidgets un moyen de le faire et je pense que ceci est possible en dérivant la classe wxStreamBase (http://docs.wxwidgets.org/stable/wx_...l#wxstreambase) et en surchargeant les trois méthodes suivantes :

    size_t OnSysRead(void* buffer, size_t bufsize)
    Internal function. It is called when the stream wants to read data of the specified size. It should return the size that was actually read.

    wxStreamBase::OnSysSeek
    off_t OnSysSeek(off_t pos, wxSeekMode mode)
    Internal function. It is called when the stream needs to change the current position.

    wxStreamBase::OnSysTell
    off_t OnSysTell() const
    Internal function. Is is called when the stream needs to know the real position.
    Malheureusement pour moi, après avoir terminé mon code, l'interpréteur me disait que la classe wxStreamBase n'existe pas. Celle-ci n'est pas inclue dans wxPython mais seulement dans wxWidgets (qui à la base est fait pour C++). wxPython inclut lui une classe Buffer qui est très pauvre et qui me semble inadaptée pour ce que je veux en faire.

    En gros: mvmt = proxy.get_id(id)
    - retourne l'information immédiatement si elle a été chargée
    - maintient (par exemple dans un dict) une "page" de rows lues par des requêtes paginées .offset().limit()
    Note: Dans un premier temps X et X+N peuvent simplement correspondre aux lignes affichées.
    Ceci, en revanche, m'intéresse beaucoup !

    Je m'en vais de ce pas écrire une fonction mémoire et stocker mes données dans un dictionnaire. Je reviens dès que j'ai terminé !

    Merci,

    Lotendan

  7. #7
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 746
    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 746
    Par défaut
    Salut,
    Le proxy sera très bien.
    Si votre fonction de mapping row -> record reste telle que décrite, une liste devrait suffire (et permettrait d'utiliser le retour du query plus directement).
    Bon courage
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  8. #8
    Membre averti
    Profil pro
    Inscrit en
    Février 2008
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 45
    Par défaut
    Bonsoir wiztricks,

    Comme je le disais, wxWidgets fournit une classe Buffer, mais ce n'est pas le cas de wxPython. J'ai donc décidé de faire mon propre buffer sous forme d'un dictionnaire comme vous me l'avez conseillé.

    Voici la bête : :p

    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
     
    def OnGetItemText(self, row, column):
     
            if row in range(top, top+self.GetCountPerPage()+1):
                id = self.itemid[row]
                mvmt = self.buffer.GetMouvement(id)
     
                if column==0:
                itm = mvmt.action.nom
                elif column==1:
                    itm = mvmt.nombre
                elif column==2:
                    itm = wx.DateTime().SetTimeT(mvmt.date).FormatDate()
                elif column==3:
                    itm = mvmt.description
     
                return itm
            else:
                return u"Chargement..." 
     
    ############################BUFFER###################
     
    class MyBuffer:
        def __init__(self, parent):
            self.parent = parent
            self.dic = {}
     
        def GetMouvement(self, id):
            if self.dic.has_key(id):
                return self.dic[id]
            else:
                self.Read(self.parent.GetCountPerPage()+1) #Nombre d'items visibles
                return self.dic[id]
     
        def Read(self, size):
            self.dic = {}
            mouvements = Mouvement.query.offset(self.parent.GetTopItem()).limit(size).all()
            for mvmt in mouvements:
                self.dic[mvmt.id] = mvmt
    Je pense que cela convient.
    Merci de m'avoir aidé !
    Si vous constatez quelque chose qui ne va pas, merci de me le faire savoir

    Lotendan

Discussions similaires

  1. Réponses: 0
    Dernier message: 13/02/2013, 17h38
  2. [MLD] Base de données pour Panier Virtuel
    Par mikael2235 dans le forum Schéma
    Réponses: 7
    Dernier message: 26/08/2009, 11h44
  3. Base de données virtuelles
    Par Breizhim dans le forum Oracle
    Réponses: 6
    Dernier message: 16/02/2009, 14h30
  4. Réponses: 0
    Dernier message: 23/06/2008, 16h26
  5. Réponses: 1
    Dernier message: 22/12/2006, 09h26

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