1. Standardele de codificare QGIS

Aceste standarde ar trebui să fie urmate de către toți dezvoltatorii QGIS.

1.1. Clase

1.1.1. Nume

Clasele din QGIS sunt prefixate cu Qgs, iar fiecare cuvânt începe cu majusculă.

Exemple:

  • QgsPoint

  • QgsMapCanvas

  • QgsRasterLayer

1.1.2. Membri

Denumirile membrilor claselor sunt prefixate cu litera m și sunt formate utilizând majuscule și minuscule.

  • mMapCanvas

  • mCurrentExtent

Toți membrii claselor ar trebui să fie privați. Membrii publici sunt TOTAL nerecomandați. Membrii protejați ar trebui să fie evitați când membrul ar putea fi accesat din subclase Python, de vreme ce membrii protejați nu pot fi utilizați din legăturile Python.

Numele membrilor statici și mutabili ai claselor ar trebui să înceapă cu un s mic, în schimb numele membrilor statici constanți ar trebui să aibă toate literele mari:

  • sRefCounter

  • DEFAULT_QUEUE_SIZE

1.1.3. Funcțiile Accesor

Valorile membrilor unei clase vor fi obținute prin intermediul funcțiilor accesor. Numele acestora nu ar trebui să înceapă cu prefixul „get”. Funcțiile accesor pentru cei doi membri privați de mai sus ar putea fi:

  • mapCanvas()

  • currentExtent()

Asigurați-vă că accesoriile sunt corect marcate cu const. Atunci când este cazul, este posibil ca variabilele membru, de tip cache de valoare, să fie marcate cu mutable.

1.1.4. Funcții

Numele funcțiilor încep cu literă mică și conțin majuscule și minuscule. Numele funcțiilor ar trebui să indice scopul acestora.

  • updateMapExtent()

  • setUserOptions()

În concordanță cu API-urile QGIS și Qt, ar trebui evitate abrevierile. De exemplu: setDestinationSize în loc de setDestSize, setMaximumValue în loc de setMaxVal.

De asemenea, acronimele ar trebui să fie denumite folosind CamelCase. De exemplu: setXml în loc de setXML.

1.1.5. Argumente ale funcțiilor

Argumentele funcțiilor ar trebui să utilizeze nume descriptive. Nu utilizați argumente cu nume format dintr-o singură literă (ex. setColor( const QColor& color ) în loc de setColor( const QColor& c )).

Acordați o atenție deosebită momentului când argumentele ar trebui să fie transmise prin referință. Cu excepția cazului în care obiectele argumentului nu au dimensiuni mari și sunt trivial de copiat (cum ar fi obiectele QPoint), ele ar trebui să fie transmise prin referințe constante. Pentru concordanța cu API-ul Qt, chiar și obiectele partajate în mod implicit sunt transmise prin referințe constante (ex.: setTitle( const QString& title ) în loc de setTitle( QString title ).

1.1.6. Valorile Returnate de Funcție

Returnați ca valori obiectele mici și trivial copiate. Obiectele mai mari ar trebui returnate prin referințe constante. Singura excepție o reprezintă obiectele partajate în mod implicit, care sunt întotdeauna returnate prin valoare. Returnați ca pointeri QObject sau obiectele subclasate.

  • int maximumValue() const

  • const LayerSet& layers() const

  • QString title() const (QString se partajează, în mod implicit)

  • QList< QgsMapLayer* > layers() const (QList se partajează, în mod implicit)

  • QgsVectorLayer *layer() const; (QgsVectorLayer moștenește pe QObject)

  • QgsAbstractGeometry *geometry() const; (QgsAbstractGeometry este abstract și, probabil, necesită specificarea tipului)

1.2. Documentația API

Este necesar să elaborați documentația API pentru fiecare clasă, metodă, enumerație și alt cod care este disponibil în API-ul public.

QGIS folosește Doxygen pentru documentație. Introduceți comentarii descriptive și semnificative care să ofere cititorului informații despre rezultatele așteptate și despre cazurile excepționale, indicii despre alte interfețe, cele mai bune practici și exemple de cod.

1.2.1. Metodele

Descrierile metodelor trebuie efectuate într-o formă descriptivă, folosind persoana a 3-a. Metodele necesită o etichetă \since care arată momentul introducerii. Ar trebui să adăugați etichete \ since suplimentare pentru modificările importante introduse ulterior.

/**
 * 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. Variabilele Membru

Variabilele membru ar trebui să se afle, în mod normal, în secțiunea private și să fie dispoibile prin intermediul funcțiilor get și set. O excepție o constituie containerele de date, cum ar fi raportarea erorilor. În astfel de cazuri, nu prefixați membrul cu un 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. Clasele Generate

Clasele QGIS care sunt generate în fișierele produse de Qt Designer (UI) ar trebui să aibă sufixul Base. Acest lucru identifică o clasă de bază, generată.

Exemple:

  • QgsPluginManagerBase

  • QgsUserOptionsBase

1.3.2. Dialoguri

Toate dialogurile ar trebui să implementeze ajutorul pentru toate pictogramele barei de instrumente și alte controale grafice relevante. Baloanele cu indicii adaugă foarte mult la descoperirea caracteristicilor, atât pentru utilizatorii noi, cât și pentru cei experimentați.

Asigurați-vă că ordinea filelor pentru controalele grafice este actualizată ori de câte ori se modifică aspectul unui dialog.

1.4. Fișierele C++

1.4.1. Nume

Fișierele antet și de implementare C++ ar trebui să aibă extensiile .h, respectiv .cpp. Numele de fișier ar trebui să conțină doar litere mici și, în cazul claselor, să se potrivească numelui clasei.

Example: Class QgsFeatureAttribute source files are qgsfeatureattribute.cpp and qgsfeatureattribute.h

Notă

În cazul în care nu este clară indicația de mai sus, cerința ca numele de fișier să se potrivească numelui de clasă, arată că, implicit, fiecare clasă trebuie să fie declarată și implementată în propriul fișier. Acest lucru facilitează nou-veniților identificarea codului care este specific anumitor clase.

1.4.2. Antetul Standard și Licența

Fiecare fișier sursă trebuie să conțină o secțiune antet, în conformitate cu exemplul următor:

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

Notă

Există un șablon pentru Qt Creator în git. Pentru a-l utiliza, copiați-l din doc/qt_creator_license_template într-un dosar local, ajustați adresa e-mail și numele - dacă este necesar - apoi configurați QtCreator pentru a-l folosi: Instrumente ► Opțiuni ► C++ ► Denumirea Fișierului.

1.5. Numele Variabilei

Numele variabilelor locale încep cu o literă mică, utilizând majuscule și minuscule. Nu folosiți prefixe precum my sau the.

Exemple:

  • mapCanvas

  • currentExtent

1.6. Tipurile Enumerate

Tipurile enumerate ar trebui să fie denumite folosind CamelCase, începând cu o majusculă, de ex.:

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

Nu utilizați tipuri de nume generice, care vor intra în conflict cu alte tipuri. De ex., folosiți mai degrabă UnkownUnit decât Unknown

1.7. Constantele locale și Comenzile Macro

Constantele locale și comenzile macro ar trebui să fie scrise cu majuscule, separate cu ajutorul caracterului de subliniere, de ex .:

const long GEOCRS_ID = 3344;

1.8. Comentarii

Comments to class methods should use a third person indicative style instead of the imperative style:

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

1.9. Semnale și Sloturi Qt

Toate conexiunile la semnale/sloturi ar trebui să fie făcute folosindu-se legăturile de „stil nou” disponibile în Qt5. Informații suplimentare privind această cerință sunt disponibile în QEP #77.

Evitați folosirea sloturilor cu autoconectare QT (adică pe acelea denumite void on_mSpinBox_valueChanged). Sloturile cu autoconectare sunt fragile și predispuse la întrerupere, fără avertisment, dacă dialogurile sunt refactorizate.

1.10. Editarea

Orice editor de text/IDE poate fi folosit pentru a edita codul QGIS, oferind garanția că sunt îndeplinite următoarele cerințe.

1.10.1. Caracterele TAB

Setați în editorul dvs. emularea TAB-ului cu ajutotul spațiilor albe. Ar trebui folosite în acest scop 2 spații.

Notă

În vim, acest lucru se realizează prin comanda set expandtab ts=2

1.10.2. Indentarea

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

Acoladele ar trebui să fie poziționate pe linia următoare expresiei:

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

1.11. Compatibilitatea API-ului

Există Documentație API pentru C++.

Încercăm să păstrăm API-ul stabil și compatibil cu versiunile anterioare. Curățarea API-ului ar trebui să fie făcută într-un mod similar cu al codului sursă Qt, de ex.:

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. Pre-procesare antet

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

To automatically generate the SIP file of a newly added C++ file scripts/sip_include.sh needs to be executed.

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.

To force recreation of SIP files, scripts/sipify_all.sh shall be executed.

1.12.3. Improving sipify script

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

1.13. Stilul de Codificare

Aici sunt descrise câteva sugestii și sfaturi de programare care vor reduce, sperăm, erorile, timpul de întreținere și dezvoltare.

1.13.1. Generalizați Codul Atunci Când Este Posibil

Decât să duplicați un anumit cod, mai bine luați în considerare consolidarea acestuia într-o funcție unică.

Acest lucru vă va permite să:

  • efectuați modificări într-o singură locație în loc de mai multe

  • preveniți încurcarea codului

  • împiedica apariția, de-a lungul timpului, a diferențelor între secțiunile identice de cod, făcându-le, astfel, mai greu de înțeles și de întreținut de către alții

1.13.2. Se preferă poziționarea Constantelor înaintea Predicatelor

Se preferă poziționarea constantelor la începutul predicatelor.

0 == value în loc de value == 0

Acest lucru va ajuta prevenirea folosirii accidentale de = în loc de ==, care poate introduce erori subtile de logică. Compilatorul va genera o eroare dacă folosiți accidental = în loc de == pentru comparații, deoarece nu se pot atribui valori constantelor.

1.13.3. Spațiile Albe Vă Pot Fi De Ajutor

Adăugarea de spații între operatori, declarații și funcții, facilitează utilizatorilor analiza codului.

Care cod este mai ușor de citit? Acesta:

if (!a&&b)

sau acesta:

if ( ! a && b )

Notă

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

1.13.4. Puneți comenzile pe linii separate

La citirea codului, este ușor să omiteți comenzile dacă acestea nu se află la începutul liniei. La parcurgerea rapidă a codului, este normal să omiteți liniile atunci când acestea nu prezintă ceea ce căutați în primele câteva caractere. Este, de asemenea, normal să vă așteptați la o comandă după un condițional ca if.

Se ia în considerare următorul cod:

if (foo) bar();

baz(); bar();

Este foarte ușor să omiteți o parte din fluxul de control. Scrieți, în schimb

if (foo)
  bar();

baz();
bar();

1.13.5. Indentați modificatorii de acces

Modificatorii de acces structurează o clasă în secțiuni de API public, API protejat și API privat. Rolul lor este de a grupa codul pe această structură. Indentați modificatorii de acces și declarațiile.

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

1.13.6. Cărți recomandate

Ar trebui, de asemenea, să citiți acest articol de la Qt Quarterly despre proiectarea în stilul Qt (API)

1.14. Recunoașterea contribuțiilor

Contribuitorii la noile funcții sunt încurajați să-i informeze pe ceilalți despre contribuția lor prin: