Qt wiki will be updated on October 12th 2023 starting at 11:30 AM (EEST) and the maintenance will last around 2-3 hours. During the maintenance the site will be unavailable.

Getting Started Programming with QML/ar

From Qt Wiki
Jump to navigation Jump to search
This article may require cleanup to meet the Qt Wiki's quality standards. Reason: Auto-imported from ExpressionEngine.
Please improve this article if you can. Remove the {{cleanup}} tag and add this page to Updated pages list after it's clean.

الشروع في برمجة َQML:


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



QML لبناء واجهات المستخدم


التطبيق الذي نقوم ببناءه هو محرر نصوص بسيطة من شأنها أن تحميل وحفظها وأداء بعض التلاعب بالنص. هذا الدليل سوف يتكون من جزأين. وسيكون الجزء الأول يشمل تصميم تخطيط تطبيق والسلوكيات باستخدام لغة التعريفي في QML. بالنسبة للجزء الثاني ، ستنفذ تحميل الملف حفظ باستخدام كيو تي سي . باستخدام Qt's Meta-Object System ، يمكننا استخدام وظائف سي + كخصائص تستطيع عناصر QML استخدامها. استخدام QML وكيو تي سي + + ، يمكننا فصل بكفاءة منطق الواجهة عن منطق التطبيق.

no

لتشغيل الشيفرة/البرنامج المثال على ال QML ، نادرا وفر أداة qmlviewer المضمنة مع ملف QML كوسيطة. جزء سي + + من هذا البرنامج التعليمي ويفترض أن القارئ يمتلك المعرفة الأساسية للإجراءات تجميع كيو تي.

البرنامج التعليمي فصول : 1.تعريف زر وقائمة. 2.تنفيذ شريط قائمة 3.بناء محرر النص 4.تنسيق نص محرر 5.تسويع ال QML باستخدام كيو تي سي + +


تحديد زر والقائمة على



مكون أساسي — زر


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

 import Qt 4.7

Rectangle{
 id:simplebutton

color: "grey"
 width: 400; height: 400

Text {
 id: buttonLabel
 text: "button label"
 anchors.centerIn:parent
 }
 }

أولا ، import QtQuick 1.0 يسمح لأداة qmlviewer لاستيراد عناصر QML التي سوف نستخدمها في وقت لاحق. يجب أن يظهر هذا الخط في كل ملف QML. لاحظ ان فيرجن/رقم وحدات Qt مضمنة في جملة الاستيراد .

هذا المستطيل بسيط لديه معرف فريد ، simplebutton ، التي ترتبط مع معرف الخاصية. لا بد لخصائص عنصر المستطيل ان ترتبط بقيم من خلال سرد الخاصية ، متبوعا بنقطتين ، ثم القيمة. في عينة البرنامج، اللون الرمادي تم ربطه مع خاصية لون المستطيل، وبالمثل ، فإننا قد ربطنا العرض والارتفاع للمستطيل.

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

وسنعمل على حفظ هذا الرمز باسم SimpleButton.qml. وتشغيل qmlviewer مع الملف كوسيطة لعرض المستطيل الرمادي مع تسمية ل النص.

no

لتنفيذ وظائف النقر على زر ، يمكننا استخدام نظام معالجة الحدث الموجود QML. معالجة الحدث في QML مشابهة جدا لإشارة َQt وفتحة الآلية. عندما تنبعث الاشارات فإن فتحة متصلة أو دالة معينة تنفذ آليا.

Rectangle{
 id:simplebutton
 

MouseArea{
 id: buttonMouseArea

anchors.fill: parent //anchor all sides of the mouse area to the rectangle's anchors
 //onClicked handles valid mouse button clicks
 onClicked: console.log(buttonLabel.text + " clicked" )
 }
}

نحن تشمل عنصرا MouseArea في simplebutton الموجود هنا. عناصر MouseArea تصف المنطقة التفاعلية حيث يتم الكشف عن حركات الماوس. للزر الموجود لدينا، ونحن نربط/ MouseArea كله إلى الأصل ، وهو simplebutton. بناء الجملة anchors.fill هي طريقة معينة للوصول إلى خاصية معينة تسمى ملء "fill" داخل مجموعة من الخصائص ودعا المراسي "anchors". QML يستخدم مرساة تخطيطات "anchor based layouts" إلى حيث يمكن ربط العناصر إلى عنصر آخر ، لإنشاء تخطيطات قوية.

وMouseArea لديها عدة معالجات اشارات التي تنادى/تنفذ خلال حركات الماوس داخل حدود MouseArea المحددة. واحد من هذه الحركات هو onClicked وينفذ كلما تم النقر على زر الماوس بشكل مقبول ، النقرة اليسرى على الماوس وضعت لتكون النقرة الافتراضية. يمكننا ربط الإجراءات/وظائف إلى معالج onClicked. في مثالنا ، ()console.log ينتج/يخرج النص كلما تم النقر على منطقة الماوس. والدالة () console.logهو أداة مفيدة لأغراض التصحيح وإخراج للنص.

الكود الموجود في SimpleButton.qml هو كافي لعرض زر على الشاشة وإخراج نص كلما يتم النقر فوقه مع ماوس.

Rectangle {
 id:Button
 

property color buttonColor: "lightblue"
 property color onHoverColor: "gold"
 property color borderColor: "white"

signal buttonClick()
 onButtonClick: {
 console.log(buttonLabel.text + " clicked" )
 }

MouseArea{
 onClicked: buttonClick()
 hoverEnabled: true
 onEntered: parent.border.color = onHoverColor
 onExited: parent.border.color = borderColor
 }

//determines the color of the button by using the conditional operator
 color: buttonMouseArea.pressed ? Qt.darker(buttonColor, 1.5) : buttonColor
}

زر التشغيل الكامل موجود Button.qml. مقتطفات البرمجية في هذه المقالة قد تم حذف بعض الكود منها، مثلت عملية هذا الحف بواسطة اشكال إما لأنها قدمت في وقت سابق من الأقسام السابقة أو غير ذات صلة لمناقشة القانون الحالي.

يتم تعريف الخصائص المخصصة باستخدام جملة "اسم نوع الخاصية". في الكود ، الخاصية buttonColor ، من نوع لون "color"، تم تعريفها ان ربطها الى قيمة "lightblue". يستخدم buttonColor في وقت لاحق في عملية مشروطة لتحديد لون الأزرار والتعبئة. علما بأن تعيين قيمة الخاصية ممكن باستخدام يساوي = اشارة المساواة ، بالإضافة إلى ربط قيمة باستخدام ":" الخصائص المخصصة السماح للعناصر الداخلية لتكون يمكن الوصول إليها/معالجتها من خارج نطاق مستطيل. وهناك أنواع QML الأساسية مثل "int" أو العدد الطبيعي ، سلسلة "string" ، الحقيقي"real" ، فضلا عن نوع يسمى متغير "variant".

بواسطة ربط معالجات إشارة onEntered وonExited للألوان ، فإن حدود الزر سوف تتحول إلى الأصفر بدوره عند مرور الماوس فوق الزر ويعود اللون عند إنهاء الفأر منطقة الماوس.

()buttonClick تم ألاعلان عنه في Button.qml عن طريق وضع كلمة signal أمام اسم الإشارة. كل الإشارات لديها معالجاتها التي تنشأ وتخلق تلقائيا ،حيث تبدأ اسمائها ب on. ونتيجة لذلك، onButtonClick هو معالج ل buttonClick. يتم تعيين onButtonClick لإجراء لتنفيذ/دالة. في مثالنا –مثال الزر- ، فإن معالج الماوس المسمى ب onClicked سيستدعى/سينفذ onButtonClick ، الذي يعرض النص. وتمكن onButtonClick الكائنات خارج المنطقة للوصول إلى الزر الماوس بسهولة. على سبيل المثال ، العناصر قد يكون لديها أكثر من MouseArea واحدة تم الاعلان عنها وإشارة buttonClick يمكن أن تميز بين عدة معالجات لإشارة MouseArea بشكل أفضل.

لدينا الآن المعرفة الأساسية لتنفيذ البنود الواردة في QML التي يمكن التعامل مع حركات الماوس الأساسية. ولقد خلقنا تسمية النص داخل مستطيل ، وغيرنا خصائصه ، وبرمجنا/كتبنا الكود للسلوكيات التي تستجيب لحركات الماوس. إن فكرة انشاء عناصر داخل عناصر هي فكرة مكررة داخل تطبيق محرر النصوص.

هذا الزر غير مفيدة ما لم يستخدم كمكون لأداء إجراء/دالة. في الجزء التالي، سنقوم قريبا إنشاء قائمة تحتوي على العديد من هذه الأزرار.

no

إنشاء صفحة القائمة

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

القوائم تعرض محتويات قائمة ، كل عنصر/بند لديها القدرة على تنفيذ إجراء. في QML ، يمكننا إنشاء قائمة في عدة طرق. أولا ، سوف نقوم بإنشاء قائمة تحتوي على أزرار التي سوف تؤدي في نهاية المطاف إجراءات مختلفة. رمز القائمة في FileMenu.qml.

 import Qt 4.7 import the main Qt QML module
 import "folderName" import the contents of the folder
 import "script.js" as Script import a Javascript file and name it as Script

الجملة المبينة أعلاه توضح كيفية استخدام الكلمة "import". هذا مطلوب لاستخدام ملفات جافا سكريبت ، أو ملفات QML التي لا تقع ضمن نفس المجلد. لأن Button.qml في نفس المجلد ك FileMenu.qml ، نحن لسنا بحاجة لاستيراد"import" ملف Button.qml لاستخدامه. يمكننا خلق عنصر ال "زر" مباشرة باعلان Button{} ، على غرار Rectangle{} الذي يقوم بالاعلان عن المستطيل.

 In FileMenu.qml:

Row{
 anchors.centerIn: parent
 spacing: parent.width/6

Button{
 id: loadButton
 buttonColor: "lightgrey"
 label: "Load"
 }
 Button{
 buttonColor: "grey"
 id: saveButton
 label: "Save"
 }
 Button{
 id: exitButton
 label: "Exit"
 buttonColor: "darkgrey"

onButtonClick: Qt.quit()
 }
 }

في FileMenu.qml، نعلن عن ثلاثة عناصرمن نوع زر"Button". يتم الإعلان عنها داخل عنصر الصف "Row" ، والتي من شأنها تحديد مواضع عناصرها الاطفال – أي العناصر الموجودة والمحتواه داخل هذا العنصر- . الإعلان عن زر موجود في Button.qml ، وهو مشابه ل Button.qml الذي كنا قد استخدمناه في المقطع السابق. روابط جديدة للخصائص يمكن الاعلان عنها داخل الازرار التي انشئت حديثا ، وعلى نحو فعال تستنسخ مجموعة الخصائص الموجودة داخل Button.qml. الزر المسمى ب exitButton يستخدم لإنهاء وإغلاق النافذة عندما يتم النقر فوقه. علما بأن معالج اشارة onButtonClick في Button.qmlسوف يستدعى بالاضافة الى onButtonClick في exitButton.

no

إن إعلان ال صف "Row" تم عمله في مستطيل ، مما خلق وعاء مستطيل لصف الأزرار. هذا المستطيل الاضافي يخلق طريقة غير مباشرة لتنظيم صف من الأزرار داخل القائمة.

كما إعلان قائمة التحرير مشابهة جدا في هذه المرحلة. القائمة لديها أزرار التي يدورها لديها التسميات : نسخ ولصق،وتحديد الكل.

no

مسلحين بمعرفتنا باستيراد "import"وتخصيص مكونات مصنوعة من قبل ، ونحن الآن قد تجمع بين صفحات القائمة هذه لإنشاء شريط القوائم ، التي تتكون من أزرار لاختيار القائمة ، وننظر في كيفية بناء البيانات باستخدام QML.

<h1align="right"> تنفيذ وبرمجة شريط قوائم

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

استخدام النماذج البيانات الواجهات

QML لديه واجهات بيانات مختلفة التي تعرض نماذج بيانات. شريط القائمة لدينا سوف يعرض القوائم في قائمة ، مع مقدمة تعرض صف من أسماء القوائم. وأعلنت قائمة من القوائم داخل VisualItemModel. العنصر VisualItemModel يحتوي على عناصر لديها واجهات"views" مسبقا مثل عناصر ال مستطيل عناصر واجهة المستخدم المستوردة. أنواع نماذج أخرى مثل العنصر ListModel بحاجة الى مندوب لعرض البيانات الخاصة بهم.

نعلن بندين بصريين في menuListModel ، وFileMenu وEditMenu. نقوم بتخصيص القائمتين ونعرضهم باستخدام ListView. الملف MenuBar.qml يحتوي على إعلانات QML ويتم تعريف قائمة تحرير بسيطة في EditMenu.qml.

 VisualItemModel{
 id: menuListModel
 FileMenu{
 width: menuListView.width
 height: menuBar.height
 color: fileColor
 }
 EditMenu{
 color: editColor
 width: menuListView.width
 height: menuBar.height
 }
 }

سيعرض العنصر ListView نموذج وفقا لمندوب" delegate". يمكن للمندوب ان يعلن عن عناصر نموذج لتعرض في عنصر صف "Row" أو تعرض العناصر في شبكة. menuListModel الموجود لدينا لديه عناصر مرئية ، ولذلك ، نحن لسنا بحاجة الى اعلان مندوب

 ListView{
 id: menuListView

//Anchors are set to react to window anchors
 anchors.fill:parent
 anchors.bottom: parent.bottom
 width:parent.width
 height: parent.height

//the model contains the data
 model: menuListModel

//control the movement of the menu switching
 snapMode: ListView.SnapOneItem
 orientation: ListView.Horizontal
 boundsBehavior: Flickable.StopAtBounds
 flickDeceleration: 5000
 highlightFollowsCurrentItem: true
 highlightMoveDuration:240
 highlightRangeMode: ListView.StrictlyEnforceRange
 }

بالاضافة، يرث ListView من Flickable ، جاعلا من القائمة تستجيب لعملية سحب الماوس واللفتات الأخرى. الجزء الأخير من الكود أعلاه يضبط خصائص Flickable لخلق الحركة المطلوبة لواجهتنا. على وجه الخصوص ، الخاصية highlightMoveDuration تغير مدة الانتقال لل "flick". قيمة أعلى ل highlightMoveDuration سوفي تؤدي إلى تبديل ابطأ في القائمة.

تحافظ ListView على عناصر النموذج من خلال فهرس حيث يمكن معالجة اي عنصر من عناصر هذا النموذج والوصول إليها من خلال هذا الفهرس بشكل مرتب حسب وقت انشاء ذهه العناصر. تغيير ال currentIndex يغير بفعالية يغير العنصر المميز في ListView. رأس شريط القوائم لدينا يمثل هذا التأثير. هناك اثنين من الأزرار في صف واحد ، كلاهما يغير القائمة الحالية عند النقر عليهما. يغير ال fileButton القائمة الحالية إلى قائمة الملف عند النقر عليها ، الفهرس يكون قيمته 0 لأن FileMenu تم إعلان عنه في menuListModel. وبالمثل ، فإن editButton سوف تغير القائمة الحالية لEditMenu عند النقر عليها.

المستطيل labelList له قيمة "z" وهي ال 1 ، والتي تدل بأنه يتم عرضها امام شريط القائمة، العناصر ذات قيم أعلى من "z" تعرض أمام العناصر ذات قيم أدنى من "z". القيمة الافتراضية هي ل "z" هي 0.

 Rectangle{
 id: labelList
 
 z: 1
 Row{
 anchors.centerIn: parent
 spacing:40
 Button{
 label: "File"
 id: fileButton
 
 onButtonClick: menuListView.currentIndex = 0
 }
 Button{
 id: editButton
 label: "Edit"
 
 onButtonClick: menuListView.currentIndex = 1
 }
 }
 }

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

no

بناء محرر النص اعلان ناحية النص محرر النص لدينا ليس محرر النص اذا لم تحتوي على مساحة نص قابل للتحرير. عنصر ال TextEdit يسمح للإعلان منطقة نص متعدد الخطوط قابلة للتحرير. TextEdit مختلفة عن عنصر Text ، والتي لا تسمح للمستخدم لتحرير النص مباشرة.

 TextEdit{
 id: textEditor
 anchors.fill:parent
 width:parent.width; height:parent.height
 color:"midnightblue"
 focus: true

wrapMode: TextEdit.Wrap

onCursorRectangleChanged: flickArea.ensureVisible(cursorRectangle)
 }

المحرر لديه مجموعة خاصية لون الخط. منطقة TextEdit مجودة داخل منطقة flickable التي سوف تنتقل بالنص إذا كان المؤشر النص خارج منطقة مرئية. الدالة ()ensureVisible سوف تفحص ما اذا كان مستطيل المؤشر المستطيل خارج حدود واضحة وتحرك ناحية النص وفقا لذلك. QML يستخدم الجافا سكريبت ل كتابة "سكريبتاتها"، وكما ذكر سابقا ، يمكن استيراد ملفات جافا سكريبت واستخدامها ضمن ملف QML.

 function ensureVisible®{
 if (contentX >= r.x)
 contentX = r.x;
 else if (contentX+width <= r.x+r.width)
 contentX = r.x+r.width-width;
 if (contentY >= r.y)
 contentY = r.y;
 else if (contentY+height <= r.y+r.height)
 contentY = r.y+r.height-height;
 }

الجمع بين مكونات لمحرر النص

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

 Rectangle{

id: screen
 width: 1000; height: 1000

//the screen is partitioned into the MenuBar and TextArea. 1/3 of the screen is assigned to the MenuBar
 property int partition: height/3

MenuBar{
 id:menuBar
 height: partition
 width:parent.width
 z: 1
 }

TextArea{
 id:textArea
 anchors.bottom:parent.bottom
 y: partition
 color: "white"
 height: partition*2
 width:parent.width
 }
 }

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

no

تنسيق محرر النص

تنفيذ واجهة الدرج

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

ويمكننا أن نضيف واجهة الدرج ، التي من شأنها توسيع أو تقليص شريط القائمة عند النقر عليها. في برمجتنا ، لدينا مستطيل رقيق يستجيب لنقرات الماوس. ال drawer"الدرج"، فضلا عن التطبيق ،لديه حالتين: هناك حالة "درج مفتوح" وحالة"درج مغلق". درج هذا البند هو قطاع من المستطيل مع ارتفاع صغير. هناك عنصر صورة متداخلة يعلن أن هناك ايقون سهم سوف يتمركز داخل درج. الدرج يعين حالة لجميع التطبيق، مع المعرف "screen"، كلما نقر المستخدم على منطقة الماوس.

 Rectangle{
 id:drawer
 height:15

Image{
 id: arrowIcon
 source: "images/arrow.png"
 anchors.horizontalCenter: parent.horizontalCenter
 }

MouseArea{
 id: drawerMouseArea
 anchors.fill:parent
 onClicked:{
 if (screen.state  "DRAWER_CLOSED"){
                     screen.state = "DRAWER_OPEN"
                 }
                 else if (screen.state  "DRAWER_OPEN"){
 screen.state = "DRAWER_CLOSED"
 }
 }
 
 }
 }

والدولة هي مجرد مجموعة من التكوينات واعلنت انها عنصر في الدولة. ويمكن سرد قائمة من الدول ومنضمة إلى الخاصية الدول. في تطبيق لدينا ، وتسمى DRAWER_CLOSED الدولتين وDRAWER_OPEN. يتم تعريف عنصر في تكوينات العناصر PropertyChanges. في الدولة DRAWER_OPEN ، وهناك أربعة بنود من شأنها أن تلقي التغييرات الملكية. وسيكون الهدف الأول ، القوائم ، تغيير الخاصية إلى 0 ذ. وبالمثل ، فإن انخفاض ناحية النص إلى موضع جديد عندما تكون الدولة DRAWER_OPEN. سوف ناحية النص ، درج ، ورمز الساحب تغيرات الملكية لمواجهة الحالة الراهنة. الاستماع

 states:[
 State {
 name: "DRAWER_OPEN"
 PropertyChanges { target: menuBar; y: 0}
 PropertyChanges { target: textArea; y: partition + drawer.height}
 PropertyChanges { target: drawer; y: partition}
 PropertyChanges { target: arrowIcon; rotation: 180}
 },
 State {
 name: "DRAWER_CLOSED"
 PropertyChanges { target: menuBar; y:-height; }
 PropertyChanges { target: textArea; y: drawer.height; height: screen.height- drawer.height }
 PropertyChanges { target: drawer; y: 0 }
 PropertyChanges { target: arrowIcon; rotation: 0 }
 }
 ]

تغييرات مفاجئة والدولة واحتياجات سلاسة الانتقال. يتم تعريف انتقالات بين الدول باستخدام العناصر الانتقالية ، والتي يمكن ثم ربط الخاصية التحولات العنصر. جهودنا لتحرير النص والانتقال الدولة كلما التغييرات إما إلى الدولة أو DRAWER_OPEN DRAWER_CLOSED. الأهم من ذلك ، يحتاج الى الانتقال من وإلى الدولة ولكن لدينا التحولات ، يمكننا استخدام بطاقة البرية * رمز للدلالة على أن الانتقال ينطبق على جميع التغييرات الدولة.

خلال التحولات ، يمكن أن نعلق صور متحركة للتغيرات الملكية. القوائم لدينا موقف من مفاتيح ص : 0 إلى ص : - التقسيم ونحن يمكن أن يديروا هذا التحول باستخدام عنصر NumberAnimation. ونعلن أن خصائص الأهداف 'سوف تبث لمدة معينة من الزمن ، واستخدام منحنى تخفيف معينة. منحنى تخفيف الضوابط معدلات الرسوم المتحركة والسلوك الاستيفاء خلال التحولات الدولة. منحنى تخفيف اخترنا Easing.OutQuint هو الذي يؤدي إلى إبطاء حركة قرب نهاية للرسوم المتحركة. قراءة المادة Pleae QML الرسوم المتحركة.

 transitions: [
 Transition {
 to: "*"
 NumberAnimation { target: textArea; properties: "y, height"; duration: 100; easing.type:Easing.OutExpo }
 NumberAnimation { target: menuBar; properties: "y"; duration: 100; easing.type: Easing.OutExpo }
 NumberAnimation { target: drawer; properties: "y"; duration: 100; easing.type: Easing.OutExpo }
 }
 ]

طريقة أخرى لموحية التغييرات الملكية باعلان عنصر سلوك. وتمر بمرحلة انتقالية تعمل فقط خلال التغييرات حالة وسلوك يمكن أن يحدد للرسوم المتحركة لتغيير الملكية العامة. في محرر النص ، السهم لديه NumberAnimation موحية ممتلكاتها التناوب كلما التغييرات الملكية.

 In TextEditor.qml:

Behavior{
 NumberAnimation{property: "rotation";easing.type: Easing.OutExpo }
 }

بالإضافة إلى ذلك ، يمكننا تعزيز مظاهر مكونات QML لدينا عن طريق إضافة تأثيرات الألوان مثل التدرجات والآثار التعتيم. ولا شك أن إعلان عنصر التدرج تجاوز الخاصية لون العنصر. قد قمت بتعريف اللون في الانحدار باستخدام عنصر GradientStop. يتم وضع التدرج باستخدام مقياس ، بين 0،0 و 1،0.

 In Button.qml:
 

color: buttonMouseArea.pressed ? Qt.darker(buttonColor, 1.5) : buttonColor
 Behavior on color { ColorAnimation{ duration: 55} }

scale: buttonMouseArea.pressed ? 1.1 : 1.00
 Behavior on scale { NumberAnimation{ duration: 55} }

Oltre a questo, possiamo intervenire sull'aspetto dei nostri componenti QML con l'aggiunta di effetti di colore, come sfumature ed effetti di trasparenza. Dichiarando un elemento Gradient si sovrascrive la proprietà color dell'elemento. Si può quindi dichiarare il colore della sfumatura utilizzando l'elemento GradientStop. La sfumatura è valorrizata utilizzando una scala tra 0,0 e 1,0.

 In MenuBar.qml
 gradient: Gradient {
 GradientStop { position: 0.0; color: "#8C8F8C" }
 GradientStop { position: 0.17; color: "#6A6D6A" }
 GradientStop { position: 0.98;color: "#3F3F3F" }
 GradientStop { position: 1.0; color: "#0e1B20" }
 }

Questo gradiente è utilizzato dalla barra dei menu per visualizzare un gradiente che simula la profondità. Il primo colore è utilizzato al valore 0,0 e l'ultimo a 1.0.

Prossimi passi

Abbiamo così finito di costruire l'interfaccia utente di un semplice editor di testo. Andando avanti, completata l'interfaccia utente, siamo in grado di implementare la logica dell'applicazione con Qt e C+. Come abbiamo visto QML funziona bene come strumento di prototipazione, separando efficacemente la logica applicativa dalla progettazione dell'interfaccia utente.

Estendere QML usando Qt C+

Ora che abbiamo il layout del nostro editor di testo, possiamo ora implementare le funzionalità dell'editor in C+. L'utilizzo di QML con C+ permette di realizzare la logica della nostra applicazione utilizzando Qt. Siamo in grado di creare un context QML in una applicazione C++ usando le classi dichiarative di Qt e visualizzare gli elementi QML tramite una Graphic Scene. In alternativa, possiamo esportare il nostro codice C++ compilato in un plugin che il programma qmlviewer è in grado di leggere. Per la nostra applicazione, implementeremo le funzioni in C++ per caricare e salvare i file e le esporteremo come plugin. In questo modo, abbiamo solo bisogno di lanciare il file QML direttamente anziché eseguire un file eseguibile.

Esportare classi C++ per QML

Vogliamo implementare le funzioni di caricamento e salvataggio file utilizzando Qt e C+. Le classi e le funzioni C+ possono essere utilizzate in QML previa una loro registrazione. La classe ha inoltre bisogno di essere compilata come un plugin Qt e il file QML avrà bisogno di sapere dove si trova il plugin nel file system.

Per la nostra applicazione, abbiamo bisogno di creare i seguenti oggetti:

  1. una classe Directory che consenta di gestire le operazioni sulle directory (cartelle)
  2. una classe File che sia un QObject, che simuli l'elenco dei file in una directory
  3. una classe plugin che registri la classe nel corretto contesto QML
  4. un file di progetto (project file) Qt per compilare il plugin
  5. in file qmldir per indicare al programma qmlviewer dove trovare il plugin

Costruire un pugin Qt

Per costruire un plugin, è necessario impostare le direttive che seguono in project file Qt. Innanzi tutto vanno specificati i file contenenti i sorgenti e i file di include, nonché i moduli Qt utilizzati. Tutti i file sorgenti C++ e di progetto si trovano nella directory FileDialog.

 In cppPlugins.pro:

TEMPLATE = lib
 CONFIG ''= qt plugin
 QT''= declarative

DESTDIR ''= ../plugins
 OBJECTS_DIR = tmp
 MOC_DIR = tmp

 TARGET = FileDialog

 HEADERS''= directory.h  file.h  dialogPlugin.h

SOURCES += directory.cpp  file.cpp  dialogPlugin.cpp

Nello specifico, compiliamo con il modulo Qt declarative e lo configuriamo come un plugin, che necessita di un modello (template) di tipo lib. Dovremmo poi mettere il plugin compilato nella directory plugins del genitore della directory corrente.

Registrare una classe in QML

 In dialogPlugin.h:

#include <QDeclarativeExtensionPlugin>

class DialogPlugin : public QDeclarativeExtensionPlugin
 {
 Q_OBJECT

public:
 void registerTypes(const char *uri);

};

La nostra classe plugin, DialogPlugin è una sottoclasse di QDeclarativeExtensionPlugin. Questa prevede che dobbiamo implementare la funzione ereditata, registerTypes(). Il file dialogPlugin.cpp assomiglia a questo:

 DialogPlugin.cpp:

#include "dialogPlugin.h"
 #include "directory.h"
 #include "file.h"
 #include <qdeclarative.h>

void DialogPlugin::registerTypes(const char '''uri){

 qmlRegisterType<Directory>(uri, 1, 0, "Directory");
 qmlRegisterType<File>(uri, 1, 0,"File");
 }

 Q_EXPORT_PLUGIN2(FileDialog, DialogPlugin);

p. La funzione registerTypes() registra le nostre classi File e Directory in QML. Questa funzione ha bisogno dell nome della classe per il suo template, un numero di versione principale, un numero di versione minore, e un nome per le nostre classi.

p. Abbiamo poi bisogno di esportare il plugin utilizzando la macro Q_EXPORT_PLUGIN2. Si noti che nel file dialogPlugin.h, abbiamo la macro Q_OBJECT in cima alla nostra classe. Inoltre, abbiamo bisogno di eseguire qmake sul file di progetto per generare il necessario codice meta-object.

Creare proprietà QML in una classe C++

p. Possiamo ora creare elementi e proprietà QML utilizzando C++ e il Qt Meta-Object System. Siamo in grado di implementare le proprietà utilizzando gli slot e le signal, rendendole note anche a Qt. Queste proprietà possono poi essere utilizzate direttamente in QML.

p. Per l'editor di testo, dobbiamo essere in grado di caricare e salvare file. Normalmente, queste funzionalità sono contenute in una finestra di dialogo. Per fortuna, possiamo usare QDir, QFile e QTextStream per implementare la lettura di directory e gli stream di input/output.

 class Directory : public QObject{

 Q_OBJECT

 Q_PROPERTY(int filesCount READ filesCount CONSTANT)
 Q_PROPERTY(QString filename READ filename WRITE setFilename NOTIFY filenameChanged)
 Q_PROPERTY(QString fileContent READ fileContent WRITE setFileContent NOTIFY fileContentChanged)
 Q_PROPERTY(QDeclarativeListProperty<File> files READ files CONSTANT )

 

p. La classe Directory utilizza il Meta-Object System di Qt per registrare le proprietà di cui ha bisogno per realizzare la gestione dei file. La classe Directory è esportata come un plugin ed è utilizzabile in QML come l'elemento Directory. Ciascuna delle proprietà definite utilizzando la macro Q_PROPERTY è anche una proprietà QML.

p. La macro Q_PROPERTY dichiara al Meta-Object System di Qt sia la proprietà, che le sue funzioni di lettura e scrittura. Ad esempio, la proprietà filename, di tipo QString, è leggibile con la funzione filename() e scrivibile utilizzando la funzione setFilename(). Inoltre, vi è una signal associato alla proprietà filename chiamata filenameChanged(), che viene emesso ogni volta che la proprietà cambia. Le funzioni di lettura e scrittura sono dichiarate come public nel file header (.h).

p. Analogamente, abbiamo le altre proprietà dichiarate secondo il loro utilizzo. La proprietà filesCount indica il numero di file in una directory. La proprietà filename è impostata con il nome del file attualmente selezionato ed il contenuto del file salvato. o caricato, viene memorizzato nella proprietà fileContent.

 Q_PROPERTY(QDeclarativeListProperty<File> files READ files CONSTANT )

p. La lista di proprietà files è un elenco di tutti i file, filtrati, presenti in una directory. La classe Directory è implementato in modo da filtrare i file di testo non validi; solo i file con estensione .txt sono validi. Inoltre, una QList può essere utilizzata nei file QML dichiarandola come QDeclarativeListProperty in C+. L'oggetto deve ereditare da un QObject, quindi, la classe File deve anche ereditare da QObject. Nella classe Directory, l'elenco di oggetti di tipo File è memorizzato in una QList chiamata m_fileList.

 class File : public QObject{

 Q_OBJECT
 Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)

 
 };

p. Le proprietà possono quindi essere utilizzate direttamente in QML come parte integrante delle proprietà dell'elemento Directory. Si noti che non abbiamo dovuto creare un proprietà identificatore id nel nostro codice C.

 Directory{
 id: directory

 filesCount
 filename
 fileContent
 files

 files[0].name
 }

p. Poiché QML utilizza la sintassi e la struttura di Javascript, siamo in grado di iterare l'elenco dei file e recuperare le sue proprietà. Per recuperare la proprietà nome del primo file, possiamo usare

files[0].name<code>

p. Anche le normali funzioni C''+ sono accessibili da QML. Le funzioni di caricamento e di salvataggio dei file sono implementate in C++ e dichiarate utilizzando la macro [http://doc.qt.nokia.com/4.7/qobject.html#Q_INVOKABLE Q_INVOKABLE]. In alternativa, possiamo dichiarare le funzioni come uno ''slot'' e renderle in questo modo accessibili da QML.
In Directory.h:
Q_INVOKABLE void saveFile();
Q_INVOKABLE void loadFile();
p. La classe Directory deve anche notificare altri oggetti ogni volta che c'è un cambiamento nel contenuto della directory. Questa funzione viene eseguita utilizzando una ''signal''. Come accennato in precedenza, le ''signal'' QML hanno un gestore associato che ha lo stesso nome preceduto dal prefisso ''on''. La ''signal'' è denominato ''directoryChanged'' e viene emesso ogni volta che c'è un aggiornamento della directory. L'aggiornamento ricarica semplicemente il contenuto della directory e aggiorna l'elenco dei file validi della directory. Gli elementi QML possono poi essere notificati implementand il gestore di ''signal'' ''onDirectoryChanged''.

p. Le proprietà ''list'' ha bisogno di essere ulteriormente esplorata. Questo perché le proprietà di tipo ''list'' utilizzano delle funzioni di callback per accedere e modificare il contenuto dell'elenco. La proprietà ''list'' è di tipo ''QDeclarativeListProperty<File>'' . Ogni volta che si accede alla lista, la funzione di accesso (accessor) deve restituire una oggetto di tipo ''QDeclarativeListProperty<File>''. Il tipo del ''template'', ''File'', deve essere un derivato di QObject. Inoltre, per creare la [http://doc.qt.nokia.com/4.7/qdeclarativelistproperty.html QDeclarativeListProperty], le funzioni di accesso e di modifica (modifier) devono essere passati al costruttore della classe come puntatori a funzione. La lista, una ''QList'' nel nostro caso, deve anche essere una lista di puntatori a ''File''.

p.Il costruttore di [http://doc.qt.nokia.com/4.7/qdeclarativelistproperty.html QDeclarativeListProperty] e l'implementazione in ''Directory'':
QDeclarativeListProperty ( QObject object, void * data, AppendFunction append, CountFunction count = 0, AtFunction at = 0, ClearFunction clear = 0 )
QDeclarativeListProperty<File>( this, &m_fileList, &appendFiles, &filesSize, &fileAt, &clearFilesPtr );
Il costruttore passa i puntatori alle funzioni che implementano rispettivamente: aggiungi alla lista, enumera la lista, recupera l'oggetto utilizzando un indice, e svuota la lista. Solo la funzione ''append'' è obbligatoria. Si noti che i puntatori a funzione devono corrispondere alla definizione di [http://doc.qt.nokia.com/4.7/qdeclarativelistproperty.html#AppendFunction-typedef AppendFunction], [http://doc.qt.nokia.com/4.7/qdeclarativelistproperty.html#CountFunction-typedef CountFunction], [http://doc.qt.nokia.com/4.7/qdeclarativelistproperty.html#AtFunction-typedef AtFunction], e [http://doc.qt.nokia.com/4.7/qdeclarativelistproperty.html#ClearFunction-typedef ClearFunction].
void appendFiles(QDeclarativeListProperty<File> * property, File * file)
File* fileAt(QDeclarativeListProperty<File> * property, int index)
int filesSize(QDeclarativeListProperty<File> * property)
void clearFilesPtr(QDeclarativeListProperty<File> *property)
Per semplificare la nostra finestra di dialogo file, la classe ''Directory'' filtra i file di testo non validi, che sono file che non hanno l'estensione .txt. Se un nome di file non ha l'estensione .txt, allora non sarà visibile nella nostra finestra di dialogo file. Inoltre, l'applicazione assicura che i file salvati abbiano l'estensione .txt nel nome del file. La classe ''Directory'' utilizza [http://doc.qt.nokia.com/4.7/qtextstream.html QTextStream] per leggere e scrivere il contenuto del file.

Tramite il nostro elemento ''Directory'', è possibile recuperare tutti i file come una lista, sapere quanti sono i file di tipo testo nella directory dell'applicazione, ottenere il nome del file corrente e il suo contenuto come stringa, ed essere informati quando ci sono cambiamenti nel contenuto delle directory.

Per costruire il plugin, bisogna eseguire ''qmake'' con il file di progetto ''cppPlugins.pro'', quindi lanciare ''make'' fare per compilare e trasferire il plugin nella directory ''plugins''.

== Importare un plugin in QML ==

Il programma ''qmlviewer'' importa i file che si trovano nella stessa directory dell'applicazione. Possiamo anche creare un file ''qmldir'' contenente la posizione dei file QML che vogliamo importare. Il file qmldir può anche memorizzare le posizioni di plugin e altre risorse.
In qmldir:

Button ./Button.qml

FileDialog ./FileDialog.qml
TextArea ./TextArea.qml
TextEditor ./TextEditor.qml
EditMenu ./EditMenu.qml

plugin FileDialog plugins

Il plugin che abbiamo appena creato si chiama FileDialog, come indicato dal campo ''TARGET'' nel file di progetto. Il plugin è compilato e copiato nella directory ''plugins''.

== Integrare una finestra di dialogo nel menu File ==

Il nostro ''FileMenu'' ha bisogno di visualizzare l'elemento ''FileDialog'', contenente un elenco dei file di testo in una directory consentendo così all'utente di selezionare un file facendo clic sulla lista. Abbiamo anche bisogno di collegare i pulsanti per salvare, caricare e creare nuovo, alle loro rispettive azioni. Il ''FileMenu'' contiene anche un campo di input di testo per consentire all'utente di digitare un nome di file utilizzando la tastiera.

L'elemento ''Directory'' viene utilizzato nel file ''FileMenu.qml'' e avvisa l'elemento ''FileDialog'' quando la directory ha aggiornato il suo contenuto. Questa notifica viene eseguita nel gestore del segnale, ''onDirectoryChanged''.
In FileMenu.qml:

Directory{

id:directory
filename: textInput.text
onDirectoryChanged: fileDialog.notifyRefresh()
}
Allineandola con la semplicità della nostra applicazione, la finestra di dialogo file è sempre visibile e non visualizza file di testo non validi, che non hanno una estensione .txt nei loro nomi.
In FileDialog.qml:

signal notifyRefresh()

onNotifyRefresh: dirView.model = directory.file
L'elemento ''FileDialog'' visualizza il contenuto di una directory leggendo la lista di proprietà chiamata ''files''. I file vengono utilizzati come modello di un elemento [http://doc.qt.nokia.com/4.7/qml-gridview.html GridView], che visualizza gli elementi dati in una griglia su indicazione di un componente delegato. Il delegato gestisce l'aspetto del modello e la nostra finestra di dialogo file utilizza semplicemente una griglia con il testo centrato nel mezzo. Cliccando sul nome del file compare un rettangolo per evidenziare il nome del file. Il ''FileDialog'' viene notificato ogni volta che la ''signal'' ''notifyRefresh'' viene emessa, ricaricando così i file nella directory.
In FileMenu.qml:

Button{

id: newButton
label: "New"
onButtonClick:{
textArea.textContent = ""
}
}
Button{
id: loadButton
label: "Load"
onButtonClick:{
directory.filename = textInput.text
directory.loadFile()
textArea.textContent = directory.fileContent
}
}
Button{
id: saveButton
label: "Save"
onButtonClick:{
directory.fileContent = textArea.textContent
directory.filename = textInput.text
directory.saveFile()
}
}
Button{
id: exitButton
label: "Exit"
onButtonClick:{
Qt.quit()
}
}

Il nostro FileMenu può adesso essere connesso alle rispettive azioni. Il pulsante saveButton trasferirà il testo dal TextEdit alla proprietà fileContent della directory, quindi copia il nome del file nel campo di input testo. Infine, il pulsante chiama la funzione savefile(), per salvare il contenuto del file. Il pulsante loadButton ha una comportamento simile. Inoltre, l'azione New svuota il contenuto del TextEdit.

Per finire, i pulsanti EditMenu sono collegate alle funzioni di TextEdit per copiare, incollare e selezionare tutto il testo nell'editor.

qml-texteditor5_filemenu.png

L'editor di testo completo

qml-texteditor5_newfile.png