7 votos

Actualizar/cambiar/comprometer un atributo/valor de campo "sobre la marcha" en QGIS 3

Estoy usando QGIS 3.2. Tengo 4 campos ("kk1"-"kk4") donde al menos uno de los campos contiene un número (real). Quiero calcular la media de estos 1-4 campos y guardar el resultado (la media) en otro campo llamado "mkviskrans". Calculo la media ( basado en esta respuesta ) de estos campos 1-4 usando esta función en la ventana del Editor de Funciones en las Propiedades de las Capas:

@qgsfunction(args=-1, group='Custom')
def mean_col(cols, feature, parent):
  vals = [feature[i] for i in cols if feature[i] is not None]
  return sum(vals)/float(len(vals))

y llamando a la función desde la ventana de Expresión en la opción Valor por defecto del widget en Propiedades de la capa:

mean_col('kk1','kk2','kk3','kk4')

ACTUALIZACIÓN : Lo intenté return 2.3 en lugar de return sum(vals)/float(len(vals)) . En este caso la tabla de atributos se actualiza automáticamente con el valor 2.3. Esto indica que los valores introducidos por el usuario en "kk1"-"kk4" tienen que ser actualizados/evaluados/"comprometidos". antes de que devuelve el valor medio, es decir, la división-expresión siempre devolverá "Ninguno"? He intentado actualizar los valores de característica/campo de "kk1"-"kk4" antes de devolverlos, pero sin suerte hasta ahora.

enter image description here Sin embargo, el valor medio no aparece automáticamente en el widget "mkviskrans" "Text Edit" (alias "Midlere kvistkransavstand (dm)"): enter image description here Ni en la tabla de atributos: enter image description here Cuando hago clic en una celda de la tabla de atributos, aparece el valor medio: enter image description here ¿Es posible obtener/mostrar/guardar el valor "sobre la marcha"? He probado diferentes variantes de actualización y/o confirmación dentro de la función:

@qgsfunction(args=-1, group='Custom')
def mean_col(cols, feature, parent):
  layer = qgis.utils.iface.activeLayer()
  fieldIndex = layer.fields().indexFromName( 'mkviskrans' )
  vals = [feature[i] for i in cols if feature[i] is not None]
  """return sum(vals)/float(len(vals))"""
  mv = sum(vals)/float(len(vals))
  layer.changeAttributeValue(feature.id(), fieldIndex, mv)
  layer.commitChanges()

Si guardo las ediciones sin hacer clic en una celda de la tabla de atributos, el promedio (el campo "mkvistkrans") no contendrá ningún valor medio.

(Por supuesto que puedo calcular los valores medios después del trabajo de campo, pero quiero que el usuario vea el valor medio "sobre la marcha" - si es posible).

2voto

Michael Puntos 11

NULL y ninguno

Un dato curioso: NULL is not None

Teniendo esto en cuenta es fácil arreglar el código

@qgsfunction(args=-1, group='Custom')
def mean_col(cols, feature, parent):
  vals = [feature[i] for i in cols if feature[i] != NULL]
  return sum(vals)/float(len(vals))

No escriba en las capas en las expresiones

Un consejo adicional: no escriba nunca a capas en expresiones. Las expresiones se ejecutan a menudo en hilos y en diferentes contextos y el uso de layer.changeAttributeValue() y otras funciones similares pueden dar lugar a un comportamiento indefinido (léase: un fallo).

Utilizar valores en lugar de nombres de atributos

Otro interesante experimento con fines educativos.

En lugar de proporcionar nombres de columnas a la función, también puede proporcionar valores.

@qgsfunction(args='auto', group='Custom')
def mean_value(vals, feature, parent):
    vals = [val for val in vals if val != NULL]
    return sum(vals)/float(len(vals))

Y luego llamarlo con

mean_value("kk1", "kk2", "kk3", "kk4")

El resultado interesante es: devolverá NULL en cuanto al menos una columna sea NULL. ¿Cómo es eso? QGIS asume que el resultado de una función de expresión es NULL tan pronto como al menos un parámetro es NULL por razones de rendimiento. Es posible controlar este comportamiento, pero desafortunadamente el @qgsfunction El decorador no lo soporta hasta la fecha (QGIS 3.2).

Para evitar este comportamiento, podemos proporcionar un QgsExpressionFunction que está fuera del alcance de esta respuesta. O simplemente dar un pequeño rodeo y utilizar un array en su lugar.

@qgsfunction(args='auto', group='Custom')
def mean_value(vals, feature, parent):
    vals = [val for val in vals if val != NULL]
    return sum(vals)/float(len(vals))

y luego llamarlo con

mean_value(array("kk1", "kk2", "kk3", "kk4"))

0 votos

@Matthias_Kuhn, muchas gracias por la exhaustiva respuesta. Ya he probado todas tus soluciones. Sin embargo, el campo "mkviskrans" sigue sin rellenarse automáticamente. Todavía tengo que hacer clic en una celda de la tabla de atributos (o volver a visitar el punto con la herramienta de identificación y hacer clic en Aceptar) para ver el valor medio.

0 votos

No es fácil, la tabla de atributos (hasta ahora) no tiene en cuenta las dependencias de los campos virtuales. Pero sería una buena mejora para el propio QGIS.

0 votos

@Matthias_Kuhn: ¿Campos virtuales? Los campos "kk1"-"kk4" y "mkviskrans" (en los que quiero que se guarde automáticamente el valor medio) son todos campos predefinidos (columnas existentes) en el shapefile de puntos. Sólo están inicialmente vacíos (ya que empiezo con un shapefile vacío (creado con ogr/python). A continuación, el usuario rellena los valores de los atributos ("kk1"-"kk4" son distancias medidas) para un punto (un árbol) a la vez. ¿El problema es que los atributos no tienen realmente valores antes de que se haga clic en OK y se guarden/commitan los valores introducidos en "kk1"-"kk4"?

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X