1. QGIS编码标准

所有QGIS开发人员都应该遵循这些标准。

1.1. 班级

1.1.1. 姓名

QGIS中的类以QGS开头,并使用驼峰大小写构成。

例如:

  • QgsPoint

  • QgsMapCanvas

  • QgsRasterLayer

1.1.2. 成员

类成员名称以小写m开头,并使用大小写混合形式构成。

  • mMapCanvas

  • mCurrentExtent

所有类成员都应该是私有的。公共班级成员被强烈劝阻。当可能需要从Python子类访问成员时,应避免使用受保护的成员,因为受保护的成员不能从Python绑定使用。

可变静态类成员名称应以小写字母开头 s ,但常量静态类成员名称应全部大写:

  • sRefCounter

  • DEFAULT_QUEUE_SIZE

1.1.3. 访问器函数

类成员值应通过accessor函数获取。该函数的命名应不带GET前缀。上述两个私有成员的访问器函数为:

  • mapCanvas()

  • currentExtent()

确保访问者正确地标记为 const 。在适当的情况下,这可能要求缓存值类型成员变量标记为 mutable

1.1.4. 功能

函数名称以小写字母开头,并使用大小写混合形式。函数名应该传达有关函数目的的某些信息。

  • updateMapExtent()

  • setUserOptions()

为了与现有的QGISAPI和QtAPI保持一致,应避免使用缩写。例如。 setDestinationSize 而不是 setDestSizesetMaximumValue 而不是 setMaxVal

为了保持一致,缩略语也应该大小写。例如。 setXml 而不是 setXML

1.1.5. 函数参数

函数参数应使用描述性名称。不要使用单个字母参数(例如 setColor( const QColor& color ) 而不是 setColor( const QColor& c ) )。

请仔细注意何时应通过引用传递参数。除非Argument对象很小并且被简单复制(例如QPoint对象),否则它们应该通过常量引用传递。为了与Qt API保持一致,甚至隐式共享对象也通过常量引用传递(例如 setTitle( const QString& title ) 而不是 setTitle( QString title )

1.1.6. 函数返回值

将复制的小对象作为值返回。较大的对象应由常量引用返回。这方面的一个例外是隐式共享对象,它们总是按值返回。返回 QObject 或作为指针的子类对象。

  • 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. API文档

它需要为公共API中提供的每个类、方法、枚举和其他代码编写API文档。

QGIS使用 Doxygen 或文档。撰写描述性和有意义的注释,为读者提供有关预期情况、在边缘情况下会发生什么的信息,并提供有关他可能正在寻找的其他接口、最佳实践和代码示例的提示。

1.2.1. 方法

方法描述应以描述性形式书写,使用第三人称。方法需要一个 \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 Designer(UI)文件生成的QGIS类应该具有基本后缀。这将该类标识为生成的基类。

例如:

  • QgsPluginManagerBase

  • QgsUserOptionsBase

1.3.2. 对话框

所有对话框应为所有工具栏图标和其他相关小工具实现工具提示帮助。工具提示极大地增加了新用户和有经验用户的可发现性。

确保每当对话框布局更改时,都会更新小部件的Tab键顺序。

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

备注

在Git库中有一个用于Qt Creator的模板。要使用它,请将其复制到 qt_creator_license_template 到本地位置,调整邮件地址和名称(如果需要),并配置QtCreator以使用它: Tools ► Options ► C++ ► File Naming

1.5. 变量名

局部变量名称以小写字母开头,并使用大小写混合形式构成。不要使用前缀,如 mythe

例如:

  • mapCanvas

  • currentExtent

1.6. 枚举类型

枚举类型应在CamelCase中命名,并以大写开头,例如:

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

不要使用会与其他类型冲突的泛型类型名称。例如,使用 UnkownUnit 而不是 Unknown

1.7. 全局常量和宏

全局常量和宏应以大写下划线分隔,例如:

const long GEOCRS_ID = 3344;

1.8. 评论

类方法的注释应该使用第三人称指示性风格,而不是命令性风格:

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

1.9. QT信号和时隙

所有信号/槽连接应使用Qt5中提供的“新式”连接。有关此要求的更多信息,请参阅 QEP #77

避免使用Qt自动连接插槽(即命名的插槽 void on_mSpinBox_valueChanged )。如果重构对话框,自动连接插槽是脆弱的,并且在没有警告的情况下容易损坏。

1.10. 编辑

只要满足以下要求,任何文本编辑器/IDE都可以用于编辑QGIS代码。

1.10.1. 制表符

将您的编辑器设置为模拟带空格的制表符。制表符间距应设置为2个间距。

备注

在VIM中,这是用 set expandtab ts=2

1.10.2. 缩进

源代码应该缩进以提高可读性。有一个 prepare_commit.sh 查找更改的文件并使用astyle重新插入它们的文件。这应该在提交之前运行。您还可以使用 astyle.sh 缩进单个文件。

由于较新版本的astyle缩进不同于用于对源进行完全重新缩进的版本,因此脚本使用旧astyle版本,我们将其包含在存储库中(启用 WITH_ASTYLE 在cmake中将其包括在构建中)。

1.10.3. 支撑架

大括号应从以下表达式后面的行处开始:

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

1.11. API兼容性

的确有 API documentation 用于C++。

我们试图保持API的稳定性和向后兼容性。对API的清理应该以类似于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. 报头预处理

正确构建SIP文件的所有信息都必须在C++头文件中找到。有些宏可用于此类定义:

  • 使用 #ifdef SIP_RUN 仅在SIP文件中生成代码或 #ifndef SIP_RUN 仅适用于C++代码。 #else 在这两种情况下都会处理语句。

  • 使用 SIP_SKIP 要放弃线路,请执行以下操作

  • 将处理以下批注:

    • 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 节不显示,除非您使用 #ifdef SIP_RUN 语句在此块中。

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

一个演示文件, sipifyheader.h ,也是可用的。

1.12.2. 正在生成SIP文件

可以使用专用脚本生成该SIP文件。例如:

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

自动生成新添加的C++文件的SIP文件 sip_include.sh 需要被执行。

一旦将SIP文件添加到其中一个源文件 (core_auto.sipgui_auto.sipanalysis_auto.sip ),它将被视为自动生成。上的测试将确保该文件使用其对应的头文件是最新的。

为了强制重新创建SIP文件, sipify_all.sh 应被处死。

1.12.3. 改进SIPIFY脚本

如果需要对SIPIFY脚本进行一些改进,请将缺失的部分添加到演示文件中 sipifyheader.h 并创建预期的标头 sipifyheader.expected.sip 。这也将作为脚本本身的单元测试进行自动测试。

1.13. 设置

QGIS代码库提供了一种声明、注册和使用设置的机制。

  • 应使用可用实现之一来定义设置 (QgsSettingsEntryStringQgsSettingsEntryInteger ,…)。

  • 设置必须集成到设置树中 (QgsSettingsTree ),则在将构造函数用于父节点时会自动执行此操作 (QgsSettingsTreeNode )。

  • 它们被声明为 const static 在专用类中或直接在注册表中(核心、图形用户界面、应用程序、…)。

  • 设置键应使用 kebab-case

1.14. 编码风格

这里描述了一些编程提示和技巧,有望减少错误、开发时间和维护。

1.14.1. 尽可能泛化代码

如果您正在剪切粘贴代码,或者多次编写相同的代码,请考虑将代码合并到单个函数中。

这将:

  • 允许在一个位置进行更改,而不是在多个位置

  • 帮助防止代码膨胀

  • 使多个副本更难随时间演变差异,从而使其他副本更难理解和维护

1.14.2. 更喜欢在谓词中先有常量

更喜欢在谓词中将常量放在第一位。

0 == value instead of value == 0

这将有助于防止程序员意外使用 = 当他们打算使用 == ,这可能会引入非常微妙的逻辑错误。如果您不小心使用了 = 而不是 == 用于比较,因为常量本身不能赋值。

1.14.3. 空格可以成为您的朋友

在运算符、语句和函数之间添加空格可以更容易地解析代码。

哪个更容易阅读,这是:

if (!a&&b)

或者这样:

if ( ! a && b )

备注

prepare_commit.sh 剧本会处理这件事的。

1.14.4. 将命令放在单独的行上

在阅读代码时,如果命令不在行首,则很容易遗漏命令。在快速阅读代码时,如果行与您在前几个字符中要查找的内容不同,则通常会跳过这些行。在条件LIKE之后期待命令也是很常见的 if

请考虑:

if (foo) bar();

baz(); bar();

很容易错过控制流的一部分。相反,请使用

if (foo)
  bar();

baz();
bar();

1.14.5. 缩进访问修饰符

访问修饰符将类构造为公有API、受保护API和私有API部分。访问修饰符本身将代码分组到此结构中。缩进访问修饰符和声明。

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

1.14.6. 图书推荐

  • Effective Modern C++ <https://www.oreilly.com/library/view/effective-modern-c/9781491908419/> _,斯科特·迈耶斯

  • More Effective C++ <https://www.informit.com/store/more-effective-c-plus-plus-35-new-ways-to-improve-your-9780201633719> _,斯科特·迈耶斯

  • Effective STL <https://www.informit.com/store/effective-stl-50-specific-ways-to-improve-your-use-9780201749625> _,斯科特·迈耶斯

  • Design Patterns <https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612> _,GoF

你也应该认真阅读这篇来自Qt季刊的文章 designing Qt style (APIs)

1.15. 捐款学分

鼓励新功能的贡献者通过以下方式让人们知道他们的贡献: