PyQGIS – Using Dynamic Parameters in Qt Designer for QGIS Projects

pyqgispyqtpyqt5qgisqt-designer

Started building my first plugin in qgis and looking for a way to create dynamic widget in pyqyt and Qt designer which allow user to add or delete parameters as much as he want (something like a '+' and '-' buttons). The goal is to get from the user list of layers and some other parameters which describe user preference for each layer (some other strings, fields etc). Looked for some table widget and didn't find some useful base neither in qt designer nor in the code part. The result widget should look something like that:

layer1. Field1. Str1. Bool1

layer2. Field2. Str2. Bool2

layer3. Field3. Str3. Bool3

Add Layer button | Remove layer button

Best Answer

You can get something like this :

enter image description here

The + button allow the user to add a new line, and the - button remove the last line. It's possible to remove the selected line with the selectedIndexes() method from the QAbstractItemView Class. The Get Value button print a dictionary in the python console with an element for each row and a list with all the values.

{'layer_1': ['connexions', 'id', 'first_layer', 'True'], 'layer_2': ['lineaire_spirit_m', 'fid', 'second_layer', 'False']}

Here is the full code, it works in the QGIS Python Console :

from PyQt5.QtWidgets import QPushButton, QTableWidget, QComboBox, QDialog

class Dialog(QDialog):
    def __init__(self, parent=None):
        super(Dialog, self).__init__(parent)
        # Create the dialog
        self.resize(700, 400)  # Set the size of the dialog.
        
        # Create the table
        self.tableWidget = QTableWidget(self)
        self.tableWidget.setGeometry(130, 50, 500, 300)  # Set the size of the table.
        self.tableWidget.setColumnCount(4)  # Number of columns.
        self.tableWidget.setRowCount(0)  # Number of rows.
        self.tableWidget.setHorizontalHeaderLabels(["Layer", "Field", "String", "Bool"])  # Set labels for columns.
        self.add_layer()  # Add the first row.
        
        # Create the add layer button
        button_plus = QPushButton(self)
        button_plus.setText("+")  # Set label of the button.
        button_plus.setGeometry(30, 50, 50, 50)  # Set the size of the button.
        button_plus.clicked.connect(self.add_layer)  # Connect the button to a function.
        
        # Create the remove layer button
        button_minus = QPushButton(self)
        button_minus.setText("-")  # Set label of the button.
        button_minus.setGeometry(30, 150, 50, 50)  # Set the size of the button.
        button_minus.clicked.connect(self.remove_layer)  # Connect the button to a function.
        
        # Create the get value button
        button_result = QPushButton(self)
        button_result.setText("Get Value")  # Set label of the button.
        button_result.setGeometry(10, 250, 100, 50)  # Set the size of the button.
        button_result.clicked.connect(self.get_result)  # Connect the button to a function.

    def add_layer(self):
        '''
        Function to add a row in the table. Add 3 widgets to the row:
        - a layer combo box
        - a field combo box
        - a boolean combo box
        '''
        row_number = self.tableWidget.rowCount()  # Get row number.
        self.tableWidget.insertRow(row_number)  # Insert a row.
        layer_comboBox = QgsMapLayerComboBox()  # Create the layer combo box.
        self.tableWidget.setCellWidget(row_number, 0, layer_comboBox)  # Add the layer combo box to the row in first column.
        field_comboBox = QgsFieldComboBox()  # Create the field combo box.
        self.tableWidget.setCellWidget(row_number, 1, field_comboBox)  # Add the field combo box to the row in second column.
        layer_comboBox.layerChanged.connect(field_comboBox.setLayer)  # Connect the layer combo box to the field combo box.
        bool_comboBox = QComboBox()  # Create the boolean combo box.
        bool_comboBox.addItems(["True", "False"])  # Add the value to the boolean combo box.
        self.tableWidget.setCellWidget(row_number, 3, bool_comboBox)  # Add the boolean combo box to the row in fourth column.

    def remove_layer(self):
        '''
        Function to remove the last row in the table.
        '''
        row_number = self.tableWidget.rowCount()  # Get row number.
        self.tableWidget.removeRow(row_number - 1)  # Remove a row.

    def get_result(self):
        '''
        Function to get the values in the table.
        '''
        result = {}  # Create a dictionary to get the values of every row.
        # Iteration over the rows.
        for row in range(self.tableWidget.rowCount()):
            row_result = []  # Create a list to get the values of every column.
            # Iteration over the columns.
            for column in range(self.tableWidget.columnCount()):
                if column == 2:
                    row_result.append(self.tableWidget.item(row, column).text())  # The third column is not a cellWidget, the value is fetch differently.
                else:
                    row_result.append(self.tableWidget.cellWidget(row, column).currentText())
            result["layer_"+str(row + 1)] = row_result  # Add the list of every column to the dictionary with the layer number as key.
        print(result)
dialog = Dialog()
dialog.show()