5

I am redesigning my years old form for collecting data in ornithologic field surveys because I finally want to update my tablet for field work from QGIS 2.8 to QGIS 3.10. One attribute contains a short name (token) of the species, which has to be chosen from a list with about 160 possible values (I use a value relation so that I see the full name of the species in the list). As scrolling through such a long list is annoying and error-prone I want to write a python script, that adds the most used values at the top of the list in the dropdown box, divided from the following complete list by a separator. So in the vast majority of cases, I don't have to scroll down, but just expand the dropdown box and tap on one of the then visible items.

And I want the script to preselect the single most used value in the list, so that in quite some cases, I don't have to change the species at all.

The experience that I gathered when writing the first version of this script ( How to store attribute values from a custom form into shapefile? ) nearly faded away but with some searching and reading I managed to write a code for python 3, which does nearly what I want (see code at the end of this posting). The only remaining problem is, that the preselection of the most used value does not work (lines 56 to 58 in code).

Surprisingly (for me) it works, when I add an empty QMessageBox just before the command for selecting the value (see line 57 in the code: QMessageBox().exec_()). After closing the Message Box, The value is selected as I want it.

Do you know a way to make my dropdown box work as intended without the annoying MessageBox?

I am developing the form on my desktop computer with QGIS 3.10 under Windows 10 64 Bit

# -*- coding: utf-8 -*-

from qgis.PyQt.QtWidgets import QDialog, QComboBox, QMessageBox
from qgis.core import QgsFeature

def FormOpen(dialog, layer, feature):
    global SpeciesField, SpeciesDictionary
    global CurrentFeature

########################################################
# Set the desired number of "most used" species at the
# top of the list

    MostUsedListMax = 28

########################################################

    CurrentLayer = layer
    MyDialog = dialog
    CurrentFeature = QgsFeature( feature )
    SpeciesField = MyDialog.findChild( QComboBox , 'ART_KURZ' )
    SpeciesField.setMaxVisibleItems( MostUsedListMax )

# Initialization of objects on first run
    if not 'SpeciesDictionary' in globals():
        InitializeData()

# Reset all counter fields to zero     
    for d in SpeciesDictionary:
        SpeciesDictionary[ d ] = ( 0 , SpeciesDictionary[ d ][1] )

# Count the occurences of each species' token in the layer
    features = CurrentLayer.getFeatures()
    for f in features:
        SpeciesDictionary[ f.attribute('ART_KURZ') ] = ( SpeciesDictionary[ f.attribute('ART_KURZ') ][0] + 1 , SpeciesDictionary[ f.attribute('ART_KURZ') ][1] )

# Make a list of the most used tokens
    k = 0
    MostUsedList = []
    for item in sorted(SpeciesDictionary.items(), key=lambda item: item[1][0] , reverse=True ):
        if item[1][0] > 0:
            MostUsedList.append( ( item[1][1] , item[0] ))
            k += 1
            if k >= MostUsedListMax :
                break

# Insert the items of the "most used" list in alphabetical order
# at the top of the list in the combobox    
    for m in sorted( MostUsedList , reverse=True ):
        SpeciesField.insertItem( 0 , m[0] , m[1] )

# Insert a separator
    SpeciesField.insertSeparator( len( MostUsedList ) )

# If feature is new, set species token to most used value
    if CurrentFeature.attribute("ART_KURZ") is None:
        QMessageBox().exec_()
        SpeciesField.setCurrentText( MostUsedList[0][0] )

def InitializeData():
    global SpeciesField, SpeciesDictionary
# Create a dictionary for all species
# Structure of each item in the dictionary
# 'Species name token': ( 'Counter' , 'Species name complete' )
    SpeciesDictionary = {}
    for i in range( 0 , SpeciesField.count() ):
        SpeciesField.setCurrentIndex( i )
        SpeciesDictionary[ SpeciesField.currentData() ] = ( 0 , SpeciesField.currentText() )
3
  • I tested the script in QGIS 3.10.1 under Windows 10 64 Bit, it worked. What is the full version of QGIS (3.10.**) ? Maybe it's a bug Commented Jun 7, 2020 at 4:01
  • I am using QGIS 3.10.6
    – oekoplaner
    Commented Jun 7, 2020 at 14:46
  • Does the script also work on your computer, if you outcomment the line "QMessageBox().exec_()" ? In this case it works here, only the preselection of the most used value does not.
    – oekoplaner
    Commented Jun 7, 2020 at 14:47

1 Answer 1

5
+50

QGIS can use NULL values as field values. Python has no concept of NULL. Therefore, for ART_KURZ which has NULL value in QGIS, the following two lines return different results.

  • CurrentFeature.attribute("ART_KURZ") is None returns False. Because NULL is a different object.

  • not CurrentFeature.attribute("ART_KURZ") returns True.

In other words, NULL is None is False, not NULL is True in PyQGIS.

Use that way:

try:
    if not CurrentFeature.attribute("ART_KURZ") and len( MostUsedList ) > 0:
        SpeciesField.setCurrentText( MostUsedList[0][0] )
except KeyError as e:
        pass

DEMO:

enter image description here


NOTE:

== compares the values of the objects. But is compares the references (identity) of the objects (as held in memory).

  • NULL == None is True, because both have the same value.
  • NULL is None is False, because both have different objects in the memory.
  • Also NULL is None equals id(NULL) == id(None).
5
  • First let me thank you for your time!! :-)Yes, the
    – oekoplaner
    Commented Jun 8, 2020 at 21:52
  • Yes, the most used species are inserted on top of the list - this part is fine. But Iwant the script to preselect the single most used species and this only works with the activated message box. I uploaded a zip-file with my test qgs.file and test data. If you want to, you can download the data under: dropbox.com/s/slihakbyd4p98ow/stackexchange.zip?dl=0 Thanks!
    – oekoplaner
    Commented Jun 8, 2020 at 21:55
  • Sounds very good. I will try this this evening!!!
    – oekoplaner
    Commented Jun 10, 2020 at 9:47
  • I had high hopes, that your hint might solve my problem, but the problem persists. :-( Without the message box, the preselection of the most used species does not work - instead the Dropdown box just shows the first value from the species list preloaded by QGIS ( "Abspielen Klangattrappe (AbspKl)" )! :-(
    – oekoplaner
    Commented Jun 10, 2020 at 21:44
  • @oekoplaner I've added more explanation about NULL and None. Commented May 2, 2021 at 10:36

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.