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() )