Prácticas recomendadas de Qt QML

Conforme siga desarrollando aplicaciones, tendrá que conocer unos cuantos detalles que pueden hacer que las aplicaciones funcionen mejor y tengan menos problemas. Este tema aborda detalles importantes que debe tener en cuenta.

Vista de mapa

ArcGIS Runtime SDK for Qt ofrece tres patrones para visualizar un mapa en una vista de mapa. En AppStudio, escribirá su aplicación en QML, de forma que utilizará el tipo de mapa MapView.

La API QML de The Qt Company permite escribir aplicaciones multiplataforma con QML, una sintaxis declarativa altamente legible para diseñar y crear interfaces de usuario dinámicas y adaptables para aplicaciones nativas de escritorio y móviles. ArcGIS Runtime SDK for Qt amplía QML con tipos de QML que proporcionan funcionalidad de ArcGIS Runtime. Los objetos se declaran jerárquicamente y tienen propiedades enlazables para proporcionar comportamiento dinámico de forma automática. Las funciones de JavaScript se utilizan para proporcionar código de procedimiento cuando se requiere. Es una característica importante para desarrolladores que ya están familiarizados con el desarrollo web y desean desarrollar aplicaciones nativas.

Declare un Rectangle que contenga MapView. En MapView, declare un Map que mostrar y un Viewpoint inicial.

Rectangle {
    width: 800
    height: 600
     property real scaleFactor: System.displayScaleFactor
     // Map view UI presentation at top
    MapView {
        id: mv
         anchors.fill: parent
        wrapAroundMode: Enums.WrapAroundModeDisabled
         Map {
            BasemapTopographic {}
            initialViewpoint: viewPoint
             FeatureLayer {
                id: featureLayer
                 ServiceFeatureTable {
                    id: featureTable
                    url: "http://sampleserver6.arcgisonline.com/arcgis/rest/services/SF311/FeatureServer/0"
                }
            }
        }
         ViewpointCenter {
            id: viewPoint
            center: Point {
                x: -13630484
                y: 4545415
                spatialReference: SpatialReference {
                    wkid: 102100
                }
            }
            targetScale: 300000
        }
    }

Variables fuertemente tipadas

Al declarar propiedades en QML, es fácil y práctico utilizar el tipo de variable genérica var; sin embargo, siempre es mejor utilizar variables fuertemente tipadas. Una variable fuertemente tipada indica de forma explícita qué tipo de dato se puede almacenar en la variable, por ejemplo, un entero, una cadena de caracteres o un valor decimal. Las variables fuertemente tipadas pueden evitar que se asignen valores incorrectos, son más fáciles de leer y son más fáciles de depurar en comparación con las variables de tipo var. Una variable de tipo var no evitaría que se le asignara un valor decimal, incluso si el valor debiera contener solo enteros.

En el ejemplo siguiente, al valor entero intValue se le asigna un valor de 10. Más adelante en el código, la misma variable se actualiza con un valor de cadena de caracteres.

property int intValue = 10
...
intValue = "A string value"

Cuando se ejecute este código, se mostrará un error en la consola que se referirá explícitamente al número de línea en que se definió el valor de cadena de caracteres y que indicará Error: No se puede asignar QString a int, de modo que será más fácil solucionar el problema.

Si se asigna un valor con el tipo incorrecto a una variable de tipo var, no se informará de ningún error.

Interfaces de usuario escalables

Al diseñar la interfaz de usuario de la aplicación, es importante tener en cuenta el cambio constante en la gama de tamaños de pantalla y resoluciones en que se puede utilizar su aplicación. Esto se puede lograr incluyendo iconos, botones y fondos para cada resolución de pantalla, o utilizando gráficos vectoriales escalables.

Al incluir un conjunto de imágenes en diferentes resoluciones, elija una convención de nomenclatura para archivos que diferencie claramente los tamaños de imagen. Por ejemplo: nombre-imagen1x.png, nombre-imagen1.5x.png y nombre-imagen2x.png representan imágenes que son 1 vez el DPI base, 1,5 veces el DPI base y 3 veces el DPI base.

Los archivos SVG son ideales para imágenes pequeñas, normalmente iconos. Los archivos SVG pueden tardar en representarse en pantalla, así que para los fondos recomendamos utilizar imágenes.

Para obtener más información, consulte Interfaces de usuario escalables de Qt.

Depuración de la aplicación

AppStudio se crea sobre el marco de Qt, es decir, tiene acceso a todas las mismas utilidades de depuración utilizadas con Qt. A continuación, se presentan algunas sugerencias y trucos útiles para la depuración de aplicaciones de Qt.

Uso de un proxy web

Al depurar aplicaciones de AppStudio, a menudo resulta útil una utilidad de depuración web para capturar y analizar las solicitudes HTTP que se envían y las respuestas que se devuelven. Fiddler y Charles son dos ejemplos de utilidades de depuración web que se emplean para realizar esta operación. Puede utilizar estas herramientas para depurar una aplicación agregando el siguiente código C++ a su aplicación: QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::HttpProxy, "127.0.0.1", 8888));. Este código se puede definir en el tiempo de ejecución y puede estar en su main.cpp o en algunas otras clases C++. Además de utilizar un proxy para la depuración, QNetworkProxy::setApplicationProxy() también se puede utilizar para especificar el servidor proxy de su organización. Para esto, especifique la URL y el puerto del servidor proxy de la organización. Puede encontrar más información en la documentación NetworkProxy de AppFramework.

Uso de JSON.stringify()

Si utiliza el método JSON.stringify(), evite pasar la referencia del objeto en sí. Use la propiedad json del objeto como argumento, por ejemplo: JSON.stringify(graphic.geometry.json).

Modelo de memoria de la API QML

El modelo de memoria usado para las aplicaciones QML se basa en la recolección de elementos no utilizados. El colector de elementos no utilizados del motor de QML limpiará las variables sin a las que no se hace referencia. Por consiguiente, se debe hacer referencia explícitamente a todos los objetos para evitar su recolección cuando todavía están en uso.

Creación de objetos usando JavaScript

El método ArccGISRuntimeEnvironment.createObject() crea y devuelve nuevas instancias de un objeto mediante el motor de QML. El objeto principal de la instancia determina la duración de estas instancias. Cuando el principal ha recopilado elementos no utilizados, pasará lo mismo con sus instancias secundarias. Existen varias formas de mantener una instancia creada con ArccGISRuntimeEnvironment.createObject(), incluidas las siguientes:

  • Puede especificar el objeto principal en el tercer parámetro opcional de ArccGISRuntimeEnvironment.createObject().
  • En el código declarativo QML, declare el objeto como una propiedad de otro objeto. A continuación, en el código de JavaScript, instancie el objeto con ArccGISRuntimeEnvironment.createObject(). La clase que contiene la propiedad se convierte en el elemento principal de la nueva instancia. Asegúrese de declarar la propiedad para que sea del mismo tipo de ArcGIS Runtime QML que el que está instanciando.

Si no especifica un principal, JavaScript se define como el principal de la instancia del objeto. QML hace un seguimiento de la instancia y la elimina cuando no queda ninguna referencia de JavaScript a la instancia (es decir, cuando la instancia queda fuera de ámbito).

Algunos tipos de ArcGIS Runtime QML incluyen un método clone() que se utiliza para crear una instancia de objeto de ese tipo. Cuando se utiliza clone() para crear una nueva instancia, JavaScript es su elemento principal. Puede cambiar el elemento principal mediante la propiedad parent.

Trabajar con modelos

Los modelos acceden a las instancias de objeto de forma explícita. Cuando se trabaja con modelos, es necesario proporcionar datos persistentes para que el modelo acceso a ellos. Colocar instancias de objeto recién creadas directamente en un QML ListModel no es suficiente para mantener la vida útil de dichas instancias, porque hacerlo no realiza un recuento de referencia de esas instancias. (Consulte la sección anterior sobre la creación de objetos con JavaScript). Una forma de evitar la recopilación de elementos sin utilizar es crear una propiedad que sea una lista y agregar las instancias a la lista guardada. La propiedad de lista tiene la misma vigencia que su elemento principal.

// In a QML declaration:
ListModel {
  id: myListModel
  property var geoms: []
}

//...
// In a JavaScript function:
var newPoint = point.clone();

// avoid the following, because newPoint is not reference counted 
// myListModel.append(
//    {"geometry", newPoint
//    });  

// do this instead to give newPoint the same lifetime as myListModel
myListModel.geoms.push(newPoint);

Este concepto también se aplica a las instancias de objeto de la lista basadas en QQmlListProperty. Aunque puede asignar directamente una lista como esa como entrada para un modelo, debería evitarlo. El modelo creará referencias no explícitas a los elementos de la lista, de modo que los elementos se recopilarán como elementos no utilizados. Para obtener resultados correctos, primero tiene que asignar todos los elementos de la lista a una lista local. Estas referencias explícitas persistirán y evitarán una recolección prematura de elementos no utilizados. En el ejemplo siguiente se crea un componente list y se insertan todos los campos de la tabla de entidades. Después, define la lista como el modelo del ComboBox.

