3

I've used the Plugin Builder tool to create the template python code. In QT Designer I have added a QgsMapLayerComboBox and QgsFieldComboBox

enter image description here

I have gone through a couple of tutorials and checked the forums (ex How to connect QgsMapLayerComboBox to QgsDataDefinedButton ; How to use QgsFieldComboBox in building QGIS plugin and Populating ComboBox with unique features in selected attribute field using PyQGIS) and also looked at some existing plugins but can't work out how.

I have connected a normal combo box successfully using https://www.youtube.com/watch?v=nWKyRDxUIrI

The simple code I want to create a plugin to use in model builder is below. Comments indicate what I want as fields in the plugin.

import operator, datetime, re

in_layer_nm='t1' # Selected dataset from canvas or file system
attrib_n='Log Reason' #Attribute list from chosen dataset
regex_s=r'(Heartbeat|End)' # Regex code for search
out_val=feat['Transmission Delay(s)'] # new value -- use feat['column name'] to use a specific column or create custom value

def UpdateRecord(f,fieldName,ntext):
    in_layer.startEditing()
    FieldIndex = feat.fields().indexFromName(fieldName)
    in_layer.changeAttributeValue(feat.id(), FieldIndex, ntext)
    in_layer.commitChanges()


in_layer = QgsProject().instance().mapLayersByName(in_layer_nm)[0]
fids = []
feats = [f for f in in_layer.getFeatures()]

for feat in feats:
    name = feat[attrib_n]
    print (name)
    if name != NULL:
        if len(re.findall(regex_s, name))>0:
            UpdateRecord(feat,attrib_n,out_val)

I haven't added the text fields to the plugin layout yet as I first need to resolve the connection between the chosen dataset and choosing the attribute column.

I have tried the following in the plugin _dialog.py file that's created by the Plugin Builder tool and it works if I use a normal combo box but not the QgsFieldComboBox and I prefer to use that as it seems to be dedicated for this purpose.

import os

from qgis.PyQt import uic
from qgis.PyQt import QtWidgets

from qgis.PyQt.uic import loadUiType
from qgis.PyQt.QtWidgets import QDialog, QAbstractItemView, QTableWidget, QTableWidgetItem
from qgis.PyQt.QtCore import Qt, QThread, QEvent, QCoreApplication

from qgis.core import QgsVectorLayer, Qgis, QgsProject, QgsWkbTypes, QgsMapLayer, QgsFields, QgsExpressionContextUtils

# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
FORM_CLASS, _ = uic.loadUiType(os.path.join(
    os.path.dirname(__file__), 'search_update_dialog_base.ui'))


class Search_UpdateDialog(QtWidgets.QDialog, FORM_CLASS):
    def __init__(self, parent=None):
        """Constructor."""
        super(Search_UpdateDialog, self).__init__(parent)
        self.setupUi(self)
        #ref https://www.youtube.com/watch?v=KQ8mOpVQ62w&t=235s
        self.pbPrint.clicked.connect(self.onpbPrint_text_Clicked) #run thepbPrint module when button is pressed
        self.mMapLayerCB.layerChanged.connect(self.load_fields) #run the load_fields def when the layer is chosen
        self.mFieldCB.currentIndexChanged.connect(self.load_text_values)
        
    def layerSelected(self):
        '''The user has made a selection so we need to initialize other
            parts of the dialog box'''
        self.initFieldList()  
        
        
    def initFieldList(self):
        selectedLayer = self.mMapLayerCB.currentIndex()
#        self.mFieldCB.clear()
#        self.mFieldCB.addItem(tr('<All Fields>'))  
        
    def updateLayers(self):
        '''Called when a layer has been added or deleted in QGIS.
        It forces the dialog to reload.'''
        # Stop any existing search
        self.killWorker()
        if self.isVisible() or len(self.results) != 0:
            self.populateLayerListComboBox()
            self.clearResults()
            self.layers_need_updating = False
        else:
            self.layers_need_updating = True 
            
    def onpbPrint_text_Clicked(self):
# Test 2 (not working)        
#        lyr_nm=self.mMapLayerCB.currentLayer()
#        feats = [f for f in lyr_nm.getFeatures()] 
#        attrib_n = "FEATURETYP"
#        for feat in feats:
#            name = feat[attrib_n]
#            print (name)
#            self.pbPrint.setText(str(name))
# Test 1   (load layer from drop down and get attribute names to fill into field combo box)
        lyr_nm=self.mMapLayerCB.currentLayer()
        print (lyr_nm)
        self.pbPrint.setText(str(lyr_nm))
        for lyr in lyr_nm.fields():
            attribname = lyr.name()
            print (attribname)
#            if attribname == "Log Reason":
#            text = lyr.attribute()
#            print (text)
#            self.pbPrint.setText(str(text))
            
    def load_fields(self, index=0): #Ref https://www.youtube.com/watch?v=nWKyRDxUIrI
        self.mFieldCB.clear()
        #prov = self.mMapLayerCB[index].dataProvider()
        lyr_nm=self.mMapLayerCB.currentLayer()
        print (lyr_nm)
        #field_name = [field.name() for field in prov.fields()]
        field_name = [field.name() for field in lyr_nm.fields()]
        print (field_name)
        self.mFieldCB.addItems(field_name)

    def load_text_values(self): 
        regex_search_val = self.RegexSearch.toPlainText()
        OutputText_val = self.OutputText.toPlainText()
#        print (regex_search_val,OutputText_val)

The push button is just to see what output I get. Once we get the text inputs how do we use them when the Ok button is pressed?

1 Answer 1

4

When using QgsFieldComboBox, you don't have to build a field list to populate the combobox. The class has setLayer property to populate fields automatically.

Remove load_fields function (you don't need it). And change

self.mMapLayerCB.layerChanged.connect(self.load_fields)

to

self.mMapLayerCB.layerChanged.connect(self.mFieldCB.setLayer)
3
  • Thanks, once we get this how do we use these values when Ok is pressed?
    – GeorgeC
    Commented Aug 29, 2022 at 10:15
  • 1
    field_name = self.mMapLayerCB.currentField() returns the field name as string, and you can get the field value of a feature using feat[field_name] (in Test 2). Commented Aug 29, 2022 at 10:23
  • Maybe this should be a new question but the code to run when OK is pressed is in the py file that's created by the plugin (not the xxx_dialog.py but xxx.py). But in that in the def run(self): doesn't recognise the values from the xxx_dialog.py file. So I get errors like field_name = self.mMapLayerCB.currentField() AttributeError: 'Search_Update' object has no attribute 'mMapLayerCB'
    – GeorgeC
    Commented Aug 29, 2022 at 10:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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