QGIS编码标准¶

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

姓名

qgis中的类从qgs开始,并使用camel case形成。

实例:

  • QgsPoint
  • QgsMapCanvas
  • QgsRasterLayer

成员

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

  • mMapCanvas
  • mCurrentExtent

所有的类成员都应该是私有的。公众阶层的成员强烈反对。当可能需要从python子类访问成员时,应避免使用受保护的成员,因为不能从python绑定中使用受保护的成员。

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

  • sRefCounter
  • DEFAULT_QUEUE_SIZE

访问器函数

类成员值应该通过accesssor函数获得。函数的命名不应使用get前缀。上面两个私有成员的访问函数是:

  • mapCanvas()
  • currentExtent()

确保访问器正确标记为 const . 在适当的情况下,这可能需要将缓存值类型成员变量标记为 mutable .

功能

函数名以小写字母开头,由混合大小写组成。函数名应该传达一些关于函数用途的信息。

  • updateMapExtent()
  • setUserOptions()

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

为了保持一致性,缩写词也应该采用驼色大小写。例如。 setXml 而不是 setXML .

函数参数

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

注意什么时候应该通过引用传递参数。除非参数对象很小并且被复制(如qpoint对象),否则它们应该通过const引用传递。为了与qt api保持一致,即使是隐式共享的对象也通过const引用传递(例如 setTitle( const QString& title ) 而不是 setTitle( QString title ) .

函数返回值

以值的形式返回复制的小对象。较大的对象应通过常量引用返回。唯一的例外是隐式共享对象,它总是按值返回。返回 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)

API文档

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

qgis使用doxygen作为文档。写一些描述性的、有意义的评论,给读者提供关于期望值、在边缘情况下发生的事情的信息,并给出关于他可能正在寻找的其他接口、最佳实践和代码示例的提示。

方法

方法说明应使用第三人以描述性的形式书写。方法需要 \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()
 */

成员变量

成员变量通常应位于 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
  };
};

设计器

生成的类

从qt设计器(ui)文件生成的qgis类应该有一个基后缀。这将类标识为生成的基类。

实例:

  • QgsPluginManagerBase
  • QgsUserOptionsBase

对话

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

确保在对话框布局更改时更新小部件的选项卡顺序。

C++文件

姓名

C++实现和头文件应该分别有.CPP和.H扩展。文件名应该全部是小写的,如果是类,则与类名匹配。

实例:类 QgsFeatureAttribute 源文件是 qgsfeatureattribute.cppqgsfeatureattribute.h

注解

如果上面的语句不清楚,那么对于与类名匹配的文件名,它隐式地意味着每个类都应该在自己的文件中声明和实现。这使得新手更容易识别代码与特定类相关的位置。

标准标题和许可证

每个源文件都应该包含一个标题部分,该标题部分在下面的示例之后有图案:

/***************************************************************************
  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模板。要使用它,请从 doc/qt_creator_license_template 到本地位置,调整邮件地址和(如果需要)名称,并将qtcreator配置为使用它: Tools ‣ Options ‣ C++ ‣ File Naming .

变量名

局部变量名以小写字母开头,并由混合大小写组成。不要使用像这样的前缀 mythe .

实例:

枚举类型

枚举类型应在camelcase中命名,并使用一个前导大写,例如:

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

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

全局常量和宏

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

const long GEOCRS_ID = 3344;

QT信号和插槽

所有信号/插槽连接应使用qt5中提供的“新样式”连接。有关此要求的更多信息,请访问 QEP #77 .

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

编辑

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

制表符

将编辑器设置为使用空格模拟选项卡。制表符间距应设置为2个空格。

注解

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

缩进

源代码应该缩进以提高可读性。有一个 scripts/prepare-commit.sh 这会查找更改后的文件,并使用Astyle重新插入它们。这应该在提交之前运行。您也可以使用 scripts/astyle.sh 缩进单个文件。

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

支撑

大括号应该从表达式后面的行开始:

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

API兼容性

API documentation 对于C++。

我们试图保持API的稳定和向后兼容。对API的清理应以类似于qt源代码的方式进行,例如

class Foo
{
  public:
    /**
     * This method will be deprecated, you are encouraged to use
     * doSomethingBetter() rather.
     * \deprecated doSomethingBetter()
     */
    Q_DECL_DEPRECATED bool doSomething();

    /**
     * Does something a better way.
     * \note added in 1.1
     */
    bool doSomethingBetter();

  signals:
    /**
     * This signal will is 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();
}

SIP绑定

一些SIP文件是使用专用脚本自动生成的。

收割台预处理

正确构建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 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 '

演示文件可在 tests/scripts/sipifyheader.h .

生成SIP文件

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

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

一旦将SIP文件添加到某个源文件中( python/core/core.sippython/gui/gui.sippython/analysis/analysis.sip )将被视为自动生成。Travis上的测试将确保该文件及其相应的头是最新的。

尚未启用自动创建的旧文件列在 python/auto_sip.blacklist .

改进sipify脚本

如果sipify脚本需要一些改进,请将缺少的位添加到演示文件中 tests/scripts/sipifyheader.h 并创建预期的标题 tests/scripts/sipifyheader.expected.si . 这也将作为脚本本身的单元测试在Travis上自动测试。

编码风格

下面介绍一些编程提示和技巧,希望能够减少错误、开发时间和维护。

尽可能通用化代码

如果要剪切n-pasting代码,或者多次编写相同的代码,请考虑将代码合并为单个函数。

这将:

  • 允许在一个位置而不是在多个位置进行更改
  • 帮助防止代码膨胀
  • 使多个副本随着时间的推移演变差异变得更加困难,从而使其他副本更难理解和维护。

在谓词中优先使用常量

倾向于将常量放在谓词的第一位。

0 == value instead of value == 0

这将有助于防止程序员意外使用 = 当他们打算使用 == 这会导致非常微妙的逻辑错误。如果不小心使用 = 而不是 == 因为常量本身不能赋值,所以无法进行比较。

空白区可以是你的朋友

在运算符、语句和函数之间添加空格使人更容易分析代码。

更容易阅读的是:

if (!a&&b)

或者:

if ( ! a && b )

注解

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

将命令放在单独的行上

在读取代码时,如果命令不在行的开头,很容易遗漏命令。当快速阅读代码时,如果行看起来不像您在前几个字符中要查找的那样,则通常跳过行。在类似于 if .

考虑:

if (foo) bar();

baz(); bar();

很容易错过控制流程的一部分。代替使用

if (foo)
  bar();

baz();
bar();

缩进访问修饰符

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

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

书籍推荐

您还应该阅读qt Quarterly上的这篇文章 designing Qt style (APIs)

供款贷项

鼓励新职能的贡献者通过以下方式让人们了解他们的贡献: