1. Standard di programmazione QGIS

Questi standard dovrebbero essere seguiti da tutti gli sviluppatori di QGIS.

1.1. Classi

1.1.1. Nomi

In QGIS le classi cominciano con Qgs e sono formate utilizzando la notazione a cammello.

Esempi:

  • QgsPoint

  • QgsMapCanvas

  • QgsRasterLayer

1.1.2. Membri

I nomi dei membri della classe cominciano con il carattere minuscolo m e sono formati utilizzando caratteri minuscoli e maiuscoli.

  • mMapCanvas

  • mCurrentExtent

Tutti i membri della classe dovrebbero essere privati. Le classi pubbliche sono FORTEMENTE scoraggiate. I membri protetti devono essere evitati quando è necessario accedere ai membri mediante l’utilizzo di sottoclassi di Python, poiché i membri protetti non possono essere utilizzati dai collegamenti Python.

I nomi dei membri della classe statica modificabile dovrebbero iniziare con una s minuscola, ma i nomi dei membri della classe statica costante dovrebbero essere tutti maiuscoli:

  • sRefCounter

  • DEFAULT_QUEUE_SIZE

1.1.3. Funzioni di Accesso

I valori dei membri di classe dovrebbero essere ottenuti attraverso le funzioni di accesso. L’uso del prefisso get nel nome della funzione dovrebbe essere evitato. Le funzioni di accesso per i due membri privati di cui sopra sarebbero:

  • mapCanvas()

  • currentExtent()

Assicurati che gli accessori siano definiti correttamente con `` const``. Quando appropriato, ciò potrebbe richiedere che le variabili membro del tipo di valore memorizzato nella cache siano contrassegnate con `` mutable``

1.1.4. Funzioni

I nomi delle funzioni che iniziano con la lettera in minuscolo e sono composti da lettere minuscole e maiuscole. Il nome della funzione dovrebbe comunicare qualcosa in riferimento alla funzione.

  • updateMapExtent()

  • setUserOptions()

In coerenza con le API di QGIS esistenti e con le API di QT dovrebbero essere evitate le abbreviazioni. Ad esempio: setDestinationSize e non setDestSize, setMaximumValue e non setMaxVal.

Anche gli acronimi, coerentemente, dovrebbero seguire la notazione a cammello. Ad esempio setXml e non setXML.

1.1.5. Argomenti della Funzione

Gli argomenti delle funzioni dovrebbero utilizzare nomi descrittivi. Non utilizzare argomenti di una sola lettera (ad esempio setColor (const QColor &color) invece di setColor (const QColor&c)`).

Presta molta attenzione a quando gli argomenti devono essere passati per riferimento. A meno che gli oggetti argomento non siano piccoli e banalmente copiati (come gli oggetti QPoint), dovrebbero essere passati per riferimento const. Per coerenza con l’API Qt, anche gli oggetti condivisi implicitamente vengono passati tramite riferimento const (ad esempio setTitle (const QString&title)” invece di setTitle (QString title).

1.1.6. Valori di Ritorno della Funzione

Restituisce oggetti piccoli e banalmente copiati come valori. Gli oggetti più grandi dovrebbero essere restituiti dal riferimento const. L’unica eccezione a ciò sono gli oggetti condivisi implicitamente, che vengono sempre restituiti per valore. Restituisce QObject” o oggetti sottoclasse come puntatori.

  • int maximumValue() const

  • const LayerSet& layers() const

  • QString title() const (QString è implicitamente condiviso)

  • QList< QgsMapLayer* > layers() const (QList` è implicitamente condiviso)

  • QgsVectorLayer *layer() const; (QgsVectorLayer eredita QObject)

  • QgsAbstractGeometry *geometry() const; (QgsAbstractGeometry è astratto e probabilmente dovrà essere trasformato)

1.2. Documentazione API

È necessario scrivere documentazione API per ogni class, method, enum ed altro codice disponibile nelle API pubbliche.

QGIS usa Doxygen per la documentazione. Scrivete commenti descrittivi e significativi che forniscano al lettore informazioni su cosa aspettarsi, cosa succede nei casi limite e diano suggerimenti su altre interfacce che potrebbe cercare, buone pratiche ed esempi di codice.

1.2.1. Metodi

Le descrizioni dei metodi devono essere scritte in una forma descrittiva, utilizzando la terza persona. I metodi richiedono un tag «since» che definisce quando sono stati introdotti. È necessario aggiungere ulteriori tag \since per modifiche importanti introdotte in seguito.

/**
 * 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. Membri Variabili

Le variabili membro dovrebbero normalmente essere nella sezione private e rese disponibili tramite getter e setter. Un’eccezione a questo è per i contenitori di dati come per la segnalazione degli errori. In questi casi non anteporre al membro una m.

/**
 * \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. Classi Generate

Le classi QGIS generate da file Qt Designer (ui) dovrebbero avere un suffisso Base. Ciò identifica la classe come una classe base generata.

Esempi:

  • QgsPluginManagerBase

  • QgsUserOptionsBase

1.3.2. Finestre di dialogo

Tutte le finestre di dialogo dovrebbero implementare l’aiuto Tooltip per tutte le icone della barra degli strumenti e altri widget rilevanti. I tooltip aggiungono notevole efficacia alla funzionalità di reperibilità per i utenti nuovi ed esperti.

Assicurarsi che l’ordine della scheda per i widget venga aggiornato ogni volta che il layout di una finestra di dialogo cambia.

1.4. File C++

1.4.1. Nomi

I file di implementazione e di intestazione C++ dovrebbero avere rispettivamente un’estensione .cpp e .h. Il nome del file deve essere tutto minuscolo e, nel caso di classi, abbinarsi al nome della classe.

Esempio: Class QgsFeatureAttribute i file sorgenti sono qgsfeatureattribute.cpp e qgsfeatureattribute.h

Nota

Nel caso in cui non sia chiaro dall’istruzione di cui sopra, perché un nome di file corrisponda a un nome di classe significa implicitamente che ogni classe deve essere dichiarata e implementata nel proprio file. Questo rende molto più facile per i nuovi arrivati identificare dove il codice è relativo alla classe specifica.

1.4.2. Intestazione Standard e Licenza

Ogni file sorgente dovrebbe contenere una sezione di intestazione modellata dopo il seguente esempio:

/***************************************************************************
  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

C’è un modello per Qt Creator in git. Per usarlo, copiarlo da doc/qt_creator_license_template in una posizione locale, modificare l’indirizzo di posta e - se richiesto - il nome e configurare QtCreator per usarlo: Tools ► Options ► C++ ► File Naming.

1.5. Nomi Variabile

I nomi delle variabili locali iniziano con una lettera minuscola e sono formati usando maiuscole miste. Non usare prefissi come my o the.

Esempi:

  • mapCanvas

  • currentExtent

1.6. Enumerated Types

I tipi enumerati devono essere denominati in CamelCase con una maiuscola iniziale, es.:

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

Non usare nomi di tipo generici che entreranno in conflitto con altri tipi. es. usare UnkownUnit piuttosto che Unknown

1.7. Costanti Globali & Macros

Costanti globali e macros dovrebbero essere scritte in maiuscolo separate da underscore es.:

const long GEOCRS_ID = 3344;

1.8. Commenti

I commenti ai metodi delle classi dovrebbero usare uno stile indicativo in terza persona invece dello stile imperativo:

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

1.9. Signal e slot Qt

Tutti le coonessioni connettori signal/slot devono essere realizzate utilizzando le connessioni «new style» disponibili in Qt5. Ulteriori informazioni su questo requisito sono disponibili in QEP #77.

Evita l’uso degli slot di connessione automatica di Qt (cioè quelli denominati void on_mSpinBox_valueChanged). Gli slot di connessione automatica sono fragili e possono interrompersi senza preavviso in caso di refactoring delle finestre di dialogo.

1.10. Modifica

Qualsiasi editor di testo/IDE può essere usato per modificare il codice QGIS, a condizione che siano soddisfatti i seguenti requisiti.

1.10.1. Schede

Imposta il tuo editor per emulare le tabulazioni con gli spazi. La spaziatura delle tabulazioni dovrebbe essere impostata a 2 spazi.

Nota

In vim questo viene fatto con set expandtab ts=2.

1.10.2. Indentazione

Il codice sorgente dovrebbe essere indentato per migliorare la leggibilità. Esiste un scripts/prepare-commit.sh che cerca i file modificati e li reindenta usando astyle. Dovrebbe essere eseguito prima del commit. Puoi anche usare scripts/astyle.sh per indentare i file singoli.

Poiché le nuove versioni di astyle indentano in modo diverso rispetto alla versione usata per fare una reindentazione completa del sorgente, lo script usa una vecchia versione di astyle, che includiamo nel nostro repository (abilitando WITH_ASTYLE in cmake per includerla nella compilazione).

1.10.3. Parentesi

Le parentesi dovrebbero iniziare sulla linea che segue l’espressione:

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

1.11. Compatibilità API

C’è Documentazione API per C++.

Cerchiamo di mantenere l’API stabile e compatibile con le versioni precedenti. Le pulizie dell’API dovrebbero essere fatte in modo simile al codice sorgente di Qt, ad esempio.

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. Collegamenti SIP

Alcuni dei file SIP sono generati automaticamente utilizzando uno script dedicato.

1.12.1. Pre-elaborazione intestazione

Tutte le informazioni per costruire correttamente il file SIP devono essere contenute nel file C++i intestazione . Sono disponibili alcune macro per tale definizione:

  • Usa #ifdef SIP_RUN per generare codice solo nei file SIP o #ifndef SIP_RUN per il solo codice C++. Le istruzioni #else vengono gestite in entrambi i casi.

  • Usare SIP_SKIP per cancellare una linea

  • Le annotazioni che seguono sono gestite:

    • 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/

  • le sezioni «private» non vengono visualizzate, a meno di usare, in questo blocco, il comando #ifdef SIP_RUN

  • SIP_PYDEFAULTVALUE(value) può essere usato per definire un valore predefinito alternativo del metodo python. Se il valore predefinito contiene una virgola ,, il valore deve essere racchiuso tra apici singoli '.

  • SIP_PYTYPE(tipo) può essere usato per definire un tipo alternativo per un argomento del metodo python. Se il tipo contiene una virgola ,, il tipo deve essere racchiuso tra apici singoli '.

Un file demo può essere trovato in tests/code_layout/sipifyheader.h.

1.12.2. Generazione del file SIP

Il file SIP può essere generato utilizzando uno script dedicato. Ad esempio:

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

Per generare automaticamente il file SIP di un nuovo file C++ aggiunto deve essere eseguito scripts/sip_include.sh.

Non appena un file SIP viene aggiunto a uno dei file sorgente (python/core/core.sip, python/gui/gui.sip o python/analysis/analysis.sip), sarà automaticamente considerato generato. Un test su Travis assicurerà che questo file sia aggiornato con l’intestazione corrispondente.

Per forzare la ricreazione dei file SIP, deve essere eseguito scripts/sipify_all.sh.

1.12.3. Miglioramento dello script sipify

Se sono necessari alcuni miglioramenti per lo script sipify, aggiungi le parti mancanti al file demo tests/code_layout/sipifyheader.h e crea l’header atteso tests/code_layout/sipifyheader.expected.sip. Questo verrà anche testato automaticamente su Travis come unit test dello script stesso.

1.13. Stile Programmazione

Qui sono descritti alcuni suggerimenti e consigli di programmazione che permetteranno di ridurre gli errori, i tempi di sviluppo e la manutenzione.

1.13.1. Dove possibile generalizza il codice

Se fai un taglia-incolla di codice, o comunque scrivi la stessa cosa più di una volta, prendi in considerazione la possibilità di consolidare il codice in un’unica funzione.

In questo modo:

  • consente di effettuare cambiamenti in una location invece che in molte

  • eviti il gonfiamento del codice

  • rende più difficile alle copie multiple di avere differenze nel tempo , così da renderne più difficile per altri la comprensione

1.13.2. Preferisci la presenza di costanti nei predicati

Preferisci mettere le costanti nei predicati.

0 == value e non value == 0

Questo aiuta il programmatore ad evitare di digitare accidentalmente «=» quando voleva digitare «==», cosa che può provocare delicati errori di logica. Infatti il compilatore può generare un errore se, per i confronti, si usa = invece di == visto che alle costanti non può essere assegnato un valore.

1.13.3. Whitespace può essere amico

aggiungere spazi tra gli operatori, i comandi e le funzioni rende più semplice alle persone la lettura del codice

È più facile leggere questo:

if (!a&&b)

o questo:

if ( ! a && b )

Nota

scripts/prepare-commit.sh si occuperà di questo.

1.13.4. Metti i comandi su linee separate

Quando si legge il codice è facile perdere i comandi, se non si trovano all’inizio della riga. Quando si legge velocemente il codice, è comune saltare le righe se non sembrano quelle che si stanno cercando nei primi caratteri. È anche comune aspettarsi un comando dopo una condizione come if.

Considera:

if (foo) bar();

baz(); bar();

È molto facile perdere parte del controllo del flusso. Utilizza invece

if (foo)
  bar();

baz();
bar();

1.13.5. Indenta i modificatori di accesso

I modificatori di accesso strutturano una classe in sezioni di API pubblica, API protetta e API privata. Gli stessi modificatori di accesso raggruppano il codice in questa struttura. Indenta il modificatore di accesso e le dichiarazioni.

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

1.13.6. Consigli per i libri

Dovresti anche leggere questo articolo di Qt Quarterly su designing Qt style (APIs)

1.14. Crediti per contributi

Chi contribuisce a nuove funzioni è invitato a far conoscere il proprio contributo: