Search code examples
pythonlayoutpyqtpyqt4qgridlayout

How to add rows of widgets after deletion in a QGridLayout


I am adding rows of widgets to a QGridLayout like this (with a button):

def ajouter_un_mot_vocab(self) :
    '''
    '''

    # Dictionnaire des mots de vocabulaire    
    self.dico_vocab_mot = {}
    # Dictionnaire des définitions des mots de vocabulaire
    self.dico_vocab_def = {}

    # Liste pour chargement des données 
    # (écriture des textes par l'utilisateur)
    self.liste_mots_vocabulaire = []

    print
    print 'self.grille_3_stack_3.rowCount() creation', self.grille_3_stack_3.rowCount()
    print 

    #
    for r in range(self.grille_3_stack_3.rowCount()) :
        # Création des widgets et taille générique
        self.dico_vocab_mot[r] = QTextEdit()
        self.dico_vocab_def[r] = QTextEdit()
        self.dico_vocab_mot[r].setMaximumWidth(180)
        self.dico_vocab_mot[r].setMinimumWidth(180)
        self.dico_vocab_mot[r].setMaximumHeight(54)
        self.dico_vocab_mot[r].setMinimumHeight(54)
        self.dico_vocab_def[r].setMaximumHeight(54)
        self.dico_vocab_def[r].setMinimumHeight(54)
        print 'r', r
        # Conditions de redimensionnement
        if r > 5 :
            self.dico_vocab_mot[r].setMaximumHeight(34)
            self.dico_vocab_mot[r].setMinimumHeight(34)
            self.dico_vocab_def[r].setMaximumHeight(34)
            self.dico_vocab_def[r].setMinimumHeight(34)
        # Répartition dans la grille
        self.grille_3_stack_3.addWidget(self.dico_vocab_mot[r], r+1, 0)
        self.grille_3_stack_3.addWidget(self.dico_vocab_def[r], r+1, 1)
        # Ecriture des n°s de lignes dans la partie mots de vocabulaire
        self.grille_3_stack_3.addWidget(self.dico_vocab_mot[r].setText(str(r+1)+'. '), r+1, 0)
        # Les données sont introduites dans une liste
        self.liste_mots_vocabulaire.append([self.dico_vocab_mot[r], self.dico_vocab_def[r]])
        # =====================================================
        # Signaux
        self.dico_vocab_mot[r].textChanged.connect(self.changements_phase_3)
        self.dico_vocab_def[r].textChanged.connect(self.changements_phase_3)
        # =====================================================

        print 'self.dico_vocab_mot', self.dico_vocab_mot
        print 'self.dico_vocab_def', self.dico_vocab_def

    print self.liste_mots_vocabulaire

And removing the rows of widgets like this:

def supprimer_un_mot_vocab(self) :
    '''
    '''
    index = len(self.liste_mots_vocabulaire)-1
    for r in reversed(range(self.grille_3_stack_3.rowCount())) :
        for c in reversed(range(self.grille_3_stack_3.columnCount())) 
            layout = self.grille_3_stack_3.itemAtPosition(r, c)
            if layout is not None :
                layout.widget().deleteLater()
                #layout_1.widget().hide()
                self.grille_3_stack_3.removeItem(layout)

    self.liste_mots_vocabulaire.pop() 
    del self.dico_vocab_mot[index] 
    del self.dico_vocab_def[index] 

    print
    print "rowCount apres suppr", self.grille_3_stack_3.rowCount()

    print
    print self.dico_vocab_mot
    print self.dico_vocab_def
    print self.liste_mots_vocabulaire
    print

Everything works well the first time, when I add rows of widgets (for example five rows of widgets). Everything works well too if I delete the rows (for example two rows of widgets). But as soon as I decide to add a row again (after deletion), it does not work properly: I end up with six lines, while I should have three.

It must be my code for deleting lines that does not work properly (yet the rows are removed visually). What am I doing wrong?


Solution

  • A QGridLayout does not delete any rows when its widgets are removed - but the empty rows do not take up any space, so you won't usually notice them. If you add widgets beyond the current row-count, the layout will simply grow to the required size. So if the current row-count is 5, and you add a widget at row 10, the row-count will immediately change to 10, and five empty intervening rows will be created.

    A grid-layout always starts with a row-count of one. But in your code, you use r + 1 when adding widgets:

    for r in range(self.grille_3_stack_3.rowCount()):
        ...
        self.grille_3_stack_3.addWidget(self.dico_vocab_mot[r], r+1, 0)
        self.grille_3_stack_3.addWidget(self.dico_vocab_def[r], r+1, 1)
        # Ecriture des n°s de lignes dans la partie mots de vocabulaire
        self.grille_3_stack_3.addWidget(self.dico_vocab_mot[r].setText(str(r+1)+'. '), r+1, 0)
    

    This will skip the first row, and start adding widgets a row index 1. So after adding five rows of widgets, the row-count will be six, rather than five. And every time you delete and re-add the widgets, the row-count will grow by one.

    Your code should instead look like this:

    for r in range(self.grille_3_stack_3.rowCount()):
        ...
        self.grille_3_stack_3.addWidget(self.dico_vocab_mot[r], r, 0)
        self.grille_3_stack_3.addWidget(self.dico_vocab_def[r], r, 1)
        self.dico_vocab_mot[r].setText(str(r+1)+'. ')
    

    (Note that I have also fixed the last line here, which previously made no sense).

    EDIT:

    As per the comments, here are the changes needed so that one row is added for each click of the first button, and all rows are cleared when clicking the second button.

    class EssaiQStackedWidget_Seq_Prof(QWidget) :
        def __init__(self, parent=None):
            ...
            self.dico_vocab_mot = {}
            self.dico_vocab_def = {}    
            self.liste_mots_vocabulaire = []
    
            self.liste_menu.currentIndexChanged.connect(self.affiche_phases)
    
        def ajouter_un_mot_vocab(self) :
            r = len(self.liste_mots_vocabulaire)
            self.dico_vocab_mot[r] = QTextEdit()
            self.dico_vocab_def[r] = QTextEdit()
            self.dico_vocab_mot[r].setMaximumWidth(180)
            self.dico_vocab_mot[r].setMinimumWidth(180)
            self.dico_vocab_mot[r].setMaximumHeight(54)
            self.dico_vocab_mot[r].setMinimumHeight(54)
            self.dico_vocab_def[r].setMaximumHeight(54)
            self.dico_vocab_def[r].setMinimumHeight(54)
            if r > 5 :
                self.dico_vocab_mot[r].setMaximumHeight(34)
                self.dico_vocab_mot[r].setMinimumHeight(34)
                self.dico_vocab_def[r].setMaximumHeight(34)
                self.dico_vocab_def[r].setMinimumHeight(34)
            self.grille_3_stack_3.addWidget(self.dico_vocab_mot[r], r, 0)
            self.grille_3_stack_3.addWidget(self.dico_vocab_def[r], r, 1)
            self.dico_vocab_mot[r].setText(str(r+1)+'. ')
            self.liste_mots_vocabulaire.append([self.dico_vocab_mot[r], self.dico_vocab_def[r]])
            self.dico_vocab_mot[r].textChanged.connect(self.changements_phase_3)
            self.dico_vocab_def[r].textChanged.connect(self.changements_phase_3)
    
        def supprimer_un_mot_vocab(self) :
            row = len(self.liste_mots_vocabulaire) - 1
            if row >= 0:
                for column in range(self.grille_3_stack_3.columnCount()):
                    item = self.grille_3_stack_3.itemAtPosition(row, column)
                    if item is not None:
                        item.widget().deleteLater()
                del self.liste_mots_vocabulaire[row]
                del self.dico_vocab_mot[row]
                del self.dico_vocab_def[row]