1. QGIS 코드 작업 표준

모든 QGIS 개발자는 이 표준을 준수해야 합니다.

1.1. 클래스

1.1.1. 이름

QGIS의 클래스는 Qgs로 시작하며 낙타 대문자 형식을 따릅니다.

예시:

  • QgsPoint

  • QgsMapCanvas

  • QgsRasterLayer

1.1.2. 멤버

클래스 멤버 이름은 소문자 m 으로 시작하며 대소문자를 혼합해서 형성됩니다.

  • mMapCanvas

  • mCurrentExtent

모든 클래스 멤버는 프라이빗(private)이어야 합니다. 퍼블릭(public) 클래스 멤버는 절대 권장하지 않습니다. 파이썬 하위 클래스가 접근해야 할 수도 있는 멤버인 경우 프로텍티드(protected) 멤버로 지정하지 말아야 합니다. 파이썬 바인딩이 프로텍티드 멤버를 사용할 수 없기 때문입니다.

뮤터블(mutable) 정적 클래스 멤버 이름은 소문자 s 로 시작해야 하지만, 콘스턴트(constant) 정적 클래스 멤버 이름은 모두 대문자여야 합니다:

  • sRefCounter

  • DEFAULT_QUEUE_SIZE

1.1.3. 접근자 함수

접근자(accesssor) 함수를 통해 클래스 멤버 값을 얻어야 합니다. 이 함수의 이름에는 접두어 get 이 없어야 합니다. 앞에 예를 든 프라이빗 멤버 2개에 대한 접근자 함수는 다음과 같을 것입니다:

  • mapCanvas()

  • currentExtent()

접근자가 const 로 정확히 표시되도록 신경을 쓰십시오. 그렇게 하는 것이 적절한 경우, 캐시된 값 유형인 멤버 변수를 mutable 로 표시해야 할 수도 있습니다.

1.1.4. 함수

함수 이름은 소문자로 시작해서 대소문자를 혼합해 형성됩니다. 함수 이름은 해당 함수의 목적을 반영해야 합니다.

  • updateMapExtent()

  • setUserOptions()

기존 QGIS API 및 Qt API와 일관성을 지키기 위해, 단어를 축약해서는 안 됩니다. 예를 들어 setDestSize 가 아니라 setDestinationSize 를, setMaxVal 이 아니라 setMaximumValue 를 이름으로 써야 합니다.

일관성을 위해 두문자어(acronym)도 낙타 대문자 형식을 따라야 합니다. 예를 들면 setXML 이 아니라 setXml 을 써야 합니다.

1.1.5. 함수 인자

함수 인자(argument)는 해당 인자를 설명하는 이름이어야 합니다. 문자 하나짜리 인자를 쓰지 마십시오. (예를 들어 setColor( const QColor& c ) 가 아니라 setColor( const QColor& color ) 를 써야 합니다.)

참조를 통해 인자를 전달해야 하는 경우 특히 신경을 써야 합니다. 인자 객체가 (QPoint 객체처럼) 소용량이고 평범하게 복사되는 경우가 아니라면, 콘스턴트 참조로 전달해야 합니다. Qt API와 일관성을 지키기 위해, 내재적으로 공유된 객체일지라도 콘스턴트 참조로 전달됩니다. (예를 들어 setTitle( QString title ) 이 아니라 setTitle( const QString& title ) 을 써야 합니다.)

1.1.6. 함수 반환값

소용량이고 평범하게 복사된 객체를 값으로 반환합니다. 대용량 객체는 콘스턴트 참조로 반환되어야 합니다. 이 규칙의 단 한 가지 예외는 내재적으로 공유된 객체로, 언제나 값으로 반환됩니다. QObject 또는 하위 클래스 객체는 포인터로 반환합니다.

  • int maximumValue() const

  • const LayerSet& layers() const

  • QString title() const (QString 은 내재적으로 공유되었습니다)

  • QList< QgsMapLayer* > layers() const (QList 는 내재적으로 공유되었습니다)

  • QgsVectorLayer *layer() const; (QgsVectorLayerQObject 를 상속합니다)

  • QgsAbstractGeometry *geometry() const; (QgsAbstractGeometry 는 축약된 이름이기 때문에 이름을 바꿔야 할 겁니다)

1.2. API 문서

퍼블릭 API에서 사용할 수 있는 모든 클래스, 메소드, 목록(enum), 기타 코드에 대해 API 문서를 작성해야만 합니다.

QGIS는 문서화 작업에 Doxygen 을 사용합니다. 독자가 어떤 것을 기대할 수 있는지, 최첨단 케이스의 경우 어떤 일이 일어나는지에 대한 정보를 제공하는, 그리고 독자가 찾아볼 수 있는 다른 인터페이스들에 대한 힌트를 알려주는 서술적이고 의미 있는 주석과 함께 최적의 예제와 코드 예시를 작성하십시오.

1.2.1. 메소드

메소드에 대한 설명은 3인칭을 사용하는 서술 형식으로 작성되어야 합니다. 메소드를 소개하는 경우 메소드를 정의하는 \since 태그를 사용해야 합니다. 향후 도입되는 중요한 변경 사항에 대해서도 또다른 \since 태그를 추가해야 합니다.

/**
 * 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. 멤버 변수

Member variables should normally be in the private section and made available via getters and setters. One exception to this is for data containers like for error reporting. In such cases do not prefix the member with an 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. Generated Classes

QGIS classes that are generated from Qt Designer (ui) files should have a Base suffix. This identifies the class as a generated base class.

예시:

  • QgsPluginManagerBase

  • QgsUserOptionsBase

1.3.2. Dialogs

All dialogs should implement tooltip help for all toolbar icons and other relevant widgets. Tooltips add greatly to feature discoverability for both new and experienced users.

Ensure that the tab order for widgets is updated whenever the layout of a dialog changes.

1.4. C++ Files

1.4.1. 이름

C++ implementation and header files should have a .cpp and .h extension respectively. Filename should be all lowercase and, in the case of classes, match the class name.

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

참고

In case it is not clear from the statement above, for a filename to match a class name it implicitly means that each class should be declared and implemented in its own file. This makes it much easier for newcomers to identify where the code is relating to specific class.

1.4.2. Standard Header and License

Each source file should contain a header section patterned after the following example:

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

참고

There is a template for Qt Creator in git. To use it, copy it from doc/qt_creator_license_template to a local location, adjust the mail address and - if required - the name and configure QtCreator to use it: Tools ► Options ► C++ ► File Naming.

1.5. Variable Names

Local variable names begin with a lower case letter and are formed using mixed case. Do not use prefixes like my or the.

예시:

  • mapCanvas

  • currentExtent

1.6. Enumerated Types

Enumerated types should be named in CamelCase with a leading capital e.g.:

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

Do not use generic type names that will conflict with other types. e.g. use UnkownUnit rather than Unknown

1.7. Global Constants & Macros

Global constants and macros should be written in upper case underscore separated e.g.:

const long GEOCRS_ID = 3344;

1.8. Comments

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. 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. 편집 작업

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

1.10.1. Tabs

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

참고

In vim this is done with set expandtab ts=2

1.10.2. Indentation

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. API Compatibility

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/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. Coding Style

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 instead of 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)

or this:

if ( ! a && b )

참고

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: