1. Estándares de codificación de QGIS

Todos los desarrolladores de QGIS deberían seguir estos estándares.

1.1. Clases

1.1.1. Nombres

Las clases en QGIS comienzan con Qgs y están formadas usando la notación caja camello.

Ejemplos:

  • QgsPoint

  • QgsMapCanvas

  • QgsRasterLayer

1.1.2. Miembros

Los nombres de los miembros de las clases comienzan con una m minúscula y se forman con mayúsculas y minúsculas.

  • mMapCanvas

  • mCurrentExtent

Todos los miembros de la clase deben ser privados. Se recomienda encarecidamente no usar miembros públicos en las clases. Los miembros protegidos deben evitarse cuando pueda ser necesario acceder al miembro desde las subclases de Python, ya que los miembros protegidos no se pueden usar desde los enlaces de Python.

Los nombres de miembros de clases estáticas mutables debería comenzar con una letra minúscula s, pero los nombres de miembros de clases estáticas constantes deberían ser todo en mayúsculas:

  • sRefCounter

  • DEFAULT_QUEUE_SIZE

1.1.3. Funciones del accesor

Los valores de los miembros de la Clase deben obtenerse a través de las funciones del accesor. La función debe ser nombrada sin un prefijo get. Las funciones de acceso para los dos miembros privados anteriores serían:

  • mapCanvas()

  • currentExtent()

Asegúrese de que los accesores estén correctamente marcados con const. Cuando sea apropiado, esto puede requerir que el valor en caché de las variables del tipo miembro estén marcadas con mutable.

1.1.4. Funciones

Los nombres de las funciones comienzan con una letra minúscula y se forman mezclando mayúsculas y minúsculas. El nombre de la función debe indicar algo acerca de su propósito

  • updateMapExtent()

  • setUserOptions()

Para guardar la consistencia con la API disponible de QGIS y con la API de Qt se deben evitar las abreviaciones, por ejemplo, setDestinationSize` en lugar de ``setDestSize o setMaximumValue en lugar de setMaxVal.

Los acrónimos también deben ser usados con CamelCase por consistencia. Por ejemplo, setXml en lugar de setXML.

1.1.5. Argumentos de la función

Los argumentos de una función deberían tener nombres descriptivos. No use argumentos nombrados con una única letra (p.e.: use setColor( const QColor& color )  en vez de setColor( const QColor& c )).

Preste atención especial a cuándo se deben pasar los argumentos por referencia. A menos que los objetos de argumento sean pequeños y se copien trivialmente (como los objetos QPoint), se deben pasar por referencia const. Para mantener coherencia con la API Qt, incluso los objetos compartidos implícitamente pasan por referencia const (por ejemplo, `` setTitle (const QString & title) `` en lugar de `` setTitle (QString title) ``.

1.1.6. Función que regresa valores

Devuelva objetos pequeños y que se puedan copiar de forma trivial como valores. Los objetos más grandes deberían devolverse como una referencia constante. Una excepción a esto son los objetos compartidos de forma implícita, que siempre se devuelven por valor. Devuelva los objetos de tipo QObject u objetos de subclases de este como punteros.

  • int maximumValue() const

  • const LayerSet& layers() const

  • QString title() const (QString se comparte de forma implícita)

  • QList< QgsMapLayer* > layers() const (QList se comparte de manera implícita)

  • QgsVectorLayer *layer() const; (QgsVectorLayer hereda de QObject)

  • QgsAbstractGeometry *geometry() const; (QgsAbstractGeometry es abstracta y probablemente necesitará ser convertida al tipo específico)

1.2. Documentación de la API

Se necesita escribir la documentación de la API para cada clase, método, enumerado y cualquier otro código que esté accesible a través de la API pública.

QGIS utiliza Doxygen o documentación. Escriba comentarios descriptivos y significativos que proporcionen al lector información sobre lo que puede esperar, lo que ocurre en casos extremos y le den pistas sobre otras interfaces que podría estar buscando, mejores prácticas y ejemplos de código.

1.2.1. Métodos

Las descripciones de los métodos deben estar escritas de forma descriptivas, usando la 3a persona. Los métodos necesitan una etiqueta \since para indicar cuando se han añadido. Debería añadir etiquetas \since adicionales para aquellos cambios importantes que se hayan introducido posteriormente.

/**
 * Cleans the laundry by using water and fast rotation.
 * It will use the provided \a detergent during the washing programme.
 *
 * \returns True if everything was successful. If false is returned, use
 * \link error() \endlink to get more information.
 *
 * \note Make sure to manually call dry() after this method.
 *
 * \since QGIS 3.0
 * \see dry()
 */

1.2.2. Atributos de clase

Los atributos de clase deberían normalmente estar en la sección private y acceder a ellos a través de métodos getters y setters. Una excepción a esto son los contenedores de datos como los que se usan para informar de errores. En esos caso no se debe añadir el prefijo m al atributo.

/**
 * \ingroup core
 * Represents points on the way along the journey to a destination.
 *
 * \since QGIS 2.20
 */
class QgsWaypoint
{
  /**
   * Holds information about results of an operation on a QgsWaypoint.
   *
   * \since QGIS 3.0
   */
  struct OperationResult
  {
    QgsWaypoint::ResultCode resultCode; //!< Indicates if the operation completed successfully.
    QString message; //!< A human readable localized error message. Only set if the resultCode is not QgsWaypoint::Success.
    QVariant result; //!< The result of the operation. The content depends on the method that returned it. \since QGIS 3.2
  };
};

1.3. Diseñador Qt

1.3.1. Clases Generadas

Las clases QGIS que se se generan desde archivos Qt Designer (ui) deberían tener un sufijo de Base. Esto identifica la clase como una clase base generada.

Ejemplos:

  • QgsPluginManagerBase

  • QgsUserOptionsBase

1.3.2. Diálogos

Todos los cuadros de diálogo deben implementar ayuda con información sobre herramientas para todos los iconos de la barra de herramientas y otros widgets relevantes. La información sobre herramientas agrega mucho a la detección de características para usuarios nuevos y experimentados.

Asegúrese de que el orden de tabulación para widgets se actualice cada vez que cambie el diseño de un diálogo.

1.4. Archivos C++

1.4.1. Nombres

La implementación de C++ y los archivos del título deben tener una extensión .cpp y .h respectivamente. Los nombres de los archivos deberán estar todos en minúsculas y, en el respecto a las clases, deberán coincidir con el nombre de la clase.

Por ejemplo: los ficheros fuente de la clase QgsFeatureAttribute son qgsfeatureattribute.cpp y qgsfeatureattribute.h

Nota

En caso de que no esté claro en la declaración anterior, para que un nombre de archivo coincida con un nombre de clase, implícitamente significa que cada clase debe declararse e implementarse en su propio archivo. Esto hace que sea mucho más fácil para los usuarios nuevos identificar dónde se relaciona el código con una clase específica.

1.4.2. Encabezado y Licencia Estándar

Cada archivo de origen debería contener una sección de encabezado que siga el siguiente patrón de ejemplo:

/***************************************************************************
  qgsfield.cpp - Describes a field in a layer or table
  --------------------------------------
  Date : 01-Jan-2004
  Copyright: (C) 2004 by Gary E.Sherman
  Email: sherman at mrcc.com
/***************************************************************************
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 ***************************************************************************/

Nota

Hay una plantilla para Qt Creator en el repositorio git. Para usarla, cópiala de qt_creator_license_template a una ubicación local, ajusta la dirección de correo y -si es necesario- el nombre y configura QtCreator para usarla: Herramientas ► Opciones ► C++ ► Nombres de archivos.

1.5. Nombres de variables

Los nombres de las variables locales inician con minúscula y se forman utilizando mayúsculas y minúsculas. No utilice prefijos como mi o el.

Ejemplos:

  • mapCanvas

  • currentExtent

1.6. Tipos Enumerados

Los tipos enumerados deben nombrarse en CamelCase con una mayúscula al inicio, p. ej:

enum UnitType
{
  Meters,
  Feet,
  Degrees,
  UnknownUnit
};

No utilice nombres de tipos genéricos que entren en conflicto con otros tipos. p.ej. use UnidadDesconocidaUnit en lugar de Desconocido

1.7. Constantes & Macros Globales

Constantes y macros globales deberían escribirse en mayúscula separada por guión bajo e.g.:

const long GEOCRS_ID = 3344;

1.8. Comentarios

Los comentarios a los métodos de clase deben usar un estilo indicativo en tercera persona en lugar del estilo imperativo:

/**
 * Creates a new QgsFeatureFilterModel, optionally specifying a \a parent.
 */
explicit QgsFeatureFilterModel( QObject *parent = nullptr );
~QgsFeatureFilterModel() override;

1.9. Qt Signals y Slots

Todos los espacios/señales conectados deben hacerse utilizando el conector «nuevo estilo» disponible en Qt5. Más información sobre este requisito está disponible en QEP #77.

Evite utilizar de Qt Ranuras de conexión automática (p. ej. los nombrados void on_mSpinBox_valueChanged). Ranuras de conexión automática son frágiles y propensos a romperse sin previo aviso si los diálogos se refactan.

1.10. Editando

Cualquier editor de texto/IDE puede ser usado para editar código QGIS, siempre que los siguientes requerimientos sean atendidos.

1.10.1. Tabulaciones

Defina su editor para emular tabulaciones con espacios. El espaciado de tabulación debería establecerse en 2 espacios.

Nota

En vim esto se hace con set expandtab ts=2

1.10.2. Indentación

El código fuente debe ser sangrado para mejorar la legibilidad. Hay un fichero prepare_commit.sh que busca los ficheros cambiados y los reentrelaza usando astyle. Debe ejecutarse antes de confirmar. También puede utilizar astyle.sh para sangrar archivos individuales.

Puesto que las nuevas versiones de astyle sangran de manera diferente a la versión que se usó para realizar el sangrado completo de todo el código fuente, el script usa una versión antigua de astyle que hemos incluido en nuestro repositorio (habilite WITH_ASTYLE en cmake para incluirla en la compilación)

1.10.3. Llaves

Llaves deberían iniciar la línea que sigue a la expresión:

if( foo == 1 )
{
  // do stuff
  ...
}
else
{
  // do something else
  ...
}

1.11. Compatibilidad API

Aquí está API de documentación para C++.

Intentamos mantener la API estable y compatible con versiones anteriores. La limpieza de la API debe hacerse de forma similar al código fuente de Qt, por ejemplo

class Foo
{
  public:
    /**
     * This method will be deprecated, you are encouraged to use
     * doSomethingBetter() rather.
     * \deprecated use doSomethingBetter()
     */
    Q_DECL_DEPRECATED bool doSomething();

    /**
     * Does something a better way.
     * \note added in 1.1
     */
    bool doSomethingBetter();

  signals:
    /**
     * This signal will be deprecated, you are encouraged to
     * connect to somethingHappenedBetter() rather.
     * \deprecated use somethingHappenedBetter()
     */
#ifndef Q_MOC_RUN
    Q_DECL_DEPRECATED
#endif
    bool somethingHappened();

    /**
     * Something happened
     * \note added in 1.1
     */
    bool somethingHappenedBetter();
}

1.12. Enlaces con SIP

Algunos de los ficheros SIP se generan de forma automática a través de un script específico.

1.12.1. Preprocesamiento de cabeceras

Toda la información necesaria para generar de manera correcta el fichero SIP debe estar presente en el fichero de cabecera de C++. Están disponibles algunas macros para dicha definición:

  • Use #ifdef SIP_RUN para generar código sólo en los ficheros SIP o #ifndef SIP_RUN para generar sólo código C++. Las sentencias #else se procesan en ambos casos.

  • Use SIP_SKIP para descartar una línea

  • Se procesan las siguientes anotaciones:

    • SIP_FACTORY: /Factory/

    • SIP_OUT: /Out/

    • SIP_INOUT: /In,Out/

    • SIP_TRANSFER: /Transfer/

    • SIP_PYNAME(name): /PyName=name/

    • SIP_KEEPREFERENCE: /KeepReference/

    • SIP_TRANSFERTHIS: /TransferThis/

    • SIP_TRANSFERBACK: /TransferBack/

  • Las secciones private no se muestran, excepto si usa una sentencia #ifdef SIP_RUN en este bloque.

  • SIP_PYDEFAULTVALUE(value) puede usarse para definir un valor alternativo por defecto para el método Python. Si el valor por defecto contiene una coma ,, el valor debe rodearse por comillas simples '

  • SIP_PYTYPE(type) puede usarse para definir un tipo alternativo para el argumento de un método Python. Si el tipo contiene una coma ,, el tipo debe rodearse por comillas simples '

También está disponible un archivo de demostración, sipifyheader.h.

1.12.2. Generando el fichero SIP

El fichero SIP se puede generar usando un script específico. Por ejemplo:

scripts/sipify.pl src/core/qgsvectorlayer.h > python/core/qgsvectorlayer.sip

Para generar automáticamente el fichero SIP de un fichero C++ recién añadido es necesario ejecutar sip_include.sh.

En cuanto se añada un fichero SIP a uno de los ficheros fuente (core_auto.sip, gui_auto.sip o analysis_auto.sip), se considerará generado automáticamente. Un test on asegurará que este fichero está actualizado con su correspondiente cabecera.

Para forzar la recreación de los ficheros SIP, se ejecutará sipify_all.sh.

1.12.3. Mejorando el script sipify

Si se requieren algunas mejoras para el script sipify, por favor, añada las partes que faltan al archivo de demostración sipifyheader.h y cree la cabecera esperada sipifyheader.expected.sip. Esto también se probará automáticamente como una prueba unitaria del propio script.

1.13. Configuración

El código base de QGIS ofrece un mecanismo para declarar, registrar y utilizar configuraciones.

  • los ajustes deben definirse utilizando una de las implementaciones disponibles (QgsSettingsEntryString, QgsSettingsEntryInteger, …).

  • se declaran como const static ya sea en una clase dedicada o en el registro directamente (core, gui, app, …).

  • siempre deben añadirse al registro utilizando el método addSettingsEntry de QgsSettingsRegistry.

  • la clave de ajuste debe usar una kebab-case.

1.14. Estilo de Codificación

Aquí se describen algunas pistas y consejos de programación que podrán reducir errores, el tiempo de desarrollo y el mantenimiento.

1.14.1. Siempre que sea Posible Generalizar Código

Si usted está cortando y pegando código, o escribiendo la misma cosa más de una vez, considere consolidar el código en una sola función.

Esto hará:

  • permite hacer cambios en una ubicacion en lugar de en multiples ubicaciones

  • ayuda a prevenir codigo innnecesariamente largo o lento

  • dificulta para multiples copias la evolucion de diferencias en el tiempo, lo cual dificulta a otros entender y mantener

1.14.2. Preffiere tener primero las constantes en los predicados

Es preferible poner constantes primero en predicados.

0 == valor en vez de valor == 0

Esto ayudara a los programadores a prevenir el uso accidental de «=» cuando intentaban usar «==», lo cual podria introducir sutiles bugs logicos. El compilador generara un error si accidentalmente usas «=» en lugar de «==» para comparaciones dado que las constantes inherentemente no pueden ser asignadas

1.14.3. espacio en blanco puede ser tu amigo

Agregar espacios entre operadores, sentencias y funciones facilita a los humanos analizar el codigo por partes.

lo cual es facil de leer, esto:

if (!a&&b)

o este:

if ( ! a && b )

Nota

prepare_commit.sh script se encargará de esto.

1.14.4. Ponga los comandos en líneas separadas

Cuando se lee codigo, es facil omitir comandos si estos no estan al comienzo de la linea. Cuando se lee rapidamente a lo largo del código, es comun saltarse lineas si estas no lucen como lo que se esta buscando en los primeros caracteres. Es tambien commun esperar un comando despues de una sentencia condicional como «if»

Considere:

if (foo) bar();

baz(); bar();

Es muy facil perder parte de lo que es el flujo de control. En lugar use

if (foo)
  bar();

baz();
bar();

1.14.5. Idente modificadores de acceso

Los modificadores de acceso estructuran una clase en secciones de API pública, API protegida y API privada. Los modificadores de acceso a ellos mismos agrupan el código en esta estructura. Sangrar el modificador de acceso y las declaraciones.

class QgsStructure
{
  public:
    /**
     * Constructor
     */
     explicit QgsStructure();
}

1.14.6. Recomendaciones de libros

Debería también leer este artículo de Qt Quarterly sobre designing Qt style (APIs)

1.15. Créditos para contribuciones

Se anima a los que contribuyen nuevas funciones a hacer conocer a la gente acerca de sus contribuciones mediante: