4

I wrote a simple plugin using Plugin Builder running QGIS 3 buffer process on selected layers/features. It works well, but if the plugin dialog window is closed and re-opened, multiple features get added instead of one (x2, x3, x4, x5, etc, depending on how often the dialog got re-opened).

Does anyone know how to clear the memory layers when the dialog close (X) button is pressed?

    def run(self):
    """Run method that performs all the real work"""

    if self.first_start == True:
        self.first_start = False
        self.dlg = PlugtestDialog()
        self.dlg.setWindowFlags(Qt.WindowStaysOnTopHint)

    self.dlg.layer_comboBox.clear()
    self.dlg.fieldwidth_comboBox.clear()

    # 1/ gets the list of all the layers loaded in QGIS:
    layers = [layer for layer in QgsProject.instance().mapLayers().values()]
    layer_list = []
    layer_list.append('')
    for layer in layers:
            layer_list.append(layer.name())

    #adds list of loaded layer to the drop-down
    self.dlg.layer_comboBox.addItems(layer_list)

    # 2/ if layers are loaded, check if there are selected features and select the layer if so. Returns associated fields. 

    selected_layer = self.iface.activeLayer()
    if selected_layer is not None:
        selected_layer_name = selected_layer.name()
        features_count = selected_layer.selectedFeatureCount()
        index_number_selected_layer = layer_list.index(selected_layer_name)

        if features_count is not 0 and selected_layer is not None:
            self.dlg.layer_comboBox.setCurrentIndex(index_number_selected_layer)
            self.dlg.fieldwidth_comboBox.clear()
            selectedComboLayerText = self.dlg.layer_comboBox.currentText()
            selectedComboLayer = QgsProject.instance().mapLayersByName(str(selectedComboLayerText))[0]
            fields = [field.name() for field in selectedComboLayer.fields()]
            self.dlg.fieldwidth_comboBox.addItems(fields)

    # 3/ function to fill the list of field for the selected layer in the layer drop-down. Gets launched when layer drop-down menu changes

    def field_select():
        #self.dlg.field_comboBox.clear()
        self.dlg.fieldwidth_comboBox.clear()

        selectedComboLayerText = self.dlg.layer_comboBox.currentText()
        #conditional to prevent 'list out of range' error in case the empty entry is selected again
        if selectedComboLayerText is '':
            self.dlg.fieldwidth_comboBox.clear()
        else:
            selectedComboLayer = QgsProject.instance().mapLayersByName(str(selectedComboLayerText))[0]
            fields = [field.name() for field in selectedComboLayer.fields()]
            self.dlg.fieldwidth_comboBox.addItems(fields)

    # This connects the function to the layer drop-down when changed. Update fields to show the fields from the associated layer in layer drop down
    self.dlg.layer_comboBox.currentIndexChanged.connect(field_select)
    self.dlg.fixed_Button.setChecked(True)


    #function to apply a buffer on selected feature
    def buffer_on_selected():

        #selected_layer = self.iface.activeLayer()
        selectedComboLayerText = self.dlg.layer_comboBox.currentText()
        selectedComboLayer = QgsProject.instance().mapLayersByName(str(selectedComboLayerText))[0]

        spinbox_value = self.dlg.spinBox.value()

        if self.dlg.fixed_Button.isChecked() is True:
            buffer_option = self.dlg.spinBox.value()

        #dynamic (by field)
        if self.dlg.field_Button.isChecked() is True:
            field_radius = self.dlg.fieldwidth_comboBox.currentText() 
            buffer_option = QgsProperty.fromExpression(field_radius)

        #dissolved?
        if self.dlg.dissolve_checkBox.isChecked() is True:
            dissolve_option = True
        else:
            dissolve_option = False

        #selected features:
        # Check if features are selected and if tick box is checked
        if selectedComboLayer.selectedFeatureCount() > 0 and self.dlg.only_selected_checkBox.isChecked() is True:


            #if there's already an ouput layer that is loaded, just update the layer:               
            if QgsProject.instance().mapLayersByName('output'):

                buffer_function = processing.run("native:buffer",{'INPUT':QgsProcessingFeatureSourceDefinition(selectedComboLayerText,True),'DISTANCE':buffer_option,'SEGMENTS':5,'END_CAP_STYLE':0,'JOIN_STYLE':0,'MITER_LIMIT':2,'DISSOLVE':dissolve_option,'OUTPUT':'memory:'})

                sourceLYR = buffer_function['OUTPUT']
                destLYR = QgsProject.instance().mapLayersByName('output')[0]
                features2 = []
                for feature in sourceLYR.getFeatures():
                    features2.append(feature)
                destLYR.startEditing()
                data_provider = destLYR.dataProvider()
                data_provider.addFeatures(features2)
                destLYR.commitChanges()

            #if not, create output
            else:

                buffer_function = processing.run("native:buffer",{'INPUT':QgsProcessingFeatureSourceDefinition(selectedComboLayerText,True),'DISTANCE':buffer_option,'SEGMENTS':5,'END_CAP_STYLE':0,'JOIN_STYLE':0,'MITER_LIMIT':2,'DISSOLVE':dissolve_option,'OUTPUT':'memory:'})
                QgsProject.instance().addMapLayer(buffer_function['OUTPUT'])

        #if not, create buffer for all features in the selected layer
        else:

            parameters = {'INPUT':selectedComboLayer,
            'DISTANCE':buffer_option,
            'SEGMENTS':5,
            'END_CAP_STYLE':0,
            'JOIN_STYLE':0,
            'MITER_LIMIT':2,
            'DISSOLVE':dissolve_option,
            'OUTPUT':'memory:'}
            buffer_function = processing.run("native:buffer", parameters)


    # show the dialog
    self.dlg.show()

    self.dlg.BigButton.clicked.connect(buffer_on_selected)
2
  • 1
    You should post some code for the main part of your plugin to allow the community to see where the problem could lie. But I would probably suggest that you create/edit a function which gets executed when the dialog closes which deletes any instances of the memory layer (e.g. del mem_layer or whatever you named it.) – Joseph Jan 25 '19 at 10:53
  • I'd really like to clear the memory outputs when the dialog gets closed but I didn't find the right way to do it. What function name does the Close (x) button has? I also can't figure a way to delete if only buffer['output'] exists. What to do to avoid returning an error if no layer exist? – Leo Jan 30 '19 at 2:35
1

Plugin Builder does check for first_start. All pushButtons should be placed there to avoid multiple launches if the plugin dialog is open/closed multiple times.

self.dlg.BigButton.clicked.connect(buffer_on_selected)

should be placed at the beginning of def run(self):

if self.first_start == True: self.first_start = False self.dlg = PlugtestDialog() self.dlg.setWindowFlags(Qt.WindowStaysOnTopHint) self.dlg.BigButton.clicked.connect(buffer_on_selected) <-----

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.