Ecmascript و Qt

الناقل : elmasry | الكاتب الأصلى : مصفوفة | المصدر : www.arabteam2000-forum.com

بسم الله الرحمن الرحيم
اخي العزيز السلام علكم ورحمة الله وبركاته
ان هذا المقال يتكون من شقين الأول للتعرف على معايير لغة ecmascript والثاني للربط بين ecmascript و Qt في حال انك اتقنت اساسيات javascript او jscript او حتى actionscript فبإمكانك الإنتقال الى الشق الثاني من المقال وانصحك بقراءة ملخص ecmascript لكي لاتجع مجالا لعدم الفهم وفي حال انك اردت تطبيق هذه اللغة فبإستطاعتك تحميل المحرر التعليمي من خلال هذا الرابط
http://www.ecmascript.org/download.php ويتوفر على انظمة الوندوز واللينوكس وال mac .
قبل ان نتعرف على ECMAScript فإنه من المنطق أن نعرف ماذا يعني scripting language ؟

لغة برمجة نصية تتحكم بالتطبيقات وغالبا ما يتم تسميتها بأسماء تختلف عن برامجها مثل actionscript وبرنامجه الفلاش وكلاهم مكتوبين بلغتين مختلفة وهي في الغالب تكون في يد المستخدم النهائي لكي يتحكم بشكل او بمخرجات البرنامج .

الآن دعنا نرجع الى لغة ecmascript وهي لغة برمجة كائنية التوجه ،،
ولقد برمجت في الأصل لتكون web script language لتقوم بتزويدها بآلية لتنشيط الويب وهذا مابدا جليا في لغات انبثقت منها مثل javascript(mozila) و jscript(microsoft) و actionscript(adobe) ولكن تطورت فيما بعد لتدخل في كل شيئ ولكن ضمن بيئتها او برنامجها وهي الآن تعتمد في محارفها على معايير unicode ابتداء من النسخة 2.1 وما تلاها من نسخ ،وهي لغة تنفذ عمليات حسابية ومعالجة ضمن بيئة عملها ( برنامجها) ففي actionscript مثل هي تنفذ ضمن بيئة الفلاش وكذلك في javascript هي تنفذ ضمن بيئة المتصفح fireFox فالفكرة هنا انه كيف بالإمكان ان تقوم بربط الكائنات من لغة برمجة أخرى مثلا c++(Qt) بلغة ecmascript .

دعنا الآن نترك تقنية Qt على جانب ونعرف ماهي معايير لغة ecmascript التي جعلت البرامج الأخرى تنشد نحوها كلغة برمجة لبيئتها .

في حال انك تعاملت مع لغات مثل javascript او غيرها ستكون مدركا لمذا التدافع على ecmascript كخيار أول لتطوير لغات برمجة لبيئات بحيث تصبح وسيلة تطوير هذا البرنامج او البيئة هي اللغة التي قمت انت بصناعتها بواسطة ecmascript التي هي بالنسبة لمبرمجي c++ ابن بار لها لأنها ببساطة تكتسب تعابيرها الرياضية والشرطية والتكرارية وووو من لغة c++ فلذلك دعنا نأخذ جولة على مابهذه اللغة :

تعاريف :

المتغيرات : وهي حاويات للقيم وتنطبق عليها نفس شروط التسمية التي تنطبق على c++ وتتأثر لحالة الحرف ولكن مايميزها هو انها لا تطلب منك تحدد نوع المتغير بل بإمكانك جعل متغير واحد يحوي قيم مختلفة ويمكنك اضافة قبلها الكلمة المفتاحية var في حال كان المتغير خاصا .

القيم والأنواع: الأرقام وتشمل العشرية منها والصحيحة بالإضافة الى NaN وهو ماليس برقم والقيم البولينية و النصية التي اما ان تكون بين علامتي تنصيص ثنائية او بين علامتي تنصيص مفردة "" او ' ' واللاشيئ Null في حال انك لم تنشئ المتغير بالإضافة القيمة الغير المحددة undefined اي انه تم انشاء متغيره ولكن لم تحدد له قيمة والنوع هو Object وسوف نفرد له حديثا آخر وبالإضافة الى الأنواع السابقة هناك الدوال function و المصفوفات array وجميعها سوف نأتي على ذكرها .

الكائنات : في ecmascript فإن الكائنات هي عبارة عن خواص ( متغيرات) و طرق(دوال) وسوف نأتي في تفصيلها اكثر.

المعاملات :
الفاصلة المنقطة ";" وتعني نهاية جملة برمجة وكن في ecmascript فليس هناك من داعي لوضعها

المعاملات الرياضية :
+ للجمع و – للطرح و * للضرب و / للقسمة و % لباقي القسمة و ++ لإضافة واحد و – لإنقاص واحد

المعاملات المحرفية :
+ للجمع بين نصين او نص ورقم و بالإضافة الى سلاسل الهروب .

المعاملات الشرطية :
هناك كلمات مفتاحية متعارفة لدى مبرمجي اللغات الأخرى
الشرط if(){} و elseif(){} و else{}
و لكنها تختلف عن ما عهدناه في لغة c++ ان عامل المقارنة == يختبر المساواة بين القيم بغض النظر عن الأنواع .
فمثلا 12=="12" هذه القيمة true ولكن في حال انك كتبت 12=== "12" فإن هذه القيمة خاطئة فثلاث علامات = تعني المساواة المطلقة في النوع و القيمة بالإضافة الى المعاملات المنطقية || و && و ! .


المتغيرات العامة والمحلية والثابتة

المتغيرات الخاصة

Var m


المتغيرات العامة
M


ويستخدم الأول في حال انك انشأت دالة واردت تجنب استخدام المتغيرات العامة حفاظا على سلامة المتغيرات خارج الدالة فمثلا
N=12
Function r(){
N=0
}
R()
Print(n);


الناتج : 0

بينما
N=12
Function r(){
Var N=0
}
R()
Print(n);


الناتج: 12

المتغيرات الثابتة ويتم تعريفها بواسطة الكلمة المفتاحية const في حال انك عرفت متغير ثابت فإن قيمته المبدئية تبقى هي قيمته ولايمكن تغييرها
مثاله
Const m=12
m=1
print(m)
الناتج = 12 .




الدوال :
بإستخدام الكلمة المفاحية function بإمكانك صنع الدوال ولإرجاع قيمها بواسطة الكلمة المفتاحية return وليس هناك تحديد لنوع المخرجات كما تآلفنا مع لغة c .
أمثلة :
f = function(p){print(p)}
f("mohammed")


الناتج : mohammed .

او بطريقة أخرى :
Function f(p){print(p)}
f("mohammed")


الناتج:mohammed .


المصفوفات :
وتكون عبارة عن قوسين [] بينهم قيم تفصل بينها فواصل "," ويأخذ اول عنصر التصنيف 0
مثاله :

a=[1,2,3,[1,2,3]]
Print(a[1])
Print(a[3][0])


الناتج :
2
1

في حال اردت حذف قيمة من المصفوفة فإنه بالإمكان فعل ذلك بإستخدام الكلمة المفتاحية delete لتصبح القيمة undfined مثالها delete a[0] ويعيد القيمة true في حال نجح .

الإستثناءات :
وهي عبارة عن ثلاث كلمات مفتاحية try و catch و throw وقد تم دعمها في ecmascript ابتداء من النسخة الثانية
وصيغتها

Try{statement}
Catch(errorvar){statement}


ف try تقوم بعمل مابداخلها والإستمرار في حال القاء استثناء تقوم بالإنتقال مباشرة الى الجملة catch الذي يقوم بمعالجة الإستثناء ويحتوي على متغير يقوم بتلقي جملة الإستثناء الملقاة عبر الكلمة المفتاحية throw بصيغتها

Throw expressin
ومثال عليها
Function test(n){
        Try{
                If(n==0) throw "mohammed"
        }
        Catch(a){
                Print(a);
        }
}
Test(0);


الناتج mohammed .

الجملة switch والكلمات المفتاحية case و default
وهي معروفة لدى أغلب لغات البرمجة فلذلك سوف نكتفي بصيغتها
Switch(var){
Case value1:
Statements1
Break
Case value2:
Statements2
Break
Default:
Statements3
}


يتم مقارنة كل قيمة موجودة في case بالقيمة المتواجدة في switch في حال تساوي أي من القيم فإنه يتم تنفيذ جملتها والا يتم تنفيذ جملة default .

جملة الشرط الثلاثي
وصيغتها

(condition)?value1:value2


في حال كان مابداخل القوسين جملة صحيحة فإنه يتم ارجاع قيمة value1 والا فإنه يتم ارجاع قيمة value2
ومثاله

l=12
r=(l<10)?1:2
print(r)


الناتج
2

جمل التكرار for و while و do-while

For
تفيد في تنفيذ مجموعة من الجمل حتى يصبح شرطها في وسيطها الثاني غير صحيح ومثال على صيغتها

For(var=1;var<10;var++){statement}


While
في حال انتفى الشرط في وسيطها الوحيد يتم ايقاف عملية التكرار
While(condition){statement}


Do – while
يتم تنفيذ مابداخل do بشكل متكرر الى ان ينتفي الشرط في وسيط while الوحيد وصيغتها
Do{statement}
While(condition)


الجملتين continue و break
وهي تستخدم داخل جمل التكرار والشرط فالأول يكمل جملة التكرار وهي تنهي جملة statement التي يجري تنفيذها الآن بينما break ينهي من جملة التكرار .

عناوين الجمل label
وهدفها وضع عناوين للجمل ليمكنك التنقل بين هذه الجمل بإستخدام جمل break و continue

وصيغتها

Mylabel:statement
Break mylabel


الكلمة المفتاحية typeof
وتعيد نوع البيانات على شكل نص

وصيغتها
Typeof var


مثالها
N=12
Print(typeof N)


الناتج number


الكائنات
كما ذكرنا مسبقا فإن الكائنات عبارة عن صفات وطرق وهي مشابهة في طريقة بنائها للدوال ولكي تضيف لها خاصية او طريقة اضف قبل متغيراتها او دوالها الجملة this تليها نقطة " . "

مثال
Function mohammed(a,n){
This.age=a
This.name=n
This.printAll=function(){
Print(a+"…"+n)
        }
}


في المثال السابق a و n هم عبارة عن خصائص ثم تلتهم الطريقة printAll وفي حال اردت انشاء متغير لهذا الكائن
M=new mohammed (20,"mohammed")


لاحظ لقد قمنا بإستخدام الجملة new تلاها اسم الكائن ولإستدعاء الخصائص والطرق

Print(M.age)
Print(M.name)
M.printAll()


فكان الناتج
20
mohammed
20…mohammed

سجل الأنواع record type
وصيغته
Var={a1:value1,a2:value2,a3:value3,……}

وعند استدعاء مابداخلها فإنها تعامل معاملة كائن لأنها كائن بكل بساطة .
Print(Var.a)




الكلمة المفتاحية with
وهي تساعدك في التعامل مع الكائنات بتخصيص الخصائص والطرق لكائن محدد بحيث تستغني عن وضع اسم الكائن قبل كل خاصية او طريقة .

وصيغتها
With(object){statement}


ومثاله
Function mohammed(a,n){
This.age=a
This.name=n
This.printAll=function(){
Print(a+"…"+n)
        }
}
M=new mohammed (20,"mohammed")
With(M){
Print(age)
Print(name)
printAll()
}


الناتج
20
mohammed
20…mohammed

لاحظ انه في المثال المسابق لم نحتاج الى وضع اسم الكائن تليه نقطة قبل كل خاصية وطريقة لأننا قمنا بتخصيص الجمل لكائن بإستخدام الكلمة المفتاحية with .

الكلمة المفتاحية for-in
لإعادة وتكرار خواص كائن ما

وصيغتها
For( var , object){statement}


فمثلا
Function mohammed(a,n){
This.age=a
This.name=n
This.printAll=function(){
Print(a+"…"+n)
        }
}
M=new mohammed (20,"mohammed")
For(z in M){
Print(z)
}


الناتج
Age
Name

وتستخدم أيضا مع المصفوفات بحيث تمر على جميع عناصرها

مثاله
A=[10,20,30,40]
For(m in A){
Print m
}


الناتج
10
20
30
40

ويماثلها في Qt foreach للحاويات .

مراجعة سريعة لأهم الكائنات في ecmascript

قبل ان نتعرف على أهم الكائنات فإننا يجب ان نعرف بعض الأمور :

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

.
الخاصية Prototype : وهي لإضافة خصائص وطرق لكائن ما.
بشرط ان يكون الكائن قابل للتمثيل في متغيرات .

ومثال عليها
Function myClass(a,n){
This.age=a;
This.name=n;
}
myClass.prototype.printAll=function(){
print(this.name+"::"+this.age)
r=new myClass(20,"mohammed")
r.printAll();


الناتج mohammed::20 .

بالإضافة الى بعض الخواص الأخرى التي تشترك او تنفرد بها بعض الكائنات عن الأخرى .

الكائن Array
ويستخدم لتوليد المصفوفات ويحوي خاصيتين prototype و length والثاني لإرجاع عدد العناصر وبعض الطرق مثل toString و join و sort و reverse

ومثالها
A=new Array(4,3,5,"rami")
A.length // 4


واضافة خاصية بإستخدام prototype

Array.prototype.age=12
A=new Array(1,2,3,4,5,6)
Print(A.age())



الكائن Function

بالإضافة الى خاصية prototype فهو ايضا لديه خاصية length لإرجاع عدد الوسائط

وصيغتها
new Function("var1","var2",…,"statement")


مثال
F=new Function("x","y","print(x*y)")
F(2,4)


الناتج : 8

بالإضافة الى الخصائص السابقة فإن الخاصية الأكثر أهمية هي arguments وهي مصفوفة بها جميع القيم في الوسائط

مثاله :
function F(){
A=arguments.length
total=0
For(i=0;i<A;i++){total+=arguments[i]}
Return total
}
Print(F(1,2,3,4,5,6,7,8,9,10,11,12))


الناتج :78 .


الكائن الرئيسي Object
هو الكائن الرئيسي الذي ترث منه جميع الكائنات بإستثناء Global وهي تدعم الخاصيتين prototype والخاصية constructor أي اسم المنشئ فلذلك جميع الكائنات تدعمهم أيضا بالإضافة الى الطرق toString وتعيد تمثيله بالحروف والثاني valueOf وتعطي القيمة البدائية .


الكائنين Global و Math
وكلاهما لايمكن تمثيلهم في متغيرات فالأول تستخدم خصائصه وطرقه مباشرة والثاني مباشرة ايضا ولكن بعد التعبير Math. لأنها static .
الأول هدفه ربط أي كائن بوظائف ومتغيرات عامة و له ثلاث خواص NaNو Infinity و Undefined وجميعهم ثوابت الأول في حال كانت القيمة غير رقمية والثني في حال القسمة على 0 ويمكنك ان تستخدمه على أي عدد لينتج لك القيمة 0 وآخر خاصية هي للمتغيرات الغير معرفة .

اما طرقه فيمكن مراجعتها من خلال الرابط التالي :
http://phrogz.net/objjob/objects.asp

وكذلك ايضا Math فهي للثوابت والدوال الرياضية فلها خواص ثابتة مثل PI و E و LN2 و LN10 وووو
ولها طرق ويمكن ايضا مراجعتها من خلال الرابط التالي:
http://phrogz.net/objjob/objects.asp


الكائنات Boolean و Number و String
بالإضافة الى دعمها خواص الكائن الرئيسي وطرقه فإن لدى كل من String و Number طرق وخواص أخرى يمكن الوصول اليها من خلال الرابط التالي:
http://phrogz.net/objjob/objects.asp


كائن التاريخ Date
بالإضافة الى خواص وطرق الكائن الرئيسي فإن لديه طرق لتحديد الوقت بالإضافة وصيغتها ويمكن الوصول الى طرقها من خلال الرابط :
http://phrogz.net/objjob/objects.asp


كائن التعابير النظامية RegExp
وهي عبارة عن تعبيرات حرفية تصف نموذجا او اسلوبا متتابعا من الحروف pattern وقد تم ضمها مع الكائنات القياسية في الإصدار الثاني من ecmascript وكانت قبلها مدعومة في javescript لمعرفة الخواص والطرق
http://phrogz.net/objjob/objects.asp


بالإضافة الى الكائنات السابقة هناك كائنات اخرى ايضا يمكنك التعرف على جميع الكائنات من خلال الرابط التالي :
http://phrogz.net/objjob/objects.asp





الشق الثاني



Qt & ecmascript262

ان تدعم لغة او مكتبة او تقنية لغة الوثائق(النصية) بجانب اللغات المرئية فإن هذا بلا شك يمثل قوة وعبقرية لهذه اللغة او المكتبة او حتى التقنية فلذلك كان من واجبات Qt4 تقديم دعم لهذا النوع فكانت البداية في Qt4.3 فقامت ببناء الوحدة QtScript ومنذ البداية جعلوها تعتمد على معايير ecmascript3 وكان قبل هذا الإصدار محاولات لدعم البرمجة بلغات نصية ولكن ليس على معايير ecmascript فنشأ مايسمى QSA اختصار ل Qt script for Application في حال انك كنت قد تعرفت على QSA وتريد الإنتقال الى QtScript الداعمة ل ecmascript فبإمكانك مرفة الفروقات وتعلم الإضافات من خلال صفحة خصصتها QtAssistant للمنتقلين من QSA الى QtScript ولكن نحن هنا نريد ان نتعلم من البداية فدعنا نبدأ على بركة الله .

اعداد QtScipt داخل المشروع :

من خلال ملف المشروع .pro قم بإضافة هذا السطر
QT+=script


ثم اضف الوحدة QtScript الى الشفرة المصدرية
#include< QtScript>


محرك ecmascript واستعراض سريع لأهم الفئات:

تعتبر الفئة QscriptEngine هو رأس الوحدة QtScript فالتعامل مع الشفرات النصية يتم عن طريقها فهي تمثل بيئة لعمل هذه الشفرات .
بينما تمثل الفئة QscriptValue انواع البيانات وهي تدعم جميع انواع البيانات المدعومة من ecmascript .
QscriptValueIterator اذا ما اردت في التجول داخل خصائص الكائنات في QtScript .

برنامجك الأول
برنامج alslam 3likom


#include <QtGui>
#include <QtScript>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QScriptEngine engine;
QScriptValue ecma=engine.evaluate("m=new String('alslam 3likom')");
QLabel lab(ecma.toString());
lab.show();
return app.exec();
}


سوف يضهر على label جملة alslam 3likom
قمنا بالتصريح عن المتغير engine والذي من خلاله سوف نقوم بتنفيذ شفرات ال script وهذا ماحدث فعلا فمن خلال الأمر evaluate التابع للفئة QscriptEngine قمنا بإرسال نص ecmascript
 m=new String('alslam 3likom')


وتعيد الدالةevaluate متغير من النوع QscriptValue والذي كما ذكرنا مسبقا انه حاوي لمخرجات برنامج ال script ثم انشأن label لنظيف اليه قيمة ecma بعد ماقمنا بتحويلها الى نص .

اضافة كائن من Qt للتحكم به بواسطة QtScript
#include <QtGui>
#include <QtScript>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QScriptEngine engine;
QLabel *lab=new QLabel;
QScriptValue mylab = engine.newQObject(lab);
engine.globalObject().setProperty("lab", mylab);
engine.evaluate("lab.text='alslam 3likom'");
engine.evaluate("lab.show()");
return app.exec();
}


QScriptValue mylab = engine.newQObject(lab); هنا قمنا بإنشاء كائن QtScript وهي عملية تغليف كائن Qt بكائن ecmascript وعملية التغليف تشمل الإشارات والمستقبلات والخصائص والأبناء ايضا
ثم اضفنا خاصية للكائن العام وسميناها lab وهي في الأصل الكائن الذي قمنا بصناعته ولكن هذا يسمح لنا بتحديد الكائن بإسم والتحكم بخصائصه وطرقه ثم قمنا بإضافة شفرات script في آخر الموضوع ليكون مخرجات البرنامج هو عبارة عن label مكتوب عليها alslam 3likom.
وبما اننا في هذا البرنامج تطرقنا الى global opject فإنه علينا ان نفصل في هذا الموضوع أكثر عند انشاء QscriptEngine فإنه يبدأ ومعه كائن رئيسي وحيد تضاف اليه كائنات أخرى ولكن على هيئة خصائص وتكون هذه الخاصية ايضا بها خواص أخرى وهي متوفرة معه وسوف تجد الخصائص لك كائن في ملف assistant كذلك تصبح دوال المستقبلات والإشارات طرق .

الربط بين الإشارات والمستقبلات
وتكون طريقة الربط بين الكائنات بواسطة شيفرة script على هذا النحو
function myInterestingScriptFunction() { ... }
myQObject.event.connect(myInterestingScriptFunction);

ومثال عليه
1.      #include <QtGui>
2.      #include <QtScript>
3.      int main(int argc, char *argv[])
4.      {
5.      QApplication app(argc, argv);
6.      QScriptEngine engine;
7.      QPushButton *button=new QPushButton("Qt-ar.com");
8.      QLabel *lab=new QLabel("mohammed");
9.      QScriptValue mybutton=engine.newQObject(button);
10.     QScriptValue mylab=engine.newQObject(lab);
11.     engine.globalObject().setProperty("button", mybutton);
12.     engine.globalObject().setProperty("lab", mylab);
13.     engine.evaluate("function slot(){this.lab.text='alabdaly'}; button.clicked.connect(slot);");
14.     QVBoxLayout layout;
15.     layout.addWidget(lab);
16.     layout.addWidget(button);
17.     QWidget w;
18.     w.setLayout(&layout);
19.     w.show();
20.     return app.exec();
21.     }

اذا انتقلنا الى السطر الثالث عشر مباشرة فإن الشفرة النصية تص على في حال الضغط على الزر فإن النص mohammed على ال label يتم تغييره الى alabdaly
وفي المقابل فإن الشفرة النصية

myQObject.event.disconnect(myInterestingFunction);

لقطع الإرتباط بين الكائن والدالة.
وفي حال اردت الربط بين كائنين فإن
myQObject.event.connect(obj, "fun");

وعكسها
myQObject.event.disconnect(obj, "fun");

ولإرسال اشارات بواسطة اللغة النصية
myQObject.event("any");


الربط بين الإشارات والمستقبلات بإستخدام QtScript
bool qScriptConnect ( QObject * sender, const char * signal, 
const QScriptValue & receiver, const QScriptValue & function )

او لقطع االإرتباط
bool qScriptDisconnect ( QObject * sender, const char * signal, 
const QScriptValue & receiver, const QScriptValue & function )

مثال :
1.      #include <QtGui>
2.      #include <QtScript>
3.      int main(int argc, char *argv[])
4.      {
5.      QApplication app(argc, argv);
6.      QScriptEngine engine;
7.      QPushButton *button=new QPushButton("Qt-ar.com");
8.      QLabel *lab=new QLabel("mohammed");
9.      QScriptValue mybutton=engine.newQObject(button);
10.     QScriptValue mylab=engine.newQObject(lab);
11.     QScriptValue slot=engine.evaluate("slot=function(){this.text='alabdaly'};");
12.     qScriptConnect(button,SIGNAL(clicked()),mylab,slot);
13.     QVBoxLayout layout;
14.     layout.addWidget(lab);
15.     layout.addWidget(button);
16.     QWidget w;
17.     w.setLayout(&layout);
18.     w.show();
19.     return app.exec();
20.     }

هو مشابه للبرنامج السابق ولكن هذه المرة وفي السطر 12 بالتحديد استخدمنا الدالة qScriptConnect
لاحظ ان في شيفرة script أن this تشير الى الكائن lab وذلك لأنها يتعامل معها مباشرة أي بداخلها وليس على انها خاصية .

التعامل مع الأبناء
عندي تغليف احد الكائنات ب QtScript فإن من ضمن مايلتحق بها الى QtScript هو ابناء هذا الكائن
وذلك بأخذ اسمائها ووضعها كمتغير في QtScript ويكون تسمية الكائنات بإستخدام الأمر
setObjectName("QString") التابعة للفئة Qobject .
مثال:

1.      #include <QtGui>
2.      #include <QtScript>
3.      int main(int argc, char *argv[])
4.      {
5.      QApplication app(argc, argv);
6.      QScriptEngine engine;
7.      QPushButton *button=new QPushButton("Qt-ar.com");
8.      button->setObjectName("button");
9.      QLabel *lab=new QLabel("mohammed");
10.     lab->setObjectName("lab");
11.     QVBoxLayout layout;
12.     layout.addWidget(lab);
13.     layout.addWidget(button);
14.     QWidget w;
15.     w.setLayout(&layout);
16.     //======================================
17.     QScriptValue myw=engine.newQObject(&w);
18.     engine.globalObject().setProperty("w", myw);
19.     engine.evaluate("w.button.text='mohammed';w.lab.text='qt-ar.com'");
20.     w.show();
21.     return app.exec();
22.     }

انظر في السطرين 8 و 10 تم تسمية الكائنين ثم بعد ترتيب في الكائن الأب المسمى w قمنا بتغليف الكائن الأب في السطرين 17 و 18 ومن ثم بتنفيذ الشيفرة النصية التالية
"w.button.text='mohammed';w.lab.text='qt-ar.com'"
ولاحظ اننا استخدمنا اسماء الكائنين button و lab الذي وضعناه مسبقا للتعريف عنهما داخل QtScript .
في حال كان لديك مجموعة كبيرة من الأبناء فبإمكانك استخدام كلا من الأمرين داخل ال script
parent.findChild("anyname");
parent.findChildren("RegExp");

الأول يرجع اسم الكائن ان وجد والثاني يرجع مصفوفة بمعايير emascript لأسماء الكائنات التي وجدها .
وهذا مثال من assistant
var okButton = myDialog.findChild("okButton");
if (okButton != null) {
// do something with the OK button
}
var buttons = myDialog.findChildren(RegExp("button[0-9]+"));
for (var i = 0; i < buttons.length; ++i) {
// do something with buttons[i]
}


التحكم بنمط ملكية الكائنات
تقوم QtScript بإنشاء حاوية تجميع (garbage) لإستعادة الذاكرة المستخدمة من قبل كائنات اللغة النصية في حال انها لم تعد ذا قيمة . فأي كائن يمكن ان تسترد ذاكرته في حال لم يكن له وجود في بيئة تنفيذ اللغة النصية و Qt Script تترك لك كامل الحرية في التحكم في مايحدث في كائنات c++ في حال تم استعادة(تحرير) الذاكرة من كائنات script (مثاله : هل يتم حذف كائن c++ ام لا) ويتم تمرير من خلال الوسيط الثاني من الدالة newQObject .
ان معرف طريقة تعامل Qt Script مع ملكية كائن جدا مهم لأنها تساعدك في تجنب حالات عدم حذف كائن c++ بينما يجب ان يكون محذوف (تسرب في الذاكرة) او العكس .
Qt Ownership QScriptEngine::QtOwnership
وهي الحالة الإفتراضية للكائنات المغلفة وهي تعني ان ملكة الكائن ليست لمحرك اللغة النصية وبمعنى آخر انها سوف تدار وفق " Qt's object ownership" فعلى سبيل المثال اذا غلفت كائن جزء من صميم التطبيق فإنه سوف يتجاهل أي حدث في بيئة اللغة النصية .
Script Ownership QScriptEngine::ScriptOwnership
وهذا يعني ان محرك اللغة النصية سوف يأخذ الملكية الكاملة للكائن ويقوم بحذفه عندما يجد الفرصة الملائمة لذلك وبتعبير آخر عند عدم توفر مؤشر للكائن في محرك اللغة النصية . وحتى يكون هذا النمط فعال يشترط ان لا يكون للكائن أب وأن يكون انشئ في محرك اللغة النصية على النسق التالي
return engine->newQObject(new MyQObject(), QScriptEngine::ScriptOwnership);
Auto-Ownership QScriptEngine::AutoOwnership
في حال لم يكن لهذا الكائن أب ووجدت حاوية التجميع (garbage) ان هذا الكائن المغلف غير متواجد ضمن بيئة محرك اللغة النصية فإنه يتم حذفهغير ذلك فإنه يعامل معاملة Qt Ownership .

ماذا يحدث اذا قام شخص ما بحذف كائن Qobject ؟
في حالة ان شخص قام بتغليف كائن ثم قام بحذفه خارج إطار Qt script بدون أي اعتبار لنمط الملكية فإن
في هذه الحالة الكائن المغلف سيبقى كما هو كائن بعكس c++ فإن الكائن في الغة النصية (script) لايتم استبداله ب nullعلى كل حال في حال حاولت الوصول لأحد خصائصه او طرقه فإنه يلقي استثناء .
ملاحظة : اذا كانت الدالة QScriptValue::isQObject() ترسل القيمة true و الدالة QScriptValue::toQObject() ترسل مؤشر null فهذا دليل ان الكائن تم حذفه خارج بيئة لغة البرمجة النصية .

تخصيص طرق تغليف الكائنات

كما ذكرنا مسبقا ان اضافتك لكائن عن طريق الأمر newQObject بدون ان تخصص له طريقة للتغليف فإنه يؤدي الى تغليف الخصائص والإشارا والمستقبلات وكذلك الأبناء ولكن في حال انك قمت بتخصيص ماسوف يقوم بتغليفه فإنه لن يغلف الا ماطلبته منه وجميع هذه الطرق موجودة في الثابت التعدادي QScriptEngine::QobjectWrapOption وذلك بإضافتها الى الوصيط الثالث من newQObject .
ومن هذه الطرق
QScriptEngine::ExcludeChildObjects لمنع تغليف الكائنات الأبناء في QtScript

QScriptEngine::ExcludeSuperClassMethods لمنع تغليف دوال الإشارات والمستقبلات المورثة من الفئة Qobject
QScriptEngine::ExcludeSuperClassProperties لمنع تغليف الخصائص المورثة من الفئة QObject
QScriptEngine::SkipMethodsInEnumeration عدم ضم الطرق( الإشارات والمستقبلات) اثناء عملية فرز محتوى الكائن بإستخدام for-in ولكنها تغلف .

QScriptEngine::PreferExistingWrapperObject اذا كان الكائن وفق الإعدادات المطلوب موجود مسبقا يتم ارجاعه .

التحويل بين انواع c++ و أنواع QtScript

في نهاية الأمر بعد حصولك على مخرجات برنامج الشيفرة النصية فأنت بحاجة الى تحويلها لأحد الأنواع المعروفة في c++ لكي تستطيع التعامل معها وهذه المخرجات يتم تحويلها عن طريق الدوال المتواجدة في الفئة
QScriptEngine و QscriptValue.
اولا التحويل من أنواع QtScript الى انواع c++
bool    QScriptValue::toBoolean()

int     QScriptValue::toInt32()

uint    QScriptValue::toUInt32()

float float(QScriptValue::toNumber())

double QScriptValue::toNumber()

short short(QScriptValue::toInt32())

ushort QScriptValue::toUInt16()

char    char(QScriptValue::toInt32())

uchar   unsigned char(QScriptValue::toInt32())

qlonglong       qlonglong(QScriptValue::toInteger())

qulonglong      qulonglong(QScriptValue::toInteger())

QString
QScriptValue::toString()

QDateTime
QScriptValue::toDateTime()

QDate
QScriptValue::toDateTime().date()

QRegExp
QScriptValue::toRegExp()

QObject*
QScriptValue::toQObject()

QWidget*
QScriptValue::toQObject()

QVariant
QScriptValue::toVariant()

QChar
اذا كان QscriptValue سلسلة نصية فإنه يأخذ الحرف الول منها .اما اذا كانت السلسلة النصية فارغة فإنه يرجع Qchar null او عندما تكون رقم فإالناتج يكون Qchar مبني من uicode .
QStringList
اذا كان QscriptValue عبارة عن مصفوفة فإنها ترجع QstringList وإلا فإنه يرسل QstringList فارغ .
QVariantList
اذا كان QscriptValue عبارة عن مصفوفة فإنها ترجع t QVarianList وإلا فإنه يرسل QVariantList فارغ .
QVariantMap
اذا كان QscriptValue عبارة عن كائن فإنها ترجع QVariantMap <key,value> وهو عبارة عن زوج من (propertyName, propertyValue.toVariant()) وبإمكانك استخدام الصف وبإمكانك ستخدام الفئة QScriptValueIterator للتجول في خصائص الصف .

QObjectList
اذا كان QscriptValue عبارة عن مصفوفة فإنها ترجع QObjectList وإلا فإنه يرسلList Qobject فارغ .
QList<int>
اذا كان QscriptValue عبارة عن مصفوفة فإنها ترجع <int>QList وإلا فإنه يرسل <int> QList فارغ .

بالإضافة الى ماسبق فإن QtScript تتعامل مع الحالات التالية بإسلوب آخر:
اذا كان QscriptValue عبارة عن كائن ل QObject وكان أصله عبارة عن مؤشرمثل
(Qlabel* ) فإن عملية القسر تحدث له لترجعه الى اصله بإستخدام qObject_cast<>(Qobject*)
ومثله اذا كان QsriptValue عبارة عن Qvariant وتنطبق عليها ذات الشروط بالإضافة الى وجود user type متفرد فإنه يتم قسره الى نوعه الأصلي بإستخدام qVariant_cast<>(QVariant*) .

التحويل من c++ الى QtScript
(البعض من هذه الدوال متوفرة في QscriptValue والآخر منها متوفر لدى QScriptEngine

void    QScriptEngine::undefinedValue()

bool    QScriptValue(engine, value)

int     QScriptValue(engine, value)

uint    QScriptValue(engine, value)

float QScriptValue(engine, value)

double QScriptValue(engine, value)

short QScriptValue(engine, value)

ushort QScriptValue(engine, value)

char    QScriptValue(engine, value)

uchar   QScriptValue(engine, value)

QString
QScriptValue(engine, value)

qlonglong       QScriptValue(engine, qsreal(value)). ملاحظة قد يؤدي ذا النوع من التحويل الى فقد بعض البيانات فمن المفضل تحويلها الى qsreal
qulonglong      QScriptValue(engine, qsreal(value)). ملاحظة قد يؤدي هذا النوع من التحويل الى فقد بعض البيانات فمن المفضل تحويلها الى
qsreal .
QChar
QScriptValue(this, value.unicode())

QDateTime
QScriptEngine::newDate(value)

QDate
QScriptEngine::newDate(value)

QRegExp
QScriptEngine::newRegExp(value)

QObject*
QScriptEngine::newQObject(value)

QWidget*
QScriptEngine::newQObject(value)

QVariant
QScriptEngine::newVariant(value)

QStringList
يتم انشاء مصفوفة داخل اللغة النصية وذلك بإستخدام الدالة QScriptEngine::newArray()

QVariantList
A new script array (created with QScriptEngine::newArray()), whose elements are created using QScriptEngine::newVariant() for each element of the list.
QVariantMap
A new script object (created with QScriptEngine::newObject()), whose properties are initialized according to the (key, value) pairs of the map.
QObjectList
A new script array (created with QScriptEngine::newArray()), whose elements are created using QScriptEngine::newQObject() for each element of the list.
QList<int>
A new script array (created with QScriptEngine::newArray()), whose elements are created using the QScriptValue(QScriptEngine *, int) constructor for each element of the list.

وفي حال اردت ان تقوم بتغليف انواع اخرى فأستخدم الدالة QScriptEngine::newVariant .
او اذا اردت صناعة مؤشر فارغ لأي نوع فأستخدم الدالة QScriptEngine::nullValue .

تصميم كائنات قابلة للنقل الى QtScript

في هذا القسم وهو الأخير سوف تتعرف كيف تعمل كائن بطريقة يصبح التعامل معه في اللغة النصية سهلة وقوية ولكن قبل ان نقول كيف تقوم بهذا الشيئ فإنه لمن المهم ان نذكر الجهود التي بدلت من أجل هذه الطريقة التي لو بحث عن أي لغة أخرى او مكتب أخرى تدعمها فلن تجد.
ان عملية تحويل كائنات c++ الى كائناتScript Qt هي ليست بالعملية السهلة فالأخير يأخذ منحنى أكثر ديناميكية من c++ في التعامل مع الأنواع (اكثر سهولة) وايضا هي قادرة على تفحص الكائنات وقت التشغيل وجميع هذه الميزات غير متوفرة في c++.
لذلك كان هذا الإنجاز التي قامت بهي الشركة لتدعم Qt بهذه المرونة في التعامل مع اللغة النصية أمرا يحسب له حسابه .
وكما عرفنا مسبقا ان هذه المعايير غير متوفرة مع c++ فلذا قمنا بإنشاء النظام Qt meta object حيث سوف يوفر لك معايير توسعة جديدة في صيغة c++ . ولكي ننقل هذه المعايير الى لغة c++ فإننا قمنا بصناعة برنامج صغير يدعى moc (meta object compiler) . ولكي تدعم كائناتك هذه المحاسن فهناك حلان اما ان ترث من Qobjec او الستخدام ال macro الذي هو Q_OBJECT ولقد اتبثت هذه الطرق نجعها وقوتها وصلابتها لسنوات عة .
ولكي تعرف كيف تقوم عملية التحويل من c++ فإننا ننصحك بقراءة Qt Object Model المتوفرة في QtAssistant .

صنع دوال قابلة للتغليف في QtScript :
كما ذكرنا في بداية حديثنا عن QtScript تغلف الإشارات والمستقبلات لتكون طرق في QtScript ولكن دعنا هذه المرة نفصل أكثر .
ان نظام meta-object يقوم بوضع معلومات عن الإشارات والمستقبلات والتي تتوفر بشكل تلقائي اثناء التشغيل . بشكل افتراضي الفئات التي ترث من Qobject هي تتوفر اشاراتها ومستقبلاتها بشكل تلقائي في اللغة النصية .
مثال على صف دواله قابلة للتغليف في QtScript:

1.      class MyObject : public QObject
2.      {
3.      Q_OBJECT
4.      public:
5.      MyObject( ... );
6.      void aNonScriptableFunction();
7.      public slots: // these functions (slots) will be available in QtScript
8.      void calculate( ... );
9.      void setEnabled( bool enabled );
10.     bool isEnabled() const;
11.     private:
12.     ....
13.     };


في دوال المستقبلات ابتداء من السطر 7 فإنها يمكن تغليفها في حال قمنا بتغليف الكائن بينما التي تعلوها فلا يمكن تغليفها .
وفي حال اردت تحديد دالة بعينها فبإمكانك استخدام المعدل Q_INVOKABLE اثناء التصريح عن الدالة ويمكن استخدامها ايضا كمستقبلات في اللغة النصية بالرغم انها لم تحدد ضمن slots .
ومثاله :

1.      class MyObject : public QObject
2.      {
3.      Q_OBJECT

4.      public:
5.      Q_INVOKABLE void thisMethodIsInvokableInQtScript();
6.      void thisMethodIsNotInvokableInQtScript();

7.      ...
8.      };


اضافة خصائص للكائنات ليتم تغليفها فيما بعد

في الأمثلة السابقة في حال انك غلفتها ب QtScript فإنها تكتب بهذه الطريقة
var obj = new MyObject;
obj.setEnabled( true );
print( "obj is enabled: " + obj.isEnabled() );
فلقد اصبحت المستقبلات طرق في اللغة النصية .
ولكن في كثير من الأحيان تجد ان بع الشفرات كتبت بهذه الطريقة

var obj = new MyObject;
obj.enabled = true;
print( "obj is enabled: " + obj.enabled);


ففي السابق obj.setEnabled( true ); والآن= true obj.enabled فهذه هي الخصائص ولتسهيل الأمر فالخصائص التي تدعمها ecmascript هي ذاتها الخصائص التي توفرها Qt في كائناتها ولكي تستطيع ان تضيف هذه الخصائص يجب ان يرث الكائن من Qobject وان تضع ال macro ( Q_OBJECT)في رأس الصف ومن ثم تضع ال macro الذي يقوم بتوفير الخصائص التي تريدها
وهو
Q_PROPERTY( type property WRITE function1 READ function2)
وهنا قمنا بإنشاء الخاصية property التي تقوم بتنفيذ الدالة function1 وتعيد الدالة function2 ونوعها هو type .
ومثاله

1.      class MyObject : public QObject
2.      {
3.      Q_OBJECT
4.      // define the enabled property
5.      Q_PROPERTY( bool enabled WRITE setEnabled READ isEnabled )
6.      public:
7.      MyObject( ... );
8.      void aNonScriptableFunction();
9.      public slots: // these functions (slots) will be available in QtScript
10.     void calculate( ... );
11.     void setEnabled( bool enabled );
12.     bool isEnabled() const;
13.     private:
14.     ....
15.     };



في هذا المثال الخاصية enabled تقوم بتنفيذ الدالة setEnabled وهو يأخذ وسيط bool وسوف يمثل هذا الوسيط القيمة التي تقوم انت بإعطائها ل enabled وعند قرائتك للخاصية فإنها سوف تستدعي الدالة isEnabled والذي سوف يقوم بإعادة القيمة المعطات للخاصية .
وسوف يصبح شكل الشيفرة في اللغة النصية على الشكل التالي
var obj = new MyObject;
obj.enabled = true;
print( "obj is enabled: " + obj.enabled );


حيث:
obj.enabled = true; = setEnabled(bool);
obj.enabled=isEnabled();


وبإمكانك ايضا الإستغناء عن كتابة دالة الإرجاع وتضع الخاصية على شكلها التالي
Q_PROPERTY( bool enabled WRITE setEnabl

مثال شامل على الخصائص والطرق

1.      #include<QtGui>
2.      #include<QtScript>
3.      class A:public QLabel{
4.      Q_OBJECT
5.      Q_PROPERTY( bool plose WRITE setPlose)
6.      public:
7.      A(QWidget*parent=0);
8.      Q_INVOKABLE void setNumber(int r);
9.      public slots:
10.     void setPlose(bool plose);
11.     };
12.    
13.     A::A(QWidget*parent):QLabel(parent){}
14.    
15.     void A::setNumber(int r){
16.     setNum(r);
17.     }
18.    
19.     void A::setPlose(bool setPlose){
20.     if(setPlose)showFullScreen ();
21.     else showNormal ();
22.     }
23.    
24.     int main(int argc, char ** argv)
25.     {
26.     QApplication app( argc, argv );
27.     QScriptEngine engine;
28.     QScriptValue s=engine.newQObject(new A);
29.     engine.globalObject().setProperty("a",s);
30.     engine.evaluate("a.setNumber(12);a.plose=true");
31.     return app.exec();
32.     }

التعامل مع الفئات التي لاثرث من QObject .
لقد تعاملنا في السابق مع كائنات ترث من Qobject وعرفنا طريقة التحويل الى ومن QtScript و Qt object
ولكن في حال كانت الفئة التي نريد تحويلها لا ترث من QObject فسوف نتبع الإسلوب التالي :


1- بناء الفئة .

struct MyStruct {
int x;
int y;
};

2- التعريف بالفئة ضمن الفئات المعرفة في ضمن QmetaType
Q_DECLARE_METATYPE(MyStruct)

3- ثم نقوم بصنع دوال التحويل من والى اللغة النصية .
3-      QScriptValue toScriptValue(QScriptEngine *engine, const MyStruct &s)
4-      {
5-      QScriptValue obj = engine->newObject();
6-      obj.setProperty("x", QScriptValue(engine, s.x));
7-      obj.setProperty("y", QScriptValue(engine, s.y));
8-      return obj;
9-      }
10-     void fromScriptValue(const QScriptValue &obj, MyStruct &s)
11-     {
12-     s.x = obj.property("x").toInt32();
13-     s.y = obj.property("y").toInt32();
14-     }


الأول يقوم بالتحويل الى QScriptValue والثاني من الأول الى انواع c++ المعروفة وكما تلاحظ فإن الدوال التي سوف نقوم بصنعها للتحويل ماهي الا اسلوب لطريقة نقل البيانت من فئات c++ الى اللغة النصية وأنواع الوسائط والمخرجات لا يمكنك تغييرها حيث سوف نسخدم مؤشر الدالتين للتعريف ضمن فئات اللغة النصية فيما بعد ويمكنك ايضا كتابة طريقة التحويل بإسلوب آخر مثال .
QScriptValue myObjectToScriptValue
(QScriptEngine *engine, MyObject* const &in)
{
 return engine->newQObject(in);
 }
void myObjectFromScriptValue(const QScriptValue &object, MyObject* &out)
{
out = qobject_cast<MyObject*>(object.toQObject());
}

لاحظ اننا حافظنا على انواع الوسائط والمخرجات .

4- وهي الخطوة الأخيرة حيث نقوم بتسجيل النوع ودوال تحويله
int qScriptRegisterMetaType ( QScriptEngine * engine, QScriptValue(* ) ( QScriptEngine *, const T & t ) toScriptValue, void(* ) ( const QScriptValue &, T & t ) fromScriptValue, const QScriptValue & prototype = QScriptValue() )
وهذه الدالة تقوم بتسجيل النوع T لذا المحرك في الوسيط الأول والوسيطين الثاني والثالث يستقبلا مؤشر لدوال التحويل الى ومن QtScript على التوالي فيصبح لديك السطر التالي .
qScriptRegisterMetaType(engine, toScriptValue, fromScriptValue);

الآن أصبح التعامل مع النوع السابق أكثر سهولة مثال شامل عليه
1.      #include <QApplication>
2.      #include <QtScript>
3.      //
4.      struct MyStruct{
5.      int x;
6.      int y;
7.      };
8.      Q_DECLARE_METATYPE(MyStruct)
9.      QScriptValue toScriptValue(QScriptEngine *engine, const MyStruct &s)
10.     {
11.     QScriptValue obj = engine->newObject();
12.     obj.setProperty("x", QScriptValue(engine, s.x));
13.     obj.setProperty("y", QScriptValue(engine, s.y));
14.     return obj;
15.     }
16.     void fromScriptValue(const QScriptValue &obj, MyStruct &s)
17.     {
18.     s.x = obj.property("x").toInt32();
19.     s.y = obj.property("y").toInt32();
20.     }
21.     int main(int argc, char ** argv)
22.     {
23.     QApplication app( argc, argv );
24.     QScriptEngine *engine=new QScriptEngine;
25.     qScriptRegisterMetaType(engine, toScriptValue, fromScriptValue);
26.     MyStruct s;
27.     QScriptValue a=qScriptValueFromValue(engine,s);
28.     return app.exec();
29.     }


تغليف الدوال الغير تابعة لكائن ما:
توفر لنا Qt script التابع QScriptEngine::newFunction() لتغليف مؤشرات الدوال حيث تمكنك من التعامل مع الدوال في بيئة اللغة النصية وتكون الدوال على الشكل التالي .
QScriptValue getProperty(QScriptContext *ctx, QScriptEngine *eng)
{
QString name = ctx->argument(0).toString();
return ctx->thisObject().property(name);
}

حيث ان المرجع QScriptValue والوسائط QScriptContext و QScriptEngine لايمكن تغييرهم ومن ثم يتم تحويلها الى دالة في اللغة النصية عن طريق استخدام الدالة QScriptEngine::newFunction() والتي تستقبل مؤشر لهذه الدالة .
الكائنQScriptContext :
وتستخدم في كل من .
التعامل مع وسائط الدوال بواسطة الأمر argument .
الحصول على الكائن this بإستخدام الدالة thisObject .
إلقاء اخطاء اللغة النصية باسخدام الدالة throwError.
الحصول على الكائنات النشطة بإستخدام الدالة activationObject.
ووظائف أخرى ايضا سوف يأتي سوف يأتي ذكرها في الشرح .
أولا: التعامل مع الوسائط
لقد تعرفنا في هذا الفصل على طريقة التعامل مع الدوال وتعرفنا على كائن الدوال أيضا ولقد تمكنا بالتعامل مع الدوال على انها ذات وسائط محددة ومن ثم تمكنا من التعامل معها مع عدة وسائط وبهذا النمط فإن QScriptContext يوفر لك التعامل مع وسائط دوال غير محدودة انر الى كل من الأمثلة التالية :
function add() {
return arguments[0] + arguments[1];
}

هذا المثال باللغة النصية .
QScriptValue add(QScriptContext *ctx, QScriptEngine *eng)
{
double a = ctx->argument(0).toNumber();
double b = ctx->argument(1).toNumber();
return QScriptValue(eng, a + b);
}

وهذا مثال مكافئ لعمل مثالنا السابق اذا قمنا بتحويله ولكن وفق معايير لغة سي ++ حيث الدالة argument ترجع QScriptValue.
ويمكنك أيضا التعرف على عدد الوسائط انظ هذا المثال في اللغة النصية
function F(){
A=arguments.length
total=0
For(i=0;i<A;i++){total+=arguments[i]}
Return total
}

لاحظ وبما ان arguments عبارة عن مصفوفة في الأصل فإننا استخدمنا الخاصية length لمعرفة عدد القيم بداخلها .
وهذا يكافئ المثال التالي :
QScriptValue add(QScriptContext *ctx, QScriptEngine *eng)
{
if (ctx->argumentCount() != 2) return QScriptValue(eng, 0);
double a = ctx->argument(0).toNumber();
double b = ctx->argument(1).toNumber();
return QScriptValue(eng, a + b);
}

حيث الدالة argumentCount() تعطيك عدد ما بالوسائط .
ثانيا:القاء الأخطاء والاسثناءات
يمكن القاء الأخطاء عن طريق دوال QScriptContext
QScriptValue throwError ( Error error, const QString & text )
حيث الوسيط الأول يحدد نوع الخطأ وفق للثابت التعدادي Error والتي لن تخرج عن التالي
QScriptContext::ReferenceError
QScriptContext::SyntaxError
QScriptContext::TypeError
QScriptContext::RangeError
QScriptContext::URIError
QScriptContext::UnknownError

والوسيط الثاني يحدد نص الخطأ الذي تريد ارساله وسوف تعيد الدالة كائن الخطأ الذي تم انشاء .
ويمكناك القاء استثناء بإستخدام الدالة
QScriptValue QScriptContext::throwValue ( const QScriptValue & value )
حيث تعيد القيمة المرسولة وهي تكافئ ما بالوسيط .
مثال التأكد من نوع قيم الوسائط :
1.      QScriptValue add(QScriptContext *ctx, QScriptEngine *eng)
2.      {
3.      if (ctx->argumentCount() != 2)
4.      return ctx->throwError("add() takes exactly two arguments");
5.      if (!ctx->argument(0).isNumber())
6.      return ctx->throwError(QScriptContext::TypeError,
7.      "add(): first argument is not a number");
8.      if (!ctx->argument(1).isNumber())
9.      return ctx->throwError(QScriptContext::TypeError,
10.     "add(): second argument is not a number");
11.     double a = ctx->argument(0).toNumber();
12.     double b = ctx->argument(1).toNumber();
13.     return QScriptValue(eng, a + b);
14.     }

ويمكنك استخدام الدالة
bool QScriptValue::isError ()
في حال اردت التأكد من انه القى خطأ .

ثالثا: الوصول الى وسائط الكائنات
انظر المثال التالي
1.      function foo() {
2.      // Let bar() take care of this.
3.      print("calling bar() with " + arguments.length + "arguments");
4.      var result = return bar.apply(this, arguments);
5.      print("bar() returned" + result);
6.      return result;
7.      }

فالكائن bar الذي يحتووي على الطريقة apply سوف يقوم بإستلام جميع القيم من الوسائط عبر الخاصية arguments التابعة لكائن الدوال والتي هي عبارة عن مصفوفة في الأصل فلنحاول اذا محاكاة هذا الكائن
1.      QScriptValue foo(QScriptContext *ctx, QScriptEngine *eng)
2.      {
3.      QScriptValue bar = eng->globalObject().property("bar");
4.      QScriptValue arguments = ctx->argumentsObject();
5.      qDebug() << "calling bar() with" << arguments.property("length").toInt32() << "arguments";
6.      QScriptValue result = bar.apply(ctx->thisObject(), arguments);
7.      qDebug() << "bar() returned" << result.toString();
8.      return result;
9.      }

قمنا في البداية بسحب الخاصية bar من الكائن الرئيسي لبيئة اللغة النصية ووضعناه في المتغير bar ثم قمنا بسحب وسائط الكائن الحالي foo وحفظها في المتغير arguments وفي السطر الخامس قمنا بطباعة عدد القيم في arguments وفي السطر السادس قمنا بتنفيذ الدالة applay من الكائن bar وبنفس اسلوب اللغة النصية جعلنا الوسيط الأول يعود للكائن الحالي foo بواسطة الأمر thisObject والوسيط الثاني ارسلنا لها المتغير arguments .

رابعا: دوال البناء
لقد عرفنا كيف ان الدوال قد تكون كائنات في اللغة النصية
مثال:-
Function mohammed(a,n){
This.age=a
This.name=n
This.printAll=function(){
Print(a+"…"+n)
        }
}
M=new mohammed (20,"mohammed")

ويمكنك معرفة ما اذا كانت الدالة تسلك هذا المنحى بإستخدام الدالة التالية
QscriptContext::isCalledAsConstructor() وهي التي تعيد القيمة true في حال كان constructor function والا فإنه يعيد false .
ملاحظة: يمكنك الحصول على قيم غير معرفة بإستخدام الدالة QScriptEngine::undefinedValue () والتي سوف تعيد QscriptVaue تعبر عن القيمة غير معرفة .
فلنقم الآن بصنع دالة بناء ولكن ليس بواسطة اللغة النصية
1.      QScriptValue Person_ctor(QScriptContext *ctx, QScriptEngine *eng)
2.      {
3.      QScriptValue object;
4.      if (ctx->isCalledAsConstructor()) {
5.      object = ctx->thisObject();
6.      } else {
7.      object = eng->newObject();
8.      object.setPrototype(ctx->callee().property("prototype"));
9.      }
10.     object.setProperty("name", ctx->argument(0));
11.     return object;
12.     }


هذه الدالة سوف تسلك منحنيين في حال انها نوديت على شكل function constructor
new Person("mohammed");
في هذه الحالة فإنها تعتبر object حيث قمنا في الأسطر 3 الى 6 بالتأكد ما اذا نوديت على شكل كائن تقوم بتخزين الكائن الحالي في المتغير object ثم ننتقل الى السطر 10 لنظيف لها خاصية الإسم ثم نقوم بإعطاء خاصية الإسم وسيط الكائن وهو mohammed .
ولكن في حال استدعيت على شكل دالة فإنه يتم انشاء كائن جديد يخزن في المتغير object لنقوم بإضافة له ال prototype للكائن المغلف من QScriptContextحيث الدالة calle يرجع QScriptValue لكائن الدالة الممثلة من قبل QScriptContext .
ملاحظة: prototype في اللغة النصية تكافئ new function ولكن لذات كائن الدالة وليس الكائن الجديد.

خامسا:البيانات المصاحبة للدوال
البيانات هنا يمكن التعامل معها بواسطة لغة c++ فقط فلا يمكن الوثول اليها بواسطة اللغة النصية فمثلا اذا اردت ان تضيف مؤشر لكائن c++ او او او ... في هذه الحالة انت تريد ان تحول بين هذه البيانات وبين مستخدم اللغة النصية
ويمكن اضافة هذه البيانات بواسطة setData انظر المثال :
1.      QScriptValue rectifier(QScriptContext *ctx, QScriptEngine *eng)
2.      {
3.      QRectF magicRect = qscriptvalue_cast<QRectF>(ctx->callee().data());
4.      QRectF sourceRect = qscriptvalue_cast<QRectF>(ctx->argument(0));
5.      return eng->toScriptValue(sourceRect.intersected(magicRect));
6.      }
7.      ...
8.      QScriptValue fun = eng.newFunction(rectifier);
9.      QRectF magicRect = QRectF(10, 20, 30, 40);
10.     fun.setData(eng.toScriptValue(magicRect));
11.     eng.globalObject().setProperty("rectifier", fun);

سادسا :تنفيذ دوال اللغة النصية بواسطة c++
يمكنك من خلال c++ نفيذ دوال اللغة النصية وذلك عن طريق الأمر QScriptValue::call حيث اذا توفر للدالة في اللغة النصية QscriptValue فإنك سوف تتبعها ب call والتي بدورها ترجع لك QscriptValue للقيمة المرجعة من الدالة ويمكنك أيضا ان تحملها كائنها this في الوسيط الأول ووسائط QscriptValueList وهي عبارة عن حاوية Qlist ل قيم QscriptValue .
1.      QScriptEngine engine;
2.      engine.evaluate("function fullName() { return this.firstName + ' ' + this.lastName; }");
3.      engine.evaluate("somePerson = { firstName: 'John', lastName: 'Doe' }");
4.      QScriptValue global = engine.globalObject();
5.      QScriptValue fullName = global.property("fullName");
6.      QScriptValue who = global.property("somePerson");
7.      qDebug() << fullName.call(who).toString(); // "John Doe"
8.      engine.evaluate("function cube(x) { return x * x * x; }");
9.      QScriptValue cube = global.property("cube");
10.     QScriptValueList args;
11.     args << QScriptValue(&engine, 3);
12.     qDebug() << cube.call(QScriptValue(), args).toNumber(); // 27


في الأسطر من 1 الى 7 قمنا بصنع الدالة fullName في بيئة اللغة النصية وكذلك بالنسبة للكائن somePerson ثم قنا بجليها من اللغة النصية وحفظها في المتغيرين fullName و who وبما ان المتغير fullName عبارة عن QscriptValue لدالة في اللغة النصية فإننا استخدمنا معها الدالة call و أضفنا الكائن this وهو somePerson في اللغة النصية .
بينما في الأسطر من 9 الى 12 قمنا بإنشاء دالة التكعيب في بيئة اللغة النصية وصنعنا متغير QscriptValueList ليحمل وسائط الدالة لنعطي وسيط وحيد في السطر 11 ثم في السطر 12 اسخدمنا الدالة call .
في المثال السابق قمنا بصنع الدوال في بيئة اللغة النصية ولكن أنر هذا المثال :
1.      QScriptValue myCompare(QScriptContext *ctx, QScriptEngine *eng)
2.      {
3.      double first = ctx->argument(0).toNumber();
4.      double second = ctx->argument(1).toNumber();
5.      int result;
6.      if (first == second)
7.      result = 0;
8.      else if (first < second)
9.      result = -1;
10.     else
11.     result = 1;
12.     return QScriptValue(eng, result);
13.     }
14.     QScriptEngine eng;
15.     QScriptValue comparefn = eng.newFunction(myCompare);
16.     QScriptValue array = eng.evaluate("new Array(10, 5, 20, 15, 30)");
17.     array.property("sort").call(array, QScriptValueList() << comparefn);
18.     // prints "5,10,15,20,30"
19.     qDebug() << array.toString();


في الأسطر من 1 الى 13 قمنا بصناعة الدالة myCompare ثم من 14 الى 19 قمنا بإضافة الدالة الى محرك اللغة النصية ومارسنا اجراءاتنا السابقة .

سابعا: الكائنات النشطة
الكائن النشط هو كائن في اللغة النصية والتي خصائصه عبارة عن متغيرات محلية وتشمل أيضا الوسائط المصاحبة لأي دوال في اللغة النصية حيث يكون للوسيط إسم . وبعبارة أخرى الكائن النشط هو الكائن الذي ينفذ محتواه حاليا.
وكل دالة تم تغليفها للغة نصية تكون لها كائن نشط حيث يمكن الوصول لها بإستخدام الدالة QScriptContext::activationObject () ويمكنك ان تحصل وتعدل وتنشئ وتحذف متغيرات محلية بواسطة الدالتين setProperty و property .
ملاحظة: الكائنات النشطة ذاتها لايمكن الوصول اليها بواسطة اللغة النصية مباشرة .
وفي شيفرة c++ هناك تطبيقان رئيسيان للكائنات النشطة :-
1- توفر لك طريقة معيارية لتمرير المتغيرات المصاحبة لإستدعاء دالة .
2- وايضا يمكن ان تستخدم لتهيئة المتغيرات المحلية التي يجب ان تكون متوفرة عند تنفيذ شيفرة اللغة النصية ويمكن ان تعتبرها ايضا طريقة لتمرير المتغيرات للغة النصية ذاتها وهذه التقنية استخدامها مرتبط مع الدالة QscriptEngine::pushContext() انظر المثال :
1-      QScriptContext *ctx = eng.pushContext();
2-      QScriptValue act = ctx->activationObject();
3-      act.setProperty("digit", QScriptValue(&eng, 7));
4-      qDebug() << eng.evaluate("digit + 1").toNumber(); // 8
5-      eng.popContext();

لاحظ اننا في هذا المثال المتغير ctx اعتبر كأنه الكائن الرئيسي في المحرك حيث ان الدالة QpushContext تقوم بإدخال محتوى جديد قابل للتنفيذ في حال اردت ان تصل للمحتوى القديم يمكنك استخدام الدالة popContext لحذف المحتوى الجديد والإنتقال الى القديم او يمكنك استخدام الدالة currentContext لترجع QscriptContext للمحتوى الحالي .

الفئة QScriptable :-
توفر لك الوصول الى بيئة اللغة النصية من خلال دوال c++ و Qt بحيث من خلال وراثة هذه الفئة سوف تحصل الفئة الجديد على الدوال التالية
thisObject(), argumentCount(), argument(), context() and engine()
من خلال هذه الفئات أصبح بإمكانك الوصول الى بيئة اللغة النصية من خلال ال slots و خصائص الفئة الجديدة
اثناء تغليفك لهذا الكائن .
الطريقة الأنسب لإستخدام QScriptable لتنفيذ كائنات prototype للفئات الخاصة في c++ حيث ترث فئتك من QObject ومن QScriptAble ثم تقوم بتغليفها بإستخدام newQObject وفي النهاية تقوم بإضافتها الى المحرك بإستخدام setDefaultPrototype والتي تقوم بإضافة prototype افتراضي لنوع c++ المعرفة من خلال رقمها في metatypeId . سوف يوفر لنا هذا واجهة لغة نصية للقيم التي نوعها تحمل ذات metatypeId حيثما تم الوصول الى القيم من QtScript .
هذا المثال يعبر عن الإستخدام المثالي لهذه الفئة :-
1.      class MyScriptableObject: public QObject, QScriptable
2.      {
3.      Q_OBJECT
4.      ...
5.      public slots:
6.      void doSomething();
7.      double doSomethingElse();
8.      }
9.      void MyScriptableObject::doSomething()
10.     {
11.     context()->throwError("Threw an error from a slot");
12.     }
13.     double MyScriptableObject::doSomethingElse()
14.     {
15.     return qscriptvalue_cast<double>(thisObject());
16.     }


مثال مشروح :-
Posted Image

النافذة التي أمامنا عبارة عن QListWidget يعاد تلوينها كلما اتت اشارة عن تغيير خيار اللون الحالي

الملف code.js
1.      listWidget.addItem("Red");
2.      listWidget.addItem("Blue");
3.      listWidget.addItem("Green");
4.      listWidget.addItem("Cyan");
5.      listWidget.addItem("Yellow");
6.      listWidget.addItem("Purple");
7.      listWidget.addItems(["Orange", "Gray"]);
8.      listWidget.currentItemChanged.connect(
9.      function(item)
10.     {
11.     listWidget.setBackgroundColor(item.text);
12.     }
13.     );
14.     listWidget.show();

حيث ان listWidget هو النافذة addItem هو slot تم انشاءه لإضافة عناصر الى ال listWidget وسوف يتم تغليفه في حال قمنا بتغليف الكائن .
وفي السطر من 8 الى 13 قمنا بربط اشارة تغير عنصر الحالي مع الدالة التي قمنا بصنعها ووظيفتها تغيير لون الخلفية وفي السطر الأخير قمنا بإهار النافذة .
ملف الرأس prototypes.h :-
1.      class ListWidgetItemPrototype : public QObject, public QScriptable
2.      {
3.      Q_OBJECT
4.      Q_PROPERTY(QString text READ text WRITE setText)
5.      public:
6.      ListWidgetItemPrototype(QObject *parent = 0);
7.      QString text() const;
8.      void setText(const QString &text);
9.      public slots:
10.     QString toString() const;
11.     };
12.     class ListWidgetPrototype : public QObject, public QScriptable
13.     {
14.     Q_OBJECT
15.     public:
16.     ListWidgetPrototype(QObject *parent = 0);
17.     public slots:
18.     void addItem(const QString &text);
19.     void addItems(const QStringList &texts);
20.     void setBackgroundColor(const QString &colorName);
21.     };

الفئة QtScriptable توفر لك الوصول الى بيئة اللغة النصية من خلال دوال c++ و Qt فلذلك هي موروثة من الفئتين الذين سيتم تغليفهم فيما بعد .
الفئة ListWidgetItemPrototype تحتوي على الخاصية text والتي ترجع من الدالة text وتنفذ الدالة setText بالإضافة الى ال slots فهناك المستقبل toString والتي يتم تغليفها ايضا فيما بعد .
الفئة ListWidgetPrototype وتحوتوي على ال slots التالية
void addItem(const QString &text);
void addItems(const QStringList &texts);
void setBackgroundColor(const QString &colorName);

والتي سوف يتم تغليفهم جميعا فيما بعد .
ملف prototypes.cpp :-
1.      Q_DECLARE_METATYPE(QListWidgetItem*)
2.      Q_DECLARE_METATYPE(QListWidget*)
3.      ListWidgetItemPrototype::ListWidgetItemPrototype(QObject *parent)
4.      : QObject(parent){}
5.      QString ListWidgetItemPrototype::text() const
6.      {
7.      QListWidgetItem *item = qscriptvalue_cast<QListWidgetItem*>(thisObject());
8.      if (item)
9.      return item->text();
10.     return QString();
11.     }
12.     void ListWidgetItemPrototype::setText(const QString &text)
13.     {
14.     QListWidgetItem *item = qscriptvalue_cast<QListWidgetItem*>(thisObject());
15.     if (item)
16.     item->setText(text);
17.     }
18.     QString ListWidgetItemPrototype::toString() const
19.     {
20.     return QString("ListWidgetItem(text = %0)").arg(text());
21.     }
22.     ListWidgetPrototype::ListWidgetPrototype(QObject *parent)
23.     : QObject(parent)
24.     {
25.     }
26.     void ListWidgetPrototype::addItem(const QString &text)
27.     {
28.     QListWidget *widget = qscriptvalue_cast<QListWidget*>(thisObject());
29.     if (widget)
30.     widget->addItem(text);
31.     }
32.     void ListWidgetPrototype::addItems(const QStringList &texts)
33.     {
34.     QListWidget *widget = qscriptvalue_cast<QListWidget*>(thisObject());
35.     if (widget)
36.     widget->addItems(texts);
37.     }
38.     void ListWidgetPrototype::setBackgroundColor(const QString &colorName)
39.     {
40.     QListWidget *widget = qscriptvalue_cast<QListWidget*>(thisObject());
41.     if (widget) {
42.     QPalette palette = widget->palette();
43.     QColor color(colorName);
44.     palette.setBrush(QPalette::Base, color);
45.     widget->setPalette(palette);
46.     }
47.     }


في السطرين الأول والثاني قمنا بالتصريح عن الفئتين التي قمنا بصنعهم على انها فئة معروفة في metaType .
وفي الأسطر من 5 الى 17 قمنا بتعريف دالتي القراءة والكتابة للخاصية property .
الدالة text :
وهي تستخدم للقراءة قمنا في البداية بإنشاء المتغير time التي تحفظ ماينتج عن عملية القسر من QscriptValue الى QListWidgetItem* في حال نجحت يتم إرجاع النص الذي تحتويه .
الدالة setText:
وهي تنفذ من قبل الخاصية text وهي تقوم بذات عملية القسر السابقة ولكن في حال نجحت تكتب النص داخل العنصر.
الدالة toString :
وهي من خلال السطر التالي
QString("ListWidgetItem(text = %0)").arg(text());
تقوم بانشاء شيفرة اللغة النصية للخاصية text .
الدالتين addItem و addItems :
لإضافة عناصر بعد نجاح عملية القسر والتأكد من أن thisObject يشير الى QlistWidget .
الدالة setBackgroundColor :
في حال نجاح عملية القسر والتأكد من أن thisObject يشير الى QlistWidget يتم ارسال اللون داخل متغير QPalette لتستقبله النافذة في السطر ماقبل الأخير للدالة .
الملف main.cpp :
1.      int main(int argc, char **argv)
2.      {
3.      QApplication app(argc, argv);
4.      QScriptEngine engine;
5.      ListWidgetItemPrototype lwiProto;
6.      engine.setDefaultPrototype(qMetaTypeId<QListWidgetItem*>(),
7.      engine.newQObject(&lwiProto));
8.      ListWidgetPrototype lwProto;
9.      engine.setDefaultPrototype(qMetaTypeId<QListWidget*>(),
10.     engine.newQObject(&lwProto));
11.     QListWidget listWidget;
12.     engine.globalObject().setProperty("listWidget",
13.     engine.newQObject(&listWidget));
14.     QFile file(":/code.js");
15.     file.open(QIODevice::ReadOnly);
16.     QScriptValue result = engine.evaluate(file.readAll());
17.     file.close();
18.     if (engine.hasUncaughtException()) {
19.     int lineNo = engine.uncaughtExceptionLineNumber();
20.     qWarning() << "line" << lineNo << ":" << result.toString();
21.     }
22.     return app.exec();
23.     }

في السطر الرابع قمنا بإنشاء محرك اللغة النصية ثم قمنا بوضع كائنه الإفتراضي وهي لها وسيطين رقم المعرف للفئة المعرفة في metaType والوسيط الثاني QscriptValue للكائن حيث الأنواع QListWidgetItem* و QListWidget* هي عبارة عن أنواع c++ لكل من ListWidgetItemPrototype و ListWidgetPrototype ودون هاذين السطرين لن يكون بوسعنا التحويل من QscriptValue الى قيم الأنواع المعرفة ولا التحويل التلقائي بالإتجاه العكسي ثم قمنا بقراءة شيفرة اللغة النصية كاملا من الملف code.js وفي سطر 18 في حال انه لم يتم القبض على أي استثناء يتم التعرف على رقم السطر وإرسل تحذير به رقم السطر والنتيجة .

النهاية