GeodatabaseFeatureTable {
    id: featureTable
}

ComboBox {
    id: comboBox
//    model: featureTable.fields // avoid this, because the model will create non-explicit references
    textRole: "name"
}

property var fieldList: []

function initFieldListModel() {
    for (var i = 0; i < featureTable.fields.length; ++i) {
        fieldList.push(featureTable.fields[i]);
    }
    comboBox.model = fieldList;
}

Unicidad del objeto

No hay garantía de que volverá a obtener el mismo objeto incluso para el mismo captador o llamada de función. Como tal, se debería evitar la comparación directa de objetos, por ejemplo:

Map { id: map }
...
var viewpoint = map.initialViewpoint;
// Avoid comparisons like the following code.
// Although the objects' contents are identical, 
// the objects may be different objects.
if (viewpoint === map.initialViewpoint)

Propiedades personalizadas en tipos de QML

Una propiedad personalizada (definida por el usuario) es aquella que agrega a una instancia de un tipo de QML cuando la declara. La propiedad no forma parte del tipo, pero existe como propiedad personalizada en la instancia específica. El lenguaje QML permite agregar propiedades personalizadas a instancias cuando se declaran. A pesar de que puede agregar propiedades personalizadas a instancias de tipo ArcGIS Runtime QML, es posible que la propiedad no se conserve en todos los casos debido a la forma en que se mantienen las instancias en la API. Por consiguiente, evite utilizar propiedades personalizadas en las instancias de tipos que no lo permitan.

Los siguientes tipos de QML y tipos derivados de estos tipos admiten propiedades personalizadas. Otros tipos de ArcGIS Runtime QML no.

  • ArcGISMapServiceInfo
  • Mapa base
  • Credencial
  • FeatureTable
  • Geodatabase
  • GraphicsOverlay
  • LayerContent
  • Mapa
  • Portal
  • PortalTask
  • VectorTileSourceInfo

Trabajar con JSON

Muchos tipos de QML se heredan de JsonSerializable, lo que permite serializar el tipo o rellenar su contenido en JSON. Cuando cree un objeto desde JSON, cree una instancia en primer lugar y luego pase esa instancia como entrada al método.

Este código de JavaScript crea y rellena un PictureMarkerSymbol de JSON y lo utiliza en un gráfico nuevo.

Graphic {
  id: myGraphic


  property var pmsJson: {"type": "esriPMS", "url": "http://myurl.com/folder/my-icon.png","width": 60,"height": 60, "angle":20}


  Component.onCompleted: {
    var pmsFromJson = ArcGISRuntimeEnvironment.createObject("PictureMarkerSymbol", {json: pmsJson});
    pmsFromJson.errorChanged.connect(function(error){ verify(false, error.message + " (" + error.additionalMessage + ")"); });
    myGraphic.symbol = pmsFromJson;
  }
}

Trabajar con cadenas de caracteres traducidas

Todas las cadenas de caracteres que se muestran en la UI de una aplicación deberían utilizar un formato qsTr() para asegurarse de poder globalizar la aplicación.

También es importante tener en cuenta las cadenas de caracteres traducidas al definir la lógica de la aplicación. Si un usuario seleccionara una opción en función de cadenas de caracteres de la UI, por ejemplo una selección de un menú desplegable, cada caso dentro de la instrucción switch debería utilizar la cadena de caracteres traducida.

En el ejemplo siguiente, el panel Acerca de se mostrará al seleccionar el elemento de menú Acerca de, independientemente del idioma en que se muestre la aplicación:

case qsTr("About"):
    pageView.hideSearchItem()
    panelDockItem.addDock("aboutPanel")
    break

Representación en pantalla de OpenGL o ANGLE en Windows

Qt tiene dos motores de representación en pantalla diferentes para la plataforma Windows: OpenGL y DirectX mediante ANGLE. De forma predeterminada, Qt intentará renderizar mediante OpenGL. Si los controladores adecuados no están disponibles, recurrirá a utilizar ANGLE para la representación en pantalla. Las versiones anteriores de ArcGIS Runtime solo admitían ANGLE. A partir de ArcGIS Runtime 100.3.0, sus aplicaciones pueden ser totalmente compatibles con OpenGL y ANGLE de forma que puede utilizar el motor de representación en pantalla y el mecanismo de reserva de Qt cuando OpenGL no se pueda utilizar en un sistema determinado. Para obtener más información, consulte la documentación de Qt.

Para elegir entre la representación de OpenGL y ANGLE como valor predeterminado, use las opciones Configuración > Plataformas > Windows > Motor de representación en pantalla de gráficos en AppStudio. No se necesitan cambios en el código.