중요

번역은 여러분이 참여할 수 있는 커뮤니티 활동입니다. 이 페이지는 현재 100.00% 번역되었습니다.

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

멤버 변수는 일반적으로 private 부분에 있어야 하며 게터(getter)와 세터(setter)를 통해 사용할 수 있어야 합니다. 이 규칙의 단 한 가지 예외는 오류 보고 작업 용 같은 데이터 컨테이너들입니다. 이런 경우 멤버의 이름 앞에 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 설계자

1.3.1. 생성된 클래스들

Qt 설계자(Qt Designer)의 (UI) 파일들로부터 생성된 QGIS 클래스들은 Base 접미어를 가져야 합니다. 이 접미어가 해당 클래스를 Qt 설계자가 생성한 기반 클래스로 식별하게 합니다.

예시:

  • QgsPluginManagerBase

  • QgsUserOptionsBase

1.3.2. 대화창

모든 대화창은 모든 툴바 아이콘과 기타 관련 위젯들을 위한 툴팁 도움말을 구현해야 합니다. 툴팁은 신규 사용자와 경험이 풍부한 사용자 모두의 기능 검색 가능성을 크게 높여줍니다.

대화창의 레이아웃이 변경될 때마다 위젯의 탭 순서가 업데이트되는지 확인하십시오.

1.4. C++ 파일

1.4.1. 이름

C++ 구현 파일과 헤더 파일은 각각 .cpp.h 확장자를 가져야 합니다. 파일 이름은 전부 소문자여야 하며, 클래스인 경우 클래스 이름과 일치해야 합니다.

예시: QgsFeatureAttribute 클래스의 소스 파일은 qgsfeatureattribute.cppqgsfeatureattribute.h 입니다.

참고

앞의 문장이 명확하지 않았다면, 파일 이름을 클래스 이름과 일치시킨다는 것은 각 클래스의 자체 파일에서 해당 클래스를 선언하고 구현해야 한다는 뜻을 암시합니다. 이렇게 하면 신규 사용자가 코드에서 특정 클래스와 관련된 위치를 훨씬 쉽게 식별할 수 있습니다.

1.4.2. 표준 헤더와 사용 허가

각 소스 파일은 다음 예시처럼 패턴화된 헤더 부분을 담고 있어야 합니다:

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

참고

깃 저장소에 Qt 설계자 용 템플릿이 있습니다. 이 템플릿을 사용하려면, qt_creator_license_template 에서 로컬 위치로 복사한 다음 메일 주소와 — 필요한 경우 — 이름을 조정하고 QtCreator가 이를 사용할 수 있도록 환경설정하십시오: Tools ► Options ► C++ ► File Naming

1.5. 변수 이름

로컬 변수 이름은 소문자로 시작하며 대소문사 혼합 형식을 따릅니다. my 또는 the 같은 접두어를 사용하지 마십시오.

예시:

  • mapCanvas

  • currentExtent

1.6. 열거형

열거형(enumerated type)의 이름은 다음과 같이 첫 글자가 대문자인 카멜 표기법 형식이어야 합니다:

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

다른 유형과 충돌하게 될 일반적인 유형 이름은 사용하지 마십시오. 예를 들면 Unknown 이라는 이름보다는 UnknownUnit 을 사용하십시오.

1.7. 전체 수준 상수 및 매크로

전체 수준 상수(constant)와 매크로(macro)의 이름은 다음과 같이 공백을 밑줄로 대체한 대문자로 작성해야 합니다:

const long GEOCRS_ID = 3344;

1.8. 주석

클래스 메소드에 붙는 주석(comment)은 명령형 스타일보다는 3인칭 직설법 스타일로 작성해야 합니다:

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

1.9. Qt 시그널 및 슬롯

모든 시그널(signal)/슬롯(slot)은 Qt5에서 사용할 수 있는 “새로운 스타일” 연결을 사용해서 이루어져야 합니다. 이 요구 사항에 대한 추가 정보는 QEP #77 에서 확인해볼 수 있습니다.

Qt 자동 연결 슬롯은 (예를 들어 void on_mSpinBox_valueChanged 같은 이름을 가진 슬롯은) 사용하지 마십시오. 자동 연결 슬롯은 취약하며, 대화창이 리팩터링 되는 경우 경고 없이 깨지기 쉽습니다.

1.10. 편집 작업

다음 요구 사항을 만족시킨다면 어떤 텍스트 편집기/IDE라도 QGIS 코드를 편집하는 데 사용할 수 있습니다.

1.10.1.

여러분의 편집기가 공백들을 가진 탭을 에뮬레이트(emulate)하도록 설정하십시오. 탭 간격을 2칸으로 (스페이스 바를 2번 누른 간격으로) 설정해야 합니다.

참고

빔(Vim) 편집기에서는 set expandtab ts=2 명령어로 설정할 수 있습니다.

1.10.2. 들여쓰기

가독성을 향상시키기 위해 소스 코드를 들여쓰기해야 합니다. 변경된 파일을 검색한 다음 Astyle(Artistic Style) 을 사용해서 다시 들여쓰기해주는 prepare_commit.sh 파일이 있습니다. 소스 코드를 커밋하기 전에 이 파일을 실행해야 합니다. 개별 파일들을 들여쓰기하려면 astyle.sh 파일을 사용해도 됩니다.

Astyle의 신 버전이 소스를 완전히 다시 들여쓰기하는 데 쓰이는 버전과는 다르게 들여쓰기를 하기 때문에, 이 스크립트는 저장소에 포함된 옛 Astyle 버전을 사용합니다. (빌드 시 이 옛 버전을 포함시키려면 CMake에서 WITH_ASTYLE 을 활성화시키십시오.)

1.10.3. 괄호

괄호는 표현식 다음 줄에서 시작해야 합니다:

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

1.11. API 호환성

C++ 용 API 문서 가 있습니다.

우리는 API를 안정적으로 유지하고 이전 버전과 호환되도록 하려 합니다. API 정리(cleanup)는 다음과 같이 Qt 소스 코드와 비슷한 방식으로 이루어져야 합니다.

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 바인딩

SIP 파일들의 일부는 전용 스크립트를 사용해서 자동 생성됩니다.

1.12.1. 헤더 전처리

C++ 헤더 파일에서 SIP 파일을 적절하게 빌드하기 위한 모든 정보를 찾아야만 합니다. 이런 정의에 사용할 수 있는 매크로가 몇 개 있습니다:

  • SIP 파일에만 코드를 생성하려면 #ifdef SIP_RUN 을 사용하고, 또는 C++ 코드만 생성하려면 #ifndef SIP_RUN 을 사용하십시오. 두 경우 모두 #else 선언문을 처리합니다.

  • 어떤 줄을 삭제하려면 SIP_SKIP 을 사용하십시오.

  • 다음과 같은 주석(annotation)들을 처리합니다:

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

  • 이 블록에 #ifdef SIP_RUN 선언문을 사용하지 않는 한 private 부분은 보이지 않습니다.

  • SIP_PYDEFAULTVALUE(value) 를 사용해서 파이썬 메소드의 기본값을 대체할 값을 정의할 수 있습니다. 기본값이 쉼표 , 를 담고 있는 경우, 값을 작은따옴표 ' 로 둘러싸야 합니다.

  • SIP_PYTYPE(type) 을 사용해서 파이썬 메소드의 인자에 대한 대체 유형을 정의할 수 있습니다. 유형이 쉼표 , 를 담고 있는 경우, 유형을 작은따옴표 ' 로 둘러싸야 합니다.

sipifyheader.h 데모 파일도 사용할 수 있습니다.

1.12.2. SIP 파일 생성하기

SIP 파일은 다음처럼 전용 스크립트를 사용해서 생성할 수 있습니다:

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

새로 추가된 C++ 파일의 SIP 파일을 자동 생성하려면 sip_include.sh 를 실행해야 합니다.

소스 파일 가운데 하나에 (core_auto.sip, gui_auto.sip 또는 analysis_auto.sip 에) SIP 파일이 추가되는 순간, 자동 생성되었다고 간주할 것입니다. 테스트를 통해 이 파일이 대응하는 헤더와 최신 상태인지 확인할 것입니다.

SIP 파일을 강제로 다시 생성하려면, sipify_all.sh 를 실행해야 합니다.

1.12.3. sipify 스크립트 향상시키기

sipify 스크립트를 조금 향상시켜야 할 필요가 있을 경우, sipifyheader.h 데모 파일에 누락된 부분들을 추가하고 예상되는 sipifyheader.expected.sip 헤더를 생성하십시오. 이 또한 스크립트 자체의 유닛 테스트로써 자동으로 테스트될 것입니다.

1.13. 설정

QGIS 코드 베이스는 설정을 선언하고, 등록하고, 사용할 수 있는 메커니즘을 제공합니다.

  • 설정은 사용할 수 있는 구현들 (QgsSettingsEntryString, QgsSettingsEntryInteger, …) 가운데 하나를 사용해서 정의해야 합니다.

  • 설정은 반드시 설정 트리(QgsSettingsTree)에 통합되어야만 합니다. 상위 노드(QgsSettingsTreeNode)를 가진 작성자(constructor)를 사용하면 자동으로 통합시킵니다.

  • 설정은 전용 클래스에 또는 레지스트리(core, gui, app, …)에 직접 const static 으로 선언됩니다.

  • 설정 키는 kebab-case 케밥 표기법을 따르는 형식이어야 합니다.

1.14. 코드 작업 스타일

이 절에서는 오류, 개발 시간, 그리고 유지·관리에 드는 수고를 줄여주리라 기대되는 몇몇 프로그래밍 힌트와 도움말을 설명합니다.

1.14.1. 가능한 부분마다 코드를 일반화하기

여러분이 코드를 잘라내서 붙여넣거나 또는 그러지 않고 동일한 내용을 한 번 이상 작성하는 경우, 해당 코드를 단일 함수로 통합하는 것을 고려해보십시오.

이렇게 하면:

  • 여러 위치에서가 아니라 한 위치에서 변경 사항을 만들 수 있게 됩니다.

  • 코드가 지저분해지는 일을 막는 데 도움이 됩니다.

  • 시간이 지남에 따라 여러 복사본들이 서로 다르게 발전해서 다른 사람들이 코드를 이해하고 유지·보수하기 어려워지는 일을 방지할 수 있게 해줍니다.

1.14.2. 술부에 상수를 먼저 넣기

술부(predicate)에 상수를 먼저 넣는 편이 좋습니다.

value == 0 말고 0 == value

이렇게 하면 프로그래머들이 == 를 써야 할 곳에 실수로 = 를 사용하는 일을 방지하는 데 도움이 될 것입니다. 이런 실수는 아주 미묘한 논리적 버그를 일으킬 수 있습니다. 비교에 == 대신 = 를 실수로 사용한 경우 컴파일러가 오류를 생성할 것입니다. 본질적으로 상수에 값을 할당할 수는 없기 때문입니다.

1.14.3. 공백은 여러분의 친구

연산자, 선언문, 그리고 함수 사이에 공백을 추가하면 인간이 이해하기 쉬운 코드를 작성할 수 있습니다.

어느 쪽이 더 읽기 쉽습니까, 이쪽?:

if (!a&&b)

아니면 이쪽?:

if ( ! a && b )

참고

prepare_commit.sh 스크립트가 이를 처리해줄 것입니다.

1.14.4. 명령어는 서로 다른 줄에 넣기

코드를 읽을 때 명령어가 그 줄의 맨 앞에 없다면 놓치기 쉽습니다. 코드를 빨리 훑어볼 때, 처음 몇 문자들이 여러분이 찾는 내용처럼 보이지 않을 경우 그 줄을 건너뛰어버리는 일도 흔합니다. 또한 if 같은 조건문 뒤에 명령어가 오는 것을 기대하는 일도 흔합니다.

다음을 보십시오:

if (foo) bar();

baz(); bar();

정말 쉽게 제어 흐름의 일부분을 놓칠 수 있습니다. 대신 다음을 사용하십시오:

if (foo)
  bar();

baz();
bar();

1.14.5. 접근 수정자 들여쓰기

접근 수정자(access modifier)는 클래스를 공개 API 부분, 보호 API 부분, 그리고 비공개 API 부분으로 구조화합니다. 접근 수정자 자체도 자신의 코드를 이런 구조로 그룹화합니다. 접근 수정자와 선언문을 들여쓰기하십시오.

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

1.14.6. 권장 도서

계간 Qt(Qt Quarterly)에 나온 Qt 스타일 설계하기 (APIs) 라는 기사도 읽어봐야 할 것입니다.

1.15. 공헌에 대한 인정

새 함수를 만든 공헌자들이 사람들에게 자신의 공헌을 다음과 같은 방법으로 알릴 것을 권장합니다: