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 :

synchronisation QTablewidget et Mysql


Sujet :

PyQt Python

  1. #1
    Membre régulier
    Homme Profil pro
    Ingénieur développement de composants
    Inscrit en
    Décembre 2019
    Messages
    113
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement de composants

    Informations forums :
    Inscription : Décembre 2019
    Messages : 113
    Points : 72
    Points
    72
    Par défaut synchronisation QTablewidget et Mysql
    Bonjour,
    le code ci dessous fonctionne mais j'ai quelques interrogations sur la manière d'aborder les choses...
    je vient donc ici pour avoir quelques conseils sur les bonnes pratiques pour éviter de m'embarquer dans des impasses ou des tournures maladroites.
    l'exemple ci dessous tourne avec sqlite mais l'application finale vise mysql (il est en cours d'install!).
    l'idée est de permettre un affichage/modification d'une base de donnée via un affichage dans un QTableWidget

    Principales interrogations:
    1 - Sur chaque fonction, est-il nécessaire d'ouvrir/fermer une connexion et un curseur? Si non, je pense créer une seule connexion/curseur depuis le init du QMainWindow... Mais dans ce cas, comment s'assurer que le curseur et la connexion sont bien clôturés lors de la fermeture de l'application? (avec des thread parfois il y a des choses qui reste derrière... est-ce pareil avec le dialogue avec une base de donnée?)

    2 - Dans l'exemple, chaque ajout de ligne implique la réécriture de toute la table... est-ce que cela ne créé pas des QTableWidgetItem "flottants" (issus de la table avant sa modification)? Vaut-il mieux cibler les ajouts vis à vis de la réelle nouvelle data? ou cela se pratique de tout réécrire?

    3 - Le fait d'avoir la possibilité de capter une modification de la table (pour que l’utilisateur la mette à jour directement dans la base sql) et de permettre l'ajout dynamique d'une ligne induit un passage "obligé" dans la fonction appelée par le cellChanged.connect du QTableWidget à chaque cellule réécrite... obligation d'utiliser un blocksignal si besoin d'éviter cela?


    merci pour vos conseils :-)


    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
    from PyQt5.QtWidgets import QApplication, QPushButton, QTableWidgetItem, QMainWindow, QVBoxLayout, QWidget, QTableWidget
    import sqlite3
    import random
    import time
    import sys
    import os.path
     
     
    class MainWindow(QMainWindow):
        def __init__(self):
            QMainWindow.__init__(self)
     
            self.fichier_ddb = "pythonsqlite_000001.db"
     
            #vérifie l'existence de la bdd
            if os.path.isfile(self.fichier_ddb):
                data, self.nb_lignes, nb_colonnes, self.liste_columns_name = self.get_datas_from_ddb()
            else:
                #si inexistante, elle est créée puis remplie avec données fictives
                self.create_ddb()
                self.create_lines()
                #récupération de la data enregistrée dans la bdd
                data, self.nb_lignes, nb_colonnes, self.liste_columns_name = self.get_datas_from_ddb()
     
            #création de la fenetre qui affichera la représentation de la bdd sous forme de QTableWidget
            self.fenetre_globale = QWidget()
            self.layout_vertical = QVBoxLayout()
     
            self.table = QTableWidget()
            self.table.setColumnCount(nb_colonnes)
            self.table.setRowCount(self.nb_lignes)
            self.table.setHorizontalHeaderLabels(self.liste_columns_name)
            self.fill_table()
     
            self.bouton_ajouter = QPushButton("Ajouter ligne")
            #self.table.clicked.connect(self.table_clic)
     
            self.layout_vertical.addWidget(self.table)
            self.layout_vertical.addWidget(self.bouton_ajouter)
     
            self.bouton_ajouter.clicked.connect(self.insert_lines)
     
            self.fenetre_globale.setLayout(self.layout_vertical)
            self.setCentralWidget(self.fenetre_globale)
     
            self.resize(980, 400)
     
        def fill_table(self):
            # récupération de la data
            data, nb_lignes, nb_colonnes, self.liste_columns_name = self.get_datas_from_ddb()
     
            #insertion du nombre de lignes manquantes à la table
            for j in range(0, int(self.nb_lignes - nb_lignes), 1):
                self.table.insertRow(self.table.rowCount())
            self.nb_lignes = nb_lignes
            #redimensionnement de la table
            #si pas présente, l'ajout d'une ligne n'est plus dynamique
            self.table.setRowCount(nb_lignes)
            self.table.setHorizontalHeaderLabels(self.liste_columns_name)
     
            #remplissage de toute la table
            for index, ligne in enumerate(data):
                for index_colonne, colonne in enumerate(ligne):
                    self.table.setItem(int(index), int(index_colonne), QTableWidgetItem(str(colonne)))
            self.table.cellChanged.connect(self.table_changed)
     
        def create_ddb(self):
     
            connection = sqlite3.connect(self.fichier_ddb)
            cursor = connection.cursor()
     
            sql_create_projects_table = """ CREATE TABLE IF NOT EXISTS projects (
                                                colonne0 TEXT,
                                                colonne1 TEXT,
                                                colonne2 TEXT,
                                                colonne3 TEXT,
                                                colonne4 TEXT,
                                                colonne5 TEXT,
                                                colonne6 TEXT,
                                                colonne7 TEXT,
                                                colonne8 TEXT)"""
            if connection is not None:
                cursor.execute(sql_create_projects_table)
                connection.commit()
            else:
                print("Error! cannot create the database connection.")
            cursor.close()
            connection.close()
     
        def create_lines(self):
            #ne sert qu'à simuler une bdd pré-existante
            connection = sqlite3.connect(self.fichier_ddb)
            cursor = connection.cursor()
            sql = """INSERT INTO projects (colonne0, colonne1, colonne2, colonne3, colonne4, colonne5, colonne6, colonne7, colonne8) VALUES(?,?,?,?,?,?,?,?,?)"""
            for i in range(0, 10, 1):
                cursor.execute(sql, (str(i),str(random.random()*10),
                                     str(random.random()*10),str(random.random()*10),
                                     str(random.random()*10),str(random.random()*10),
                                     str(random.random()*10),str(random.random()*10),
                                     str(random.random()*10)))
            cursor.close()
            connection.commit()
            connection.close()
     
     
        def insert_lines(self):
            #sert à simuler la création d'une ligne dans la bdd (pas forcément sur le même PC)
            connection = sqlite3.connect(self.fichier_ddb)
            cursor = connection.cursor()
            sql = """INSERT INTO projects (colonne0, colonne1, colonne2, colonne3, colonne4, colonne5, colonne6, colonne7, colonne8) VALUES(?,?,?,?,?,?,?,?,?)"""
            for i in range(0, 1, 1):
                cursor.execute(sql, (str(i + self.nb_lignes),str(random.random()*10),
                                     str(random.random()*10),str(random.random()*10),
                                     str(random.random()*10),str(random.random()*10),
                                     str(random.random()*10),str(random.random()*10),
                                     str(random.random()*10)))
            cursor.close()
            connection.commit()
            connection.close()
            self.fill_table()
     
        def get_datas_from_ddb(self):
            debut = time.time()
            connection = sqlite3.connect(self.fichier_ddb)
            cursor = connection.cursor()
            cursor.execute("SELECT * FROM projects")
            rows = cursor.fetchall()
            names = list(map(lambda x: x[0], cursor.description))
            colonnes = len(rows[0])
            lignes = len(rows)
            connection.close()
            print("Get All Data: " + str(time.time()-debut))
            return rows, lignes, colonnes, names    
     
     
        def table_changed(self):
            try:
                debut = time.time()
                connection = sqlite3.connect(self.fichier_ddb)
                cursor = connection.cursor()
                dt_updated, new_val, colonne_updated = (self.table.item(self.table.currentRow(), 0).text(),
                                                            self.table.item(self.table.currentRow(), self.table.currentColumn()).text(),
                                                            self.liste_columns_name[int(self.table.currentColumn())])
                new_val = self.table.item(self.table.currentRow(), self.table.currentColumn()).text()
                sql_update= """UPDATE projects SET """ + str(colonne_updated) + """ = ? WHERE colonne0 = ?"""
                data_new = (new_val, dt_updated)
                cursor.execute(sql_update, data_new)
                cursor.close()
                connection.commit()
                connection.close()
                print(str(time.time()-debut))
     
            except Exception as er:
                print(er)
     
    if __name__ == "__main__":
        appli = QApplication(sys.argv)
        fenetre_main = MainWindow()
        fenetre_main.show()
        sys.exit(appli.exec_())

  2. #2
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 690
    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 690
    Points : 30 986
    Points
    30 986
    Billets dans le blog
    1
    Par défaut
    Bonjour
    Citation Envoyé par clement_74 Voir le message
    l'exemple ci dessous tourne avec sqlite mais l'application finale vise mysql (il est en cours d'install!).
    l'idée est de permettre un affichage/modification d'une base de donnée via un affichage dans un QTableWidget
    Il est pas mal foutu ton code. Tu commences à t'en sortir avec Qt.
    Quelques détails
    • l'héritage se fait via super() (plus d'explications ici. Remplace QMainWindow.__init__(self) par super().__init__()
    • tu peux éviter de faire "je créé Y puis je place Y comme enfant de X" en disant directement "je crée Y comme enfant de X". Exemple au lieu de créer layout_vertical (qui n'a d'ailleurs absolument pas besoin d'être attribut de l'objet) puis finir par fenetre_globale.setLayout(self.layout_vertical) tu peux mettre directement layout_vertical=QVBoxLayout(fenetre_globale). Pareil pour fenetre_globale, qui là aussi n'a pas besoin d'être attribut (un attribut n'est utile que s'il est utilisé par plusieurs méthodes de l'objet) et même n'a pas besoin d'exister (suffit de commencer par créer le widget central self.setCentralWidget(QWidget()) puis d'utiliser self.centralWidget() chaque fois qu'on a besoin du widget central)


    Citation Envoyé par clement_74 Voir le message
    1 - Sur chaque fonction, est-il nécessaire d'ouvrir/fermer une connexion et un curseur? Si non, je pense créer une seule connexion/curseur depuis le init du QMainWindow... Mais dans ce cas, comment s'assurer que le curseur et la connexion sont bien clôturés lors de la fermeture de l'application? (avec des thread parfois il y a des choses qui reste derrière... est-ce pareil avec le dialogue avec une base de donnée?)
    La connexion non. Tu peux l'ouvrir dans le __init__ et la fermer dans le __del__ (n'oublie pas que le __del__ existe aussi). Normalement si tu ouvres la connexion avant les thread, ceux-ci en héritent et si les thread sont fermés avant la fin de la connexion il n'y a alors "rien qui reste" (accessoirement on peut aussi se demander pourquoi tu aurais besoin de threads...).
    Le curseur à toi de choisir. Il faut voir la connexion comme une opération "lourde" (le système fait des vérifs, demande un travail au serveur bdd) donc en général à ne faire qu'une fois. Tandis que le curseur lui c'est juste une espèce de "variable de travail" donc tu peux le créer dans les fonctions/méthodes qui modifient la bdd (c'est ce que je fais moi). Et n'oublie pas que le with marche aussi pour les curseurs.
    Et d'ailleurs je te propose même d'aller plus loin dans la programmation MVC et de créer aussi l'objet associé à ta "table" pour la bdd (au final tu gagnes en modularité).
    Donc tu crées un objet "projects.py". Cet objet possèdera une méthode "update()" recevant une ligne d'info (plus la clef plus l'identifiant de connexion bdd) et se chargeant de faire l'update en bdd. Donc c'est lui qui ouvre et ferme le curseur.
    Et ton objet "QProjects.py" lui se contente d'afficher les infos dans le QTable et si une info est modifiée, appelle alors l'update de l'objet "projects.py" en lui passant la ligne d'info.

    Citation Envoyé par clement_74 Voir le message
    2 - Dans l'exemple, chaque ajout de ligne implique la réécriture de toute la table...
    Toute la table??? C'est pas ce que je vois. Dans la fonction "insert_lines" il y a juste un "insert" ce qui insère donc une ligne, cela ne réécrit pas les lignes déjà existantes. Juste se demander pourquoi le for i in range(0, 1, 1) mais je pense que ça doit être une "option" permettant d'insérer n lignes d'un coup si on en a envie.

    Citation Envoyé par clement_74 Voir le message
    est-ce que cela ne créé pas des QTableWidgetItem "flottants" (issus de la table avant sa modification)? Vaut-il mieux cibler les ajouts vis à vis de la réelle nouvelle data? ou cela se pratique de tout réécrire?
    Chez-moi, quand je modifie une info, je modifie dans ma table toute la ligne contenant l'info. Donc je lui réécris aussi les autres infos de la ligne. Tu peux ne modifier en bdd que l'info touchée mais ça a un coût (faut que tu commences par la repérer cette info puis ensuite écrire le code "si info1 update info1, puis si info2 update info2 etc"). Donc j'ai personnellement choisi de ne pas payer ce coût, estimant que ça n'en valait pas le... coup. Donc j'ai posé ma granularité de modification à une ligne complète. Mais si tu veux faire plus fin tu en as le droit (et aussi la possibilité).

    Citation Envoyé par clement_74 Voir le message
    3 - Le fait d'avoir la possibilité de capter une modification de la table (pour que l’utilisateur la mette à jour directement dans la base sql) et de permettre l'ajout dynamique d'une ligne induit un passage "obligé" dans la fonction appelée par le cellChanged.connect du QTableWidget à chaque cellule réécrite... obligation d'utiliser un blocksignal si besoin d'éviter cela?
    Là je ne suis pas certain d'avoir compris. Ou plutôt je ne suis pas certain que tu aies compris l'utilité du blockSignals
    Un blockSignals sert à modifier un widget sans que le signal associé à la modification soit activé. Par exemple tu as un QLineEdit, tu veux checker l'orthographe, tu lui mets un signal "edited()" relié à un slot grammatical. Puis tu veux le remplir avec une valeur par défaut, inutile alors que cette valeur soit checkée puisque c'est toi qui la places. Donc tu bloques le signal avant modif, tu modifies le LineEdit puis tu débloques le signal.
    Là d'ailleurs il y a un souci dans ton code. Tu as la fonction fill_table qui remplit le QTableWidget et qui crée ensuite une connexion "cellChanged". Et cette fonction est appelée plusieurs fois. C'est là que tu peux avoir souci car ça crée alors plusieurs connexions identiques.
    Une connexion c'est un peu comme un branchement EDF. Tu as un nouvel appareil, tu le branches mais tu ne le branches qu'une fois. Ensuite tu peux l'éteindre et l'allumer plusieurs fois si tu veux.
    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
    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

    Vouloir consulter / modifier / filtrer / extraire une table d'une base de données relationnelle se fait avec un QTableView et non un QTableWidget. Les échanges entre la base de données et le graphique sont déjà programmées, y compris s'il y a des contraintes. Et Qt est déjà livré avec des pilotes pour les SGBDR principaux (dont sqlite3 et mysql).

    Si ça intéresse quelqu'un, je devrais pouvoir donner un petit code exemple.
    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

  4. #4
    Expert confirmé Avatar de papajoker
    Homme Profil pro
    Développeur Web
    Inscrit en
    Septembre 2013
    Messages
    2 105
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nièvre (Bourgogne)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Septembre 2013
    Messages : 2 105
    Points : 4 455
    Points
    4 455
    Par défaut
    bonjour

    En effet, avec Qt, on utilise mvc, des Models (QAbstractItemModel, QAbstractTableModel , ... , QSqlTableModel ...) et pas du tout ce type de code...
    On va lier un Model(les datas) à un composant graphique Combo...table... (A voir aussi les xxxxDelegate )

    une recherche rapide ici (facile lorsque l'on connait le nom des classes) me donne par exemple en premier https://www.developpez.net/forums/d1.../#post10733685
    $moi= ( !== ) ? : ;

  5. #5
    Membre régulier
    Homme Profil pro
    Ingénieur développement de composants
    Inscrit en
    Décembre 2019
    Messages
    113
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement de composants

    Informations forums :
    Inscription : Décembre 2019
    Messages : 113
    Points : 72
    Points
    72
    Par défaut
    Bonjour,
    merci pour toutes les réponses, après quelques modifs/améliorations j'étais "satisfait" de mon code...
    Mais finalement il y a bien mieux à faire! (je ne connaissais pas PyQt5.QtSql...)
    Bon c'est chouette quand ça se goupille comme ça, au final mon approche initiale n'aura été qu'un exercice de codage.
    Le lien de papajoker me parait plus adapté/puissant, et à priori, je comprends le code! (pour une fois)
    merci

  6. #6
    Membre régulier
    Homme Profil pro
    Ingénieur développement de composants
    Inscrit en
    Décembre 2019
    Messages
    113
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement de composants

    Informations forums :
    Inscription : Décembre 2019
    Messages : 113
    Points : 72
    Points
    72
    Par défaut
    Bonjour,
    j'ai presque terminé de convertir mon code initial avec QSqlDatabase, QSqlTableModel et QTableView.
    Globalement la conversion est plutôt simple... mais il y a 1 chose que je ne trouve pas dans la doc: comment changer la couleur d'une cellule?

    avec un QTableWidget il suffit d'une ligne de ce type:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    table.item(index_ligne, index_colonne).setBackground(QColor(0,255,0))
    Avec le QTableView et sa doc je n'arrive pas à trouver un point d'entrée pour modifier les couleurs de cellule

    ci dessous le code (en travaux) => c'est dans la fonction on_Click() que j'essaie de prendre la main de la couleur d'une cellule.

    NB: Au final l'objectif est de colorer certaines cellules/lignes en fonction de certaines valeurs... mais avant ça j'essaie juste d'avoir la main sur le changement de couleur en lui même).
    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
    import sys, sqlite3, os, time, random
    from PyQt5.QtWidgets import (QMainWindow, QApplication, QWidget, 
                                 QVBoxLayout, QTableView, QPushButton, QAbstractItemView)
    from PyQt5.QtGui import QColor
    from PyQt5.QtSql import (QSqlDatabase, QSqlQuery, QSqlTableModel,
                                    QSqlRelation,QSqlRelationalTableModel)
    from PyQt5.QtCore import QModelIndex, Qt
     
    class MyWindow(QMainWindow):
            def __init__(self):
                super(MyWindow, self).__init__()
     
                self.fichier_ddb = "pythonsqlite_007.db"
                data = QSqlDatabase.addDatabase("QSQLITE")
                data.setDatabaseName(self.fichier_ddb)
                self.connection = sqlite3.connect(self.fichier_ddb)
     
                widget = QWidget(self)
                self.setCentralWidget(widget)
                layout = QVBoxLayout()
     
                self.tableViewTravaux = QTableView(self)
                #si modification doit être impossible:
                #self.tableViewTravaux.setEditTriggers(QAbstractItemView.NoEditTriggers)
                self.bouton_ajouter = QPushButton("Ajouter ligne")
                self.bouton_supprimer = QPushButton("Supprimer ligne courante")
     
                layout.addWidget(self.tableViewTravaux)
                layout.addWidget(self.bouton_ajouter)
                layout.addWidget(self.bouton_supprimer)
     
                self.bouton_ajouter.clicked.connect(self.insert_line)
                self.bouton_supprimer.clicked.connect(self.delete_line)
                self.tableViewTravaux.clicked.connect(self.on_Click)
     
                widget.setLayout(layout)
                self.modellistetravaux = QSqlTableModel()
                self.modellistetravaux.setTable("projects")
                self.modellistetravaux.select()
                self.tableViewTravaux.setModel(self.modellistetravaux)
     
                self.nblignes = self.modellistetravaux.rowCount()
     
            def insert_line(self):
                cursor = self.connection.cursor()
                sql = """INSERT INTO projects (colonne0, colonne1, colonne2, colonne3, colonne4, colonne5, colonne6, colonne7, colonne8) VALUES(?,?,?,?,?,?,?,?,?)"""
                self.nblignes = self.modellistetravaux.rowCount()
                last_id = self.modellistetravaux.record(self.nblignes - 1).value("colonne0")
                cursor.execute(sql, (str(int(last_id) + 1),str(random.random() * 10),
                                     str(random.random() * 10),str(random.random() * 10),
                                     str(random.random() * 10),str(random.random() * 10),
                                     str(random.random() * 10),str(random.random() * 10),
                                     str(random.random() * 10)))
                self.connection.commit()
                self.modellistetravaux.select()
                cursor.close()
     
            def delete_line(self):
                cursor = self.connection.cursor()
                sql_update = """DELETE FROM projects WHERE colonne0 = ?"""
                cursor.execute(sql_update, (self.selected_id,))
                self.connection.commit()
                self.modellistetravaux.select()
                cursor.close()
     
            def on_Click(self):
                index=(self.tableViewTravaux.selectionModel().currentIndex())
                self.selected_id = index.sibling(index.row(), 0).data()
                print(self.modellistetravaux.data(index))
                print(self.tableViewTravaux.selectionModel().currentIndex().data())
                #self.tableViewTravaux.selectionModel().currentIndex().setBackground(QColor(0,255,0))
                #self.tableViewTravaux.selectionModel().setStyleSheet("background-color: red")
                #self.modellistetravaux.data(QBrush(Qt.yellow))
     
                #self.tableViewTravaux.selectRow(index.row())
                value = index.sibling(index.row(),index.column()).data()
                print(value, self.selected_id)
    if __name__ == "__main__":
            application = QApplication(sys.argv)
            win = MyWindow()
            win.show() 
            sys.exit(application.exec_())

  7. #7
    Expert confirmé Avatar de papajoker
    Homme Profil pro
    Développeur Web
    Inscrit en
    Septembre 2013
    Messages
    2 105
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nièvre (Bourgogne)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Septembre 2013
    Messages : 2 105
    Points : 4 455
    Points
    4 455
    Par défaut
    Citation Envoyé par papajoker Voir le message
    (A voir aussi les xxxxDelegate )
    c'est ces objets "Delegate" qui permettent de changer l'affichage (la vue) en fonction de certaines données. couleurs, police, alignement ... (par exemple mettre en rouge toutes les valeurs négatives)
    Ici aussi, on doit lier un "QtWidgets.QStyledItemDelegate" personnel à un composant graphique, ce delegate.initStyleOption(xxxx) va recevoir notre "Model" en paramètre

    ressemble 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
    class listDelegate(QtWidgets.QStyledItemDelegate):
        OUTOFDATE = QtGui.QColor(160, 0, 0, 220)  # QtGui.QColor("salmon")
        GREEN = QtGui.QColor(0, 60, 0, 220)    # QtGui.QColor("green")
     
        def __init__(self, parent, time_since_update: int):
            super().__init__(parent)
     
        def initStyleOption(self, option, index):
            # not datas venant du Model sont dans "index"
            if not index.isValid():
                return None
            super(listDelegate, self).initStyleOption(option, index)
     
            # var pkg est l'objet donnée dans mon modèle
           # si on est dans un tableau, c'est la ligne complete
            if index.internalPointer():
                pkg = index.internalPointer()
            else:
                pkg = self._data[index.row()]
            if not pkg:
                return
     
            # ici on ne traite que l'affichage
            if index.data(QtCore.Qt.DisplayRole):
                # on récupère la colonne (ce sont des constantes ici dans le Model ) puis, en fonction des datas , ici on change le style
                if index.column() == packageModel.ID_NAME and pkg.is_installed():
                    option.palette.setBrush(
                        QtGui.QPalette.Text, QtGui.QPalette().highlight()
                    )
     
                if index.column() == packageModel.ID_VERSION:
                    if -pkg:  # is outofdate
                        option.palette.setBrush(QtGui.QPalette.Text, self.OUTOFDATE)
                        option.font.setBold(True)
                    return
                if index.column() == packageModel.ID_DATE:
                    if pkg.first_submitted > self.time_since_update:
                        option.palette.setBrush(
                            QtGui.QPalette.Text, QtGui.QPalette().highlight()
                        )
                    return
                return
    $moi= ( !== ) ? : ;

  8. #8
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    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 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Citation Envoyé par clement_74 Voir le message
    Globalement la conversion est plutôt simple... mais il y a 1 chose que je ne trouve pas dans la doc: comment changer la couleur d'une cellule?
    Remplacer un QTableWidget par un QTableView pour faire la même chose n'a pas de sens.
    Vous prendre le temps de comprendre l'architecture MVC proposée par Qt et comment s'utilisent les Delegate.
    Suivant votre niveau, ça peut être passablement compliqué et prendre pas mal de temps. Mais il y a plein de tutos à potasser...

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

  9. #9
    Membre régulier
    Homme Profil pro
    Ingénieur développement de composants
    Inscrit en
    Décembre 2019
    Messages
    113
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement de composants

    Informations forums :
    Inscription : Décembre 2019
    Messages : 113
    Points : 72
    Points
    72
    Par défaut
    merci pour les orientations,
    je potasse... (et je comprends pas tout) mais j'avance un peu.

    je suis arrivé à avoir la main sur l'affichage des données de la tableview avec un QAbstractTableModel.
    par contre j'ai perdu la possibilité de la modifier "en live" (ce que fait spontanément QSqlTableModel() si je comprends bien... et qui m'arrangeait beaucoup)

    du coup, pour updater la table "à la main" tout en ayant la main sur son apparence, il faut que je parte sur ce qui s'appelle les "delegate"? et assigner un model personnalisé à QSqlTableModel()?

  10. #10
    Expert confirmé Avatar de papajoker
    Homme Profil pro
    Développeur Web
    Inscrit en
    Septembre 2013
    Messages
    2 105
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nièvre (Bourgogne)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Septembre 2013
    Messages : 2 105
    Points : 4 455
    Points
    4 455
    Par défaut
    pas trop compris la question, il te suffit d'ajouter ton delegate au widget (table, combo ,...)
    La fonction du Delegate recoit un QModelIndex qui est générique à tous les Models
    On va plutôt utiliser un AbstractModel si nos datas viennent par exemple d'un fichier json.
    Si on a utilisé un AbstractModel, il est possible alors de recevoir un objet perso ("Client, Voiture, ...") - c'était mon premier code Delegate. Mais pas utile dans ton cas

    ps: existe aussi une autre fonction au Delegate qui permet de modifier les datas (par exemple formater un No de téléphone avec des espaces)

    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 CustomSqlModel(QSqlQueryModel):
        # tu as déjà ou utile autre modèle
     
    class MonDelegate(QStyledItemDelegate):
     
        def initStyleOption(self, option, index):
            if not index.isValid():
                return None
            super(MonDelegate, self).initStyleOption(option, index)
     
            if not index.data(Qt.DisplayRole):
                return
     
            if index.column() == 0:
                valeur = index.data(Qt.DisplayRole)
                # si int(valeur) < 0 alors couleur rouge
     
            if index.column() == 1:
                option.font.setBold(True)
                option.palette.setBrush(QPalette.Text,
                    QPalette().highlight()  # utilise une couleur du thème de l'utilisateur
                )
     
    ...
     
    view = QTableView()
    view.setModel(CustomSqlModel())
    view.setItemDelegate(MonDelegate())

    -------
    Note, en plus général : A voir les exemples de pyQt5 c'est une très bonne source pour un départ avec sql
    $moi= ( !== ) ? : ;

  11. #11
    Membre régulier
    Homme Profil pro
    Ingénieur développement de composants
    Inscrit en
    Décembre 2019
    Messages
    113
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur développement de composants

    Informations forums :
    Inscription : Décembre 2019
    Messages : 113
    Points : 72
    Points
    72
    Par défaut
    Bonjour,
    merci pour le bout de code et les conseils :-)
    sans cela j'aurais rien trouvé en me perdant dans la doc.

    ci dessous le code tel qu'il "tourne" aujourd'hui.
    J'arrive à colorier mes cellules!! (enfin en tout cas ça tourne)

    différentes questions me sont venues en écrivant le code ci-dessous:

    1 - Est-ce que l'imbrication view-model-delegate est bien correcte?
    2 - Est-ce que pour permettre des filtrages selon des valeurs le choix de QSortFilterProxyModel est bon?
    3 - Si oui à la question précédente, l'imbrication view-model-filtre-delegate est-elle correcte?
    4 - J'ai positionné les ajouts/suppression de lignes dans le QsqlQueryModel, est-ce OK ou farfelu?
    5 - Pour rafraichir la table après ajout-suppression de ligne j'utilise un setQuery(QSqlQuery("SELECT * FROM table")) => y a-t-il plus adapté?

    j'aimerais permettre de la modification "live" de la table (par l'utilisateur au clavier-souri).
    pour rendre éditable les cellules je potasse les flags et l'utilisation de QAbstractItemModel... bonne voie?
    Aussi, j'envisage de positionner la fonction de modification dans le QsqlQueryModel (comme pour l'ajout et la suppréssion d'une ligne) via une requete type "update"... est-ce ok sur le principe?

    encore merci :-)

    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
    class filtrage_table(QSortFilterProxyModel):
        def __init__(self, parent=None):
            QSortFilterProxyModel.__init__(self, parent)
     
        def filtrer_colonne_combobox1(self, value_combobox):
            print(value_combobox)
            self.sortColumn()
     
     
    class creation_sql_query_model(QSqlQueryModel):
        def __init__(self, parent=None):
            QSqlQueryModel.__init__(self, parent)
            self.setQuery(QSqlQuery("SELECT * FROM projects"))
     
        def ajouter_ligne(self):
            self.setQuery(QSqlQuery("""INSERT INTO projects (colonne0, colonne1, colonne2, colonne3, colonne4, colonne5, colonne6, colonne7, colonne8) VALUES(5,6,7,8,9,10,11,12,13)"""))
            self.setQuery(QSqlQuery("SELECT * FROM projects"))
     
        def supprimer_ligne(self):
            self.setQuery(QSqlQuery("""DELETE FROM projects WHERE colonne0 = 6"""))
            self.setQuery(QSqlQuery("SELECT * FROM projects"))
     
     
    class mon_delegate(QStyledItemDelegate):
     
        def initStyleOption(self, option, index):
            if not index.isValid():
                return None
     
            super(mon_delegate, self).initStyleOption(option, index)
     
            if not index.data(Qt.DisplayRole):
                print("test1")
     
            if index.data(Qt.BackgroundRole):
                print("test2")           
     
            if index.column() == 1:
                option.font.setBold(True)
                option.backgroundBrush = QColor(255,0,0)
     
            if index.row() == 3:
                option.backgroundBrush = QColor(255,100,150)
     
     
    class MyWindow(QMainWindow):
            def __init__(self):
                super(MyWindow, self).__init__()
     
                self.db_file = "pythonsqlite_007.db"
                self.create_connection()
     
                #cas sans filtrage
    # =============================================================================
    #             self.table_View_Travaux = QTableView()
    #             self.model_liste_travaux = creation_sql_query_model()
    #             self.table_View_Travaux.setModel(self.model_liste_travaux)
    #             self.table_View_Travaux.setItemDelegate(mon_delegate())
    # =============================================================================
     
                #cas avec filtrage
                self.table_View_Travaux = QTableView()
                self.model_liste_travaux = creation_sql_query_model()
                self.filtre_modele = filtrage_table()
                self.filtre_modele.setSourceModel(self.model_liste_travaux)
                self.table_View_Travaux.setModel(self.filtre_modele)
                self.table_View_Travaux.setItemDelegate(mon_delegate())
     
                #si modification doit être impossible:
                #self.tableViewTravaux.setEditTriggers(QAbstractItemView.NoEditTriggers)
     
                self.bouton_ajouter = QPushButton("Ajouter ligne")
                self.bouton_supprimer = QPushButton("Supprimer ligne courante")
                self.combobox_filtre = QComboBox()
                self.combobox_filtre.addItems(["1","2","3", "n"])
     
                self.bouton_ajouter.clicked.connect(self.insert_line)
                self.bouton_supprimer.clicked.connect(self.delete_line)
                self.table_View_Travaux.clicked.connect(self.on_Click)
                self.combobox_filtre.currentTextChanged.connect(self.combo_change)
     
                widget = QWidget(self)
                self.setCentralWidget(widget)
                layout = QVBoxLayout()
                layout.addWidget(self.table_View_Travaux)
                layout.addWidget(self.bouton_ajouter)
                layout.addWidget(self.bouton_supprimer)
                layout.addWidget(self.combobox_filtre)
                widget.setLayout(layout)
     
     
            def create_connection(self):
                self.db = QSqlDatabase.addDatabase("QSQLITE")
                self.db.setDatabaseName(self.db_file)
                if not self.db.open():
                    print("connexion impossible au fichier indiqué")
                    return False
                return True
     
            def insert_line(self):
                scroll_position = self.table_View_Travaux.verticalScrollBar().value()
                self.model_liste_travaux.ajouter_ligne()
                #self.table_View_Travaux.verticalScrollBar.setValue(scroll_position) => KO
     
            def delete_line(self):
                self.model_liste_travaux.supprimer_ligne()
     
            def on_Click(self):
                index = (self.table_View_Travaux.selectionModel().currentIndex())
                self.selected_id = index.sibling(index.row(), 0).data()
                self.selected_value = index.sibling(index.row(),index.column()).data()
     
                print(self.model_liste_travaux.data(index))
                print(self.table_View_Travaux.selectionModel().currentIndex().data())
                print(self.selected_id, self.selected_value)
     
            def combo_change(self):
                self.filtre_modele.filtrer_colonne_combobox1(self.combobox_filtre.currentText())
     
     
    if __name__ == "__main__":
            application = QApplication(sys.argv)
            win = MyWindow()
            win.show() 
            sys.exit(application.exec_())

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

Discussions similaires

  1. [AS400] synchronisation db2 et mysql
    Par jmxlinux dans le forum DB2
    Réponses: 7
    Dernier message: 23/11/2008, 23h06
  2. Pb de synchronisation avec Navicat MYSQL
    Par koni42 dans le forum SQL Procédural
    Réponses: 0
    Dernier message: 21/02/2008, 09h56
  3. [MySQL] Synchroniser php et MySQL sans rafraichissement de la page
    Par Khleo dans le forum PHP & Base de données
    Réponses: 4
    Dernier message: 16/01/2008, 08h10
  4. Synchronisation DB/400 <-> MySQL
    Par ruiz.nicolas dans le forum AS/400
    Réponses: 4
    Dernier message: 29/12/2007, 13h20
  5. Synchronisation deux Bases Mysql
    Par eric_89 dans le forum SQL Procédural
    Réponses: 4
    Dernier message: 14/12/2006, 15h57

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