6
\$\begingroup\$

I am developing a plugin for QGIS (a Geographic Information Systems) software which uses Python. I have several functions where each of them is connected to a QCheckbox. So when a checkbox is checked, the associated function then does the following:

  1. It reads the text of a QComboBox which contains a list of layers.
  2. Identifies the layer by its name.
  3. Sets an expression to be used.
  4. Selects all features which satisfies the expression.

And when the checkbox is unchecked:

  1. Deselects all features which satisfies the expression.

Is there a way to merge these functions into one or make it more consise?

Here is the code:

def score_0():
    opp_selectedLayerIndex = self.dockwidget.areaOpporunityMap_combo.currentText()
    opp_sel_layer = QgsMapLayerRegistry.instance().mapLayersByName(str(opp_selectedLayerIndex))[0]   
    # Select all features where "Score" = NULL
    expr = QgsExpression( """ "Score" IS NULL """ )
    it = opp_sel_layer.getFeatures( QgsFeatureRequest( expr ) )
    ids = [i.id() for i in it]
    area = 0
    if self.dockwidget.score0_checkbox.isChecked():
        opp_sel_layer.select( ids )            
    else:  
        opp_sel_layer.deselect( ids)            

def score_1():
    opp_selectedLayerIndex = self.dockwidget.areaOpporunityMap_combo.currentText()
    opp_sel_layer = QgsMapLayerRegistry.instance().mapLayersByName(str(opp_selectedLayerIndex))[0]   
    # Select all features where "Score" = 1
    expr = QgsExpression( """ "Score" = 1 OR "Category" = 'Possible' OR "Category" = 'Favourable'  """ )
    it = opp_sel_layer.getFeatures( QgsFeatureRequest( expr ) )
    ids = [i.id() for i in it]
    area = 0
    if self.dockwidget.score1_checkbox.isChecked():
        opp_sel_layer.select( ids )             
    else:    
        opp_sel_layer.deselect( ids)            

def score_2():
    opp_selectedLayerIndex = self.dockwidget.areaOpporunityMap_combo.currentText()
    opp_sel_layer = QgsMapLayerRegistry.instance().mapLayersByName(str(opp_selectedLayerIndex))[0]     
    # Select all features where "Score" = 2
    expr = QgsExpression( """ "Score" = 2 OR "Category" = 'Intermediate' OR "Category" = 'Likely' """ )
    it = opp_sel_layer.getFeatures( QgsFeatureRequest( expr ) )
    ids = [i.id() for i in it]
    area = 0
    if self.dockwidget.score2_checkbox.isChecked():
        opp_sel_layer.select( ids )              
    else:   
        opp_sel_layer.deselect( ids)            

def score_3():
    opp_selectedLayerIndex = self.dockwidget.areaOpporunityMap_combo.currentText()
    opp_sel_layer = QgsMapLayerRegistry.instance().mapLayersByName(str(opp_selectedLayerIndex))[0]  
    # Select all features where "Score" = 3  
    expr = QgsExpression( """ "Score" = 3 OR "Category" = 'Sensitive' OR "Category" = 'Unlikely' """ )
    it = opp_sel_layer.getFeatures( QgsFeatureRequest( expr ) )
    ids = [i.id() for i in it]
    area = 0
    if self.dockwidget.score3_checkbox.isChecked():
        opp_sel_layer.select( ids )            
    else:   
        opp_sel_layer.deselect( ids)            

def score_4():
    opp_selectedLayerIndex = self.dockwidget.areaOpporunityMap_combo.currentText()
    opp_sel_layer = QgsMapLayerRegistry.instance().mapLayersByName(str(opp_selectedLayerIndex))[0]    
    # Select all features where "Score" = 4
    expr = QgsExpression( """ "Score" = 4 OR "Category" = 'Showstopper' """ )
    it = opp_sel_layer.getFeatures( QgsFeatureRequest( expr ) )
    ids = [i.id() for i in it]
    area = 0
    if self.dockwidget.score4_checkbox.isChecked():
        opp_sel_layer.select( ids )           
    else:   
        opp_sel_layer.deselect( ids)


# Connect checkboxes with their associated functions
self.dockwidget.score0_checkbox.stateChanged.connect(score_0)
self.dockwidget.score1_checkbox.stateChanged.connect(score_1)
self.dockwidget.score2_checkbox.stateChanged.connect(score_2)
self.dockwidget.score3_checkbox.stateChanged.connect(score_3)
self.dockwidget.score4_checkbox.stateChanged.connect(score_4)
\$\endgroup\$
3
\$\begingroup\$

you only have a difference in two places: query and attribute of dockwidget you are going to check against. so you can pass them as parameters to your score function.

def score(self, query, check_attr):    
    opp_selectedLayerIndex = self.dockwidget.areaOpporunityMap_combo.currentText()
    opp_sel_layer = QgsMapLayerRegistry.instance().mapLayersByName(str(opp_selectedLayerIndex))[0]   
    # Select all features where "Score" = NULL
    expr = QgsExpression(query)
    it = opp_sel_layer.getFeatures(QgsFeatureRequest(expr))
    ids = [i.id() for i in it]

    checkbox = self.dockwidget.getattr(check_attr)

    if checkbox.isChecked():
        opp_sel_layer.select(ids)            
    else:  
        opp_sel_layer.deselect(ids)
\$\endgroup\$
2
\$\begingroup\$

You could exploit the fact that in Python functions are first-class objects. What this means is that you can return a function from a function. This can be used to define a function factory, a factory that produces a function depending on the arguments passed to the outer function.

def score_factory(self, expr_str, widget):
    def score():
        opp_selectedLayerIndex = self.dockwidget.areaOpporunityMap_combo.currentText()
        opp_sel_layer = QgsMapLayerRegistry.instance().mapLayersByName(str(opp_selectedLayerIndex))[0]   
        expr = QgsExpression(expr_str)
        it = opp_sel_layer.getFeatures(QgsFeatureRequest(expr))
        ids = [i.id() for i in it]
        area = 0
        if widget.isChecked():
            opp_sel_layer.select(ids)
        else:  
            opp_sel_layer.deselect(ids)
    return score

Then you can use this like this:

self.dockwidget.score1_checkbox.stateChanged.connect(self.score_factory(""" "Score" = 1 OR "Category" = 'Possible' OR "Category" = 'Favourable'  """, self.dockwidget.score1_checkbox))

or, even better, do it all in a loop:

widgets = [self.dockwidget.score0_checkbox,
           self.dockwidget.score1_checkbox,
           ...]
score_exprs = [""" "Score" IS NULL """,
               """ "Score" = 1 OR "Category" = 'Possible' OR "Category" = 'Favourable'  """,
               ...]
for widget, expr in zip(widgets, score_exprs):
    widget.stateChanged.connect(self.score_factory(expr, widget))
\$\endgroup\$

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.