1. QGIS معايير الترميز

معايير كتابة الشيفرة في QGIS موصوفة في وثيقة السياسات المتاحة في QEP #314. يُطلب من جميع المطورين اتباع هذه السياسات. يُرجى ملاحظة أن QEP #314 وثيقة حيّة، وأن هذه السياسات قد تتغير مع مرور الوقت.

1.1. اصناف

1.1.1. وظائف الموصل

تأكد من تمييز دوال الوصول (accessors) بشكل صحيح بالكلمة const. وقد يتطلب ذلك عند الحاجة تمييز متغيرات الأعضاء التي تخزّن قيمًا مؤقتة بالكلمة mutable.

1.1.2. الوسيطات الدالة

انتبه جيدًا للحالات التي يجب فيها تمرير الوسائط بالمرجع. ما لم تكن كائنات الوسائط صغيرة وسهلة النسخ (مثل كائنات QPoint)، فيجب تمريرها بمرجع ثابت (const reference). وللاتساق مع واجهة Qt البرمجية، تُمرَّر حتى الكائنات المشتركة ضمنيًا بمرجع ثابت (مثلًا setTitle( const QString& title ) بدلًا من setTitle( QString title ).

1.1.3. قيم إرجاع الدالة

أعِد الكائنات الصغيرة وسهلة النسخ كقيم. أما الكائنات الأكبر فيجب إعادتها بمرجع ثابت. والاستثناء الوحيد هو الكائنات المشتركة ضمنيًا، إذ تُعاد دائمًا بالقيمة. أعِد كائنات QObject أو الكائنات المشتقة منها كمؤشرات.

  • int maximumValue() const

  • const LayerSet& layers() const

  • QString title() const (QString مشترك ضمنيًا)

  • QList< QgsMapLayer* > layers() const (QList مشترك ضمنيًا)

  • QgsVectorLayer *layer() const; (QgsVectorLayer يرث QObject)

  • QgsAbstractGeometry *geometry() const; (QgsAbstractGeometry صنف مجرد وسيحتاج على الأرجح إلى تحويل نوعه)

1.2. توثيق الواجهة البرمجية (API)

يجب كتابة توثيق للواجهة البرمجية لكل صنف ودالة وتعداد (enum) وأي شيفرة أخرى متاحة في الواجهة البرمجية العامة.

تستخدم QGIS أداة Doxygen للتوثيق. اكتب تعليقات وصفية وذات معنى تعطي القارئ معلومات عمّا يتوقعه، وما يحدث في الحالات الحدّية، وتلمّح إلى واجهات أخرى قد يبحث عنها، إضافة إلى أفضل الممارسات وأمثلة الشيفرة.

1.2.1. متغيرات الأعضاء

يجب عادةً أن تكون متغيرات الأعضاء في القسم private وأن تُتاح عبر دوال الجلب والتعيين (getters و setters). ومن الاستثناءات على ذلك حاويات البيانات مثل تلك المستخدمة للإبلاغ عن الأخطاء. في هذه الحالات لا تضع البادئة m قبل اسم العضو.

1.3. Qt Designer

1.3.1. الفئات المتولدة

يجب أن تحتوي الفئات التي يتم تكوينها من ملفات QT مصمم (ui) على لاحقة أساسية.هذا يحدد الفئة كفئة أساسية تم إنشاؤها

أمثلة:

  • QgsPluginManagerBase

  • QgsUserOptionsBase

1.3.2. حوارات

يجب على جميع مربعات الحوار تنفيذ تعليمات تلميح الأدوات لجميع رموز شريط الأدوات وأدوات أخرى ذات صلة. تضيف أدوات الأدوات إلى حد كبير قابلية اكتشاف ميزة للمستخدمين الجدد وذوي الخبرة.

تأكد من تحديث ترتيب علامات التبويب الخاصة بالتطبيقات عندما يتغير تخطيط مربع الحوار.

1.4. C++ ملفات

1.4.1. رأس قياسي والترخيص

يجب أن يحتوي كل ملف مصدر على قسم رأس مزخرف بعد المثال التالي:

/***************************************************************************
  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 Creator في مستودع git. لاستخدامه، انسخه من qt_creator_license_template إلى موقع محلي، وعدّل عنوان البريد الإلكتروني والاسم إن لزم، ثم اضبط QtCreator لاستخدامه: Tools ► Options ► C++ ► File Naming.

1.5. تحرير

يمكن استخدام أي محرر نصوص أو بيئة تطوير متكاملة (IDE) لتحرير شيفرة QGIS، شريطة استيفاء المتطلبات التالية.

1.5.1. علامات التبويب

عيّن المحرر لمضاهاة علامات التبويب بمسافات. يجب تعيين تباعد التبويب إلى 2 مسافات

ملاحظة

في محرر vim يتم ذلك بالأمر set expandtab ts=2

1.5.2. مسافة فارغة

يجب إزاحة الشيفرة المصدرية (indentation) لتحسين قابلية قراءتها. يوجد ملف prepare_commit.sh يبحث عن الملفات المعدّلة ويعيد إزاحتها باستخدام astyle. يجب تشغيله قبل الإيداع (commit). ويمكنك أيضًا استخدام astyle.sh لإزاحة ملفات منفردة.

بما أن الإصدارات الأحدث من astyle تُزيح الشيفرة بطريقة مختلفة عن الإصدار المستخدم لإعادة إزاحة الشيفرة المصدرية بالكامل، فإن السكربت يستخدم إصدارًا قديمًا من astyle نضمّنه في مستودعنا (فعّل WITH_ASTYLE في cmake لتضمينه في البناء).

1.6. توافق واجهة برمجة التطبيقات

يوجد API documentation للغة C++.

نحاول الحفاظ على استقرار الواجهة البرمجية وتوافقها مع الإصدارات السابقة. يجب إجراء عمليات تنظيف الواجهة البرمجية بطريقة مشابهة لشيفرة 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.7. روابط SIP

تُولَّد بعض ملفات SIP تلقائيًا باستخدام سكربت مخصص.

1.7.1. المعالجة المسبقة لملفات الترويسة

يجب أن تتوفر جميع المعلومات اللازمة لبناء ملف SIP بشكل صحيح في ملف ترويسة C++. وتتوفر بعض وحدات الماكرو لهذا التعريف:

  • استخدم #ifdef SIP_RUN لتوليد شيفرة في ملفات SIP فقط أو #ifndef SIP_RUN لشيفرة C++ فقط. وتُعالَج عبارات #else في الحالتين.

  • استخدم SIP_SKIP لتجاهل سطر

  • تُعالَج التعليقات التوضيحية (annotations) التالية:

    • 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) لتعريف قيمة افتراضية بديلة لدالة python. وإذا احتوت القيمة الافتراضية على فاصلة , فيجب إحاطتها بعلامتي اقتباس مفردتين '

  • يمكن استخدام SIP_PYTYPE(type) لتعريف نوع بديل لوسيط في دالة python. وإذا احتوى النوع على فاصلة , فيجب إحاطته بعلامتي اقتباس مفردتين '

يتوفر أيضًا ملف توضيحي هو sipifyheader.h.

1.7.2. توليد ملف SIP

يمكن توليد ملف SIP باستخدام سكربت مخصص. على سبيل المثال:

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

لتوليد ملف SIP تلقائيًا لملف C++ مضاف حديثًا، يجب تنفيذ sip_include.sh.

بمجرد إضافة ملف SIP إلى أحد الملفات المصدرية (core_auto.sip أو gui_auto.sip أو analysis_auto.sip)، سيُعتبر مولَّدًا تلقائيًا. وسيتحقق اختبار من أن هذا الملف محدَّث ومتوافق مع ملف الترويسة المقابل له.

لفرض إعادة إنشاء ملفات SIP، يجب تنفيذ sipify_all.sh.

1.7.3. تحسين سكربت sipify

إذا لزمت بعض التحسينات لسكربت sipify، فيُرجى إضافة الأجزاء الناقصة إلى الملف التوضيحي sipifyheader.h وإنشاء ملف الترويسة المتوقع sipifyheader.expected.sip. وسيُختبر ذلك تلقائيًا أيضًا كاختبار وحدة للسكربت نفسه.

1.8. إعدادات

توفر الشيفرة المصدرية لـ QGIS آلية للإعلان عن الإعدادات وتسجيلها واستخدامها.

  • يجب تعريف الإعدادات باستخدام أحد التطبيقات المتاحة (QgsSettingsEntryString، QgsSettingsEntryInteger، …).

  • يجب دمج الإعدادات في شجرة الإعدادات (QgsSettingsTree)، ويتم ذلك تلقائيًا عند استخدام المُنشئ (constructor) مع عقدة أب (QgsSettingsTreeNode).

  • يُعلَن عنها كـ const static إما في صنف مخصص أو في السجل مباشرة (core، gui، app، …).

  • يجب أن يستخدم مفتاح الإعداد صيغة kebab-case.

1.9. نمط الترميز

فيما يلي وصف لبعض تلميحات البرمجة والنصائح التي من المأمول أن تقلل الأخطاء ووقت التطوير والصيانة.

1.9.1. حيثما ممكن من أي وقت ممكن تعميم قانون

إذا كنت تقص الشيفرة وتلصقها، أو تكتب الشيء نفسه أكثر من مرة بأي طريقة أخرى، ففكّر في دمج الشيفرة في دالة واحدة.

هذا سوف:

  • السماح بإجراء التغييرات في مكان واحد بدلاً من أماكن متعددة

  • مساعدة في منع شفرة منتفخ

  • يجعل من الصعب على النسخ المتعددة أن تتباين مع مرور الوقت، مما يجعلها أصعب فهمًا وصيانة على الآخرين

1.9.2. ضع الأوامر على خطوط منفصلة

عند قراءة الشيفرة يسهل تفويت الأوامر إذا لم تكن في بداية السطر. فعند القراءة السريعة للشيفرة، من الشائع تخطي الأسطر إذا لم تبدُ أحرفها الأولى شبيهة بما تبحث عنه. ومن الشائع أيضًا توقّع أمر بعد عبارة شرطية مثل if.

يعتبر:

if (foo) bar();

baz(); bar();

من السهل جدًا أن تفوت جزءًا من تدفق التحكم. بدلا من استخدامها

if (foo)
  bar();

baz();
bar();

1.9.3. توصيات كتاب

يجب عليك أيضًا قراءة هذه المقالة من Qt Quarterly على تصميم نمط Qt (APIs)

1.10. قروض للمساهمات

يتم تشجيع المساهمين في الوظائف الجديدة على إعلام الناس بمساهمتهم من خلال: