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 :

Afficher les informations de la BDD dans Table Modele en PyQt5


Sujet :

PyQt Python

  1. #1
    Membre régulier
    Homme Profil pro
    Data Lover
    Inscrit en
    Décembre 2014
    Messages
    96
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Data Lover
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2014
    Messages : 96
    Points : 101
    Points
    101
    Par défaut Afficher les informations de la BDD dans Table Modele en PyQt5
    Bonjour,

    Je galère un peu pour trouver comment faire pour afficher les informations d'une base de données dans un modèle table en PyQt5. D'après ce que j'ai compris du module QAbstractTableModel est qu'il faut implémenter : headerData, columnCount, rowCount et data pour que ça fonctionne. Ces noms me parlent, mais je ne sais pas comment les implémenter. Auriez-vous quelques explications et/ou exemples de code ?

    Merci !

  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,

    Tu te lances dans un truc très puissant, mais très complexe: accroche-toi!

    Le principe pour afficher une table d'une base de données dans un QTableView:
    - on ouvre la base de données avec QSqlDatabase, en citant le pilote (driver) du type de base de données (PyQt5 est livré avec de pilotes pour sqlite3, mysql, odbc et postgresql).
    - on crée un "model" comme QSqlRelationalTableModel qui établira la communication entre la base de données et le widget graphique d'affichage
    - on crée le widget graphique d'affichage QTableView et on lui dira quel "model" il devra utiliser.
    - quand on veut modifier les données de façon interactive avec enregistrement dans la base de données, on utilise un "delegate" comme QSqlRelationalDelegate.

    A titre de complément optionnel, on peut ajouter un "QSortFilterProxyModel" qui s'interposera entre le QSqlRelationalTableModel et le QTableView pour permettre des tris et filtrages plus puissants. Avec une solution de ce genre, on peut faire des recherches sur plusieurs colonnes basées sur des expressions régulières ou même des algorithmes de mots proches. Etc... Je n'ai pas dit que c'était simple à faire...

    En résumé: le widget graphique d'affichage ne sait pas communiquer directement avec la base de données: il ne le fait que par l'intermédiaire d'un "model".

    On va créer une base de données sqlite3 pour faire des tests:

    - une base sqlite3 appelée "mabase.db3"
    - une table appelée "matable" composées de 100 enregistrements de 4 champs au hasard de type INTEGER, BOOLEAN, FLOAT et TEXT. A noter que le type BOOLEAN n'existe pas avec sqlite3, alors on va le créer.

    Voilà le code creabase.py qui fait ça:

    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
    #!/usr/bin/python3
    # -*- coding: utf-8 -*-
     
    import sqlite3
    import random
    random.seed()
     
    #############################################################################
    def creamot(lg1=5, lg2=10, alphas="ABCDEFGHIJKLMNOPQRSTUVWXYZ"):
        """retourne un mot au hasard, composé avec les lettres de alphas, et de
           longueur au hasard entre lg1 et lg2 (bornes comprises)
        """
        lg = random.randint(lg1, lg2) # longueur au hasard du mot à créer
        maxalphas = len(alphas)-1 # indice de la dernière lettre de alphas
        lettres = [alphas[random.randint(0, maxalphas)] for i in range(0, lg)]
        return ''.join(lettres)
     
    #############################################################################
    def ouvrebase(basesql, contrainte=True):
        """ouvre la base et renvoie la connexion
           si contrainte est True, active les contraintes de clé étrangère
           en cas d'échec, retourne None
        """
        try:
            cnx = sqlite3.connect(basesql)
            if contrainte:
                cnx.execute("PRAGMA foreign_keys=on;")  # active le foreign key
            # cnx.text_factory = str    # permet les E/S avec la base en utf-8
        except sqlite3.Error:
            return None
        return cnx
     
    #############################################################################
    def fermebase(cnx):
        """Ferme la base ouverte avec la connexion cnx
        """
        try:
            cnx.close()
        except sqlite3.Error:
            pass
     
    #############################################################################
    def creabase(mabase, matable):
        """créer une base sql pour test avec une table
        """
        cnx = sqlite3.connect(mabase, detect_types=sqlite3.PARSE_DECLTYPES)
        # crée le type BOOLEAN
        sqlite3.register_adapter(bool, int)
        sqlite3.register_converter("BOOLEAN", lambda z: bool(int(z)))
        # crée un curseur pour la base ouverte
        cur = cnx.cursor()
        # efface la table 'matable' si elle existe déjà
        cur.execute("""DROP TABLE IF EXISTS '%s' """ % (matable,))
        # crée la table 'matable'
        cur.execute("""CREATE TABLE '%s' (
                         num INTEGER,
                         dig BOOLEAN, 
                         nbf FLOAT,
                         mot TEXT
                         )""" % (matable,)
                   )
        # remplit la table avec des données au hasard
        try:
            for i in range(0, 100):
                num = random.randint(10000, 99999)
                dig = bool(random.randint(0,1)) # simulation d'un booléen: 1=True 
                nbf = random.random()
                mot = creamot()
                cur.execute("""INSERT INTO '%s'
                               VALUES (?, ?, ?, ?)""" % (matable,), (num, dig, nbf, mot))
            cnx.commit()
        except sqlite3.Error as err:
            cnx.rollback()
            print("erreur: %s" % (str(err.args[0]),))
        cur.close()
        cnx.close()
     
    #############################################################################
    if __name__ == '__main__':
        nombase = "mabase.db3"
        table = "matable"
        creabase(nombase, table)
    On récupère alors dans le même répertoire la base de données sqlite3: "mabase.db3", et c'est sa table "matable" qu'on va afficher maintenant.

    Solution d'affichage minimal:

    Voilà un code minimal qui va faire l'affichage:

    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
    #!/usr/bin/python3
    # -*- coding: utf-8 -*-
     
    import sys
    from PyQt5 import (QtWidgets, QtCore, QtSql)
     
    #############################################################################
    def ouvrebaseqt(basesql):
        """ouvre la base 'basesql' avec le pilote "QSQLITE" pour sqlite3, et 
           renvoie la connexion (ou None si échec)
        """
        db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
        db.setDatabaseName(basesql)
        if not db.open():
            db = None
        return db
     
    #############################################################################
    def fermebaseqt(db):
        if db!=None:
            db.close()
     
    #############################################################################
    class VoirTableSql(QtWidgets.QMainWindow):
     
        def __init__(self, basesql, nomtable, parent=None):
            super().__init__(parent)
     
            self.setWindowTitle("Affichage de la table %s" % (nomtable,))
            self.resize(800, 600)
     
            # ouvre la base SQL
            self.basesql = basesql
            self.db = ouvrebaseqt(self.basesql)
            if self.db == None:
                QtWidgets.QMessageBox.critical(self,
                            "Ouverture de la base de données",
                            "Erreur d'ouverture")
                self.close()
                sys.exit()
     
            # table à afficher
            self.nomtable = nomtable
     
            # crée le modèle et sa liaison avec la base SQL ouverte
            self.model = QtSql.QSqlRelationalTableModel(self, self.db)
     
            # stratégie en cas de modification de données par l'utilisateur
            self.model.setEditStrategy(QtSql.QSqlRelationalTableModel.OnManualSubmit)
     
            # crée la table graphique et son lien avec le modèle
            self.vuetable = QtWidgets.QTableView(self)
            self.vuetable.setModel(self.model)
     
            # active le tri en cliquant sur les têtes de colonnes
            self.vuetable.setSortingEnabled(True)
     
            # crée un bouton pour enregistrer les modifications dans la base
            self.bouton1 = QtWidgets.QPushButton("Enregistrer les modifications", self)
            self.bouton1.clicked.connect(self.appliquer)
     
            # crée un bouton pour annuler les modifications non enregistrées
            self.bouton2 = QtWidgets.QPushButton("Annuler les modifications", self)
            self.bouton2.clicked.connect(self.annuler)
     
            # positionne les widgets dans la fenêtre QMainWindow
            self.setCentralWidget(QtWidgets.QFrame())
            posit = QtWidgets.QGridLayout()
            posit.addWidget(self.bouton1, 0, 0)
            posit.addWidget(self.bouton2, 0, 1)
            posit.addWidget(self.vuetable, 1, 0, 1, 2)
            self.centralWidget().setLayout(posit)
     
            # affiche la table demandée
            self.model.setTable(self.nomtable)
            self.model.select() # peuple le modèle avec les données de la table
     
            # met un delegate standard pour l'édition
            self.vuetable.setItemDelegate(QtSql.QSqlRelationalDelegate(self.vuetable))
     
            # trie si nécessaire selon la colonne 0
            self.model.sort(0, QtCore.Qt.AscendingOrder) # ou DescendingOrder
     
                # ajuste hauteur lignes et largeur colonnes selon contenus
            self.vuetable.resizeColumnsToContents()
            self.vuetable.resizeRowsToContents()
     
     
        #========================================================================
        def appliquer(self):
            """Enregistre les modifications des données
            """
            if self.model.submitAll():
                # ajuste hauteur lignes et largeur colonnes selon contenus
                self.vuetable.resizeColumnsToContents()
                self.vuetable.resizeRowsToContents()
                # message ok
                QtWidgets.QMessageBox.information(self,
                    "Enregistrement des modifications",
                    "Enregistrement terminé")
            else:
                # message erreur
                QtWidgets.QMessageBox.warning(self,
                    "Enregistrement des modifications",
                    "Erreur: %s" % self.model.lastError().text())
     
        #========================================================================
        def annuler(self):
            """Annule les modifications des données faites avant enregistrement
            """
            self.model.revertAll()
     
        #========================================================================
        def closeEvent(self, event=None):
            """Méthode appelée automatiquement à la fermeture de la fenêtre
            """
            # ferme la base 
            fermebaseqt(self.db) 
            event.accept()
     
    #############################################################################
    if __name__ == "__main__":
        app = QtWidgets.QApplication(sys.argv)
     
        nombase = "mabase.db3"
        table = "matable"
        fen = VoirTableSql(nombase, table)
        fen.show()
     
        sys.exit(app.exec_())
    Bon. Ça marche, mais il y a quelques défauts:
    - le type BOOLEAN apparait comme un 0 ou un 1 au lieu d'une case à cocher
    - quand on édite (double-clic) le nombre flottant, il vient un QDoubleSpinBox qui n'est pas configuré pour le format des nombres qu'on a.

    Solution avec l'édition par des widgets personnalisés:

    On va donc créer un nouveau "delegate" qui héritera du QSqlRelationalDelegate et qui ajoutera les widgets qu'on veut utiliser pour éditer les données en fonction de leur type!

    Voilà un exemple:

    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
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    #!/usr/bin/python3
    # -*- coding: utf-8 -*-
     
    import sys
    from PyQt5 import (QtWidgets, QtCore, QtSql)
     
    #############################################################################
    def ouvrebaseqt(basesql):
        """ouvre la base 'basesql' avec le pilote "QSQLITE" pour sqlite3, et 
           renvoie la connexion (ou None si échec)
        """
        db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
        db.setDatabaseName(basesql)
        if not db.open():
            db = None
        return db
     
    #############################################################################
    def fermebaseqt(db):
        if db!=None:
            db.close()
     
    #############################################################################
    class CheckBox(QtWidgets.QFrame):
        """QCheckBox personnalisé placé dans un QFrame
        """
     
        def __init__(self, parent=None):
            super().__init__(parent)
     
            # crée le QChecBox à l'intérieur du QFrame
            self.box = QtWidgets.QCheckBox(self)
     
            # positionne le QCheckBox au milieu du QFrame
            posit = QtWidgets.QGridLayout()
            posit.addWidget(self.box, 0, 0)
            posit.setAlignment(QtCore.Qt.AlignCenter)
            posit.setContentsMargins(0, 0, 0, 0)
            self.setLayout(posit)
     
    #############################################################################
    class RelationalDelegate(QtSql.QSqlRelationalDelegate):
     
        #========================================================================
        def createEditor(self, parent, option, index):
            """Créer et initialise le widget d'édition
                 parent: type 'PyQt5.QtWidgets.QWidget'
                 option: type 'PyQt5.QtWidgets.QStyleOptionViewItem'
                 index: type 'PyQt5.QtCore.QModelIndex'
               Retourne l'éditeur utilisé
            """
            # trouve les propriétés de la colonne en cours de l'enregistrement
            field = index.model().record().field(index.column())
     
            if field.type() == QtCore.QVariant.Bool: # => on est dans la colonne de type booléen
                # crée le QCheckBox personnalisé
                editor = CheckBox(parent)
                # donne la taille du QFrame = taille de la case 
                editor.resize(option.rect.size())           
                # retourne l'éditeur spécialisé
                return editor        
     
            elif field.type() == QtCore.QVariant.Double: # => on est dans la colonne de type float
                # mettre un QDoubleSpinBox
                editor = QtWidgets.QDoubleSpinBox(parent)
                # configure
                editor.setMinimum(-10**10)
                editor.setMaximum(10**10)
                editor.setDecimals(17)
                # retourne l'éditeur spécialisé
                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 à l'entrée du mode édition
                 editor: QtWidgets.QWidget
                 index: QtCore.QModelIndex
               Retour: None
            """
            # met un fond de couleur en entrant dans le mode édition
            editor.setStyleSheet("background-color: yellow;")
            # trouve les propriétés de la colonne en cours de l'enregistrement
            field = index.model().record().field(index.column())
     
            if field.type() == QtCore.QVariant.Bool: # => on est dans la colonne de type booléen
                # récupère la valeur de la base de données, destinée à la case
                membre = index.model().data(index, QtCore.Qt.EditRole)
                # définit l'état coché ou non du QCheckBox
                editor.box.setChecked(membre!=0)
     
            else:
                # traite les autres cas
                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
            """
            # trouve les propriétés de la colonne en cours de l'enregistrement
            field = index.model().record().field(index.column())
     
            if field.type() == QtCore.QVariant.Bool: # => on est dans la colonne de type booléen
                #lit la valeur affichée dans le QCheckBox en fin d'édition
                membre = int(editor.box.isChecked())
                # enregistre dans le modèle la valeur affichée du QLineEdit
                model.setData(index, membre, QtCore.Qt.EditRole)
     
            else:
                # traite les autres cas
                super().setModelData(editor, model, index)
     
        #========================================================================
        def paint (self, painter, option, index):
            """permet d'afficher les widgets personnalisés en dehors du mode 
               "édition"
            """
            # trouve les propriétés de la colonne en cours de l'enregistrement
            field = index.model().record().field(index.column())
     
            if field.type() == QtCore.QVariant.Bool: # => on est dans la colonne de type booléen
                # lit la donnée de la base, et la convertit en booléen (0=False, 1=True)
                data = bool(index.model().data(index, QtCore.Qt.EditRole))
                # crée le style et la position au centre de la cellule
                checkboxstyle = QtWidgets.QStyleOptionButton()
                checkbox_rect = QtWidgets.QApplication.style().subElementRect(QtWidgets.QStyle.SE_CheckBoxIndicator, checkboxstyle)
                checkboxstyle.rect = option.rect
                checkboxstyle.rect.setLeft(option.rect.x() + option.rect.width()//2 - checkbox_rect.width()//2)
                # met ou pas la coche selon la valeur de la donnée booléenne de la base
                if data:
                    checkboxstyle.state = QtWidgets.QStyle.State_On | QtWidgets.QStyle.State_Enabled
                else:
                    checkboxstyle.state = QtWidgets.QStyle.State_Off | QtWidgets.QStyle.State_Enabled
                # en cas de sélection de la case, le fond est coloré
                if option.state & QtWidgets.QStyle.State_Selected:
                    painter.fillRect(option.rect, option.palette.highlight())
                # dessine le QCheckBox au centre de la cellule
                QtWidgets.QApplication.style().drawControl(QtWidgets.QStyle.CE_CheckBox, checkboxstyle, painter)            
     
            else:
                # traite les autres cas
                super().paint(painter, option, index)    
     
    #############################################################################
    class VoirTableSql(QtWidgets.QMainWindow):
     
        def __init__(self, basesql, nomtable, parent=None):
            super().__init__(parent)
     
            self.setWindowTitle("Affichage de la table %s" % (nomtable,))
            self.resize(800, 600)
     
            # ouvre la base SQL
            self.basesql = basesql
            self.db = ouvrebaseqt(self.basesql)
            if self.db == None:
                QtWidgets.QMessageBox.critical(self,
                            "Ouverture de la base de données",
                            "Erreur d'ouverture")
                self.close()
                sys.exit()
     
            # table à afficher
            self.nomtable = nomtable
     
            # crée le modèle et sa liaison avec la base SQL ouverte
            self.model = QtSql.QSqlRelationalTableModel(self, self.db)
     
            # stratégie en cas de modification de données par l'utilisateur
            self.model.setEditStrategy(QtSql.QSqlRelationalTableModel.OnManualSubmit)
     
            # crée la table et son lien avec le modèle
            self.vuetable = QtWidgets.QTableView(self)
            self.vuetable.setModel(self.model)
     
            # active le tri en cliquant sur les têtes de colonnes
            self.vuetable.setSortingEnabled(True)
     
            # crée un bouton pour enregistrer les modifications dans la base
            self.bouton1 = QtWidgets.QPushButton("Enregistrer les modifications", self)
            self.bouton1.clicked.connect(self.appliquer)
     
            # crée un bouton pour enregistrer les modifications dans la base
            self.bouton2 = QtWidgets.QPushButton("Annuler les modifications", self)
            self.bouton2.clicked.connect(self.annuler)
     
            # positionne le QTableView dans la fenêtre
            self.setCentralWidget(QtWidgets.QFrame())
            posit = QtWidgets.QGridLayout()
            posit.addWidget(self.bouton1, 0, 0)
            posit.addWidget(self.bouton2, 0, 1)
            posit.addWidget(self.vuetable, 1, 0, 1, 2)
            self.centralWidget().setLayout(posit)
     
            # affiche la table demandée
            self.model.setTable(self.nomtable)
            self.model.select() # peuple le modèle avec les données de la table
     
            # met un delegate personnalisé pour l'édition
            self.vuetable.setItemDelegate(RelationalDelegate(self.vuetable))
     
            # trie si nécessaire selon la colonne 0
            self.model.sort(0, QtCore.Qt.AscendingOrder) # ou DescendingOrder
     
            # ajuste la largeur des colonnes en fonction de leurs contenus
            self.vuetable.resizeColumnsToContents()
     
        #========================================================================
        def appliquer(self):
            """Enregistre les modifications des données
            """
            if self.model.submitAll():
                # mettre la hauteur des lignes en fonction du contenu
                self.vuetable.resizeRowsToContents()
                # message ok
                QtWidgets.QMessageBox.information(self,
                    "Enregistrement des modifications",
                    "Enregistrement terminé")
            else:
                # revenir en arrière pour annuler les modifications
                self.model.revertAll()
                # message erreur
                QtWidgets.QMessageBox.warning(self,
                    "Enregistrement des modifications",
                    "Erreur: %s" % self.model.lastError().text())
     
        #========================================================================
        def annuler(self):
            """Annule les modifications des données
            """
            # revenir en arrière pour annuler les modifications
            self.model.revertAll()
     
        #========================================================================
        def closeEvent(self, event=None):
            """Méthode appelée automatiquement à la fermeture de la fenêtre
            """
            #ferme la base
            fermebaseqt(self.db)
            event.accept()
     
    #############################################################################
    if __name__ == "__main__":
        app = QtWidgets.QApplication(sys.argv)
     
        nombase = "mabase.db3"
        table = "matable"
        fen = VoirTableSql(nombase, table)
        fen.show()
     
        sys.exit(app.exec_())
    Voilà ce que ça donne comme copie d'écran.

    Nom : voirtablesql.jpg
Affichages : 2430
Taille : 133,0 Ko

    Essaie déjà de faire tout ça et de comprendre comment ça marche!
    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
    Membre régulier
    Homme Profil pro
    Data Lover
    Inscrit en
    Décembre 2014
    Messages
    96
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Data Lover
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2014
    Messages : 96
    Points : 101
    Points
    101
    Par défaut
    Bonsoir Tyrtamos,

    Un GRAND MERCI pour aide. Je vais tester ce le code que tu m'as donné. Par ailleurs, comme tu l'as dit en préambule :
    Tu te lances dans un truc très puissant, mais très complexe: accroche-toi!
    Aurais-tu un bon ouvrage, pour débutants, à me conseiller pour massacrer mes peines ?

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

    Citation Envoyé par master4 Voir le message
    ...Aurais-tu un bon ouvrage, pour débutants, à me conseiller...?
    Il y avait un bon bouquin qui m'a bien aidé au début, mais il a 10 ans maintenant et il ne parle que de PyQt4:

    http://oez.es/Rapid%20GUI%20Programm...20and%20Qt.pdf

    Il y a peut-être encore des choses intéressantes dedans...

    Sur le site de developpez, il y a des tutos et du code à voir: https://pyqt.developpez.com/.

    On peut aussi faire des recherches dans le forum!

    Compte tenu de la complexité de ce type de développement, on doit avoir la documentation de PyQt5 et de Qt5 en ligne en permanence:

    http://pyqt.sourceforge.net/Docs/PyQt5/

    https://doc.qt.io/qt-5/qt5-intro.html

    Sinon, on cherche sur internet: il y a des compétences dans le monde entier.

    Le bon côté de tout ce bazar, c'est qu'à force, on apprend à chercher!
    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

  5. #5
    Membre régulier
    Homme Profil pro
    Data Lover
    Inscrit en
    Décembre 2014
    Messages
    96
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Data Lover
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2014
    Messages : 96
    Points : 101
    Points
    101
    Par défaut
    Merci beaucoup

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

Discussions similaires

  1. [Débutant] Afficher les données de la bdd dans un datagridview
    Par mrbrams dans le forum C#
    Réponses: 5
    Dernier message: 09/10/2014, 15h37
  2. [MySQL] Transfert sur OVH, impossible d'afficher les informations de la BDD
    Par laurentche dans le forum PHP & Base de données
    Réponses: 1
    Dernier message: 18/06/2014, 18h47
  3. EXT-GWT: afficher les données d'une bdd dans une grid
    Par baya1 dans le forum GWT et Vaadin
    Réponses: 9
    Dernier message: 20/11/2011, 00h14
  4. Afficher les information liées à une sélection dans un UserForm
    Par zelda12 dans le forum Macros et VBA Excel
    Réponses: 2
    Dernier message: 18/12/2010, 11h56
  5. afficher les informations d'une liste dans la jsp
    Par mystro7200 dans le forum Struts 1
    Réponses: 18
    Dernier message: 11/09/2008, 14h36

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