1. QGIS standaarden voor coderen

Deze standaarden zouden door alle ontwikkelaars van QGIS moeten worden gevolgd.

1.1. Klassen

1.1.1. Naamgeving

Een klasse in QGIS begint met Qgs en wordt gevormd met behulp van camel case.

Voorbeelden:

  • QgsPoint

  • QgsMapCanvas

  • QgsRasterLayer

1.1.2. 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

1.1.3. 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.

1.1.4. 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.

1.1.5. 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 ).

1.1.6. 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 erft QObject)

  • QgsAbstractGeometry *geometry() const; (QgsAbstractGeometry is abstract en zal waarschijnlijk moeten worden gecast)

1.2. 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 gebruikt Doxygen voor documentatie. Schrijf beschrijvende en betekenisvolle opmerkingen die een lezer informatie geven over wat te verwachten, wat gebeurt er in randgevallen en geef hints over andere interfaces die hij zou kunnen gebruiken, goede beste werkwijzen en voorbeelden van code.

1.2.1. 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()
 */

1.2.2. 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
  };
};

1.3. Qt Designer

1.3.1. 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

1.3.2. 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.

1.4. C++-bestanden

1.4.1. 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.

1.4.2. 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.

1.5. 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

1.6. 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

1.7. 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;

1.8. 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;

1.9. 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.

1.10. Bewerken

Elke tekstbewerker/IDE kan worden gebruikt om de code van QGIS te bewerken, vooropgesteld dat aan de volgende eisen wordt voldaan.

1.10.1. 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

1.10.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).

1.10.3. Haakjes

Haakjes zouden op de regel moeten beginnen volgens de volgende expressie:

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

1.11. 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();
}

1.12. SIP-bindingen

Sommige van de SIP-bestanden worden automatisch gemaakt met een toegewezen script.

1.12.1. 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 negeren

  • De 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/code_layout/sipifyheader.h.

1.12.2. 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

Om automatisch het SIP-bestand van een nieuw toegevoegd C++-bestand te maken, moet file:scripts/sip_include.sh worden uitgevoerd.

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.

Voor het forceren van het opnieuw maken van SIP-bestanden zal scripts/sipify_all.sh worden uitgevoerd.

1.12.3. Verbeteren van script sipify

Als enkele verbeteringen vereist zijn voor het script sipify, voeg dan de ontbrekende gedeelten toe aan het demo-bestand tests/code_layout/sipifyheader.h en maak de verwachte header tests/code_layout/sipifyheader.expected.sip. Dit zal ook automatisch worden getest op Travis als een eenheidstest van het script zelf.

1.13. Stijl van coderen

Hier worden enkele hints en tips beschreven voor het programmeren die hopelijk het aantal fouten, ontwikkeltijd en onderhoud reduceren.

1.13.1. 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

1.13.2. 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.

1.13.3. 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.

1.13.4. 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();

1.13.5. 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();
}

1.13.6. Aanbevolen boeken

U zou ook echt dit artikel moeten lezen uit Qt Quarterly ove designing Qt style (APIs)

1.14. Vermelding van bijdragen

Zij die nieuwe functies bijdragen worden aangemoedigd om mensen kennis te laten nemen van hun bijdrage door: