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 is implicitly shared)

  • QList< QgsMapLayer* > layers() const (QList is implicitly shared)

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

  • QgsAbstractGeometry *geometry() const; (QgsAbstractGeometry is abstract and will probably need to be casted)

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. Qt Signals and Slots

All signal/slot connects should be made using the «new style» connects available in Qt5. Futher information on this requirement is available in QEP #77.

Avoid use of Qt auto connect slots (i.e. those named void on_mSpinBox_valueChanged). Auto connect slots are fragile and prone to breakage without warning if dialogs are refactored.

1.10. Modifica

Any text editor/IDE can be used to edit QGIS code, providing the following requirements are met.

1.10.1. Schede

Set your editor to emulate tabs with spaces. Tab spacing should be set to 2 spaces.

Nota

In vim this is done with set expandtab ts=2

1.10.2. Indentazione

Source code should be indented to improve readability. There is a scripts/prepare-commit.sh that looks up the changed files and reindents them using astyle. This should be run before committing. You can also use scripts/astyle.sh to indent individual files.

As newer versions of astyle indent differently than the version used to do a complete reindentation of the source, the script uses an old astyle version, that we include in our repository (enable WITH_ASTYLE in cmake to include it in the build).

1.10.3. Braces

Braces should start on the line following the expression:

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

1.11. Compatibilità API

There is API documentation for C++.

We try to keep the API stable and backwards compatible. Cleanups to the API should be done in a manner similar to the Qt sourcecode e.g.

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 Bindings

Some of the SIP files are automatically generated using a dedicated script.

1.12.1. Header pre-processing

All the information to properly build the SIP file must be found in the C++ header file. Some macros are available for such definition:

  • Use #ifdef SIP_RUN to generate code only in SIP files or #ifndef SIP_RUN for C++ code only. #else statements are handled in both cases.

  • Use SIP_SKIP to discard a line

  • The following annotations are handled:

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

  • private sections are not displayed, except if you use a #ifdef SIP_RUN statement in this block.

  • SIP_PYDEFAULTVALUE(value) can be used to define an alternative default value of the python method. If the default value contains a comma ,, the value should be surrounded by single quotes '

  • SIP_PYTYPE(type) can be used to define an alternative type for an argument of the python method. If the type contains a comma ,, the type should be surrounded by single quotes '

A demo file can be found in tests/scripts/sipifyheader.h.

1.12.2. Generating the SIP file

The SIP file can be generated using a dedicated script. For instance:

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

As soon as a SIP file is added to one of the source file (python/core/core.sip, python/gui/gui.sip or python/analysis/analysis.sip), it will be considered as generated automatically. A test on Travis will ensure that this file is up to date with its corresponding header.

Older files for which the automatic creation is not enabled yet are listed in python/auto_sip.blacklist.

1.12.3. Improving sipify script

If some improvements are required for sipify script, please add the missing bits to the demo file tests/scripts/sipifyheader.h and create the expected header tests/scripts/sipifyheader.expected.si. This will also be automatically tested on Travis as a unit test of the script itself.

1.13. Stile Programmazione

Here are described some programming hints and tips that will hopefully reduce errors, development time and maintenance.

1.13.1. Where-ever Possible Generalize Code

If you are cut-n-pasting code, or otherwise writing the same thing more than once, consider consolidating the code into a single function.

This will:

  • allow changes to be made in one location instead of in multiple places

  • help prevent code bloat

  • make it more difficult for multiple copies to evolve differences over time, thus making it harder to understand and maintain for others

1.13.2. Prefer Having Constants First in Predicates

Prefer to put constants first in predicates.

0 == value e non value == 0

This will help prevent programmers from accidentally using = when they meant to use ==, which can introduce very subtle logic bugs. The compiler will generate an error if you accidentally use = instead of == for comparisons since constants inherently cannot be assigned values.

1.13.3. Whitespace Can Be Your Friend

Adding spaces between operators, statements, and functions makes it easier for humans to parse code.

Which is easier to read, this:

if (!a&&b)

o questo:

if ( ! a && b )

Nota

scripts/prepare-commit.sh will take care of this.

1.13.4. Put commands on separate lines

When reading code it’s easy to miss commands, if they are not at the beginning of the line. When quickly reading through code, it’s common to skip lines if they don’t look like what you are looking for in the first few characters. It’s also common to expect a command after a conditional like if.

Consider:

if (foo) bar();

baz(); bar();

It’s very easy to miss part of what the flow of control. Instead use

if (foo)
  bar();

baz();
bar();

1.13.5. Indent access modifiers

Access modifiers structure a class into sections of public API, protected API and private API. Access modifiers themselves group the code into this structure. Indent the access modifier and declarations.

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

1.13.6. Book recommendations

You should also really read this article from Qt Quarterly on designing Qt style (APIs)

1.14. Credits for contributions

Contributors of new functions are encouraged to let people know about their contribution by: