Getting Started Programming with QML/mk

From Qt Wiki
Revision as of 16:12, 4 May 2015 by AutoSpider (talk | contribs) (AutoSpider moved page GettingStartedQMLMacedonian to Getting Started Programming with QML/mk: Localisation)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
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, декларативниот UI јазик. Во овој почетнички водич, ќе создадеме едноставен текст едитор користејќи QML. По читањето на ова упаство, треба да бидете подготвени да развивате сопствена апликација со употреба на QML и Qt C+.

QML за градење на кориснички интерфејси

Апликацијата што ја градиме е едноставен текст едитор кој ќе вчитува, снима, и врши некаква текст манипулација. Овој водич ќе се состои од два дела. Првиот дел ќе вклучи дизајнирање на распоредот (layout) на апликацијата и однесување со користење на QML декларативниот јазик. За вториот дел, вчитување и снимање на фајлови ќе биде имплементирано со користење на Qt C. Со користење на Мета-Објектниот Систем, можеме да ги изложиме C+ функциите како својства (properties) што QML елементите можат да ги користат. Со користењето на QML и Qt C+, ние можеме ефикасно да ја одвоиме интерфејс логиката од апликациската логика.

QML текст едитор

За да се стартува QML кодот со примери, доволно е да се вклучи qmlviewer алатката заедно со QML фајлот како аргумент. C+ делот од овој туторијал претпоставува дека читателот има основни познавања на Qt компилациските процедури.

Дефинирање на копче и мени

Базична компонента - копче

Го започнуваме нашиот текст едитор со изградба на копче (button). Функционално, копчето има област сензитивна на глувче и етикета (label). Копчињата вршат дејствија кога корисникот ќе го притисне копчето.

Во QML, основниот визуелен елемент е Rectangle елементот. Rectangle (правоаголник) елементот има својства за контрола на изгледот и локацијата на елементот.

import Qt 4.7

Rectangle{
 id:simplebutton
 color: "grey"
 width: 150
 height: 80
 Text{
 id: buttonLabel
 text: "button label"
 anchors.centerIn: simplebutton;
 anchors.verticalCenterOffset: 1
 }
 }

Прво, import Qt 4.7 овозможува qmlviewer алатката да импортира QML елементи кои покасно ќе ги користиме. Оваа линија мора да постои во секој QML фајл. Забележете дека верзијата на Qt модулите е ставена во импорт изјавата.

Овој едноставен правоаголник има единствен идентификатор, simplebutton, кој е врзан за id својството. Својствата на Rect елементот се врзуваат на вредности со листање на својството, следено од две точки, потоа вредноста. Во ова парче код, бојата grey (сива) е врзана за својството color (боја) на правоаголникот. Слично, ги врзуваме width (ширина) и height (висина) на правоаголникот.

Тext елементот е текст поле без можност за едитирање. Го крстиме овој Text елемент buttonLabel. За да го поставите стрингот во Текст полето, ние ја врзуваме вредноста на text својството. Етикетата е во рамките на правоаголникот и со цел да ја центрираме во средината, ние доделуваме сидра (anchors) на Текст елементот на неговиот родител, кој се вика simplebutton. Сидрата може да се врзуваат на сидра на други елементи, дозволувајќи поедноставно доделување на распоредот.

Треба овој код да го снимиме како SimpleButton.qml. Стартувањето на qmlviewer со овој фајл како аргумент ќе прикаже сив правоаголник со текст етикета.

Simple Button

За имплементирање на клик функционалноста на копчето, можеме да користиме QML-овото справување со настани. Справувањето со настани во QML е доста сличен на Qt-овиот механизам на сигнали и слотови. Сигналите се емитираат и поврзаниот слот е повикан.

Rectangle{
 id:simplebutton
 
 MouseArea{
 id: buttonMouseArea
 anchors.fill: parent //усидри ги сите страни на областа на глушецот на сидрата на правоаголникот
 //сигналот onClicked ги справува валидните кликови на копчињата на глушецот
 onClicked: console.log(buttonLabel.text + " clicked" )
 }
 }

Го користиме MouseArea елементот во нашиот simplebutton. MouseArea елементите опишуваат интерактивна област каде движењата на глушецот се детектираат. За нашето копче, го усидруваме целиот MouseArea на неговиот родител, кој е simplebutton. синтаксата anchors.fill е еден начин на пристапување на специфично својство наречен fill внатре во групата на својства наречени anchors (сидра). QML користи распоред базиран на сидра каде елементите можат да се усидрат со други елементи, креирајќи робустни распореди.

MouseArea има многу справувачи со сигнали кои се повикуваат ако има движења на глушецот внатре во специфираните MouserArea граници. Еден од нив е 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
 }

//ја одредува бојата на копчето со користење на условниот оператор
 color: buttonMouseArea.pressed ? Qt.darker(buttonColor, 1.5) : buttonColor
 }

Целосно функционално копче е во Button.qml. Некој код во оваа статија е испуштен, означено со три точки бидејќи тие биле претходно воведени во претходните секции или се ирелевантни за тековната дискусија.

Приспособените својства се декларираат со користење на property type name синтаксата. Во овој код, својството buttonColor, од типот color, е декларирано и врзано за вредноста "lightblue". buttonColor подоцна се користи во условна операција за да се одреди бојата со која ќе се исполни копчето. Да се забележи дека доделување на вредност на својството е можно со користење на = еднакво симболот, а врзување на вредноста со : две точки карактерот. Приспособените својства овозможуваат интерните елементи да бидат пристапни надвор од Rectangle опсегот (scope). Постојат основни QML типови на податоци како што се int, string, real како и тип наречен variant.

Врзувањето на onEntered и onExited справувачите на сигнали со боите овозможува границата на копчето да стане жолта кога глувчето лебди на копчето и ја враќа бојата кога глувчето ќе излезе од областа.

buttonClick() сигналот е деклариран во Button.qml со ставање на signal пред името на сигналот. Кај сите сигнали справувачите автоматски се креираат, нивните имиња стартуваат со on. Како резултат, onButtonClick е справувач на buttonClick. На onButtonClick потоа му се доделува акција за извршување. Во нашиот пример, onClicked справувачот едноставно ќе го повика onButtonClick, кој ќе прикаже текст. onButtonClick овозможува надворешни објекти едноставно да пристапат на Button областа на глувчето. На пример, елементите може да имаат повеќе од една MouseArea декларација и buttonClick сигналот може да направи разлика помеѓу неколку MouseArea справувачи со сигнал подобро.

Сега имаме основно познавање како да имплементираме елементи во QML кои можат да се справат со основните потези на глувчето. Креиравме Text етикета внатре во Rectangle, приспособувајќи ги нивните својства, и имплементиравме однесувања кои реагираат на движење на глувчето.

Копчето не е корисно освен ако не се користи како компонента која врши акција. Во наредната секција, ќе креираме мени кој ќе ги содржи овие копчиња.

button label

Креирање на страница со мени

До ова ниво, ние покривме како се креираат елементи и доделуваат однесување внатре во единечен QML фајл. Во оваа секција, ќе покажеме како да се импортираат QML елементи и како повторно да се искористат веќе креираните компоненти за да се изградат други компоненти.

Менито прикажува содржина на листа, секој елемент има можност да изврши одредена акција. Во QML, можеме да креираме мени на неколку начини. Прво, ќе креираме мени кое ќе содржи неколку копчиња кои евентуално би извршувале различни акции. Кодот за мени е во FileMenu.qml.

 import Qt 4.7 импортирање на главниот Qt QML модул
 import "folderName" импортирање на содржината на фолдерот
 import "script.js" as Script импортирање на Javascript фајл кој би се именувал како Script

Синтаксата прикажана горе покажува како се употребува import. Ова е потребно за користење на Javascript фајлови, или QML фајлови кои не се во истиот директориум. Бидејќи Button.qml е во ист директориум како и FileMenu.qml нема потреба да се импортира Button.qml за да се користи. Можеме директно да креираме Button елемент со декларирање на Button{}, слично на Rectangle декларацијата.

 В файле 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 декларацијата останува во Button.qml, која е иста како и Button.qml што ја користевме во претходната секција. Нови врзувања на својствата може да бидат декларирани во новите креирани копчиња, ефективно пребришувајќи ги својствата сетирани во Button.qml. Копчето наречено exitButton ќе излезе и затвори прозорот кога е кликнато. Да се забележи дека справувачот со сигнали onButtonClick во Button.qml ќе биде повикан заедно со onButtonClick справувачот во exitButton.

filemenu

Row декларацијата е декларирана во Rectangle, креирајќи правоаголен контејнер за редица од копчиња. Овој додатен правоаголник креира индиректен начин на организирање на редица на копчиња внатре во менито.

Декларацијата на менито за едитирање е многу слична во оваа фаза. Менито има копчиња кои имаат етикети: Copy, Paste, и Select All.

editmenu

Вооружани со нашето знаење за импортирање и прилагодување на претходно направени компоненти, сега можеме да ги комбинираме овие страници со менија за да креираме мени лента, која ќе е составена од копчиња за селекција на менито, и да видиме како можеме да ги структуираме податоците со користење на QML.

Имплементирање на мени лента

Нашата апликација ќе има потреба некако да ги прикаже менијата со користење на мени лента. Мени лентата ќе префрли на различни менија и корисникот ќе може да одбере кое мени да се прикаже. Менувањето на менито имплицира дека менијата имаат потреба од нешто повеќе отколку само да се прикажуваат во редица. QML користи модели и прикази (views) за да ги структуира податоците и да прикаже структуирани податоци.

Користење на податочни модели и прикази

QML има различни податочни прикази кои прикажуваат "податочни модели"http://doc.qt.nokia.com/4.7/qdeclarativemodels.html. Нашата мени лента ќе ги прикажува менијата во листа, со заглавје кое прикажува редица на имиња на менијата. Листата на менијата се декларирани внатре во VisualItemModel. VisualItemModel елементот содржи елементи кои веќе имаат свои прикази како што се Rectangle елементите и импортираните UI елементи. Други типови на модели како што е 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 елементот ќе го прикаже моделот во зависност од делегатот. Делегатот може да ги декларира елементите на моделот за прикажување во Row елемент или да ги прикаже елементите во мрежа. Нашето menuListModel веќе има видливи елементи, затоа, нема потреба од декларирање на делегат.

 ListView{
 id: menuListView

//сидрата се сетирани да реагираат на сидрата на прозорецот
 anchors.fill:parent
 anchors.bottom: parent.bottom
 width:parent.width
 height: parent.height

//моделот ги содржи податоците
 model: menuListModel

//контрола на движењето на менувањето на менито
 snapMode: ListView.SnapOneItem
 orientation: ListView.Horizontal
 boundsBehavior: Flickable.StopAtBounds
 flickDeceleration: 5000
 highlightFollowsCurrentItem: true
 highlightMoveDuration:240
 highlightRangeMode: ListView.StrictlyEnforceRange
 }

Додатно, ListView наследува од Flickable, овозможувајќи на листата да реагира на влечења на глувчето и други гестови. Последното парче на кодот погоре ги сетира Flickable својствата за да се создаде пожелно допирно (flicking) движење на нашиот приказ. Конкретно, својството highlightMoveDuration го менува времетраењето на допирната транзиција. Поголеми highlightMoveDuration вредности резултира со поспоро менување на менијата.

ListView ги одржува елементите на моделот преку index и секој визуелен елемент во моделот може да му се пристапи преку index, по редослед на декларацијата. Менувањето на currentIndex ефективно го менува нагласениот (highlighted) елемент во ListView. Заглавјето на нашата мени лента е пример на овој ефект. Постојат две копчиња во редица, двете го менуваат моментално мени кога се кликнати. fileButton го менува моменталното мени во мени за фајлови кога е кликнато, каде index станува 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%0 
 onButtonClick: menuListView.currentIndex = 1
 }
 }
 }

Мени лентата што ја креиравме може да се движи со допир за да се пристапи на менијата или со кликање на имињата на менијата. Менувањето на менијата е интуитивно и респонзивно.

мени лента

Градење на текст едитор

Декларирање на TextArea (текстуална област)

Нашиот текст едитор не е текст едитор ако не содржи област за едитирање на текстот. QML-овиот 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)
 }

Својството на боја за фонтот е сетирано, исто така сетирано е текстот да се замотува (wrap). TextEdit областа е внатре во допирната (flickable) област која ќе го скролува текстот ако курсорот на текстот е надвор од видливата област. Функцијата ensureVisible() ќе провери дали правоаголникот на курсорот е надвор од видливите граници и ќе ја помести текстуалната област соодветно. QML користи Javascript синтакса за неговите скрипти, и како што беше претходно спомнато, Javascript фајловите може да бидат импортирани и користени внатре во 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

//екранот е поделен на MenuBar и TextArea. 1/3 од екранот е доделен на 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 изгледа многу поедноставно. Ние потоа можеме да ја прилагодиме главната апликација, без да се секираме за својствата кои имаат дефинирани однесувања. Со користење на овој пристап, распоредите на апликацијата и UI компонентите може лесно да се креираат.

Едноставен едитор

Декорирање на текст едиторот

Имплементирање на фиока (drawer) интерфејс

Нашиот текст едитор изгледа едноставно и ние имаме потреба да го декорираме. Користејќи QML, можеме да декларираме транзиции и да го анимираме нашиот текст едитор. Нашата мени лента опфаќа една третина од екранот и би било фино да се појави тогаш кога ние сакаме.

Можеме да додадеме фиока интерфејс, кој би ја собирал и ширел мени лентата кога е кликнат. Во нашата имплементација, ние имаме тенок правоаголник кој реагира на кликови на глувчето. drawer, како и апликацијата, имаат две состојби: „фиоката отворена“ состојба и „фиоката затворена“ состојба. drawer елементот е лента од правоаголникот со мала висина. Постои и вгнезден Image елемент деклариран така да иконата со стрелка биде центрирана внатре во фиоката. Фиоката доделува состојба на целата апликација, со идентификатор 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"
 }
 }
 
 }
 }

Состојба (state) е едноставно колекција на конфигурации и се декларира со State елементот. Листа на состојби може да бидат листани и врзани за states својството. Во нашата апликација, две состојби се наречени DRAWER_CLOSED и DRAWER_OPEN. Конфигурациите на елементите се декларирани во PropertyChanges елементите. Во DRAWER_OPEN состојбата, постојат четири елементи кои ќе примат промени на својствата. Првиот таргет, menuBar, ќе го промени своето y својство во 0. Слично, textArea ќе оди на пониска нова позиција кога состојбата е DRAWER_OPEN. textArea, drawer, и иконата на фиоката ќе ги менат своите својства за да ја задоволат моменталната состојба.

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 }
 }
 ]

Промените на состојбите се нагли и имаат потреба од помеки транзиции. Транзициите помеѓу својствата се дефинирани со Transition елементот, кој може да е врзан за transitions својството. Нашиот текст едитор има транзиција било кога состојбата се менува од/во DRAWER_OPEN или од/во DRAWER_CLOSED. Поважно, транзицијата има потреба од from и to состојба но за нашите транзиции, можеме да користиме џокер * симбол да обележиме дека транзициите се однесуваат на сите промени на состојбите.

Во текот на транзициите, можеме да доделиме анимации на промените на својствата. Нашиот menuBar ја менува позицијата од y:0 во y:-partition и ние можеме да ја анимираме оваа транзиција со користење на NumberAnimation елементот. Ние ги декларираме кои својства ќе се анимираат за одредено времетраење и со која одредена транзициона крива (easing curve). Кривата ја контролира стапката на анимација и интерполациското однесување во текот на транзицијата. Кривата што ја одбравме е Easing.OutQuint, која го успорува движењето близу крајот на анимацијата. Прочитајте ја статијата за 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 }
 }
 ]

Друг начин за анимирање на промени на својствата е со декларирање на Behavior елементот. Транзицијата работи дури има промена на состојбата и Behavior може да ја сетира анимацијата за генерална промена на својството. Во текст едиторот, стрелката има NumberAnimation анимирајќи го rotation својството било кога својството ќе се промени.

In TextEditor.qml:

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

Кога ќе се вратиме на нашите компоненти со познавање на состојби и анимации, може да го подобриме изгледот на компонентите. Во Button.qml ќе додадеме color и scale промени на својства кога копчето е притиснато. Бојата се анимира со користење на ColorAnimation и бројките се анимираат со користење на NumberAnimation. on propertyName синтаксата подолу помага кога се таргетира единечно својство.

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} }

Додатно, можеме да го подобриме изгледот на нашите QML компоненти со додавање колор ефекти како што се градиенти или транспаретност. Декларирањето на Gradient елементот ќе го надреди color својството на елементот. Можете да декларирате боја во градиентот со користење на GradientStop елементот. Градиентот е позициониран со користење на скала, од 0.0 до 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" }
 }

Градиентот е искористен во мени лентата за да симулира длабочина. Првата боја стартува од 0.0 а последната на 1.0.

Што понатаму

Ние го завршивме градењето на кориснички интерфејс на едноставен текст едитор. Одејќи нанапред, корисничкиот интерфејс е имплементиран, и можеме да имплементираме логика на апликацијата со користење на регуларен Qt и C+. QML работи добро како прототипна алатка, одвојувајќи ја логиката на апликацијата од UI дизајнот.

Текст едитор

Проширување на QML со користење на Qt C+

Сега кога го имаме распоредот на нашиот текст едитор, ние можеме да имплементираме додатни функционалности во C+. Користење на QML со C+ ни овозможува да ја креираме апликациската логика со користење на Qt. Можеме да креираме QML контекст во C++ апликација со користење на декларативните класи на Qt и да прикажеме QML елементи користејќи графичка сцена (Graphics Scene). Алтернативно, можеме да го експортираме C++ кодот во дополнителна компонента (plugin) кој qmlviewer алатката може да ја прочита. За нашата апликација, ќе имплементираме функции на вчитување и снимање во C++ и ќе ги експортираме како плагин. На овој начин, нас ни е доволно да го вчитаме QML фајлот директно наместо да го стартуваме како апликација.

Изложување на C++ класите во QML

Ќе имплементираме вчитување и снимање на фајлови со користење на Qt и C+. C+ класите и функциите може да бидат користени во QML со нивно регистрирање. Класата е доволно да биде компајлирана како Qt плагин и на QML фајлот доволно ќе му биде каде е плагинот лоциран.

За нашата апликација, потребно е да се креираат следниве елементи:

  1. Directory класа која ќе се справува со директориумите
  2. File класа која е QObject, што ќе симулира листа на фајлови во директориум
  3. плагин класа која ќе регистрира класа во QML контекстот
  4. Qt проект фајл кој ќе го компајлира плагинот
  5. qmldir фајл кој ќе му каже на qmlviewer алатката каде да го најде плагинот

Градење на Qt плагин

За да се изгради плагинот, мораме следниве работи да ги сетираме во Qt проект фајлот. Прво, неопходните сорсови, заглавја, и Qt модули треба да се додадат во нашиот проект фајл. Сиот C++ код и проект фајлови се во filedialog директориумот.

Во 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

Конкретно, компајлираме со declarative модулот и го конфигурираме како plugin, на што му треба lib шаблонот (template). Ќе го ставиме компајлираниот плагин во родителот на plugins директориум.

Регистрирање на класа во QML

 Во dialogPlugin.h:

#include <QDeclarativeExtensionPlugin>

class DialogPlugin : public QDeclarativeExtensionPlugin
 {
 Q_OBJECT

public:
 void registerTypes(const char *uri);

};

Во нашата плагин класа, DialogPlugin e подкласа на QDeclarativeExtensionPlugin. Треба да имплементираме наследената функција, registerTypes(). dialogPlugin.cpp изгледа вака:

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);

registerTypes() функцијата ги регистрира нашите File и Directory класи во QML. На оваа функција и потребно името на класата за нејзиниот шаблон, броевите на верзијата, и името на нашите класи.

Треба да го експортираме плагинот со користење на Q_EXPORT_PLUGIN2 макрото. Да се забележи дека во нашиот dialogPlugin.h, мора да имаме Q_OBJECT макро на врвот на нашата класа. Исто така, мораме да го стартуваме qmake на нашиот проект фајл за да се генерира неопходниот мета-објектен код.

Креирање на QML својства во C++ класа

