QGIS standaarden voor coderen¶
Deze standaarden zouden door alle ontwikkelaars van QGIS moeten worden gevolgd.
Klassen¶
Naamgeving¶
Een klasse in QGIS begint met Qgs en wordt gevormd met behulp van camel case.
Voorbeelden:
QgsPoint
QgsMapCanvas
QgsRasterLayer
Leden¶
Namen van leden van klassen beginnen met een kleine letter m en worden gevormd met behulp van mixed case.
mMapCanvas
mCurrentExtent
Alle leden van klassen zouden private moeten zijn. Leden van Public-klassen worden STERK ontraden. beveiligde leden zouden moeten worden vermeden als toegang tot het lid moet worden verkregen via subklassen van Python, omdat beveiligde leden niet kunnen worden gebruikt vanuit de Python bindings.
Muteerbare statische leden van klassen zouden moeten beginnen met een kleine letter s
, maar namen van constante statische leden van klassen zouden allemaal hoofdletters moeten zijn:
sRefCounter
DEFAULT_QUEUE_SIZE
Functies Accessor¶
Waarden voor leden van klassen zouden moeten worden verkregen door middel van functies accesssor. De functie zou moeten worden benoemd zonder een voorvoegsel get. Functies Accessor voor de twee bovenstaande private leden zouden zijn:
mapCanvas()
currentExtent()
Zorg er voor dat accessors juist zijn gemarkeerd met const
. Waar van toepassing zou dit er toe kunnen leiden dat gecachte waardentypen van variabele leden zijn gemarkeerd met mutable
.
Functies¶
Namen van functies beginnen met een kleine letter en worden gevormd met behulp van mixed case. De functienaam zou iets over het doel van de functie moeten zeggen.
updateMapExtent()
setUserOptions()
Voor consistentie met de bestaande API van QGIS en met de API van Qt zouden afkortingen moeten worden vermeden. Bijv. setDestinationSize
in plaats van setDestSize
, setMaximumValue
in plaats van setMaxVal
.
Acroniemen zouden ook camel case moeten zijn om redenen van consistentie. Bijv. setXml
in plaats van setXML
.
Argumenten voor functies¶
Argumenten voor functies zouden beschrijvende namen moeten hebben. Gebruik geen argumenten van één letter (bijv. setColor( const QColor& color )
in plaats van setColor( const QColor& c )
).
Wees uitermate zorgvuldig als argumenten zouden moeten worden doorgegeven door middel van verwijzingen. Tenzij de objecten voor de argumenten klein zijn en eenvoudig zijn te kopiëren (zoals objecten QPoint), zouden zij moeten worden doorgegeven door middel van een verwijzing const. Voor consistentie met de API van Qt dienen zelfs impliciet gedeelde objecten te worden doorgegeven door een verwijzing const (bijv. setTitle( const QString& title )
in plaats van setTitle( QString title )
.
Teruggegeven waarden van functies¶
Geef kleine en eenvoudig eenvoudig te kopiëren objecten als waarden. Grotere objecten zouden moeten worden teruggegeven door een verwijzing const. De enige uitzondering hierop zijn de impliciet gedeelde objecten, die altijd met hun waarde worden teruggegeven. Geef QObject
of objecten van subklassen terug als pointers.
int maximumValue() const
const LayerSet& layers() const
QString title() const
(QString
is impliciet gedeeld)QList< QgsMapLayer* > layers() const
(QList
is impliciet gedeeld)QgsVectorLayer *layer() const;
(QgsVectorLayer
erftQObject
)QgsAbstractGeometry *geometry() const;
(QgsAbstractGeometry
is abstract en zal waarschijnlijk moeten worden gecast)
API-documentatie¶
Het is vereist om documentatie voor de API te schrijven voor elke klasse, methode, enumeratie en andere code die beschikbaar is in de publieke API.
QGIS uses Doxygen for documentation. Write descriptive and meaningful comments that give a reader information about what to expect, what happens in edge cases and give hints about other interfaces he could be looking for, best practices and code samples.
Methoden¶
Beschrijvingen voor methoden zouden in een beschrijvende vorm moeten worden geschreven, in de 3e persoon. Methoden vereisen een tag \since
die definieert wanneer zij zijn geïntroduceerd. U zou aanvullende tags \since
kunnen toevoegen voor belangrijke wijzigingen die later werden geïntroduceerd.
/**
* 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()
*/
Variabele leden¶
Variabele leden zouden normaal gesproken in het gedeelte private
moeten staan en beschikbaar moeten komen via getters en setters. Één uitzondering hierop is voor data containers zoals voor het rapporteren van fouten. Gebruik in dergelijke gevallen niet het voorvoegsel m
voor het lid.
/**
* \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
};
};
Qt Designer¶
Gegenereerde klassen¶
Klassen voor QGIS die worden gegenereerd uit Qt Designer (ui)-bestanden zouden een achtervoegsel Base moeten hebben. Dat identificeert de klasse als een gegenereerde basisklasse.
Voorbeelden:
QgsPluginManagerBase
QgsUserOptionsBase
Dialoogvensters¶
Alle dialoogvensters zouden helptips moeten implementeren voor alle pictogrammen van de werkbalk en andere relevante widgets. Helptips voegen een grote mate van ontdekkingsdrang toe voor zowel nieuwe als ervaren gebruikers.
Zorg er voor dat de tabvolgorde voor widgets wordt bijgewerkt, iedere keer als de lay-out van het dialoogvenster wijzigt.
C++-bestanden¶
Naamgeving¶
C++ implementatie en headerbestanden zouden respectievelijk de extensie .cpp en .h moeten hebben. De bestandsnaam zou geheel in kleine letters moeten zijn en, in het geval van klassen, moeten overeenkomen met de naam van de klasse.
Voorbeeld: Bronbestanden van de klasse QgsFeatureAttribute
zijn qgsfeatureattribute.cpp
en qgsfeatureattribute.h
Notitie
In het geval het niet duidelijk is uit het argument hierboven: om een bestandsnaam overeen te laten komen met een naam voor de klasse betekent het impliciet dat elke klasse zou moeten zijn gedeclareerd en geïmplementeerd in zijn eigen bestand. Dit maakt het voor nieuwkomers veel gemakkelijker om te identificeren waar de code gerelateerd is aan een specifieke klasse.
Standaard header en licentie¶
Elk bronbestand zou een gedeelte header moeten hebben met een patroon als in het volgende voorbeeld:
/***************************************************************************
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.
*
***************************************************************************/
Notitie
Er staat een sjabloon voor Qt Creator in GIT. Kopieer het, om het te gebruiken, vanuit doc/qt_creator_license_template
naar een lokale locatie, pas het mailadres aan en - indien vereist - de naam en configureer QtCreator om het te gebruiken: Tools
-> Options
-> C++
-> File Naming
.
Namen van variabelen¶
Namen van variabelen beginnen met een kleine letter en worden gevormd met behulp van mixed case. Gebruik geen voorvoegsels zoals my
of the
.
Voorbeelden:
mapCanvas
currentExtent
Geënumereerde typen¶
Geënumereerde typen zouden moeten worden benoemd in CamelCase met een hoofdletter aan het begin, bijv.:
enum UnitType
{
Meters,
Feet,
Degrees,
UnknownUnit
};
Gebruik geen algemene namen voor typen die kunnen conflicteren met andere typen. Gebruik bijvoorbeeld UnkownUnit
in plaats van alleen Unknown
Globale constanten & macro’s¶
Globale constanten en macro’s zouden moeten worden geschreven in hoofdletters, gescheiden door een underscore, bijv.:
const long GEOCRS_ID = 3344;
Commentaar¶
Opmerkingen bij klassemethoden zouden een derde persoon indicatieve stijl moeten gebruiken in plaats van de imperatieve stijl:
/**
* Creates a new QgsFeatureFilterModel, optionally specifying a \a parent.
*/
explicit QgsFeatureFilterModel( QObject *parent = nullptr );
~QgsFeatureFilterModel() override;
Qt signalen en slots¶
Alle verbindingen naar signal/slot zouden moeten worden gemaakt met behulp van de verbindingen “new style” die beschikbaar zijn in Qt5. Meer informatie over dit vereiste is beschikbaar in QEP #77.
Vermijd het gebruiken van Qt auto connect slots (d.i. die welke zijn genaamd void on_mSpinBox_valueChanged
). Auto connect slots zijn fragiel en gevoelig voor beschadigingen zonder waarschuwing als dialoogvensters opnieuw worden opgebouwd.
Bewerken¶
Elke tekstbewerker/IDE kan worden gebruikt om de code van QGIS te bewerken, vooropgesteld dat aan de volgende eisen wordt voldaan.
Tabs¶
Stel uw bewerker in om tabs te laten zien als spaties. De afstand voor de tab zou moeten zijn ingesteld op 2 spaties.
Notitie
In VIM wordt dit gedaan met set expandtab ts=2
Inspringen¶
Broncode zou moeten worden ingesprongen om de leesbaarheid te verbeteren. Er is een scripts/prepare-commit.sh
dat de gewijzigde bestanden ophaalt en ze opnieuw laat inspringen met astyle. Dit zou moeten worden uitgevoerd vóór het indienen. U kunt ook scripts/astyle.sh
gebruiken om individuele bestanden in te laten springen.
Omdat nieuwere versies van astyle anders inspringen dan de gebruikte versie voor een volledige nieuw inspringen van de bron, gebruikt het script een oude versie van astyle, die we op hebben genomen in onze opslagplaats (schakel WITH_ASTYLE
in cmake in om het op te nemen in de build).
Compatibiliteit met de API¶
Er is API documentatie voor C++.
We proberen de API stabiel en achterwaarts compatibel te houden. Opschonen van de API zou op een manier moeten worden gedaan die soortgelijk is aan de broncode voor Qt bijv.
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();
}
SIP-bindingen¶
Sommige van de SIP-bestanden worden automatisch gemaakt met een toegewezen script.
Voorverwerking header¶
Alle informatie om op de juiste manier het SIP-bestand te bouwen moet te vinden zijn in het C++ header-bestand. Enkele macro’s zijn beschikbaar voor een dergelijke definitie:
Gebruik
#ifdef SIP_RUN
om alleen code te maken in SIP-bestanden of#ifndef SIP_RUN
voor alleen code voor C++. Argumenten#else
worden in beide gevallen afgehandeld.Gebruik
SIP_SKIP
om een regel te negerenDe volgende annotaties worden afgehandeld:
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/
gedeelten
private
worden niet weergegeven, behalve wanneer u een argument#ifdef SIP_RUN
in dit blok gebruikt.SIP_PYDEFAULTVALUE(value)
kan worden gebruikt om een alternatieve standaard waarde van de methode van Python te definiëren. Als de standaard waarde een komma,
bevat, zou de waarde moeten worden omsloten door enkele aanhalingstekens'
SIP_PYTYPE(type)
kan worden gebruikt om een alternatieve type voor een argument van de methode van Python te definiëren. Als het type een komma,
bevat, zou het type moeten worden omsloten door enkele aanhalingstekens'
Een bestand ter demonstratie is te vinden in tests/scripts/sipifyheader.h
.
Het SIP-bestand maken¶
Het SIP-bestand kan worden gemaakt met een toegewezen script. Bijvoorbeeld:
scripts/sipify.pl src/core/qgsvectorlayer.h > python/core/qgsvectorlayer.sip
Zodra een SIP-bestand is toegevoegd aan een van de bronbestanden (python/core/core.sip
, python/gui/gui.sip
of python/analysis/analysis.sip
), zal het worden beschouwd als automatisch gemaakt. Een test op Travis zal er voor zorgen dat dit bestand up to date is met zijn overeenkomende header.
Oudere bestanden waarvoor het automatisch maken niet is ingeschakeld zijn vermeld in python/auto_sip.blacklist
.
Verbeteren van script sipify¶
Als enkele verbeteringen vereist zijn voor het script sipify, voeg dan de ontbrekende gedeelten toe aan het demo-bestand tests/scripts/sipifyheader.h
en maak de verwachte header tests/scripts/sipifyheader.expected.si
. Dit zal ook automatisch worden getest op Travis als een eenheidstest van het script zelf.
Stijl van coderen¶
Hier worden enkele hints en tips beschreven voor het programmeren die hopelijk het aantal fouten, ontwikkeltijd en onderhoud reduceren.
Waar mogelijk: generaliseer code¶
Indien u code knipt-en-plakt, of op een andere manier hetzelfde meer dan eens schrijft, overweeg dan om de code te consolideren in één enkele functie.
Dat zal:
het mogelijk maken wijzigingen op één plaats door te voeren in plaats van op meerdere plaatsen
helpen bij het tegengaan van overbodige code
het moeilijker maken voor meerdere kopieën om in de tijd verschillen te ontwikkelen, wat het moeilijker maakt voor anderen om het te begrijpen en te onderhouden
Voorkeur voor het hebben van constanten als eerste in predicaten¶
Heb een voorkeur voor het als eerste plaatsen van constanten in predicaten.
0 == value
in plaats van value == 0
Dit zal programmeurs helpen om te voorkomen dat zij per ongeluk =
gebruiken wanneer zij bedoelen ==
te gebruiken, wat zeer subtiele logische bugs kan introduceren. De compiler zal een fout genereren als u per ongeluk =
gebruikt in plaats van ==
voor vergelijkingen omdat inherent aan constanten geen waarden kunnen worden toegewezen.
Witruimte kan uw vriend zijn¶
Toevoegen van spaties tussen operatoren, statements en functies maken het voor mensen gemakkelijker code te parsen.
Wat is gemakkelijker te lezen, dit:
if (!a&&b)
of dit:
if ( ! a && b )
Notitie
scripts/prepare-commit.sh
zal hier voor zorgen.
Plaats opdrachten op afzonderlijke regels¶
Het is bij het lezen van code eenvoudig om opdrachten te missen als zij niet aan het begin van de regel staan. Bij het snel doorlezen van code worden vaak regels, die er niet uitzien als zoals u verwacht in de eerste tekens, over te slaan. Het is ook algemeen gebruik om een opdracht te verwachten na een voorwaarde, zoals if
.
Overweeg:
if (foo) bar();
baz(); bar();
Het is heel eenvoudig om delen van de beheersstroom te missen. Gebruik in plaats daarvan
if (foo)
bar();
baz();
bar();
Laat access modifiers inspringen¶
Acess modifiers structureren een klasse die is opgedeeld in public API, protected API en private API. Access modifiers zelf groeperen de code binnen deze structuur. Laat de access modifier en declaraties inspringen.
class QgsStructure
{
public:
/**
* Constructor
*/
explicit QgsStructure();
}
Aanbevolen boeken¶
Effective Modern C++, Scott Meyers
More Effective C++, Scott Meyers
Effective STL, Scott Meyers
Design Patterns, GoF
U zou ook echt dit artikel moeten lezen uit Qt Quarterly ove designing Qt style (APIs)
Vermelding van bijdragen¶
Zij die nieuwe functies bijdragen worden aangemoedigd om mensen kennis te laten nemen van hun bijdrage door:
een opmerking toe te voegen in het log van wijzigingen voor de eerste versie waar de code is ingevoegd, in de vorm van:
This feature was funded by: Olmiomland https://olmiomland.ol This feature was developed by: Chuck Norris https://chucknorris.kr
een blogartikel te schrijven over de nieuwe mogelijkheid en dat toe te voegen aan de QGIS planet https://plugins.qgis.org/planet/
hun naam toe te voegen aan: