تعَّلم كيفية كتابة إضافات nvda

تعلم كيفية كتابة إضافات قارئ الشاشة nvda
تُتابِعون في هذه الصفحة بعض الشروحات حول كيفية كتابة إضافات nvda.
عزيزي القارئ: عليك أولا قبل الشروع في متابعة هذه الدورة متابعة الدورة الخاصة بمبادئ وأساسيات بايثون حتى تتمكن من فهم الأُمور بشكلٍ جيد.
أيها المُتابِع الكريم لا تنسَ أن هذه الدورة هي امتداد لدورة الأساسيات الخاصة بالأستاذ صلاح البرعصي فلا تنساه من صالح دُعائك.
أدامهُ الله مُعلِما للغيرِ معطاءً للخير، نسأل الله الكريم رب العرش العظيم أن يرزقه سعادةً لا تنتهي ويرزقه خيرَيْ الدنيا والآخِرة.
والآن لِنبدأ على بركة الله،
يمكنكم الانتقال بين الدروس بالرقم 1
والانتقال بين النماذج بالرقم 2،
أو الانتقال بحرف h للمرور على جميع العناوين.

الدرس الأول: إنشاء ملف السكربت والمكتبة ui


بسم الله الرحمن الرحيم.
أسعد الله أوقاتِكم بذِكْرِ الله.
فلنبدأ على بركة الله رحلتنا مع إضافات nvda.
أول شيء علينا فعله هو:
الذهاب إلى إعدادات البرنامج عن طريق هذا الاختصار:
nvda+n
وnvda هنا نقصد به الإنسرت أي المفتاح المُصاحِب للبرنامج سواء كان مفتاح تكبير الحروف أو الإنسرت المعروف فوق مفتاح الحذف في لوحة الحاسوب المكتبي ويختلف مكانه من لوحة لأخرى في الحواسب المحمولة أو الصفر في لوحة الأرقام.
ثم ننزل إلى قائمة تفضيلات ومنها الإعدادات، ننزل حتى نصل إلى متقدِم
هنا نتحرك تاب ولا نضغط مفتاح الإدخال، سنجد:
متقدم  صفحة الخصائص  تحذير!
الإعدادات الآتية للمستخدمين المتقدمين، وقد يؤدي تغييرُها لعمل NVDA بشكل غير صحيح. يُرجى تغيير هذه الإعدادات فقط في حال كنت مدركا لحقيقة ما تجريه، أو في حال توجيه خاص من قِبَل المطورين
أتفهّمُ أن تغيير هذه الإعدادات قد يتسبب في عمل NVDA على غير الوجه الصحيح     غير محدد
نقوم بتحديد هذا المربع
ثم نتحرك تاب حتى نجد:
إتاحة تحميل الكود المخصص من دليل مسودة المطور     غير محدد
نقوم بتحديده
ثم نكمل سنجد 
فتح الدليل المتضمن لمسودة المطوِّر   
يمكنُنا الوصول إلى مجلد العمل بالضغط على هذا الزر.
ثم نضغط إنتر أو موافق لحفظ الإعداد.
حسناً، الآن إذا لم نضغط على الزر كيف نصل لهذا المجلد؟
نذهب إلى مجلد المستخدِم 
1 إذا كانت النسخة مثبتة نكتب هذا المسار في الرن بالضغط على windows+r ونقوم بلصقِه هناك
%APPDATA%\nvda
2 إذا كانت محمولة نذهب إلى المجلد الذي وضعناها فيه ونضغط حرف u حتى نجد
userConfig
الآن نحن داخل مجلد المستخدِم، نريد الوصول إلى مسودة المُطَوِر،
نبحث عن مجلد باسم scratchpad ونفتحه،
هنا سنجد عدة مجلدات أهمها:
appModules: وهذا يتعلق بالإضافات التي نستخدمها مع برنامج معين.
globalPlugins: وهذا للإضافات التي لا تتعلق ببرنامج وتعمل من أي مكان في الجهاز.
والثاني هذا هو ما نحتاج إليه الآن ندخل فيه، وننشئ مجلدا جديدا ونسميه أي اسم لا مشكلة في تسميته،
ثم نفتحه ونُنشِئ فيه ملف نصي بامتداد py
يعني نقوم بحذف اسم الملف وامتداده الذي سيكون txt ونكتب اسم الملف هكذا:
__init__.py، وهذا الامتداد يعني أنه بايثون فكما نلاحظ py هم أول حرفين من كلمة python.
وننتبه إلى أنهُ يجب إظهار المجلد بهذه الطريقة المذكورة أعلاه وعدم إنشائه بشكل يدوي.

الآن نفتح هذا الملف باستخدام المفكرة [notepad].
ونكتُب أول إضافة لنا، وسنتعلم فيها قِراءة الرسائل الترحيبية أو أي رسائل خاصة نسمعها من ال nvda فقط ولا تظهر على الشاشة.
نقوم باستيراد المكتبات التي سنحتاج إليها:
1
import globalPluginHandler
وهذه المكتبة لا تعمل إلا مع nvda وبالطبع هي موجودة معه بشكل افتراضي، وتتعلق بالإضافات التي تعمل من أي مكان في الجهاز.
2
import ui
وهذه الثانية هي المسؤولة عن قِراءة الرسائل النصية، نعم النصية فقط فإذا أردنا أن تقرأ أرقام علينا تحويل الرقم إلى نص أولا كما أنها لا تعمل إلا مع nvda أيضا.

بعد الاستيراد ننزل إلى سطر جديد وننشِئ به كلاساً محترماً, 
طبعاً أنتم تعرفون الآن ما معنى الكلاس، لا يسبقه تثليم اسم الكلاس سيكون GlobalPlugin.
بعد كتابة الاسم الخاص بالكلاس افتح قوس هلالي واكتب فيه اسم المكتبة التي تم إستيرادها وما يلزم بعدها من محتويات وأغلِق القوس الهلالي ثم نقطتين رأسيتين على النحو التالي:

class GlobalPlugin(globalPluginHandler.GlobalPlugin):
وهذا الكِلاس ثابت مع أي إضافة تعمل من أي مكان
وانتبِهوا جيدا للحروف الكبيرة والصغيرة، فإذا تم كتابة حرف كبير بدل صغير أو العكس، لن أقول شيء فأنتم تعرفون الباقي ههههههههه .
إلى الإسكريبت
سنكتُب فيه دالة واحدة
	def script_hi (self, gesture):
كما نرى هنا بدأنا بكتابة إزاحة لِتُناسب النقطتين في نهاية الكلاس، ثم script وهذه الكلمة تعني أننا سنربِط هذه الدالة باختصار، ثم نلحقها بخط سفلي واسم الدالة أي اسم نريده يدل على عمل دالَتِنا.
الوسائط هنا self ونعرفها من قبل أي الإشارة إلى هذا الكلاس، gesture وهو  ما نقصد به الاختصار الذي سنربطه بالسكريبت لاحقا.
بعد ذلك نكتب وظيفة هذا السكريبت وهو قِراءة رسالة ترحيبية هكذا:
		ui.message("مرحبا بالأصدقاء، لا تنسوا ذكر الله")
ui اسم المكتبة نتبعه بنقطة كما نعلم في التعامل مع المكتبات ثم نستخدم الدالة message ونكتب الرسالة التي نريدها بين قوسي الدالة.

سنكتب الآن قاموساً خفيفاً ظَريفاً يعمل هذا القاموس على ربط الوظيفة 
بالمفتاح الذي سنختاره.
والآن سنستَذْكِر القواميس قليلاً قبل أن نَكتُبَ قاموسَنا , القاموس نوع من البيانات يبدأ بقوس مزخرف وينتهي بقوس مزخرف واحد للفتح والآخر للإغلاق, بين القوسين نكتب العناصر, وَ نَفصِل بين هذه العناصر بالفاصلة, وكل عنصر ينقسم من الداخل إلى قسمين الأول نسميه المفتاح والثاني نسميه القيمة. 
في المفتاح سنكتب اسم المفتاح المراد أن نربطه بالسكربت وفي القيمة نكتب اسم السكربت لكن بدون كلمة script_ يعني اسم الوظيفة فحسب.
نوع المفتاح والقِيمة سلسلة نصّية، نَفْصِل بينهما بنقطتينرأسَيّتَينِ لا تستوجبانِ الإزاحة, لأنهما ليسا في آخر السطر البرمجي.
سَنَسْنُد القاموس إلى متغير نُسَمّيه: __gestures 
عملية الإسناد تكون على النحو التالي:
	__gestures= {
"kb:nvda+shift+f1": "hi"
}
لو كانت عندكَ أكثر من وظيفة على شكل سكربت فمن المُفَضَّلِ أن تَخُصَّ كُلَّ سكربت ومفتاحه بسطْرٍ في القاموس وفي نهاية كل سطر اكتُب علامة الفاصلة لتفصِلَ بين العناصر.
واعْلَمْ أنَّ لك مطلَق الحُرِّيَّةِ في كتابة المفتاح الذي تُحِبُّ للسكربت بشرط أن لا يتعارض مع اختصار في الويندوز أو اختصار خاص بالnvda أو اختصار يتعلق بإضافة أخرى.

هذه كانت الطريقة القديمة في ربط السكريبت بالمفتاح، هناك طريقة أخرى حديثة وهي:
نقوم أولا باستيراد كِلاس يُسمى script من مكتبة scriptHandler  هكذا:
from scriptHandler import script
بالطبع يكون هذا في بداية الملف مع مجموعة المكتبات التي سيتم العمل عليها.
ثم نأتي قبل الدالة التي تحتوي السكريبت ونكتب هذا:
	@script(gesture="kb:nvda+shift+f1")
نلاحظ هنا لم نكتب اسم الدالة كما فعلنا في المرة الأولى عند استخدام القاموس، بل اكتفينا بكتابة مفتاح الاختصار فقط.
وفي هذه الحالة نستغني عن القاموس فهذه الطريقة هي بديل له.

والآن انتهينا من العمل نقوم بحفظ ما كتبناه في الملف ونغلقه، ثم نقوم بإعادة تحميل الملحقات بالضغط على:
nvda+control+f3
أو نقوم بإعادة تشغيل البرنامج.
ثم نجرب الاختصار الذي كتبناه وإن شاء الله يعمل، وإذا لم يعمل نراجع ما كتبنا ربما وقعنا في خطأ ما.

إن شاء الله لن توجد أخطاء وبالتوفيق.

تدريب للتطبيق:
قم بكتابة سكريبت يقوم بفتح أي موقع تريده، ثم اكتب في الرسالة النصية فضلا انتظر جاري فتح موقع كذا، واربِط هذا السكريبت بأي مفتاح بحيث لا يتعارض مع غيره من الاختصارات الأخرى.

اللهم علِمنا ما ينفعنا وانفعنا بما علَمْتَنا وزِدنا عِلْما.

نموذج على فتح موقع وسماع رسالة تفيد ذلك


# -*- coding: utf-8 -*-
#هذا السطر يعمل على جعل المستند النصي بترميز UTF-8
import globalPluginHandler
import ui
import webbrowser
from scriptHandler import script

class GlobalPlugin(globalPluginHandler.GlobalPlugin):
	@script(gesture="kb:NVDA+f3")
	def script_hi (self, gesture):
		ui.message ("مرحبا بك عزيزي فضلا انتظر جاري فتح موقع صوت الأقصى")
		webbrowser.open('www.alaqsavoice.ps')

الدرس الثاني: المكتبة api ودالة getFocusObject


أسعد الله أوقاتِكم وزادكُم من فضله.
مع درس اليوم حول كائنات nvda،
سنعمل على نفس المجلد الذي عمِلنا عليه في الدرس السابق ونفس الملف الذي كتبنا فيه سنُكمِل عليه بإضافة معلومات جديدة.
نُضيف إلى المكتبات التي تم استيرادِها مكتبة جديدة تُسَمى api هكذا:
import api
وهذه المكتبة api هي أيضا مثل السابقين توجد تلقائيا مع nvda وهي خاصة به فلا تعمل مع غيره، وهي المسئولة عن قِراءة كل حرف نكتبه أو نقرأه.
الآن ننزل إلى الوظيفة script_hi ونكتب في السطر الموالي لها عملية إسناد على النحو التالي:
o = api.getFocusObject()
كتبنا اسم متغير وهو o وبإمكانِكَ تسميَة أي اسم تشاء إلا أنه جرتْ العادة عند إسناد قيمة من نوع NVDAObjects على تسمية المتغير o أو obj وأُفَضِّل أن تلتزِمَ بالعرف الجاري في هذا الباب حتى تَعْرِفَ متى مررْتَ عليه في سكربتك المترامي الأطراف أقول حتى تعرف أنه من نوع NVDAObjects.
ربما سيسأل أحدكم ما هذا النوع من القِيَم المُسَمَّى NVDAObjects؟
إخوتي ما يميز بايثون عن كثير من اللغات أنها لغة من لغات البرمجة الكائنية وهذا يجعلها تتصف بصفة رائعة وهي إمكانية إضافة أنواع جديدة من القِيَم غير القِيَم القياسية المعروفة سابقاً وللتذكير بالقيم القياسية نرجو منك قراءة الأسطر التالية:
أنواع القِيَم القياسية:
السلاسل النصية- الأرقام الصحيحة- الأرقام العشرية- الأرقام الطويلة- الصفوف - القواميس - العدم -العبارات المنطقية-القوائم.
ويمكن إضافة أي نوع آخر لهذه الأنواع متى دعتْ الحاجة إلى ذلك.
بعد كتابة اسم المتغير الذي سمّيْناه o نكتب علامة يساوي مرة واحدة لإتمام عملية الإسناد ثم اسم المكتبة وهو api وللعلم فإن هذه المكتبة تحوي في غالبِها دوال تُعيدُ قيمة NVDAObjects وهذه الدوال كما أسلفنا تُعنى بالكائنات الرسومية للتطبيقات المختلفة كسطح المكتب فكل عنصر من عناصر سطح المكتب ينظر إليه nvda باعتباره NVDAObjects وهذا الكلام ينطبق على كل التطبيقات سواء ويندوز أو تطبيقات أدُبي أو فايرفوكس أو أي تطبيقات أخرى فنظرة nvda إليها من هذا المنطلق يراها عناصر رسومية يمكن أن ننقل التركيز إليها أو قراءتها وإن لم يكن التركيز عليها، أو التأكد من وجودها. 
قلْنا أن أغلب دوال هذه المكتبة هي من نوع nvdaObject نأخذ واحدة من تلك الدوال وهي دالة getFocusObject()
من اسمها نعرف أنها تُمَثِّلُ الكائن الذي يقع عليه تركيز الحاسوب (pc focus) وتركيز الحاسوب هو التركيز الذي ينتقل بمجرد ضغطكَ على الأسْهُم أو التاب أو الشِفْت تاب في التطبيقات التي تسمح بذلك.
نفترض أنك على سطح المكتب وبالذات على المستندات, ثم انتَقَلْتَ إلى جهاز الكمبيوتر مجرد حركة بالأسْهُم ولم تدخلْ فيه, ما عمِلْتَه من تحريك للأسهم هو باختصار نقل للتركيز من المستندات إلى عنصر آخر على سطح المكتب وهو جهاز الكمبيوتر.
لكل عنصر أو لنَقُلْ كائن , لكل كائن من الكائنات الرسومية اسم وفئة ونوع تحكم ورقم تحكم وهذه كلها خصائص تشترك فيها الدوال من نوع nvdaObjects كدالة getFocusObject () أو دالة getFourgroundObject(), إذاً هناك خواص يتم كتابتُها بَعد الدوال من هذا النوع فَ name خاصّيّةٌ تُعنى باسم الكائن و role خاصّيّة تُعنى بنوع تحكم الكائن و windowClassName خاصّية تُعنى باسم فئة نافذة الكائن.
الخاصية تُكتَب بعد اسم الدالة ونفصل بينهما بنقطة على النحو التالي:
api.getFocusObject().name
هذا بالنسبة لاسم الكائن واسم الكائن في مثالنا السابق(جهاز الكمبيوتر)
نأخذ خاصّيّة أخرى وهي windowClassName 
نكتبها هكذا 
api.getFocusObject().windowClassName
نلاحظ أننا لم نعبث بأقواس الدالة getFocusObject() فالنقطة لا تكون إلا بَعدَها ثم نكتب الخاصيّة ولا أقواس للخاصية.
خاصّية role والمتعلقة بنوع تحكم الكائن نكتبها هكذا:
api.getFocusObject().role
ونوع التحكم هو على سبيل المثال:
زر أو مربع تحرير أو عنصر قائمة أو مربع تحديد وهكذا.
وهذه الخاصية role تُرجِع لنا رقم هذا التحكُم وليس اسمه فعلى سبيل المِثال مربع التحرير رقم التحكم الخاص به هو 8 أما الزر فرقمه 9 وهكذا كل كائن له رقم يُمَيِز نوعه عن غيره.
فإذا أردنا معرفة رقم الكائن الذي عليه التركيز نكتب عملية الإسناد هكذا:
o = api.getFocusObject().role
ui.message(str(o))
نُلاحظ هنا قمنا بتحويل قيمة o إلى نص حتى تتمكن دالة message من قِراءتها فكما أسلفنا أنها لا تقرأ غير النصوص.
جميل نعود إلى الملف الذي نعمل عليه، بعد عملية الإسناد
نكتب بعد القوسين الخاصية name
o = api.getFocusObject().name
ثم نضع o بين قَوسَي ui.message على النحو التالي:
ui.message(o)
لاحِظْ مع أننا نستخدم دالة message من مكتبة ui لنمرر من خلالها سلاسل نصية عبر معامِلها, لكن لم نجد في مثالنا السابق أي علامات تنصيص ويرجع السبب في ذلك أننا كتبنا اسم المتغير o وهو يحمل قيمة نصية وهذه القيمة عبارة عن اسم الكائن الذي عليه التركيز مثال ذلك المستندات جهاز الكمبيوتر تشغيل في قائمة ابدأ كافة البرامج في قائمة ابدأ وفي كل تطبيق نجد كائنات ولهذه الكائنات أسماء, ربما لا يكون في بعض التطبيقات أسماء للكائنات الموجودةِ بتلك التطبيقات مثل المكتبة الشاملة قبل التعديل فيقول لك مجهول None وهذا راجع لأخطاء يرتكبها المبرمجون لهذه التطبيقات.

هكذا ينتهي هذا الدرس،
ونريد أن نشير إلى نقطة غاية في الأهمية وهي عندما لا يعمل الكود كما توقعت له أن يعمل عليك بضغط الاختصار التالي: nvda+f1
وسيفتح لك نافذة تُعرف بعارض تقارير برنامج NVDA
بعد ضغط الاختصار الخاص بالسكريبت الذي كتبته قم بفتح هذا المستعرض وتحرك سهم لأعلى وستجد تقريرا عن الخطأ الذي وقعت فيه والسطر الذي يحتوي على الخطأ بالتحديد.

التطبيق:
قم بكتابة سكريبت يعمل على عد حروف اسم الكائن الذي عليه التركيز ثم يخبرنا بعددها ويقرأه بعدد تلك الحروف.

اللهم علِمنا ما ينفعنا وانفعنا بما علَمْتَنا وزِدنا عِلْما.

نموذج على قراءة اسم الكائن الذي عليه التركيز


import globalPluginHandler
import ui
import api
from scriptHandler import script

class GlobalPlugin(globalPluginHandler.GlobalPlugin):
	@script(gesture="kb:NVDA+f3")
	def script_hi (self, gesture):
		o=api.getFocusObject().name
		l=len(o)
		ui.message(f"عدد حروف الاسم هو: {l},"+ f"{o}+' '"*l)

الدرس الثالث: مكتبة أنواع التحكمات ودوال الحَدَث


أهلا وسهلا ومرحبا بكم من جديد.
لقد عرفْنا في الدرس السابق نوع جديد من أنواع القِيَم وهو NVDAObjects, وأخذنَا مثالاً على ذلك دالة من دوال مكتَبَة api, كما درسْنَا خاصّيَتينِ هما name و role, 
إلا أننا لم نقتُلْ الخاصّيّة role بحثاً, من ملاحظاتنا التجريبية في درسنا السابق أن role ما هي إلا integer وتعطِي أرقاماً مختلفة باختلاف أنواع الكائنات, فكيف يمكننا الإحاطة بأنواع الكائنات؟
في الأسطُر القادمة ستجد دليلاً تفصيلياً عن هذا على النحو التالي:
ROLE_EDITABLETEXT = 8 مُرَبع التحرير ومثاله المفكرة موضع الكتابة فيها وإلا فهي تحوي أنواع مختلفة من الكائنات الرسومية.
ROLE_CHECKBOX = 5 مربع التحديد 
ROLE_CLOCK = 37 الساعة الموجودة في صينية المهام. 
ROLE_COMBOBOX = 13 صندوق الخيارات. 
ROLE_BUTTON = 9 الأزرار العادية كزِر موافق وإلغاء الأمر وزر بحث في محرك بحث google.
ROLE_DIALOG = 4 مربع المحاورَة. 
ROLE_FRAME = 34 الإطار فمن المعروف أن الكائنات عندَ وضعها برمجيّاً لا توضع في الهواء الطلق أوعلى الصبورة بل توضع على كائن يقال له الإطار فمثلاً لو أننا نريد إنشاء برنامج به خمسة أزرار ومربع تحرير قابل للكتابة وآخر للقراءة فقط فإننا سنُنْشِئ إطاراً نضع عليه كل هذه الكائنات وننسقها تنسيقاً جميلاً وهكذا فكل هذه الكائنات أطفال وأبوهم هو الإطار يمكن الذهاب إلى الكائن الأب بضغط مفتاح nvda مع رقم ثمانية في لوحة الأرقام.
ROLE_GRAPHIC = 16 واضح من اسمه أنه رسم أو صورة ومثاله ما نجده من صور في صفحات الإنترنت لأن ما نتكلم عنه الآن ليس خاص بالتطبيقات العادية فقط بل حتى الإنترنت. 
ROLE_HEADING = 40 هذه تجدها في الانترنت وهي الرأسية يمكن الذهاب إليها عن طريق ضغط حرف h وأنت على صفحة النت هذا إن وُجِدَتْ. 
ROLE_HELPBALLOON = 17 البالون الذي يظهر ليقدم لك رسالة سريعة مثال ذلك ما يظهر لك من رسائل حول تعرض الحاسوب للخطر حال عدم تحديثه, أو Local Area Connection 3: كبل الشبكة غير موصول بالمأخذ. 
ROLE_LABEL = 73 اللافتة التي تشير إلى أمر ما مثال ذلك بعض المبرمجين لا يسمّون الأزرار بل يضعونا تحتها لافتة بها اسم الزر وهي عبارة عن نص ثابت. 
ROLE_LINK = 19 الروابط سواء في صفحات النت أو غيرها من التطبيقات التي نجد فيها رابطاً ما. 
ROLE_LIST = 14 القوائم كقائمة أسماء الأصدقاء في السكايب أو الياهو ماسنجر. 
ROLE_LISTITEM = 15 العناصر الموجودة في القوائم كاسم من أسماء الأصدقاء في قائمة الأصدقاء ببرنامج السكايب أو الياهوماسنجر. 
ROLE_MENU = 121 القائمة التي يتم تنشيطها بضغط alt في بعض التطبيقات. 
ROLE_MENUBAR = 10 شريط القائمة 
ROLE_PAGE = 75 الصفحة 
ROLE_PANEL = 122 لَوح واللوح يمكن أن نقول هو أشبَه بالإطار نجده يحوي مجموعة من الكائنات وهنا يمثل دور الأب لهذه الكائنات إلا أنه ليس الجسم الرئيسي للتطبيق بل نجده مُثبّت على frame يمثل الجسم الأصلي لكل الكائنات. 
ROLE_PASSWORDEDIT = 123 مربع تحرير كلمة المرور. 
ROLE_POPUPMENU = 12 القوائم المتفرعة من أخرى. 
ROLE_PROGRESSBAR = 25 شريط التقدّم 
ROLE_RADIOBUTTON = 6 زر الراديو أو ما يسمَّى بزر الاختيار. سر تسميته بهذا الاسم أنه يشبه أزرار الراديو في السبعينات وبداية الثمانينات, حيث كانت أزرار أنواع الموجات تشبه أزرار المسجّل كزر التقدم وزر التسجيل والعرض والإيقاف المؤقت وفتح الباب. فأنت لا يمكنكَ بحال ضغط أكثر من زر من أزرار الراديو في وقت واحد فلو كنتَ ضاغطاً لزر sw ثم ضغطتَ زر mw أو fm فإن الزر الأول سيرتفع بمجرد نزول الثني وهذه هي فكرة أزرار الاختيار.
ROLE_STATICTEXT = 7 النص الثابت. 
ROLE_STATUSBAR = 2 7 شريط الحالة. 
ROLE_TABLE = 28 الجداول. 
ROLE_TABLECELL = 29 الخلايا 
ROLE_TABLECOLUMN = 30 الأعمدة . 
ROLE_TOOLBAR = 35 شريط الأدوات. 
ROLE_TOOLTIP = 18 رسالة إرشادية مثال ذلك عندما تتحرك على سطح المكتب تسمع nvda يسرد لك حجم الملف الذي تقف عليه ونوعه وبعض التفاصيل الأخرى أو موقِع الملف إن كان اختصاراً. 
ROLE_TREEVIEWBUTTON = 111 زر شجري. 
ROLE_TREEVIEW = 20 عرض شجري. 
ROLE_TREEVIEWITEM = 21 عنصر ضمن العرض الشجري. 
ROLE_WINDOW = 1 نافذة يمكن أن نقول هي نوع من أنواع الأجسام التي يتم تثبيت بقية الكائنات عليها لكنه جسم غير رئيسي بعكس الإطار.

الآن نريد أن نعمل في ضوء ما فات, قبل كل شيء خذوا مني هذه النصيحة الذهبية لا تتعبوا أنفسكم في حفظ كل هذه المسميات بمنتهى الدقة بل استعينوا بالدليل السابق الذي أعدَدْتُه لكم وذلك حتى لا يكون عائقاً في وجوهكم فيصدَّكم عن التركيز على ما هو أهم, بالتدريج وبدون ما تقصد ستجد نفسَكَ مُلِم بها ولن تحتاج بَعد فترة للاستعانة بهذا الدليل.
الآن نذهب إلى ملف العمل ونستورد مكتبة أنواع التحكم واسمها controlTypes هكذا:
import controlTypes as ct
وهذه طريقة لاختصار اسم المكتبة الطويل لا بد وأنكم تعرفونها.
لمَن لا يعرفها يمكنك الرجوع إلى درس المكتبات في دورة تعليم الأساسيات.
ثم نكتبْ في script_hi الآتي 
	def script_hi(self, gesture):
		if api.getFocusObject().role == ct.ROLE_BUTTON:
			ui.message('هذا زِر')
الآن لو وقفت على أي زر وضغطت مفتاح الاختصار سيقول لك القارئ هذا زر
بإمكانكَ أن تضع في الطرف الثاني من المقارنة الشرطية الرقم تسعة لأنه يساوي تحكم الزر ويغنيك عن استيراد مكتبة أنواع التحكم، مع تفضيلي لاستيرادها وكتابة اسم المكتبة الخاصة بالتحكم ثم نقطة ثم نوع التحكم بدل الاكتفاء بكتابة رقم نوع التحكم.

عودة مع كائنات nvda (NVDAObjects) 
أخذنا name و role ونريد أن نعلم ما الفائدة من استخدام role و name.
يمكن الاستفادة منهما في كثير من الأُمور وأبسط مثال تخيل لو أن ثمةَ برنامج unreadble يعني غير قابل للقراءة وهذا النوع من البرامج متعِب حيث إنك حين تتحرك بين كائنات البرنامج سيقول لك مجهول مجهول مجهول وهكذا. 
يمكن التخلص من هذه المشكلة وذلك باستخدام دوال الحدَث فما هي دوال الحدَث؟ 
دوال الحدَث متعددة والقاسم المشترك بينها أنها لا تحتاج وضع مفتاح اختصار لتنفيذها بل تتنفذ بمجرد توفر حدث معين مثل:
نقل التركيز من عنصر إلى عنصر: event_gainFocus
ظهور رسالة في الشاشةمثل الرسالة الإرشادية: event_show.
أو تغير النص في المستند أو تغير اسم الكائن, وغيرها.
سنجرب الآن حدث نقل التركيز وسنستخدم مكتبة api ومكتبة controlTypes. 
الآن نضيف دالة جديدة في كلاس global يمكننا كتابتها قبل دالة script أو بعدها.
	def event_gainFocus(self, obj,nextHandler):
		if obj.role == ct.ROLE_BUTTON:
			obj.name = 'yes'
		nextHandler()

وهذه الوظيفة وظيفة نقل التركيز لا ترتبط بمفتاح اختصار بل تعمل بمجرد نقل التركيز عن طريق استخدام مفاتيح الأسهم أو التاب.
نأتي لمعاملات الوظيفة:
obj معامل يعني api.getFocusObject()
كأن نقول obj=api.getFocusObject()
لكن obj هنا مُعرفة تلقائيا فلا نحتاج لكتابة عملية إسناد تُعرفها، وهكذا هي في جميع دوال الحدث.
وكما ذكرنا فيمكن كتابة أي اسم وسيكون معرف حينها بأنه الكائن الذي عليه التركيز.
أما nextHandler: فتعني أن الحدث سيتم بمجرد نقل التركيز.
ثم قلنا:
		if obj.role == ct.ROLE_BUTTON:
يعني إذا مررت بأي زر
			obj.name = 'yes'
وهذا جواب الشرط قلنا له اجعل اسم هذا الزر yes
يعني كلما مر على زر سيتجاهل اسمه المعروف به ويقول yes.
وإذا لم نضع الشرط فسيقرأ كل شيء yes سواء أزرار أو مربعات تحديد أو حتى العناصر العادية كأسماء الملفات والمجلدات وكل شيء.
		nextHandler()
وهذه تعني استكمال القراءة بشكل طبيعي، فإن لم نكتبها سيتوقف القارئ ولن يقرأ إلا عند توفر الحدث فقط.

تدريبات:
1 قم بكتابة دالة حدث تصدر صوت بيب كلما مررنا بكائن أحد حروفه حرف e.
2 قم بإضافة شرط في نفس الدالة يعمل على تغيير اسم كائن بعينه.
على سبيل المثال مجلد المستخدِم في سطح المكتب الذي يكون اسمه باسم صاحب الجهاز، نقوم بتغيير اسمه إلى مجلد المستخدِم.
وهذا ما نفعله مع الvoice over أو التوكباك عند تغيير اسم كائن إلى اسم آخر من عندنا.
3 قم بكتابة دالة تغير نوع تحكم ما كالأزرار مثلا إلى روابط.

اللهم علِمنا ما ينفعنا وانفعنا بما علَمْتَنا وزِدنا عِلْما.

نموذج على تغيير أسماء الكائنات وأنواع التحكم


import globalPluginHandler
from tones import beep

class GlobalPlugin(globalPluginHandler.GlobalPlugin):
	def event_gainFocus (self, obj, nextHandler):
#هذه دالة الحدث التي تعمل بمجرد المرور على الكائنات كما تعرفنا عليها وعلى معاملاتها سابقا.
		if 'e' in obj.name:
			beep(600,60)
#قلنا له إذا مررت بكائن أحد حروف اسمه e فأصدر صوت البيب.
		if obj.name == "Control Panel":
			obj.name = "لَوْحةُ التحكم"
#هنا أعطيناه شرط آخر أنه إذا كان الكائن اسمه control panel فاجعل اسمه لوحة التحكم، وهذا ما نفعله مع الvoice over عند تغيير اسم عنصر ما، فهذا الأمر يختلف عن تغييير كلمة في المعجم يعني إذا كان هناك نص مكتوب يحتوي على كلمة control panel سيتم قِراءتها بشكل طبيعي ولن يقول لوحة التحكم، ويمكن استخدامِها أيضا في تعريب البرامج بتغيير أسماء الكائنات بهذه الطريقة.
		if obj.role ==9:
			obj.role=5
#أما هنا فقلنا إذا كان الrole يساوي 9 أي زر فاجعله 5 أي مربع تحديد، الآن إذا مررنا بأي زر سيقول مربع تحديد وإذا كنا نستخدم إضافة أصوات الكائنات فسنسمع صوت مربع التحديد.
#كما يمكننا هنا استخدام مكتبة controlTypes في تحديد نوع تحكم الكائن.
		nextHandler()
#كما نرى ختمنا الدالة بnextHandler حتى لا يحدث عطل في قِراءة باقية الكائنات.

ملحق بالدرس الثالث وظيفة الحدث event_show


إخوتي الكرام اليوم سنتحدث عن وظيفة أخرى من وظائف الحدَث وهي وظيفة event_show 
تختص هذه الوظيفة بمراقبة الرسائل التي تظهر على الشاشة كالرسائل الإرشادية التي تخبِرُكَ بأن الملف الذي يقع عليه التركيز حجمه كذا ونوعه كذا وكاتبه فلان وإن كان صوتاً أو فيديو فطول عرضه كذا, وغيرها من المعلومات التي تنبثق فور سقوط التركيز على عنصر من عناصر سطح المكتب أو بقية المجلدات, 
القاسم المشترك بينها وبين كل وظائف الحدث أنها تقع فور حصول الحدث ولا يتم ربطها بأي اختصارات مفتاحية.
الآن نتناول هذه الوظيفة بالمثال المباشر.

# -*- coding: utf-8 -*-
الآن ماذا علينا أن نستورد من مكتبات؟

import globalPluginHandler
import ui
import tones
from controlTypes import ROLE_TOOLTIP
استيراد الرسالة الإرشادية من مكتبة أنواع التحكمات.


class GlobalPlugin(globalPluginHandler.GlobalPlugin):
ما نريد أن نفعله الآن هو إنشاء إضافة بها نتحكم في عرض الرسائل الإرشادية وقت نشاء, بدل الذهاب إلى محاورة الكائنات بالضغط على nvda+control+o ثم إلغاء التحديد من على الإعلام عن الرسالة الإرشادية, سنتحكم بهذا الأمر عن طريق وضع اختصار به يتم تفعيل الإعلام عن الرسالة الإرشاديةأو إلغائه, 
ما الذي نحتاجه كي نجرِي هذه العملية؟
نعم أول ما نحتاج إليه متغيّر هو عبارة عن صفة بهذا الكلاس المتغير ستكون قيمته منطقية, أي كائن منطقي أو تعبير منطقي, وهما اثنان False و True, طيب ما الذي نستفيده من هذه الصفة. نعم هذه تعمل دور مفتاح تشغيل وإطفاء الكهرباء في غرفَتِكَ, فمرة سيكون False ومرة سيكون True.
أي أنهُ إذا كانت قيمته True قرأ الرسالة الإرشادية فور ظهورها وإلا فلا يقرأها، وسنربِط هذه الصفة بمفتاح يتحكم في قيمتها كما سنرى لاحقا.
	onOff = False
الآن إلى وظيفة الحدث وهي show 
	def event_show (self,obj, nextHandler):
نلاحظ أنها مشابهة لوظيفة gainFocus نعم هذا صحيح كل وظائف الحدث وهي تزيد عن السبعة كلها تُكتَبُ بنفس الطريقة ونختمها في العادة بنفس الكيفية.
		if self.onOff == True:
إذا الصفة onOff قيمتها صواب :
			ui.message(obj.name)
اقرأ الرسالة الإرشادية وهي عبارة عن اسم الكائن والكائن هو obj ويُمَثِّل الرسالة الإرشادية واسمه يمثل نص هذه الرسالة الإرشادية.
		else:
وإلا , يعني إن لم تكن قيمة الصفة صواباً أي تكون خَطَأً:
			pass
لا تفعل شيئاً, فهذه الإفادة معناها لا تفعل أي شيء.
		nextHandler()
ختمنا بما هو حقيق بنا أن نختم به عند التعامل مع وظائف الحدث, هذا ليس بواجب لكنه الأفضل حتى تتخلص من أي مشاكل قد لا تخطر على بَالِكَ.

الآن نريد وضع اختصار نتحكم من خلاله بإيقاف أو تشغيل الإعلام عن الرسالة الإرشادية لنختار أي اسم لهذا السكربت ما دمنا نحترم شروط تسمية المُعَرَّفات.
	def script_SayToolTip(self, gesture):
		self.onOff =not self.onOff
أي إذا كانت False اجعلها True والعكس.
		if self.onOff == True:
أي إذا كانت قيمتها صوابا:
			ui.message('On')
أخبرنا يا nvda أنه تم تشغيل الإعلام عن الرسائل الإرشادية.
		else:
وإلا , يعني وإلا إذا كانت قيمة الصفة خطأ.
			ui.message('Off')
ثم أخبرنا أيها العزيزي nvda بأنه تم إيقاف الإعلام عن الرسائل الإرشادية.
والآن نختم كالعادة بالقاموس gestures 
__gestures = {
'kb:nvda+f7':'SayToolTip'
}
نغْلِق القوس المزخرف وَتُوتَا توتا خِلْصتْ الحَدُّوتَة.

الآن حاول فهم هذه العملية ثم قم بتطبيقها بنفسك.

لا تنسَ ذكر الله.

نموذج على دالة الحدث event_show


import globalPluginHandler
import ui
import api
from scriptHandler import script

class GlobalPlugin(globalPluginHandler.GlobalPlugin):
	onOff = False
	def event_show (self, obj, nextHandler):
		if obj.role == 18:
			role = obj.name
		if self.onOff == True:
			ui.message (role)
		nextHandler()

	@script(gesture="kb:nvda+o")
	def script_sayToolTip (self, gesture):
		self.onOff =not self.onOff
		if self.onOff == True:
			ui.message (u'تشغيل قراءة الرسائل الإرشادية')
		else:
			ui.message (u'تعطيل قراءة الرسائل الإرشادية')

الدرس الرابع، عمل إضافة لتطبيق بعينه


حياكم الله، ورزقكم من فضله.
بعد أن تعرفنا على طريقة عمل إضافة تعمل مع كل التطبيقات ومن أي مكان، الآن نتعلم كيف نعمل إضافة لبرنامج بعينه فلا تعمل مع غيره لأنها مرتبطة به هو فقط.
وسنتناول في هذه الأسطُر شرح لإضافة الأستاذ الفاضل أبو أيوب الليبي المتعلقة بالقراءة المستمرة عند تقليب صفحات المكتبة الشاملة.
علينا أولا الإتيان باسم التطبيق الذي نريد عمل إضافة له، فكيف يكون ذلك؟
مُؤكَد أنكم تذكرون الخواص التي تحدثنا عنها name و role
الآن نتعرف على خاصية جديدة واسمها appModule.appName
ويبدو من اسمها أنها تتعرف على اسم التطبيق.
هنا يمكننا عمل سكريبت يقرأ اسم التطبيق الذي نحن عليه عند ضغط الاختصار هكذا:
o = api.getFocusObject().appModule.appName
قمنا بإسناد اسم التطبيق إلى المتغير o ثم نجعل ui.message يقرأ لنا o.
هناك طريقة أخرى وهي: عندما نكون داخل التطبيق نقوم بفتح عارض التقارير هل تذكرونه؟
نعم هو الذي نفتحه بnvda+f1 أحسنتم،
نحن الآن على تطبيق النوتباد سأفتحه لنرى كيف نصل لاسمه،
بعد ضغط الاختصار nvda+f1 نتحرك بالسهم السفلي حتى نجد:
appModule: <'appModuleHandler' (appName 'notepad', process ID 8868) at address 84aa630>
ما يهمنا هنا هو: appName 'notepad'
وما يعنينا بالتحديد هو الاسم الذي بين علامتَيْ ''.
الآن عندما أردنا عمل إضافة تعمل على جميع التطبيقات ذهبنا إلى مجلد globalPlugins
الآن نذهب إلى مجلد: appModules
وهو موجود في نفس المجلد الذي يحتوي مجلد globalPlugins.
جميل نقوم بإنشاء ملف نصي باسم التطبيق الذي حصلنا عليه ونجعل الامتداد py، يعني يُصبح هكذا:
notepad.py
نلاحظ هنا لم نُنشِئ مجلد كما فعلنا من قبل بل نضع الملف مباشرةً داخل appModules.

الآن مع إلى التعرف على إضافة الشاملة لنتعلم منها بعض الأُمور.
لنرى ما هي المكتبات التي سنحتاج إليها:
import appModuleHandler
هذه المكتبة بَدِيلة عن المكتبة globalPluginHandler , فهذه تختص بالربط بين الإضافة والتطبيق المراد عمل الإضافة له, بعكس globalPluginHandler التي تجعل الإضافة تعمل في كل التطبيقات، وبالطبع نحتاج لاستيرادِها في أي إضافة تعمل مع تطبيق معين.
from keyboardHandler import KeyboardInputGesture
هذه المكتبة تتيح لك أن تنفِّذ مفتاحاً معيّناً للتطبيق, مثلاً لو أنك استدعيتَ هذه المكتبة والدالة الخاصة بتنفيذ أمر المفاتيح وكتبتَ بها مفتاح كنترول وحرف c فإن الإضافة حين ضغط مفتاحها الذي قد يكون أي مفتاح مثلاً مفتاح f6 ستعمل على نسخ ما تحت المؤشر من أمور ملفات أو نصوص أو أي نوع آخر من البيانات.
المكتبة sayAllHandler
هذه المكتبة مسؤولة عن القراءة المتواصلة والتي تكون في وضع مفاتيح الحاسوب المكتبي nvda مع السهم السُفْلِي، سنستورد منها دالة قِراءة النصوص.
from sayAllHandler import readText

الآن لِنُنشِئ الكلاس الذي يرْبِطُ ملف الإضافة بتطبيق المكتبة الشاملة:
class AppModule(appModuleHandler.AppModule):
والآن إلى أول وظيفة وهي مختصة بقلْبِ الصفحة الحالية ولانتقال إلى التالية وهذا طبعاً متوفر في المكتبة الشاملة وليس بالأمر الجديد, لكنَّ الجديد هو قراءة الصفحة التالية بمجرد الدخول إليها ما يُسمى بالقراءة الكلِّيَّة أو المستمرة.
	def script_shamelaForWardReading(self,gestur):
ما فات هو رأس الوظيفة head ما أحسبكم ترونه بالأمر المستجد.
		KeyboardInputGesture.fromName("f5").send()
طبعاً نحن فيما سبَقَ استوردنا المكتبة keyboardHandler ولكننا لم نستوردها كلها بل استوردنا منها كلاس بعينه وهو KeyboardInputGesture , نكتب هذا الكلاس متبوعاً بنقطة كعادة الكلاسات ثم وظيفة من ذاك الكلاس وهي وظيفة fromName والتي لها معامل واحد نوعه سلسلة نصية والسلسلة النصية ستكون أسماء المفاتيح المراد ضغطها في الخلفية, ثم نغلق القوس ونكتب نقطة بعدها كلمة send أي إرسال. 
الأخ نضال كتبَ مفتاح f5 وهو المفتاح المسؤول على الأتيان بالصفحة التالية في تطبيقنا الحالي تطبيق المكتبة الشاملة، لأننا فيما بعد سنربط هذا السكريبت بنفس المفتاح f5 فلو أننا لم نكتب هذا السطر لتم تعطيل قلب الصفحة واشتغلت القراءة فقط.
		readText(0)
نعم لقد استعملَ الأخ نضال دالة readText من مكتبة sayAllHandler التي استوردناها في البداية, وهذه الدالة لا تأخذ إلا integer سلسلة عددية إما صفر أو واحد :
CURSOR_CARET = 0
CURSOR_REVIEW = 1
والأخ نضال استعمل الصفر وهو المؤشر العادي للقراءة وليس مؤشر الاستعراض.
	script_shamelaForWardReading.__doc__ = _("changing the page forward one page, and start reading from the begining of the page")
السطر السابق مسؤول عن قراءة المساعدة لهذا السكربت وذاك بعد تشغيل المساعدة التفاعلية بالضغط على مفاتيح nvda+1 حيث إنك متى كنتَ مُشَغِّلاً لوضع المساعدة التفاعلية وأنت بالمكتبة الشاملة وضغطتَ f5 فإن الناطق سيقرأ لك مساعدة مختصرة لمفتاح الاختصار هذا، ونص المساعدة هنا هو النص الموجود بين علامَتَيْ التنصيص يمكن ترجمته لاحقا أو يمكن كتابته بالعربية إذا كانت الإضافة مُوَجهة إلى العرب فقط.

	def script_shamelaBackWardReading(self,gestur):
		KeyboardInputGesture.fromName("f6").send()
		readText(0)
	script_shamelaBackWardReading.__doc__ = _("changing the page backward one page, and start reading from the begining of the page")
لاحظوا أن السطر المسؤول عن المساعدة التفاعلية لا يندرج تحت وظيفة السكربت بل هو وهي على مستوى واحد والمتمثل في إزاحة واحدة.
__gestures= {
"kb:f5": "shamelaForWardReading",
"kb:f6": "shamelaBackWardReading"}
لا تخلطوا بين الأمور فلقد اختار نضال مفتاح f5 ليكون مفتاح هذا السكربت, ولو أنكم غيرتم مفتاح السكربت فإنه لن يتغير شيء بخصوص قلب الصفحة فالعبرة بكلاس KeyboardInputGesture، أي أنهُ المفتاح الذي نكتبه هو بديل للمفتاح الأصلي مع عدم تعطيل وظيفة المفتاح الأصلي طبعا.

لا تنسَ ذكر الله.

ملحق بالدرس الرابع: شرح KeyboardInputGesture

مرحبا بكم من جديد:
نتحدث هنا سريعا بتفصيل أكثر عن KeyboardInputGesture
قلنا أن وظيفة هذه الدالة استخدام مفتاح بديل عن المفتاح الأصلي، على سبيل المثال في برنامج winamb الإيقاف المؤقت والتشغيل يكون بحرف c فبإمكاننا استبداله بمسطرة المسافات هكذا:
from keyboardHandler import KeyboardInputGesture
ثم في الإسكريبت الذي سننشئه لاحقا ويكون مُخصص لبرنامج الوينامب كما تعلمنا كيفية تخصيص الإضافة لبرنامج بعينه نقول:
KeyboardInputGesture.fromName('c').send()
ونجعل المفتاح الذي يشغل هذا السكريبت هو space
فعندما نضغط space يقوم بوظيفة المفتاح c مع البقاء على وظيفة c كما هي.

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

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

لا تنسَ ذِكْرَ الله.

نموذج على إنشاء مجلد باختصار واحد.

import globalPluginHandler
استيراد المكتبة التي تجعل الإضافة تعمل مع أي تطبيق ومن أي مكان في الجهاز،
فإذا كانت إضافتنا ستعمل من مكان معين مع برنامج معين نستورد المكتبة المُخصصة لذلك وهي:
appModuleHandler
from keyboardHandler import KeyboardInputGesture
from scriptHandler import script
قد تعرفنا عليهم من قبل.
from time import sleep
وهذه دالة sleep من مكتبة time وتعمل على إيقاف الوقت فترة زمنية حتى يتم عمل شيء ما.

class GlobalPlugin(globalPluginHandler.GlobalPlugin):

	@script(gesture="kb:nvda+x")
اخترنا مفتاح اختصار للإضافة وهو: nvda+x
	def script_newFolder (self, gesture):
سيعمل هذا السكريبت على إنشاء مجلد جديد، وهو مجرد نموذج فقط وإلا فهو غير مكتمل ويحتاج لضبط بعض الأُمور، كالتأكُد أنه داخل مجلد من مجلدات إكسبلورر.
سنفترض أنه على عنصر ما
		KeyboardInputGesture.fromName('control+space').send()
هنا قمنا بإزالة التحديد عن العنصر.
		sleep(0.1)
وأوقفنا الوقت قليلا حتى ننتقل للخطوة التالية.
		KeyboardInputGesture.fromName('applications').send()
		sleep(0.1)
الضغط على مفتاح التطبيقات.
		KeyboardInputGesture.fromName('upArrow').send()
		sleep(0.1)
		KeyboardInputGesture.fromName('upArrow').send()
		sleep(0.1)
ثم صعد بالسهم العلوي مرتين.
		KeyboardInputGesture.fromName('enter').send()
		sleep(0.1)
وضغط إنتر على new أو جديد.
		KeyboardInputGesture.fromName('enter').send()
ثم إنتر مرة أخرى هنا يظهر لنا مربع كتابة اسم المجلد وهنا ينتهي عمل السكريبت حتى نكتب الاسم الذي نريده.

نموذج على عمل إضافة لتطبيق مُعين

import appModuleHandler
import api
import ui
from scriptHandler import script

htmlList = [["علامة البداية", ""], ["رأس عنوان المستوى الأول", "

\n

"], ["رأس عنوان المستوى الثاني", "

\n

"], ["علامة النهاية", "

الدرس الخامس: الدمج بين سكريبتين أو أكثر وإعداد الإضافة للتثبيت الآلي.

بسم الله الرحمن الرحيم.
أسعد الله أوقاتِكم بذكر الله.
سنتعلم اليوم أُمور جديدة متعلِقة بالاختصارات يمكننا من خلالِها الدمج بين إسكربتين أو أكثر، أي أنه بدلا من أن نعمل اختصار لكل سكريبت منهم سندمجهم معا 
فعلى سبيل المثال ستكون الضغطة الأولى لعمل شيء ما وإذا تم ضغط الاختصار مرتين يعمل شيء آخر 
مثلا عندنا إضافة بها إسكريبتين الأول لقِراءة الرسائل الإرشادية وليكن nvda+o هنا سنُضيف له كود آخر في نفس الوظيفة ينسخ الرسالة الإرشادية عندما نضغط الاختصار مرتين يعني nvda+ الضغط مرتين على حرف o.
تعتمد هذه العملية على مكتبة scriptHandler
وننتبه لاستيرادها بالشكل الصحيح فحرف h من كلمة Handler هو حرف كبير.
import scriptHandler

نأتي لوظيفة السكريبت ونكتب السطر الموالي لعنوانها هذا السطر
isSameScript =scriptHandler.getLastScriptRepeatCount()
if isSameScript == 0:
أي الضغط على الاختصار مرة واحدة هنا في جواب الشرط نكتب السكريبت الأول.
elif isSameScript <= 1:
ثم هنا في جواب هذا الشرط نكتب السكريبت الثاني الذي سيتم تنفيذه بالضغط على الاختصار مرتين 
أما عن ربط الاختصار بهذا السكريبت فيتم بنفس الطريقة التي تم ذكرها سابقا.


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

نأتي لأول سطر بعد عنوان الوظيفة أيضا ونكتب فيه:
k= gesture.mainKeyName
طبعا k هنا هو المتغير الذي يُشير للزر الذي سيتم ضغطه يمكننا تسميته key أو أي شيء آخر.
ثم نقول if k =="1":
ونكتب الحدث الذي نريده، وننتبه هنا إذا كنا نريد استخدام مفتاح آخر مع الاختصار يعني مثلا nvda+1 أو control+1 لا نكتبه هنا بل نكتب 1 فقط فهذه الدالة تتعرف على ضغط المفاتيح المنفردة بل نكتبه في القاموس أو الgestures التي سنربطها بهذا السكريبت.

يمكننا أيضا ربط السكريبت بعدد من المفاتيح هكذا:
for i in "1234567890":
self.bindGesture(f'kb:{i}', 'script')
هنا مكان script نكتب اسم السكريبت الذي سيتم الربط بينه وبين الاختصارات.
وإذا أردنا إضافة مفتاح آخر كال nvda أو الكنترول يكون السطر الموالي لfor هكذا:
self.bindGesture(f'kb:control+{i}', 'script')
أرجو أن يكون الأمر واضحا وسيتضح أكثر إن شاء الله بالنماذج التوضيحية.

لا تنسَ ذكر الله.

دوال clipBoard:

حياكم الله من جديد.
اليوم نتعلم طريقة الإرسال والاستقبال من حافظة الويندوز.

الدالة الأولى لنَسخ نص إلى الحافظة
str = 'دُمتم بخير وسعادة'
api.copyToClip(str)
هنا يمكننا كتابة اختصار يربط بين دالة معينة على سبيل المثال قِراءة الرسالة الإرشادية عندما نضغط عليه ينسخ الرسالة.
الدالة الثانية وهي عبارة عن سلسلة نصية تقابل محتوى الحافظة النصية لويندوز, والمقصود هنا بالنصية أنها تتعامل مع النصوص فقط وليس الملفات وما إلى ذلك.
api.getClipData ()
أي أنهُ عن طريق هذه الدالة نأخذ محتوى الحافظة النصي.

التطبيق على هاتين الدالتين:
أريد منكم إضافتين :
الأولى لنسخ الرسالة الإرشادية إلى الحافظة بضغط اختصار بعينه. واختصار آخر لقراءة الرسالة الإرشادية الأخيرة. يعني هذه الإضافة تحوي سكربتين ووظيفة حدث. وظيفة حدث لاستخلاص الرسالة الإرشادية ووضعها في متغير وسكربتان الأول لقراءة هذا المتغير والثاني لنقله إلى الحافظة, وعلى فكرة فهذه الإضافة احترافية ومفيدة وليست لمجرد التجربة فكم من مرة أردنا سماع الرسالة الإرشادية مرة ثانية ولم نستطع استدعائها كما أننا في بعض الأحيان لا يسَعُنا فهْم بعض ما في الرسالة الإرشادية ونحتاج إلى قراءة محتواها كلمة كلمة, أو حرفَ حرف.فهل رأيتم ما مدى فائدتها, فهي ليست للعب فقط بل لإفادة معشر المكفوفين.
والإضافة الثانية فتح أي رابط داخل الحافظة فأنت لا تحتاج إلى نسخ الرابط من الملف النصي ثم الذهاب به إلى المتصفح ثم ضغط f6 ثم اللصق ثم ضغط انتر لفتح الرابط بل يكفيك نسخ الرابط ثم ضغط الاختصار الذي سيفتح لك هذا الرابط فكيف نفعل هذا يا أصدقاء؟

نموذج على دمج سكريبتين باختصار واحد.

# -*- coding: utf-8 -*-

import globalPluginHandler
import ui
import api
import scriptHandler

#كل هذه المكتبات معروفة فقد تحدثنا عنها كثيرا.
class GlobalPlugin(globalPluginHandler.GlobalPlugin):
	def script_version (self, gesture):
#نأتي هنا لسكريبتنا الذي يجمع بين وظيفتين هم معرفة اسم وإصدار البرنامج.
		o = api.getFocusObject()
#الكائن الذي عليه التركيز وقد أسندناه إلى متغير اسمه o.
		name = o.appModule.appName
#متغير آخر وهو اسم التطبيق الذي عليه التركيز وقد استخدمنا appModule.appName لمعرفته.
		try:
			v = o.appModule.productVersion
#محاولة لمعرفة رقم إصدار البرنامج لأنه ربما لا يوجد رقم محدد.
		except:
			v ="عُذرا لا يوجد رقم لهذا البرنامج"
#رسالة تظهر عند عدم وجود رقم للإصدار.
		isSameScript = scriptHandler.getLastScriptRepeatCount()
#استخدام الدالة getLastScriptRepeatCount() من مكتبة scriptHandler لمعرفة عدد مرات ضغط الاختصار.
		if isSameScript == 0:
			ui.message (name+u'الإصدار'+v)
#إذا كان عدد مرات الضغط 0 يعني تم الضغط مرة واحدة:
#يُعلِن عن اسم البرنامج زائد رقم الإصدار.
		elif isSameScript <=1:
			api.copyToClip(name+u'الإصدار'+v)
			ui.message(u'تم نَسْخ الاسم والإصدار إلى الحافظة')
#أما إذا تم ضغطه مرتين يتم نسخ الاسم والإصدار إلى الحافظة مع رسالة تخبرنا بأنه قد تم النسخ.

	__gestures= {
"kb:nvda+shift+v": "version"}

إعداد ملف التثبيت

أسعد الله أوقاتِكم بذكر الله.
في هذا الدرس سنتعلم كيفية إعداد الإضافة للتثبيت الآلي، أي عندما نضغط عليها تظهر محاورة التثبيت مباشرةً.
سنُطبِق على الإضافة التي تعمل على قِراءة أكواد html ونسخها إلى الحافظة، هل تذكرونها؟
إذن هيا بِِنا.
نقوم بإنشاء مجلد باسم الإضافة وليكن htmlCodes,
ثم نفتحه ونُنشئ بداخله مجلد آخر باسم appModules,
لأن هذه الإضافة تعمل مع تطبيق معين فقط هو notepad, فإن كانت الإضافة ممَن تعمل من أي مكان سيكون المُجَلَد باسم globalPlugins،
بداخل هذا المجلد نضع الملف المُسمى باسم التطبيق الذي ستعمل معه وهنا في مثالُنا notepad.py
فإن كانت الإضافة من الفئة التي تعمل من أي مكان وضعنا المجلد المحتوي على ملف __init__.py داخل مجلد globalPlugins، أو ملف باسم الإضافة داخل مجلد globalPlugins مباشرةً.
الآن نعود إلى المجلد الرئيسي للإضافة المُسَمى htmlCodes
ونُنشِئ فيه ملف نصي باسم manifest ثم نغير امتداده من txt إلى ini أي يكون اسمه هكذا: manifest.ini
ويمكننا الإتيان بملف مُماثل من إضافة أخرى وتغيير البيانات لكن نتأكد أن البيانات تكون كاملة لأنه إذا كان هناك عنصر واحد من البيانات غير موجود فسيحدث خطأ عند محاولة تثبيتِها.
وإليكم هذا المثال:
كما سنُلاحظ هنا هناك بعض البيانات باللغة العربية لكن الصحيح أن تكون جميعُها بالإنجليزية.
#اسم الإضافة الذي سيتم التثبيت به.
name = htmlCodes
#اسم آخر يحتوي على وصف مختصَر.
summary = "أكواد html"
#وصف يوضح عمل الإضافة.
description = """
تعمل هذه الإضافة على نطق أسماء أكواد html،
ومن ثم نسخِها إلى الحافظة لتجهيزِها لللصقِها في المكان المطلوب."""
#كاتب الإضافة، وبريد إلكتروني للتواصل معه عند الحاجة.
author = "صوت الأقصى "
#الرابط الذي يوجد عليه الإضافة
url = https://drive.google.com/file/d/1XoHfyVS_c7O51XyGHFwtndNqP2KU1g9v/view?usp=sharing
#رقم الإصدار.
version = 1.0
#ملف المساعدة وسنتحدث عنه لاحقا.
docFileName = readme.html
#أول إصدار لnvda الذي تتوافق معه الإضافة.
minimumNVDAVersion = 2019.3
#آخر إصدار تتوافق معه الإضافة.
lastTestedNVDAVersion = 2020.4
updateChannel = None

مع ملاحظة إذا لم يتوفر أي من تلك البيانات كملف المساعدة أو الرابط نستبدلها ب None

الآن بعد الانتهاء من كتابة هذا الملف والتأكد من سلامة بياناته نقوم بإغلاقه.
بعد ذلك ونحن في مجلد الإضافة الرئيسي نحدد جميع الملفات والمجلدات باستخدام  control+a ثم نضغط مفتاح التطبيقات application وننزل إلى إضافة إلى الأرشيف أو ما يقابلها في الإنجليزية Add to archive...
أو إذا كان الويندوز إنجليزي نضغط حرف a وسيتم فتحها مباشرةً
هنا نتحرك بالتاب إلى Archive format ونختار zip 
سيقوم بضغط الملفات وهنا وصلنا إلى النهاية نقوم بتغيير الامتداد من zip إلى nvda-addon وانتهى الأمر.
هذا وبالله التوفيق.

لا تنسَ ذكر الله.

الدرس السادس: الترجمة

كيفية جعل الإضافة قابلة للترجمة:

1 نكتب جميع الرسائل أو النصوص التي تحتاج إلى ترجمة بين قوسين ونسبقها بعلامة الخط السفلي هكذا: _('sound') وإذا كان النص بين قوسي دالة يكون هكذا: ui.message(_('speech on')) 2 افتح ملف الإضافة المنتهي بِ .py والذي في العادة يكون اسمه __init__ لكن في أحيان أخرى ستجده باسم الإضافة ادخُلْ إلى هذا الملف واسْتَورِدْ هذه المكتبة. import addonHandler ثم اكتبْ تحتها مباشرة هذا الكود addonHandler.initTranslation()

الطريقة الأولى للترجمة

نَتَحَدَّث الآن عن الترجمة, نعم ترجمة إضافات nvda فما هو السبيل إلى ذلك؟ أولاً عليكَ أنْ تعلم أن المسألة تختلف باختلاف الوضع الخاص بالترجمة للإضافة, فهو لا يخرج عن حالتين: أُولاهُما وجود ملفات ترجمة لبعض اللغات وعدم وجود ترجمة للغة العربية. وثانيهما عدم وجود أي ترجمة لهذه الإضافة. والحالة الأولى بالتأكيد هي أكثر سهولة من الثانية. فأنتَ في الأولى ستفتح أي ملف من ملفات الترجمة الجاهزة وتحذف الترجمة منه مثلاً الترجمة الأسبانية وتبدأ في ترجمة المفردات والعبارات التي تجدها أمامكَ, فما هي الخطوات العملية لذلك؟ أولاً نعمل على تحميل برنامج يساعدنا في تحرير الترجمة هو برنامج poedit نقوم بتحميله من الرابط التالي، ثم ننصّبُه بشكل اعتيادي جدّاً. اضغط هنا للتنزيل ثانياً نذهب إلى أي إضافة مثبّتة على جهازِكَ نفْتَحها ثم ننظر في أمْرِها هل يوجد في مجلّدها الرئيسي مجلّدٌ باسم locale فإن توفَّرَ هذا المجلّد فإن ترجمتنَا للإضافة ستكون أسهل مما نتصوّر وإلا فينبغي علينا إنشاء هذا المجلّد واتباع الطريقة الأخرى الأكثر تعقِيداً لأنكَ ستبدأ من الصفر. على افتراض أنّكَ وجدْتَ المجلد locale فأنتَ إذاً في الطريقة الأولى, ادخلْ المجلد ومن الفور ستلاحظ وجود مجلدات أخرى تحمِلُ أسماء قصيرة متكونة من حرفين أو ثلاث, هذه الحروف ما هي إلا رموز عالمية لبعض اللغات فمثلاً اللغة العربية يُرمز إليها بالحرفين ar والفرنسية بالحرفين fr وهكذا, طبعاً لو وجدتَ مجلداً باسم العربية ar فهذا يعني أن مهمتكَ انتهتْ قبل أن تبدأ فلا حاجة للترجمة لوجود المجلد المطلوب فإن لم تجده فهذا يعني أنكَ عليك أن تتمّ ما بدأتَه وهذه الخطوة الثالثة قد انتهتْ. الخطوة الرابعة داخل مجلد ar أَنْشِئْ ملفّاً سمّهِ manifest.ini اكتبْ فيه ترجمة لملف manifest.ini الموجود في المجلد الرئيسي للإضافة وإليكم هذا المثال: name = 4shared version = 1.0 summary = حمِّل من موقع فور شيرد دون الحاجة لحساب أو انتظار وقت ضائع. description = بالضغط على ctrl+shift+d يمكنك أن تحمّل من موقع فور شيرد بشرط أن يكون الرابط في الحافظة ودون الحاجة لوجود حساب أو انتظار أوقات ضائعة. author = صلاح البرعصي طبعاً هذه البيانات ستظْهَرُ بدل البيانات الموجودة في المجلد الرئيسي وفائدة هذه البيانات أنها تعرّف مستخدم الإضافة بمعلومات حولها وهذه المعلومات يمكن إظهارها من خلال إدارة الإضافات الموجودة في قائمة أدوات المتفرعة من القائمة الرئيسية من برنامج nvda. الخطوة الخامسة في مجلد ar الذي لا نزال موجودين فيه نعمل على إنشاء مجلد آخر نسمِّيه LC_MESSAGES. الخطوة السادسة اذهب إلى مجلد أي لغة غير العربية ومن هناك ادخل إلى مجلد LC_MESSAGES الموجود في مجلد تلك اللغة ولنفْتَرض الفرنسية fr ندخل إلى LC_MESSAGES ونجد به ملفين هما: nvda.mo nvda.po الملف الذي يعنينا هو الملف الثاني المسمّى nvda.po انسَخْه إلى مجلد LC_MESSAGES الموجود بمجلد ar ثم افتحه بواسطة برنامج poedit. الخطوة السابعة ستتعامل مع الملف فأوّل ما تفتحه تجد قائمة بها نصّين الأول النص الانجليزي والثاني النص الفرنسي مثلاً, نقول هذه قائمة فكلما نزلتَ بالسهم وجدْتَ عبارة أخرى من عبارات الرسائل الموجودة بالإضافة المعنيّة, وإذا تحركْتَ بالتاب ستجد أوّل ما تجد النص الانكليزي وطبعاً نافذتُه للقراءة فقط, أي أنكَ لا يسَعكَ أن تغير فيه شيئاً, الآن لنتحرك بالتاب مرة ثانية سنجد مربع تحرير آخر لكنه قابل للكتابة امسح ما به من كتابة فرنسية واكتبْ ما بدا لك من معنى عربي طبعاً ستترجم العبارة الانكليزية وليس لكَ أي دخل باللغة الأخرى. بَعد أن تنتهي من الترجمة الأولى عُد بالشفت تاب مرتين إلى القائمة وانزِلْ بالسهم لتصل إلى العبارة الثانية ثم تحرك بالتاب تجد النص الانكليزي ثم تحرك بالتاب تجد اللغة الأخرى اعملْ على إزالة النص الموجود في تلك النافذة واكتب باللغة العربية المعنى المناسب وكرر العملية حتى تنتهي من العبارات الانكليزية كلِّها . ثم احفظ الملف بالضغط على ctrl+s وهكذا تنتهي العملية بنجاح مُطْبِق. والآن لننظر في هذا المثال وهي صورة لما قد تجدُه في القائمة الخاصة ببرنامج poedit وهي القائمة التي نجد فيها الترجمة والنص المترجم إليه وهذه القائمة ليست قابلة للتغيير أو التعديل من خلالها بل من خلال نافذة تحرير النص المترجم إليه والذي يمكن الوصول له من خلال ضغط تاب مرتين ننظر في هذا المثال بمزيد من التأمّل: مثال عنملف nvda.po الموجود بفي إضافة dropBox Account; حساب Activate next Dropbox preferences dialog tab; تنشيط الصحيفة التالية بمحاورة التفضيلات لبرنامج DropBox Activate previous Dropbox preferences dialog tab; تنشيط الصحيفة النشطة بمحاورة التفضيلات ببرنامج DropBox Advanced; متقدم announce active Dropbox preferences dialog tab; الإعلان عن الصحيفة النشطة بمحاورة التفضيلات ببرنامج DropBox Announce Dropbox state and make preferences tabs accessible; الإعلان عن حالة برنامج DropBox وإتاحة التجول بين تبويباته Announces Dropbox status, version or open the Dropbox systray menu.\nShortcut: NVDA+Shift+D\nP; الإعلان عن حالة برنامج رفع الملفات DropBox, أو إصداره أو فتح قائمته.\nمفتاح الاختصار: NVDA+Shift+D\nكما تعمل تبويبات محاورة التفضيلات بالضغط على Ctrl+tab أو Shift+Ctrl+Tab.\nيعلن مفتاح الاختصار Ctrl+Alt+t عن التبويب النشط.\nيمكنك تنشيط زر الإلغاء بالضغط على م Bandwidth; عرض نطاق التردد Cancel; Cancel الآن لنتحرك بالتاب ونحن على هذه العبارة من القائمة ماذا سنجد؟ نعم سنجد عبارة النص الأصلي أي اللغة الانكليزية : نص المصدر: Cancel والآن لنتحَرَّك بالتاب مرة ثانية للوصول إلى نافذة تحرير النص المترجم إليه. الترجمة: Cancel طبعاً لا فرق بين النصين , نص المصدر ونص الترجمة لأن السيد المترجم لم يترجمه والله أعلم. إذاً نأخذ مثال آخر مترجم. سنجد في القائمة التي تحوي النصين الآتي: Cancel button not found; لم يتم العثور على زر الإلغاء نتحرك بتاب واحدة للوصول إلى نص المصدر. نص المصدر: Cancel button not found طيِّب نتحرك مرة ثانية بالتاب للوصول إلى النص المترجم إليه. الترجمة: لم يتم العثور على زر الإلغاء سنتحرك تاب أخرى بعد هذه النافذة, يا تُرى ما الذي سنجدُه؟ ملاحظات للمترجمين: طبعاً فارغة الرجل لم يكتب أي ملاحظات ويبدو أنه ليس محتاجاً لهذا إذاً فهذه الخطوة اختيارية وليستْ إجبارية. التعليق: حتى هذه الخانة تركها فارغة نعم فهي اختيارية أيضاً. لو تحركْنا بالتاب سنرجع من حيث أتينا, وهي القائمة وسنقف على العنصر Cancel button not found; لم يتم العثور على زر الإلغاء.

اختصارات برنامج poedit:

الرسالة السابقة: Ctrl+Up الرسالة التالية: Ctrl+Down والمقصود بالرسالة أي نص المصدر أو نص الترجمة. يعني لو كنتَ واقفاً على نص الترجمة وذهبتَ إلى الرسالة التالية فإنكَ بالفعل ستذهب إلى النص التالي من الترجمة دون الحاجة إلى الذهاب إلى القائمة ومغادرة هذه النافذة. تم, و إلى التالي: Ctrl+Enter وهذا يعني اعتماد ما تم ترجمته والانتقال إلى الرسالة الموالية. السابقة التي لم يتم ترجمتها: Ctrl+Shift+Up التالية التي لم يتم ترجمتها: Ctrl+Shift+Down نسخ من نص المصدر: Ctrl+B مسح الترجمة: Ctrl+K حرر التعليق: Ctrl+M يمكن تنشيط القوائم من خلال alt وستجد القوائم التالية: فتح... Ctrl+O حفظ Ctrl+S بحث... Ctrl+F ملف تحرير كاتالوج تنفيذ عرض مساعدة النظام كل واحدة منها تتفرع إلى فروع. والبرنامج له واجهة عربية فلا تقلقوا من فهم ما بهذه القوائم, والأمر جِدُّ يسير. الآن كل واحد منكم يختار إضافة لم يتم تعريبُها ويعرّبها لنا مع الشكر.

الطريقة الثانية للترجمة:

السلام عليكم ورحمة الله وبركاته ها نحن نستكمل ما بدأناه في هذا الدرس من تناول طريقة ترجمة ملف nvda.po ولقد ذكَرنا أنه حين الترجمة ستجد نفسَكَ بين أمرين الأول وجود ملف nvda.po ولقد تحدّثنا عنه سابقاً والآن سنعمل على الأمر الثاني وهو افتراض عدم وجود هذا الملف أصلاً, فالإضافة لا تحوي أي ترجمات لا بالعربي ولا بغيره, وفي هذه الحالة سنُنشِئ كخطوة أولى ملف nvda.po والخطوة الثانية سنترجم ما به من عبارات, وقبل أن نبدأ في شرْحِ الموضوع علينا أن نُحَمِّلَ الملفات التاليَة: مكتبة xFind xFind اضغط هنا للتنزيل وإليكم هذا الملف القياسي nvda.po الذي ستجرون عليه التعديلات اللازمة: nvda.po اضغط للتنزيل نبدأ الآن في الجد 1-انسخ المكتبة xFind إلى مجلد nvda سواء في المحمولة أو الثابتة, وأعِدْ تشغيل قارء الشاشة. 2- اذهبْ إلى الإضافة المراد إنشاء ملف nvda.po لها, ادخُلْ ملف الأكواد الخاص بها سواء أكان باسم __init__ أو ملف باسم الإضافة، وحدد كل ما فيه ثم انسخه, الآن أنتَ تحوز في الحافظة نصاً كبيراً يحوي على بعض الرسائل,. 3- افتح البوابة واكتب ما يأتي: from xFind import nvdaPo تم استيراد الدالة nvdaPo من المكتبة xFind الآن ننفِّذُ الدالة nvdaPo() لاحظْ أن الدالة لا تأخذ أي معامل كما أنها ستطبع لك الرسائل في الشكل المطلوب , أي الطريقة المتبعة في ملفات nvda.po نظرة سريعة إلى النتائج وهي من عمل سابق لي, وضعتُ فيه ترجمة لإضافة لا يوجد لها ترجمة أصلاً وهي إضافة speech_beep_omiter ووظيفة هذه الإضافة لمن لم يعرفها تخطي المرحلة الثانية وهي مرحلة beep حين الضغط على nvda+s فنحن كنا في الوضع الافتراضي نمر على ثلاث حالات هي تشغيل الصوت وإصدار صوت الصفير وإيقاف الصوت, وطبعاً حالة الصفير هذه خاصة بمن يستخدم البرايل مع nvda وباعتبار قلة أجهزة قراءة البرايل بسبب ارتفاع أسعارها فإنه من الأفضل إلغاء هذه الحالة, على العموم أنا دخلتُ الإضافة ونسختُ كل ما فيها دون فرق ثم كتبتُ في البوابة ما ذكرتُه لكم سابقاً وكانت النتائج وكالتالي: >>> from xFind import nvdaPo >>> nvdaPo() msgid "speech off." msgstr "" msgid "speech on." msgstr "" انسخ نتيجة الكود السابق ولا تنسخ الأكواد. 4- اذهبْ إلى الملف المضغوط الجاهز فك عنه الضغط ادخِله ثم ادخل إلى مجلد ar ثم تعمق حتى تصل إلى nvda.po افتحه بالnotepad ثم اذهب إلى نهاية الملف ثم ألْصِق ما نسختَ سابقاً ثم احفظ وَاخْرُجْ. 5- افتح الملف nvda.po مرة ثانية ولكن هذه المرة بواسطة برنامج poedit واعملْ على ترجمتِه ستلاحظ وجود نجمة star أمام كل عبارة وهذه علامة على أن هذه العبارات غير مترجمة, كما أنك ستسمع صوت بيب كلما تحركت على كلمة لم يتم ترجمتها،بعد أن تُتِمّ ترجمة كل العبارات من قوائم البرنامج اذهبْ إلى كاتالوج واختر منه خصائص في الخانة الأولى اكتب اسم الإضافة واملَأ الخانات التي كُتِبَ فيها none وهي عبارة وضعتُها لكم بمعنى بِلَا أي بِلَا تسمية, خانة اسم الفريق والبريد الالكتروني خياريتان بإمكانكَ تركُهما, بعد الانتهاء احفظْ واخْرُجْ. 6- اذهبْ إلى ملف منفست العربي ستلاحظ أني وضعتُ لكم أسماء افتراضية اعملوا على تغييرها بما يناسب. 7- أعِدْ تشغيل الإضافة وهكذا انتهتْ العملية بنجاح. لا تنسَ ذكر الله. والسلام عليكم ورحمة الله وبركاته.

مكتبة api بشكل أكثر تفصيلا

الدوال ومثال لكلٍ منها:

copyToClip(text)
تنسَخ النص المكتوب بين القوسين إلى حافظة ويندوز.
مثال:

text = "وقل ربي زدني عِلْما"
copyToClip(text)
@تُرجِع True إذا كُتِبَ لها النجاح، وFalse إذا لم تنجح عملية النَسْخ.

getCaretObject()
يتَحَصَّل على الكائن الذي يحوي الإقحام 
في العادة يكون هذا الكائن نفسه الكائن الذي عليه التركيز, أي مشابه لِgetFocusObject () لكن هذا الأمر ليسَ دائماً ولكن في الأعم الغالب.
@تُرْجِع الكائن الذي يتضمن الإقحام.
@نوع القِيمَة التي ترجِعها الدالة :L{baseObject.ScriptableObject}

مثال على ذلك:
o = api.getCaretObject()
print ('اسم الكائن الذي عليه نقطة الإقحام هو %s واسم فئة نافذة هذا الكائن هو %s ورقم تحكم الكائن هو %d' %(o.name, o.windowClassName, o.role))
اسم الكائن الذي عليه نقطة الإقحام هو واسم فئة نافذة هذا الكائن هو Edit ورقم تحكم الكائن هو 8

getClipData()
تتلَقَّى النص من حافظةِ ويندوز.
@تُرجِع نص الحافظة.
@نوع القِيمَة التي ترْجِعُها هذه الدالة:string
مثال عليها:
print (api.getClipData())
والنفسُ كالطِّفْلِ إنْ تُهْمِلْهُ شبَّ على*** حُبِّ الرِّضاعِ وإنْ تفْطِمْه ينفَطِمِ

getDesktopObject()
تتحصّل على الكائن المُسَمَّى سطح المكْتَب.
مثال عليها:
print (api.getDesktopObject().name)
سطح المكتب

getFocusAncestors()
@تُرجِع هذه الدالة قائمة بأب الكائن الذي عليه التركيز والكائن الذي يحويه والكائن الذي يحوي الذي يحويه إلى أن نصِل كائن سطح المكتب وهو الكائن الذي يحوي كل كائنات ويندوز.
@نوع القيمة التي تُرجِعها الدالة: list 
مثال على هذه الدالة:
for i in api.getFocusAncestors():
	print ('اسم هذا الكائن هو: ', i.name)
	print ('نوع تحكم هذا الكائن هو: ' , i.role)

اسم هذا الكائن هو: سطح المكتب
نوع تحكم هذا الكائن هو: 1
اسم هذا الكائن هو: بوابة لغة البرمجة بايثون
نوع تحكم هذا الكائن هو: 1
اسم هذا الكائن هو: بوابة لغة البرمجة بايثون
نوع تحكم هذا الكائن هو: 3
اسم هذا الكائن هو: ...
نوع تحكم هذا الكائن هو: 1
نلاحظُ في المثال السابق أن القائمة حَوتْ كائن سطح المكتب نزولاً إلى الكائن الذي يحوي الكائن الذي عليه التركيز وفي مثالنا السابق نافذة كتابة الأكواد في بوابة بايثون.

getFocusObject()
يتحصّل على الكائن الحالي الذي معه التركيز
@تُرجِع: الكائن الذي عليه التركيز.

@نوع القِيمة التي تُرجِعها الدالة:L{NVDAObjects.NVDAObject}

getForegroundObject()
تتحصل على الكائن الذي في الأمامية, فقد تكون هناك عدَّة نوافذ مفتوحة مثلاً برنامج فاير فوكس والسكايب والأنتي فايروس هذه الدالة ستجعلك تتحصل على النافذة الأمامية وليست النوافذ الموجودة في الخلفية, فلو كنتَ على سكايب ستعطيك اسم نافذة سكايب متى طلبتَ اسمها, أو تعطيك رقم نوع التحكم متى طلبتَه. وتتغاضى عن النوافذ المفتوحة في الخلفية.
@تُرجِع كائن النافذة الأمامية.
@نوع القِيمَة التي تُرجِعها الدالة:L{NVDAObjects.NVDAObject}

مثال على هذه الدالة:
print (api.getForegroundObject().name)
بوابة لغة البرمجة بايثون

getMouseObject()
ترْجِع الكائن الذي يقع تحتَ الماوس مباشرة.
@نوع القِيمَة التي تُرجِعها الدالة:L{NVDAObjects.NVDAObject}
مثال على ذلك:
لقد وضعتُ الفأرة على زر إغلاق الإطار في بوابة بايثون ثم كتبتُ الآتي 
print (api. getMouseObject().name)
فخرجَتْ لي النتيجة التالية:
إغلاق

getNavigatorObject()
تتحصّل هذه الدالة على الكائن الحالي الذي يمكن التعرف عليه في اختصارات nvda بالضغط على nvda+ numpad 5.
@تُرجِع : الكائن الحالي.

@نوع القِيمَة التي ترجِعُها الدالة :L{NVDAObjects.NVDAObject}
مثال على هذه الدالة:
سأُحَرِّك الكائن الحالي بواسطة استخدام مفاتيح nvda+ numpad 2 4 6 8 حتى أَصِل إلى جهاز الكمبيوتر ثم اضغط انتر لتنفيذ الدالة.

print api. getNavigatorObject().name
جهاز الكمبيوتر

getStatusBar()
الحصول على شريط الحالة لِنافذةِ الأمامية.
تُرجِع كائن شريط حالة النافذة الأمامية إنْ وُجِدَتْ وإلا ستُرجع None 
@نوع القيمة التي تُرجِعها : L{NVDAObjects.NVDAObject}
مثال على ذلك:
سأستخدم البوابة كمثال فهل لها شريط حالة؟
print (api. getStatusBar())
None

getStatusBarText(obj)
تتحصَّل هذه الدالة على النص من شريط الحالة.
وهذا يشمل اسم شريط الحالة وأسماء وقيم كل من ابنائها.
@المعامل الذي تأخذه الدالة: obj: شريط الحالة.
@نوع قيمة obj: L{NVDAObjects.NVDAObject}
@تُرجِع هذه الدالة :نص شريط الحالة.
@نوع القِيمة التي تُرجِعها الدالة : str

moveMouseToNVDAObject(obj)
تُحرِّك الفأرة إلى كائن nvda الذي تم كتابته بين القوسين.
@المعامل الذي تأخذه الدالة: obj.
@نوع قيمة obj: L{NVDAObjects.NVDAObject}
مثالاً على ذلك:
obj = api. getNavigatorObject()
api. moveMouseToNVDAObject(obj)

setFocusObject(obj)
ينقِل التركيز إلى الكائن المكتوب بين القوسين.

@المعامل obj: يمثل الكائن الذي سيتم نقل التركيز إليه.
@نوع قِيمة obj : NVDAObjects.NVDAObject
نريد مثال لهذه الحالة يتم فيها نقل التركيز إلى الكائن الذي تحت الفأرة, وقبل فعل هذا عليكم أن تضعوا الفأرة في مكان معروف بالنسبة لكم ويكون قابل لنقل التركيز إليه, لأنه ليس كل كائن يمكن نقل التركيز إليه.
setMouseObject(obj)
تأمُرُ هذه الدالة برنامج nvda أن يتعامل مع المعامل أي الكائن بين القوسين كأنه الكائن الذي يقع تحت الفأرة مباشرة.
في المثال التالي سنكتب أكواداً تنقل الفأرة افتراضياً إلى الكائن الحالي, فما الذي نحتاجه؟
سنحتاج دالة الكائن الحالي:
obj = api. getNavigatorObject()
api.setMouseObject(obj)

setNavigatorObject(obj, isFocus=False)
تعْمَلُ على تحريكِ الكائن الحالي إلى الكائن المكتوب بين القوسين أي المُعامِل . 
مثال على ذلك:
أولاً بَاعِدْ بين الكائن الحالي والكائن الذي عليه التركيز بتحريك الكائن الحالي إلى كائن آخر يدوياً وذلك عن طريق استخدام اختصارات تحريك مؤشر الحركة بين الكائنات.
ثم اكتب ونفِّذْ هذين الكودَين وستجد أن مؤشر الحركة بين الكائنات انتَقَلَ إلى الكائن الذي عليه التركيز.
obj = api.getFocusObject()
api.setNavigatorObject(obj, isFocus=False)

نريد منكم شيئاً مُمَاثِل لِلْمِثالِ الأخير بشرط أن يكون الوسيط بين القوسين هو الكائن الذي تحت مؤشر الفارة مباشرة.

لا تنسَ ذكر الله.

كائنات nvda

يُحكَى أن شخصاً حاول أن يُفْهِمَ آخر ما معنى المفكرة, فلمّا عجزَ عن إفهامه أخرجَ من جيبه مفكرة وأعطاها له قائلاً ما في يدكَ الآن هي عبارة عن مفكرة, والحقيقة أنا بإذن الله لن أعجز عن إفهامكم لأنكم ما شاء الله لا قوة إلا بالله خير من يفهم, ولكن هذه الطريقة في الشرح والتعريف هي طريقة معتبرة من طرق التعريفات المنطقية بل هي من أسهل طرق الشرح, فلو سألك أحدهم ما الشجرة , فبإمكانكَ الإشارة إلى أول شجرة وتقول هذه التي أمامك الآن شجرة. ومن هنا السؤال ما هي كائنات nvda؟ والإجابة ستكون ما سأكتبه بعد هذا السطر هو كود يمثل كائن من كائنات nvda: import api api.getFocusObject() طبعاً الحديث عن ما سبق كان مثالاً لكائنات nvda, طيب هل ثمة من كائنات nvda أخرى, الإجابة نعم. هل تذكر الكود الذي يساوي الكائن الذي عليه مؤشر الفأرة فهذا أيضاً عبارة عن كائن من كائنات nvda, وهل تذكر الدالة التي تساوي الكائن الذي عليه مؤشر الحركة, أجل هو أيضاً كائن من كائنات nvdaطيب هل ثمة أخريات غير هذه, نعم وهذا ما أحاول تقديمه اليوم وإلا فما سبق لا يتضمن أي جديد. تأمل معي هذا الكود من فضلكَ: import api api.getFocusObject().next ما الذي يساوي هذا الكود من وجهة نظرك؟ نعم أحسنتم هو لا يساوي الكائن الذي عليه التركيز بل يساوي الكائن الموالي للكائن الذي عليه التركيز وهو أيضاً كائن من كائنات nvda ما رأيكم أن نجربه ولكن ليس في البوابة بل في ملف إضافة نعدُّه لغرض إجراء التجارب الكثيرة. import globalPluginHandler import ui import api class GlobalPlugin(globalPluginHandler.GlobalPlugin): def script_test(self,gesture): o1 = api.getFocusObject() o2 = o1.next ui.message(o1.name) ui.message(o2.name) __gestures = { "kb:shift+f1":"test"} جربوا ما سبق ثم للحديث بقية ولكن احذروا أن تقف على آخر كائن وتضغط مفتاح الاختصار فهذا سيؤدي إلى مشكلة سأعلمكم كيفية التخلص منها والمشكلة بإيجاز لو أنك واقف على آخر كائن ثم ضغطت الاختصار فإنه سيقرأ اسم الكائن الذي عليه التركيز ولكن هل يوجد كائن موالي, أكيد لا فأنت على آخر عنصر ولا شيء بعد الأخير وهنا تكمن المشكلة. الآن نحل مشكلة عدم وجود كائن لاحق للكائن الذي عليه التركيز: لكن قبل ذلك تابِعوا معي السطور التالية: parent = api.getFocusObject().parent نلاحظ فيما سبق وجود صفة أخرى لكائنات nvda وهذه الصفة ستمثل في حد ذاتها كائن من كائنات nvda إن هذه الصفة أو الخاصية هي parent أي الكائن الوالد أو الكائن الأب. علاقة الكائنات ببعضها هي علاقة عائلية حميمية لأبعد المستويات, سنلاحظ وجود أب وإخوة وأبناء وهذه تسميات بلا شك مجازية وليست على سبيل الحقيقة. فالكائن ينبغي أن يكون له كائن أعلى منه والكائن الأعلى يحوي الكائنات الأسفل منه فكل كائن هو مندرج تحت كائن آخر إلا كائن سطح المكتب العلوي فلا كائن فوقه. وربما يكون للكائنات ابن أو أكثر وربما يكون عقيماً. كما يمكن أن يكون له إخوة وربما يكون وحيداً. تفصيل أكثر عن هذه الكائنات: عندما نقوم بتعطيل اللوحة الرقمية ونتحرك بالمؤشر، nvda+8: الانتقال إلى الكائن الأعلى أي الأب للكائن الذي كنا عليه.. nvda+4: الكائن السابق أي الابن السابق.. nvda+6: الكائن التالي أي الابن التالي. nvda+2: الكائن الأسفل أو الابن للكائن الذي نحن عليه. الآن جربوا معي، بعد أن نعطل اللوحة الرقمية، وبالطبع هذه اختصارات نمط لوحة الحاسوب المكتبي، إذا تحركنا nvda+8 عدة مرات إلى أن يقول سطح المكتب، وسيكون هذا أعلى شيء لا يوجد أي كائنات فوقه، كما أنه لا يوجد كائنات تالية له ولا سابقة، فهو الأب لكل الكائنات، بعد ذلك نتحرك nvda+2 هكذا نكون نزلنا درجة واحدة، وهؤلاء هم أبناء سطح المكتب، عندما نتحرك على الكائن التالي والكائن السابق سيقرأ لنا أسماء الأبناء، يعني هؤلاء كلهم إخوة في مرتبة واحدة، فإذا وقفنا على أحد الأبناء وتحركنا للأسفل سنجد أبناء هذا الكائن، وربما يقول لنا لا يتفرع من هذا الكائن كائنات أخرى فهذا يعني أن هذا الابن لا يوجد لديه أطفال. هيا نتعرف على هذه الأُمور بشكل أوضح وبأسمائها البرمجية: سنستكمل ما بدأناه مع parent فلقد أنشأنا متغيراً هو حامل لِقيمة الكائن الذي يحتوي الكائن الذي عليه التركيز أي الأب له, تخيل أنك على سطح المكتب وعلى المستندات بالذات, المستندات لها أب اسمه سطح المكتب وهذا سطح مكتب آخر غير الذي ذكرتُه منذ قليل، فهذا هو سطح المكتب المعروف الذي يحتوي على اختصارات البرامج والأيقونات الأخرى، فإذا وقفنا على أي عنصر من عناصر سطح المكتب وضغطنا nvda+8 على اللوحة الرقمية سيقول سطح المكتب أو Desktop، وسطح المكتب هذا له أب ليس كالذي تحدثنا عنه سابِقا، بل هذا له أب فهناك أكثر من كائن يحمل اسم سطح المكتب, لكننا هنا نتحدث عن سطح المكتب والذي هو أب المستندات وأب جهاز الكمبيوتر وأب مواضع شبكة الاتصال وغيرها من عناصر سطح المكتب. طيب أنا أريد قائمة بأسماء أبناء سطح المكتب, بل أي عناصر أخرى في أي مكان, تأمل معي: children = parent.children for i in children: ui.message (i.name) ما مضى هو عبارة عن وضع لكل الأبناء في قائمة والقائمة اسندناها إلى متغير هو children فالكود parent.children لا يساوي كائن من كائنات nvda بل يساوي قائمة [] وهذه القائمة تحوي كائنات nvda ويمثلها في الحلقة مع كل دورة i ونقول i ليست اسماً للكائن بل الكائن نفسه, وإن كُنْتَ تريد اسمه فعليك أن تستدعي الكائن ثم تُتْبِعْه بنقطة ثم كلمة name هكذا: i.name والجدير بالملاحظة أن عدد الدورات في الحلقة سيكون بعدد الأبناء فإن وقفت على سطح المكتب ستحصل على عدد من الدورات تتيح لكَ قراءة أسماء جميع عناصر سطح المكتب دون زيادة أو وقوع في الخطأ ولو استعملتَ نفس السكربت في قائمة المتصلين على سكايب ستحضى بجميع أسماء المتصلين لا زيادة ولا نقصان. والآن نذكر العلاقات المختلفة بين كائنات nvda، أي الاسم البرمجي له إذا أردنا استدعائه: الأب: parent الابن الأول: firstChild الأخ التالي next الأخ السابق previous طيب أنا أريد اسم أول ابن من أبناء الكاءن الذي عليه التركيز import api focus = api.getFocusObject() child = focus.firstChild name = child.name طيب الآن أريد ثاني ابن من أبناء الكاءن الذي عليه التركيز أنا معي المتغير الذي يحمل الابن الأول وهو: child child2 = child.next أي الكائن الموالي للأول، ثم أستدعي اسمه هكذا: name = child2.name ui.message(name) حسناً أريد عم الكائن الذي عليه التركيز أي أخ أب الذي عليه التركيز وبمفهوم آخر أريد الكائن الموالي للكائن الذي يحوي الكائن الذي عليه التركيز. parent = focus.parent قد يسأل أحدكم ما هي focus نعم هو المتغير الذي أسندنا له قيمة الكائن الذي عليه التركيز في الأسطر العليا من هذا الشرح. الآن عندنا الأب ونريد الكائن العم أي الموالي للأب obj = parent.next طيب أريد السابق للأب obj1 = parent.previous طيب أريد الجد أي أب الأب groundFather = parent.parent طيب أريد ابن ابن الابن child = focus.firstChild.firstChild.firstChild ui.message(child.name) أريد ثالث أخ لهذا الحفيد obj = child.next.next.next أرجو أن تكون الصورة واضحة. إكمالاً لمَا سبق من صفات كائنات nvda فإننا سنتعرض بالشرح لأنواع أخرى من هذه الكائنات فركِّزوا بمزيد من المقارنة والتحري بين ما ستدرس اليوم وما درستَ سابقاً: لقد درسْنا firstChild وهذه طبعاً صفة لكائنات nvdaوهذه الصفة تمثل في حد ذاتها قيمة مساوية لكائن من كائنات nvda واليوم سنُتْبِعها بأختها وهي lastChild فالأولى تمثل أول ابن من أبناء الكائن والثانية تمثل آخر ابن من أبناء الكائن. ماذا سنضيف على ما سبق؟ هل ثمة من كائنات أخرى؟ نعم وإليكَهَا بكل حب: simpleNext simplePrevious simpleParent simpleFirstChild simpleLastChild طيب ما الفرق بينها وبين ما درسْنَا سابقاً؟ الفرق جِدُّ بسيط, فالأولى كانت معنية بكائنات nvda سواء كانت مسَمّاة أو غير مسمّاة وأما الثانية فهي تمثل تلك المُسمّاة وقد تختصر الأخيرة علينا الكثير من تعب كتابة الأكواد, فقد تكون العلاقة بين الكائن السفلي والكائن العلوي في النوع الثاني مباشرة بدون أي كائنات بين الكائنين. ولكن لو اتبعنا المجموعة الأولى فقد يكون بين الكائنين طريق طويلة غير معبّدة من الكائنات فهذا ابن وله أب وهذا الأب غير مُسَمّى وهو ابن لآخر وهذا الآخر غير مسَمّى والآخر ابن لثالث وهذا الثالث غير مسمّى لنصل إلى الرابع وهو المسمّى والمطلوب. وأما في طريقة السمبل فنلاحظ أنه سينقلنا من الأول إلى الرابع فيكون الرابع أب للأول مباشرة وليس جداً له. فهذه طريقة للتخلص من الكائنات غير الميسورة لأي سبب من الأسباب, وإليك أمثلة من البوابة: >>> import api >>> o = api.getFocusObject() >>> p = o.parent >>> lastChild = p.lastChild >>> print lastChild.name أفقي >>> print lastChild.simplePrevious.name >>> >>> print p.simpleParent.name بوابة لغة البرمجة بايثون والآن نُجيب عن سؤال ماذا لو أنه لا يوجد كائن موالي أو سابق أو ابن أو حتى أب ؟ تأملوا هذه الأكواد: # -*- coding: utf-8 -*- import globalPluginHandler import ui import api class GlobalPlugin(globalPluginHandler.GlobalPlugin): def script_test(self,gesture): o1 = api.getFocusObject() if api.getFocusObject().next: #شرط إذا كان هناك كائن موالي o2Name = o1.next.name else: o2Name = 'لا توجد أي كائنات أخرى' #جواب الشرط، جعل المتغير o2 يساوي اسم الكائن الموالي، أما إذا لم يكن كائن موالي نجعل المتغير يساوي لا يوجد كائن موالي. ui.message(o1.name) ui.message(o2Name) #ثم يقرأ اسم الكائن الأول والثاني إن وُجِد وإلا سيقول لا يوجد كائن موالي. __gestures = { "kb:shift+f1":"test"}

تسمية الكائنات غير المسماة، أو إعادة تسمية كائنات مسماة من قبل

السلام عليكم ورحمة الله وبركاته الآن سنتكلم عن أمر مهم وهو تسمية الكائنات غير المسمّاة, فالكثير من البرامج كالمكتبة الشاملة إصدار المبصرين لا تتيح للكفيف التعرف على أسماء الكائنات سواء كانت أزرار أو غيرها إلا ما ندر, فكيف نحل هذه المشكلة؟ الحل هو أن تربط اسم الكائن بقيمة من قيم الكائنات مثل controlID وهذا ما سنفعله الآن, السؤال كيف نصل إلى قيمة controlID لأي كائن؟ المسألة بسيطة قف على الكائن المراد معرفة قيمة كنترول أي دي له ثم افتح تقرير nvda بالضغط على nvda+f1 ثم انزلْ بالسهم حتى تصل إلى windowControlID للعنصر المراد, انسخه كي تستفيد منه في عملك القادم, المثال الذي سنطبق عليه هو برنامج مسجل الصوت الذي يأتي بشكل افتراضي مع الويندوز, وإليكم الإضافة التي تعرف أزرار هذا البرنامج لأنه لا يعطي أسماء للأزرار تلقائياً بل عن طريق إضافة أكواد إلى العملاق nvda وقد حدث هذا حقاً عن طريق مطوّري البرنامج, فالإضافة موجودة مسبقاً ونحن نريد التعديل فيها بالتغيير فحسب, الآن نرى ما كتبَ مطوري إن في دي أي حتى أصبح يتعرف على أزرار مسجل الصوت, إليكم الأكواد: import appModuleHandler import controlTypes mainWindowButtonNames={ 205:_("Rewind"), 206:_("Fast forward"), 207:_("Play"), 208:_("Stop"), 209:_("Record") } طبعا هذا قاموس يجمع بين رقم ControlID للعنصر الذي لا يوجد له اسم سواء كان زر أو كائن آخر، واسم تمت كتابته للدلالة على عمل الزر. class AppModule(appModuleHandler.AppModule): def event_NVDAObject_init(self, obj): if obj.role == controlTypes.ROLE_BUTTON: إذا كان نوع الكائن زر try: obj.name=mainWindowButtonNames[obj.windowControlID] حاول أن تجعل اسم الكائن هذا الاسم الذي هو قيمة المفتاح في القاموس السابق، والمفتاح هو رقم ID لهذا الزر. except KeyError: pass أما إذا كان هناك خطأ في المفتاح، أي أن هذا المفتاح غير موجود، فلا تفعل شيء. ولتعلم صديقي القارئ أن معرفة الكنترول أي دي للكائنات غير المسماة والاستعانة بذاك الرقم في الربط بين الاسم الجديد والكائن أقول هي طريقة من عدة طرق لتسمية الكائنات غير المسماة, بل هي أسهل الطرق وهي أول طريقة على المبرمج أن ينظر في أمرها فإن كانت متاحة فَبَهَا ونِعْمَتْ وإن كانتْ غير متاحة فينتقل المبرمج إلى الطريقة الثانية في الربط. ويعيب هذه الطريقة أن الكنترول أي دي للكائنات ضمن البرامج غير ميسورة الوصول قد تحمل قيمة صفر وهذا يعني أن الأمر متعذر قولاً واحداً, أو تحمل رقم ثابت لكل الكائنات وهذه أيضاً مشكلة تجعلك تعدل عن هذه الطريقة لغيرها, أو أنها تحمل أرقاماً مختلفة إلا أنها بمجرد إطفاء الجهاز وإعادة تشغيله فإن هذه الأرقام ستتغير مباشرة وهذا أيضاً يعني أن الطريقة غير مفيدة بالمرة، وعلينا البحث عن طريقة أخرى. وإلى لقاءٍ آخر. لا تنسوا ذكر الله.

تعليقات