3

I have a QTableWidget which contains the names of layers and values. The code below creates a range of values from a list, iterates through some groups whilst creating a dictionary containing the layer name and value from the QTableWidget.

The output is a simple SQL expression which contains the layer name and its value in order of when the layer appears in the table. I want to change this order so that the layer with the higher values are included first.

Here is the code I used:

def func(self):
    root = QgsProject.instance().layerTreeRoot()
    group_name = root.findGroup('Group')
    qTable = self.dockwidget.tableWidget
    delim = ' WHEN '
    # Number of layers in 5 groups
    layer_count = [15, 14, 1, 2, 1]                  

    top = list(np.cumsum(layer_count))
    bottom = list(np.subtract(np.cumsum(layer_count),layer_count))
    pairs = zip(bottom,top)
    ranges = iter([range(x[0], x[1]) for x in pairs])

    # Iterate through the 5 groups
    for group in group_name.children():
        value_dict = {}    
        for row in next(ranges):
            first_item = qTable.item(row, 0)
            first_name = str(first_item.text())
            second_item = qTable.item(row,1)
            value = str(second_item.text())
            value_dict[first_name] = value

        layers = [g.layerName() for g in group.children()]
        fscorestrs = {k:delim.join('"{layer}_Score" = {N} THEN "{layer}_Score"'.format(
                layer = l, N = value_dict[first_name]) for l in layers) for k in range(1)}
        formula = ("CASE WHEN " + fscorestrs[0] + " ELSE NULL END")

For example:

Here is a simple layout for the QTableWidget:

QTableWidget

If I use the above code, the formula reads:

CASE 
WHEN "Layer_1_Score" = 0.1 THEN "Layer_1_Score" 
WHEN "Layer_2_Score" = 0.7 THEN "Layer_2_Score" 
WHEN "Layer_3_Score" = 0.5 THEN "Layer_3_Score" 
WHEN "Layer_4_Score" = 0.8 THEN "Layer_4_Score" 
WHEN "Layer_5_Score" = 0.6 THEN "Layer_5_Score" 
ELSE NULL 
END

What I am looking for is to instead produce an output where the layers are ordered in terms of the value, so the above should look like:

CASE 
WHEN "Layer_4_Score" = 0.8 THEN "Layer_4_Score" 
WHEN "Layer_2_Score" = 0.7 THEN "Layer_2_Score" 
WHEN "Layer_5_Score" = 0.6 THEN "Layer_5_Score" 
WHEN "Layer_3_Score" = 0.5 THEN "Layer_3_Score" 
WHEN "Layer_1_Score" = 0.1 THEN "Layer_1_Score" 
ELSE NULL 
END

How could I achieve this?

  • 1
    Most of the qgis code is irrelevant here - its just a python problem. You just need to sort your object on the value, but the way you've constructed that object is a bit obscured by all the other code. Boil it down to its essence, to create a minimal example. Its hard for anyone to duplicate your python object as it stands – Spacedman Apr 7 '17 at 13:27
  • @Spacedman - Hopefully this will suffice =) – Joseph Apr 7 '17 at 14:04
2

Firstly, import this library at the beginning of the code:

import operator

Then, once you have defined the layers list and assuming that:

delim = 'WHEN ' # the difference is only on the blank space before the 'W' letter

you may use this code:

# sort 'value_dict' by values
sorted_value_dict = sorted(value_dict.items(), key=operator.itemgetter(1), reverse = True)
fscorestrs = delim.join('"{layer}_Score" = {N} THEN "{layer}_Score"\n'.format(
                layer = sorted_value_dict[w][0], N = sorted_value_dict[w][1]) for w in range(0, len(sorted_value_dict)))
formula = ("CASE"+"\n"+"WHEN " + fscorestrs + "ELSE NULL" +"\n" + "END")

instead of what you have provided. I also slightly edited the final formatting of the expression, hoping you like it.

Using random values as a test, I get these prints:

print value_dict
{'layer_1': 0.3457999606457025, 'layer_3': 0.8843901948820355, 'layer_2': 0.6233214287240216, 'layer_5': 0.4854387498039854, 'layer_4': 0.07955152029877766}

print sorted_value_dict
[('layer_3', 0.8843901948820355), ('layer_2', 0.6233214287240216), ('layer_5', 0.4854387498039854), ('layer_1', 0.3457999606457025), ('layer_4', 0.07955152029877766)]

print formula
CASE
WHEN "layer_3_Score" = 0.884390194882 THEN "layer_3_Score"
WHEN "layer_2_Score" = 0.623321428724 THEN "layer_2_Score"
WHEN "layer_5_Score" = 0.485438749804 THEN "layer_5_Score"
WHEN "layer_1_Score" = 0.345799960646 THEN "layer_1_Score"
WHEN "layer_4_Score" = 0.0795515202988 THEN "layer_4_Score"
ELSE NULL
END
  • 1
    Genius! This works great, many thanks! Although I did have to replace from operator import itemgetter with import operator :) – Joseph Apr 10 '17 at 9:24
  • 1
    @Joseph Thanks for this note, I immediately edit my answer! =) – mgri Apr 10 '17 at 10:29

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.