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 peQObject
)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: .
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.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 lineThe 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
Effective Modern C++, Scott Meyers
More Effective C++, Scott Meyers
Effective STL, Scott Meyers
Design Patterns, GoF
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:
adăugarea unei note la jurnalul schimbărilor, pentru prima versiune în care a fost încorporat codul, de tipul:
This feature was funded by: Olmiomland https://olmiomland.ol This feature was developed by: Chuck Norris https://chucknorris.kr
writing an article about the new feature on a blog, and add it to the QGIS planet https://plugins.qgis.org/planet/
adăugarea numelui lor la:
1.8. Comentarii
Comments to class methods should use a third person indicative style instead of the imperative style: