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/mk
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 кодот со примери, доволно е да се вклучи 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 со овој фајл како аргумент ќе прикаже сив правоаголник со текст етикета.
За имплементирање на клик функционалноста на копчето, можеме да користиме 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, приспособувајќи ги нивните својства, и имплементиравме однесувања кои реагираат на движење на глувчето.
Копчето не е корисно освен ако не се користи како компонента која врши акција. Во наредната секција, ќе креираме мени кој ќе ги содржи овие копчиња.
Креирање на страница со мени
До ова ниво, ние покривме како се креираат елементи и доделуваат однесување внатре во единечен 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.
Row декларацијата е декларирана во Rectangle, креирајќи правоаголен контејнер за редица од копчиња. Овој додатен правоаголник креира индиректен начин на организирање на редица на копчиња внатре во менито.
Декларацијата на менито за едитирање е многу слична во оваа фаза. Менито има копчиња кои имаат етикети: Copy, Paste, и Select All.
Вооружани со нашето знаење за импортирање и прилагодување на претходно направени компоненти, сега можеме да ги комбинираме овие страници со менија за да креираме мени лента, која ќе е составена од копчиња за селекција на менито, и да видиме како можеме да ги структуираме податоците со користење на 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 фајлот доволно ќе му биде каде е плагинот лоциран.
За нашата апликација, потребно е да се креираат следниве елементи:
- Directory класа која ќе се справува со директориумите
- File класа која е QObject, што ќе симулира листа на фајлови во директориум
- плагин класа која ќе регистрира класа во QML контекстот
- Qt проект фајл кој ќе го компајлира плагинот
- 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 функциите за копирање, лепење и селектирање на сиот текст во текст едиторот.