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 :

Contrôle en temps réel connexion avec base de données


Sujet :

PyQt Python

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    Homme Profil pro
    Touche à tout
    Inscrit en
    Mai 2017
    Messages
    479
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Allier (Auvergne)

    Informations professionnelles :
    Activité : Touche à tout

    Informations forums :
    Inscription : Mai 2017
    Messages : 479
    Par défaut Contrôle en temps réel connexion avec base de données
    Bonjour,

    Je souhaite contrôler en temps réel la connexion avec ma base de données locale. Je ne sais pas de quelle manière procéder, je ne voudrais pas me retrouver avec une interface graphique "freezée" ou bien une surcharge processeur trop importante...

    Merci par avance pour vos retours.

    :-)

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

    Citation Envoyé par Supernatural Voir le message
    Je souhaite contrôler en temps réel la connexion avec ma base de données locale. Je ne sais pas de quelle manière procéder, je ne voudrais pas me retrouver avec une interface graphique "freezée" ou bien une surcharge processeur trop importante...
    Si vous ouvrez une connexion au démarrage de l'application et qu'elle se plante durant la session, vous allez avoir une erreur à la requête suivante qui suffit de gérer.
    Après si la base de données est "locale" (i.e. sur le même ordinateur), probable que la connexion se plante lorsque l'ordinateur se plante (mais dans ce cas là, l'application est morte aussi).

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

  3. #3
    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,

    Un QTableView avec le bon modèle et le bon pilote pour le SGBDR, pourra afficher une table normale de la base de données ou même une table temporaire crée par une requête.

    Une modification des données saisies par le graphique (affichage => base de données) pourra mettre à jour la base de données (automatiquement en fin de saisie ou par un clic de souris sur un bouton: ça se configure).

    Dans l'autre sens (base de données => affichage), c'est moins clair: je ne crois pas qu'une modification de la base par un autre client relance automatiquement l'affichage mais on peut toujours relancer cet affichage (="peupler") périodiquement avec un timer (toutes les secondes par exemple déclenché pendant les périodes d'attente).

    Dernier point: si la base de données doit être modifiée par plusieurs clients simultanément, on ne peut pas utiliser sqlite3 puisque ce n'est pas un serveur. Il faudrait dans ce cas prendre un SGBDR dans un serveur comme mysql ou postgre. Et il faudra veiller à ce que les modifications se fasse sous forme de transactions afin qu'une interruption en cours de travail ne casse pas l'intégrité de la base de données.

  4. #4
    Membre éclairé
    Homme Profil pro
    Touche à tout
    Inscrit en
    Mai 2017
    Messages
    479
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Allier (Auvergne)

    Informations professionnelles :
    Activité : Touche à tout

    Informations forums :
    Inscription : Mai 2017
    Messages : 479
    Par défaut
    Merci à vous deux pour vos réponses.

    @tyrtamos, c'est très instructif ce que tu viens de dire :-) mais aurais-tu des exemples plus concret s'il te plaît?

    J'essaye juste de tester si ma base de données locale existe mais je n'y arrive pas... c'est incompréhensible ça... je tourne en rond...

    Je fais cette procédure, mais à la place de me dire que ma base données n'est pas présente, l'application me la créé...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
        def dbConnexion(self):
            self.logger.log(logging.INFO,"Test database connexion")
            self.db = QSqlDatabase.addDatabase("QSQLITE")
            self.db.setDatabaseName("sql/test.db")
            if not self.db.open():
                print("open DB error.")
    Merci par avance.

  5. #5
    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,

    Citation Envoyé par Supernatural Voir le message
    @tyrtamos, c'est très instructif ce que tu viens de dire :-) mais aurais-tu des exemples plus concret s'il te plaît?
    Désolé pour le délai, mais j'ai essayé de faire un exemple assez général mais simple, et ça ne l'est pas!

    Je suis parti d'une base de données sqlite3 "mabase.db3" ne contenant qu'une seule table, avec 3 champs de type INTEGER, REAL et TEXT (en Python, int, float, et str). Il y a un 4ème type possible: BLOB pour des bytes (par exemple pour des images). Voilà la requête SQL pour la création de cette base:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    CREATE TABLE 'matable' (
                         num INTEGER,
                         nbf REAL,
                         mot TEXT
                         );
    Et voilà la base en question (à désarchiver): ===> mabase.zip

    Pour le code de test: s'il n'y avait que des types "TEXT" (str), ce serait très facile puisqu'il ne faudrait qu'un delegate standard (un "QSqlRelationalDelegate"). Mais comme il y a des entiers et des flottants, il faut qu'on puisse les modifier avec des widgets adaptés, en l'occurrence un QSpinBox pour les entiers et un QDoubleSpinBox pour les flottants. Ce qui a nécessité de créer un delegate personnalisé héritant de QSqlRelationalDelegate.

    Voilà le code de test:

    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
    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
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    #!/usr/bin/python3
    # -*- coding: utf-8 -*-
     
    import sys
    import os
     
    from PyQt5 import QtCore, QtGui, QtWidgets, QtSql
     
    #############################################################################
    class MonSqlRelationalDelegate(QtSql.QSqlRelationalDelegate):
     
        #========================================================================
        def __init__(self, parent=None):
            super().__init__(parent)
     
        #========================================================================
        def createEditor(self, parent, option, index):
            """Crée et initialise le widget d'édition
                 aParent: type 'PyQt5.QtWidgets.QWidget'
                 option: type 'PyQt5.QtWidgets.QStyleOptionViewItem'
                 index: type 'PyQt5.QtCore.QModelIndex'
               Retourne l'éditeur utilisé
            """
            if index.column() == 0: # => on est dans la colonne des entiers
                # crée le widget d'édition des entiers
                editor = QtWidgets.QSpinBox(parent)
                editor.setRange(-1000000, +1000000)
                # retourne le widget
                return editor       
     
            elif index.column() == 1: # => on est dans la colonne des flottants
                # crée le widget d'édition des flottants
                editor = QtWidgets.QDoubleSpinBox(parent)
                editor.setRange(-1000000.0, +1000000.0)
                editor.setDecimals(15)
                # retourne le widget
                return editor       
     
            else:
                # cas d'une autre colonne: on repasse la main à l'ancêtre
                return super().createEditor(parent, option, index)
     
        #========================================================================
        def setEditorData(self, editor, index):
            """Définit l'état du widget concerné à l'entrée du mode édition
                 editor: QtWidgets.QWidget
                 index: QtCore.QModelIndex
               Retour: None
            """
            if index.column() == 0: # => on est dans la colonne des entiers
                # récupère la valeur de la base de données
                valeur = index.model().data(index, QtCore.Qt.EditRole)
                # charge la valeur dans le widget avec le bon type
                editor.setValue(int(valeur))
     
            elif index.column() == 1: # => on est dans la colonne des flottants
                # récupère la valeur de la base de données
                valeur = index.model().data(index, QtCore.Qt.EditRole)
                # charge la valeur dans le widget avec le bon type
                editor.setValue(float(valeur))
     
            else:
                # cas d'une autre colonne: on repasse la main à l'ancêtre
                super().setEditorData(editor, index)
     
        #========================================================================
        def setModelData(self, editor, model, index):
            """ Enregistre la valeur modifiée dans la base de données 
                  editor: type 'QtWidgets.QWidget'
                  model: type 'QtCore.QAbstractItemModel'
                  index: type 'QtCore.QModelIndex'
                Retour: None
            """
            if index.column() == 0: # => on est dans la colonne des entiers
                #lit la valeur affichée dans le widget en fin d'édition
                valeur = editor.value()
                # enregistre dans le modèle la valeur affichée
                model.setData(index, str(valeur), QtCore.Qt.EditRole)
                return
     
            elif index.column() == 1: # => on est dans la colonne des entiers
                #lit la valeur affichée dans le widget en fin d'édition
                valeur = editor.value()
                # enregistre dans le modèle la valeur affichée
                model.setData(index, str(valeur), QtCore.Qt.EditRole)
                return
     
            else:
                # traite les autres cas
                super().setModelData(editor, model, index)
                return
     
    #############################################################################
    def ouvrebaseqt(basesql):
        """ouvre la base sqlite3 "basesql" sous PyQt5 et renvoie la connexion
           si échec, retourne None
        """
        driver = "QSQLITE"
        cnx = QtSql.QSqlDatabase.addDatabase(driver)
        cnx.setDatabaseName(basesql)
        if not cnx.open():
            cnx = None
        return cnx
     
    #############################################################################
    def fermebaseqt(cnx):
        """Ferme la base ouverte avec la connexion cnx de PyQt5
        """
        if cnx!=None:
            cnx.close()
     
    ##############################################################################
    class Fenetre(QtWidgets.QWidget):
     
        #=========================================================================
        def __init__(self, basesql, nomtable, parent=None):
            super().__init__(parent)
     
            self.setWindowTitle("Code test SQL")
            self.resize(800, 600)
     
            self.basesql = basesql # nom de la base de donnnées
            self.nomtable = nomtable # nom de la table à afficher
     
            # ouvre la base de données
            self.cnx = ouvrebaseqt(self.basesql)
            if self.cnx==None:
                QtWidgets.QMessageBox.critical(self, 
                    "Ouverture de la base de données", 
                    "Echec: base défaillante ou non trouvée")
                self.close()
                sys.exit()
     
            # crée le modèle
            self.model = QtSql.QSqlRelationalTableModel(self, self.cnx)
            # pour afficher la table demandée
            self.model.setTable(self.nomtable)
            # pour mise à jour de la base à chaque modification d'une valeur
            self.model.setEditStrategy(QtSql.QSqlTableModel.OnFieldChange)
     
            # crée la grille d'affichage
            self.vuetable = QtWidgets.QTableView(self)
            # lien avec le modèle
            self.vuetable.setModel(self.model)
            # active la possibilité de tri 
            self.vuetable.setSortingEnabled(True)
            # lien avec un delegate personnalisé pour l'édition
            self.vuetable.setItemDelegate(MonSqlRelationalDelegate(self.vuetable))
     
            # peuple le modèle avec les données de la table de la base de données
            self.vuetable.model().select()  
     
            # tri si nécessaire selon la colonne 0
            self.model.sort(0, QtCore.Qt.AscendingOrder) # ou DescendingOrder
     
            # positionne la grille d'affichage dans la fenêtre
            layout = QtWidgets.QGridLayout()
            layout.addWidget(self.vuetable, 0, 0)
            self.setLayout(layout)
     
        #=========================================================================
        def closeEvent(self, event):
     
            if self.cnx!=None:
                fermebaseqt(self.cnx)
            event.accept()
     
    ##############################################################################
    if __name__ == '__main__':
     
        app = QtWidgets.QApplication(sys.argv)
     
        basesql = os.path.abspath("mabase.db3")
        table = "matable"
     
        fen = Fenetre(basesql, table)
        fen.show()
     
        sys.exit(app.exec_())
    Sur cette base-là, il peut y avoir de très nombreux compléments:

    - Le type BOOLEAN, qui existe dans certains bases de données, n'existe pas dans sqlite3, mais on peut le créer avec des entiers (0=>False, 1=>True), et les afficher / modifier avec des QCheckBox. Voir ici une discussion sur le sujet: https://www.developpez.net/forums/d1...ge-qtableview/. On pourrait aussi créer un type Date avec un widget personnalisé pour n'éditer que des dates valides.

    - il faudrait, si c'est adapté, ajouter au code ci-dessus un "popupmenu" pour pouvoir à la demande ajouter des enregistrements nouveaux ou en supprimer.

    - on peut vouloir configurer plus finement les widgets d'édition pour limiter les valeurs acceptables. Par exemple une échelle de 0,00 à 100,0 pour un pourcentage avec un QDoubleSpinBox. On devrait même pouvoir utiliser un QValidator.

    - On peut vouloir afficher les widgets d'édition même en dehors de l'édition elle-même. Il faut alors ajouter une méthode "paint" au delegate personnalisé. On peut aussi vouloir cadrer les valeurs dans les cellules en fonction de leur type et/ou de leur valeur, et même les afficher avec des couleurs et/ou des polices de caractères différentes.

    - la mise à jour de la base est déclenchée ici par la modification de chaque cellule (c'est la ligne: self.model.setEditStrategy(QtSql.QSqlTableModel.OnFieldChange)). On pourrait faire cette mise à jour à chaque changement de ligne. On pourrait aussi avoir un bouton qui fait la mise à jour de la base de tout ce qu'on a changé par édition, et même un bouton qui revient aux valeurs de la mise à jour précédente.

    - si la base est une vraie base de données relationnelle avec plusieurs tables et des liens entre elles (contraintes), on peut en tenir compte dans l'édition. Par exemple si pour remplir une adresse on veut limiter le choix du pays dans une liste de pays appartenant à une autre table, le widget d'édition sera un QComboBox. On peut aussi ne faire que des modifications par transaction, c'est à dire qu'on lance la mise à jour d'une liste de modifications préparées par édition, mais si une seule d'entre elle est refusée, ou si il y a une interruption au cours de cette application, un retour aux valeurs initiales est faite et donc aucune n'est appliquée.

    - On peut faire des recherches sophistiquées dans la base de données en ajoutant un "QSortFilterProxyModel" qui s'interpose entre le modèle et le QTableView. Cela donne la possibilité de chercher des données avec des méthodes non prévues dans sqlite3 (regex, ...).

    - Etc...

    En résumé, on a un outil très puissant qui fait à peu près tout ce qu'on veut. Mais ça peut être assez complexe à faire si on est ambitieux dans les fonctionnalités...

  6. #6
    Membre éclairé
    Homme Profil pro
    Touche à tout
    Inscrit en
    Mai 2017
    Messages
    479
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Allier (Auvergne)

    Informations professionnelles :
    Activité : Touche à tout

    Informations forums :
    Inscription : Mai 2017
    Messages : 479
    Par défaut
    Merci beaucoup pour ta réponse!!! Je regarde ça de suite :-)

Discussions similaires

  1. Kotlin Connexion avec base de données PostgreSql
    Par JC JC dans le forum Kotlin
    Réponses: 3
    Dernier message: 22/03/2019, 18h16
  2. Réponses: 1
    Dernier message: 04/01/2016, 11h09
  3. Connexion avec base de données Postgres
    Par olfa_bl dans le forum API standards et tierces
    Réponses: 1
    Dernier message: 10/10/2012, 10h21
  4. problème de connexion avec base de donnée
    Par granit dans le forum VB.NET
    Réponses: 0
    Dernier message: 04/11/2009, 14h41
  5. Mise à jour en temps réel de la base de données
    Par Clotilde dans le forum Bases de données
    Réponses: 2
    Dernier message: 11/06/2004, 22h09

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