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 para su documentación. Escriba comentarios descriptivos y con sentido para dar al lector información sobre lo que debe esperar, que sucede en los casos extremos y dar pistas acerca de otras interfaces que podría estar buscando, buenas 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 git. doc/qt_creator_license_template uselo, copielo de la ubicación local, ajuste la dirección de correo electrónico y -si es necesario- el nombre y configure QtCreator para usarlo: Herramientas► Opciones ► C++ ► Nombre de Archivo.

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 sangrarse para facilitar su lectura. Existe un script scripts/prepare-commit.sh que revisa los ficheros que se han cambiado y les incluye la sangría correspondiente usando astyle. Este script debe ejecutarse antes de realizar una confirmación de cambios. También puede usarse el script scripts/astyle.sh para sangrar ficheros 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++.

Tratamos de mantener la API estable y compatible con versiones anteriores. Los ajustes en la API deben hacerse de modo similar al codigo 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 '

Un archivo de demostración puede ser encontrado en tests/code_layout/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 archivo SIP de un archivo C++ mas recientemente añadido scripts/sip_include.sh necesita ser ejecutado.

Tan pronto como un fichero SIP se añade a un fichero fuente (python/core/core.sip, python/gui/gui.sip or python/analysis/analysis.sip), se considerará como generado de forma automática. Un fichero de test en Travis se encargará de asegurar que este fichero esté actualizado con su correspondiente cabecera.

Para forzar la recreación de archivos SIP, scripts/sipify_all.sh debe ser ejecutado.

1.12.3. Mejorando el script sipify

Si se requieren algunas mejoras para el script sipify, agregue los bits que faltan al archivo de demostración tests/code_layout/sipifyheader.h y crear el encabezado esperado tests/code_layout/sipifyheader.expected.sip. Esto también se probará automáticamente en Travis como una prueba unitaria del propio script.

1.13. 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.13.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.13.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.13.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

El script scripts/prepare-commit.sh se encargará de esto.

1.13.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.13.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.13.6. Recomendaciones de libros

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

1.14. Créditos para contribuciones

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