Можеме да креираме QML елементи и својства со користење на C++ и мета-објектниот систем. Можеме да имплементираме својства користејќи слотови и сигнали, правејќи Qt да е свесно за овие својства. Овие својства може да се употребат во QML.

За нашиот текст едитор, нас ни треба да можеме да вчитуваме и снимаме фајлови. Типично, вакви карактерстики се содржани во фајл дијалог. За наша среќа, можеме да ги користиме QDir, QFile и QTextStream за да имплементираме читање на директориум и влезни/излезни текови (streams).

 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 )

 

Directory класата користи мета-објектен систем за да ги регистрира потребните својства за да постигне справување со фајловите. Directory класата е експортирана како плагин и е употреблива во QML како Directory елемент. Секој од излистаните својства го користи Q_PROPERTY макрото и е истовремено QML својство.

Q_PROPERTY декларира својство, како и функциите за читање и запишување во мета-објектниот систем. На пример, filename својството, од типот QString може да се чита користејќи ја filename() функцијата и може да се запишува користејќи ја setFilename() функцијата. Додатно, постои сигнал асоциран со filename својството наречен filenameChanged(), кој се емитира кога својството ќе се промени, Функциите за читање и запишување се декларирани како public во заглавјето.

Слично, ние имаме други својства декларирани според нивната употреба. filesCount својството го покажува бројот на фајлови во директориумот. filename својството е сетирано на моменталниот селектиран фајл и вчитувањето/снимањето на содржината на фајлот е во fileContent својството.

Q_PROPERTY(QDeclarativeListProperty<File> files READ files CONSTANT )

files својството е листа на сите филтрирани фајлови во директориумот. Directory класата е имплементирана за да ги филтрира невалидните текст фајлови; фајловите со екстензија .txt се валидни. Понатаму, QList класите можат да бидат користени во QML фајловите декларирајќи ги како QDeclarativeListProperty во C+. Шаблонизираниот објект мора да наследува од QObject, затоа, File класата мора да наследува од QObject. Во Directory класата, листата на File објекти се чуваат во QList наречено m_fileList.

 class File : public QObject{

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

 
 };

Својствата потоа може да бидат користени во QML како дел од Directory својствата. Да се забележи дека ние не креиравме својство индентификатор id во нашиот C+ код.

 Directory{
 id: directory

 filesCount
 filename
 fileContent
 files

 files[0].name
 }

Бидејќи QML користи Javascript синтакса и структура, ние можеме да итерираме низ листата на фајлови и да ги превземеме нивните својства. Да се превземе својството име на првиот фајл, ние повикуваме files[0].name.

Регуларните C++ функции исто така се пристапни во QML. Функциите на вчитување и снимање на фајлови се имплементирано во C++ и декларирани користејќи го Q_INVOKABLE макрото. Алтернативно, можеме да декларираме функции како слот и тие функции ќе бидат пристапни во QML.

 Во Directory.h:

 Q_INVOKABLE void saveFile();
 Q_INVOKABLE void loadFile();

Directory класата исто така мора да ги извести другите објекти кога и да има промена содржината на директориумот. Оваа особеност се прави со користење на signal. Како што спомнавме претходно, QML сигналите имаат соодветен справувач каде нивните имиња имаат префикс on. Сигналот се нарекува directoryChanged и се емитира кога и да има освежување на директориумот. Освежувањето едноставно ја вчитува повторно содржината на директориумот и ја освежува листата на валидни фајлови во директориумот. QML елементите потоа можат да бидат известени со закачување на акцијата во onDirectoryChanged справувачот на сигнал.

list својствата треба да се истражат повеќе. Ова е потрено затоа што листа својствата користат обратни повици (callbacks) за пристап и модификација на содржината на листата. Листа својството е од типот QDeclarativeListProperty<File>. Кога ќе се пристапи на листата, акцесор функција (accessor function) треба да врати QDeclarativeListProperty<File>. Типот на шаблон, File, мора да е наследува од QObject. Понатаму, за да се креира QDeclarativeListProperty, акцесорот на листата и модификаторите треба да бидат предадени во конструкторот како функциски поинтери. Листата, QList во нашиот случај, исто така мора да е листа од File поинтери.

Конструкторот на QDeclarativeListProperty и 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 );

Конструкторот ги предава поинтерите кон функции кои ќе додаваат на листата, бројат на листата, да се добие елемент со користење на индекс, и да се испразни листата. Само функцијата за додавање е мандаторна. Да се примети дека функциските поинтери мора да одговара на дефиницијата на AppendFunction, CountFunction, AtFunction, или 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)

За да го поедноставиме нашиот фајл дијалог, Directory класата ги филтрира сите невалидни текст фајлови, фајлови кои немаат .txt екстензија. Ако фајлот нема .txt екстензија, тогаш нема да прикажан на нашиот дијалог. Исто така, имплементацијата се осигурува да снимените фајлови имаат .txt екстензија. Directory користи QTextStream за читање на фајлот и запишување на содржината во фајлот.

Со нашиот Directory елемент, можеме да ги земеме фајловите како листа, да знаеме колку текст фајлови има во директориумот, да се прочита името на фајлот и содржината како стринг, и да бидеме известени кога ќе има промени во содржината на директориумот.

За да се изгради плагинот, стартувајте го qmake на cppPlugins.pro проектниот фајл, потоа стартувајте го make за да се изгради и пренесе плагинот во plugins директориумот.

Импортирање на плагинот во QML

qmlviewer алатката импортира фајлови кои се во истиот директориум како и апликацијата. Можеме исто така да креирамe qmldir фајл кој ќе содржи локации на QML фајлови кои сакаме да се импортираат. qmldir фајлот може да зачува локации на плагини и други ресурси.

 Во qmldir:

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

plugin FileDialog plugins

Плагинот што ние го креиравме се нарекува FileDialog, како што се гледа во TARGET полето во проектниот фајл. Компајлираниот плагин е во plugins директориум.

Интегрирање на File Dialog во File Menu

Нашето FileMenu има потреба да го прикаже FileDialog елемент, кој ќе содржи листа на текст фајлови во директориумот, а со тоа, овозможувајќи на корисникот да селектира фајл со кликање на листата. Нас ни е уште потребно да доделиме save, load и new копчињата на нивните соодветни акции. FileMenu содржи влезен едитирачки текст да му овозможи на корисникот да го напише името на фајлот со користење на тастатура.

Directory елементот е искористен во FileMenu.qml фајлот и го известува FileDialog елементот дека директориумот ја освежил својата содржина. Ова известување се извршува со справувач на сигнали, onDirectoryChanged.

Во FileMenu.qml:

Directory{
 id:directory
 filename: textInput.text
 onDirectoryChanged: fileDialog.notifyRefresh()
 }

Зачувувајќи ја едноставноста на нашата апликација, дијалогот секојпат ќе биде видлив, и нема да прикажува невалидни текст фајлови, кои немаат .txt екстензија на нивните имиња.

Во FileDialog.qml:

signal notifyRefresh()
 onNotifyRefresh: dirView.model = directory.files

FileDialog елементот ќе ја прикаже содржината на директориумот со читање на листа својството наречено files. Фајловите се користат како модел во GridView елементот, кој прикажува податочни елементи во мрежа одредено од делегатот. Делегатот е одговорен за изгледот на моделот и нашиот дијалог едноставно ќе креира мрежа со текст центриран во средината. Со кликање на името на фајлот ќе резултира со појавување на правоаголник кој ќе го означи името на фајлот. FileDialog се известува кога notifyRefresh сигналот е емитиран, вчитувајќи ги повторно фајловите во директориумот.

В 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()
 }
 }

Нашето FileMenu сега може да се поврзе со соодветните акции. saveButton ќе го пренесе текстот од TextEdit во fileContent својството на директориумот, потоа ќе го копира името на фајлот во влезен едитирачки текст. Конечно, копчето ја повикува saveFile() функција, снимајќи го со тоа фајлот. loadButton се извршува слично. Исто така, New акцијата ќе ја испразни содржината на TextEdit.

Понатаму, EditMenu копчињата се конектирани со TextEdit функциите за копирање, лепење и селектирање на сиот текст во текст едиторот.

File Menu

Завршување со текст едиторот

New File