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 deQObject
)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: .
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.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íneaSe 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
Effective Modern C++, Scott Meyers
More Effective C++, Scott Meyers
Effective STL, Scott Meyers
Design Patterns, GoF
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:
añadir una nota al registro de cambios para la primer versión donde el código ha sido incorporado, del tipo:
This feature was funded by: Olmiomland https://olmiomland.ol This feature was developed by: Chuck Norris https://chucknorris.kr
escribiendo un artículo sobre la nueva funcionalidad en un blog, y añadirlo a planeta QGIS https://plugins.qgis.org/planet/
agregando su nombre a:
1.8. Comentarios
Los comentarios a los métodos de clase deben usar un estilo indicativo en tercera persona en lugar del estilo imperativo: