Започнување на програмирање со QML

Добредојдовте во светот на QML, декларативниот UI јазик. Во овој почетнички водич, ќе создадеме едноставен текст едитор користејќи QML. По читањето на ова упаство, треба да бидете подготвени да развивате сопствена апликација со употреба на QML и Qt C++.

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

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

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

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

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

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

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

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

  1. import Qt 4.7
  2.  
  3. Rectangle{
  4.      id:simplebutton
  5.      color: "grey"
  6.      width: 150
  7.      height: 80
  8.      Text{
  9.          id: buttonLabel
  10.          text: "button label"
  11.          anchors.centerIn: simplebutton;
  12.          anchors.verticalCenterOffset: -1
  13.        }
  14.  }

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

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

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

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

Simple Button

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

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

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

MouseArea има многу справувачи со сигнали кои се повикуваат ако има движења на глушецот внатре во специфираните MouserArea граници. Еден од нив е onClicked и се повикува кога копчето на глушецот е кликнато, каде левиот клик е стандарден. Можеме да врземе акции на onClicked справувачот. Во нашиот пример, console.log() испишува текст кога областа на глувчето е кликната. Функцијата console.log() е корисна алатка за дебагирање и испишување текст.

Кодот во SimpleButton.qml е доволен да прикаже копче на екранот и да испише текст кога ќе се кликне на глувчето.

  1. Rectangle {
  2.      id:Button
  3.      ...
  4.  
  5.      property color buttonColor: "lightblue"
  6.      property color onHoverColor: "gold"
  7.      property color borderColor: "white"
  8.  
  9.      signal buttonClick()
  10.      onButtonClick: {
  11.          console.log(buttonLabel.text + " clicked" )
  12.      }
  13.  
  14.      MouseArea{
  15.          onClicked: buttonClick()
  16.          hoverEnabled: true
  17.          onEntered: parent.border.color = onHoverColor
  18.          onExited:  parent.border.color = borderColor
  19.      }
  20.  
  21.      //ја одредува бојата на копчето со користење на условниот оператор
  22.      color: buttonMouseArea.pressed ? Qt.darker(buttonColor, 1.5) : buttonColor
  23.  }

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

Приспособените својства се декларираат со користење на property type name синтаксата. Во овој код, својството buttonColor, од типот color, е декларирано и врзано за вредноста “lightblue”. buttonColor подоцна се користи во условна операција за да се одреди бојата со која ќе се исполни копчето. Да се забележи дека доделување на вредност на својството е можно со користење на = еднакво симболот, а врзување на вредноста со : две точки карактерот. Приспособените својства овозможуваат интерните елементи да бидат пристапни надвор од Rectangle опсегот (scope). Постојат основни QML типови на податоци [doc.qt.nokia.com] како што се 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.

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

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

  1.  В файле FileMenu.qml:
  2.  
  3.      Row{
  4.          anchors.centerIn: parent
  5.          spacing: parent.width/6
  6.  
  7.          Button{
  8.              id: loadButton
  9.              buttonColor: "lightgrey"
  10.              label: "Load"
  11.          }
  12.          Button{
  13.              buttonColor: "grey"
  14.              id: saveButton
  15.              label: "Save"
  16.          }
  17.          Button{
  18.              id: exitButton
  19.              label: "Exit"
  20.              buttonColor: "darkgrey"
  21.  
  22.              onButtonClick: Qt.quit()
  23.          }
  24.      }

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

Декларираме два визуелни елементи во menuListModel, FileMenu и EditMenu. Ги прилагодуваме двете менија и ги прикажуваме со користење на ListView [doc.qt.nokia.com]. MenuBar.qml фајлот содржи QML декларации и едноставно мени за едитирање е дефинирано во EditMenu.qml.

  1.      VisualItemModel{
  2.          id: menuListModel
  3.          FileMenu{
  4.              width: menuListView.width
  5.              height: menuBar.height
  6.              color: fileColor
  7.          }
  8.          EditMenu{
  9.              color: editColor
  10.              width:  menuListView.width
  11.              height: menuBar.height
  12.          }
  13.      }

ListView [doc.qt.nokia.com] елементот ќе го прикаже моделот во зависност од делегатот. Делегатот може да ги декларира елементите на моделот за прикажување во Row елемент или да ги прикаже елементите во мрежа. Нашето menuListModel веќе има видливи елементи, затоа, нема потреба од декларирање на делегат.

  1.      ListView{
  2.          id: menuListView
  3.  
  4.          //сидрата се сетирани да реагираат на сидрата на прозорецот
  5.          anchors.fill:parent
  6.          anchors.bottom: parent.bottom
  7.          width:parent.width
  8.          height: parent.height
  9.  
  10.          //моделот ги содржи податоците
  11.          model: menuListModel
  12.  
  13.          //контрола на движењето на менувањето на менито
  14.          snapMode: ListView.SnapOneItem
  15.          orientation: ListView.Horizontal
  16.          boundsBehavior: Flickable.StopAtBounds
  17.          flickDeceleration: 5000
  18.          highlightFollowsCurrentItem: true
  19.          highlightMoveDuration:240
  20.          highlightRangeMode: ListView.StrictlyEnforceRange
  21.      }

Додатно, 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.

  1.      Rectangle{
  2.          id: labelList
  3.          ...
  4.          z: 1
  5.          Row{
  6.              anchors.centerIn: parent
  7.              spacing:40
  8.              Button{
  9.                  label: "File"
  10.                  id: fileButton
  11.                  ...
  12.                  onButtonClick: menuListView.currentIndex = 0
  13.              }
  14.              Button{
  15.                  id: editButton%0       ...
  16.                  onButtonClick:    menuListView.currentIndex = 1
  17.              }
  18.          }
  19.      }

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

мени лента

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

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

Нашиот текст едитор не е текст едитор ако не содржи област за едитирање на текстот. QML-овиот TextEdit [doc.qt.nokia.com] елемент овозможува декларирање на мултилиниска област за едитирање на текст. TextEdit [doc.qt.nokia.com] е различен од Text [doc.qt.nokia.com] елементот, кој не дозволува на корисникот директно да го едитира текстот.

  1. TextEdit{
  2.          id: textEditor
  3.          anchors.fill:parent
  4.          width:parent.width; height:parent.height
  5.          color:"midnightblue"
  6.          focus: true
  7.  
  8.          wrapMode: TextEdit.Wrap
  9.  
  10.          onCursorRectangleChanged: flickArea.ensureVisible(cursorRectangle)
  11.      }

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

  1. function ensureVisible(r){
  2.          if (contentX >= r.x)
  3.              contentX = r.x;
  4.          else if (contentX+width <= r.x+r.width)
  5.              contentX = r.x+r.width-width;
  6.          if (contentY >= r.y)
  7.              contentY = r.y;
  8.          else if (contentY+height <= r.y+r.height)
  9.              contentY = r.y+r.height-height;
  10.      }

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

Сега ние сме спремни да креираме распоред на нашиот текст едитор користејќи QML. Текст едиторот има две компоненти, мени лента која што ја креиравме и текстуална област. QML овозможува повторно користење на компонентите, а со тоа правејќи го нашиот код полесен, со импортирање компоненти и прилагодувајќи ги ако е потребно. Нашиот текст едитор го разделува прозорот на два дела; една третина на екранот е посветен на мени лентата, а другите две третини на екранот е текстуалната област. Мени лентата се прикажува пред другите компоненти.

  1. Rectangle{
  2.  
  3.          id: screen
  4.          width: 1000; height: 1000
  5.  
  6.          //екранот е поделен на MenuBar и TextArea. 1/3 од екранот е доделен на MenuBar
  7.          property int partition: height/3
  8.  
  9.          MenuBar{
  10.              id:menuBar
  11.              height: partition
  12.              width:parent.width
  13.              z: 1
  14.          }
  15.  
  16.          TextArea{
  17.              id:textArea
  18.              anchors.bottom:parent.bottom
  19.              y: partition
  20.              color: "white"
  21.              height: partition*2
  22.              width:parent.width
  23.          }
  24.      }

Со импортирање на повторно употребливи компоненти, кодот на нашиот TextEditor изгледа многу поедноставно. Ние потоа можеме да ја прилагодиме главната апликација, без да се секираме за својствата кои имаат дефинирани однесувања. Со користење на овој пристап, распоредите на апликацијата и UI компонентите може лесно да се креираат.

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

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

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

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

Можеме да додадеме фиока интерфејс, кој би ја собирал и ширел мени лентата кога е кликнат. Во нашата имплементација, ние имаме тенок правоаголник кој реагира на кликови на глувчето. drawer, како и апликацијата, имаат две состојби: „фиоката отворена“ состојба и „фиоката затворена“ состојба. drawer елементот е лента од правоаголникот со мала висина. Постои и вгнезден Image [doc.qt.nokia.com] елемент деклариран така да иконата со стрелка биде центрирана внатре во фиоката. Фиоката доделува состојба на целата апликација, со идентификатор screen, било кога корисникот ќе кликна на областа на глувчето.

  1. Rectangle{
  2.          id:drawer
  3.          height:15
  4.  
  5.          Image{
  6.              id: arrowIcon
  7.              source: "images/arrow.png"
  8.              anchors.horizontalCenter: parent.horizontalCenter
  9.          }
  10.  
  11.          MouseArea{
  12.              id: drawerMouseArea
  13.              anchors.fill:parent
  14.              onClicked:{
  15.                  if (screen.state == "DRAWER_CLOSED"){
  16.                      screen.state = "DRAWER_OPEN"
  17.                  }
  18.                  else if (screen.state == "DRAWER_OPEN"){
  19.                      screen.state = "DRAWER_CLOSED"
  20.                  }
  21.              }
  22.              ...
  23.          }
  24.      }

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

  1. states:[
  2.          State {
  3.              name: "DRAWER_OPEN"
  4.              PropertyChanges { target: menuBar; y: 0}
  5.              PropertyChanges { target: textArea; y: partition + drawer.height}
  6.              PropertyChanges { target: drawer; y: partition}
  7.              PropertyChanges { target: arrowIcon; rotation: 180}
  8.          },
  9.          State {
  10.              name: "DRAWER_CLOSED"
  11.              PropertyChanges { target: menuBar; y:-height; }
  12.              PropertyChanges { target: textArea; y: drawer.height; height: screen.height - drawer.height}
  13.              PropertyChanges { target: drawer; y: 0 }
  14.              PropertyChanges { target: arrowIcon; rotation: 0 }
  15.          }
  16.      ]

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

Во текот на транзициите, можеме да доделиме анимации на промените на својствата. Нашиот menuBar ја менува позицијата од y:0 во y:-partition и ние можеме да ја анимираме оваа транзиција со користење на NumberAnimation [doc.qt.nokia.com] елементот. Ние ги декларираме кои својства ќе се анимираат за одредено времетраење и со која одредена транзициона крива (easing curve). Кривата ја контролира стапката на анимација и интерполациското однесување во текот на транзицијата. Кривата што ја одбравме е Easing.OutQuint [doc.qt.nokia.com], која го успорува движењето близу крајот на анимацијата. Прочитајте ја статијата [doc.qt.nokia.com] за QML анимациите.

  1. transitions: [
  2.          Transition {
  3.              to: "*"
  4.              NumberAnimation { target: textArea; properties: "y, height"; duration: 100; easing.type:Easing.OutExpo }
  5.              NumberAnimation { target: menuBar; properties: "y"; duration: 100; easing.type: Easing.OutExpo }
  6.              NumberAnimation { target: drawer; properties: "y"; duration: 100; easing.type: Easing.OutExpo }
  7.          }
  8.      ]

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

  1. In TextEditor.qml:
  2.  
  3.      Behavior{
  4.          NumberAnimation{property: "rotation";easing.type: Easing.OutExpo }
  5.      }

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

  1. In Button.qml:
  2.      ...
  3.  
  4.      color: buttonMouseArea.pressed ? Qt.darker(buttonColor, 1.5) : buttonColor
  5.      Behavior on color { ColorAnimation{ duration: 55} }
  6.  
  7.      scale: buttonMouseArea.pressed ? 1.1 : 1.00
  8.      Behavior on scale { NumberAnimation{ duration: 55} }

Додатно, можеме да го подобриме изгледот на нашите QML компоненти со додавање колор ефекти како што се градиенти или транспаретност. Декларирањето на Gradient [doc.qt.nokia.com] елементот ќе го надреди color својството на елементот. Можете да декларирате боја во градиентот со користење на GradientStop [doc.qt.nokia.com] елементот. Градиентот е позициониран со користење на скала, од 0.0 до 1.0.

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

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

Што понатаму

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

Текст едитор

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

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

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

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

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

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

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

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

  1.  
  2. Во cppPlugins.pro:
  3.  
  4.      TEMPLATE = lib
  5.      CONFIG += qt plugin
  6.      QT += declarative
  7.  
  8.      DESTDIR +=  ../plugins
  9.      OBJECTS_DIR = tmp
  10.      MOC_DIR = tmp
  11.  
  12.      TARGET = FileDialog
  13.  
  14.      HEADERS +=     directory.h \
  15.              file.h \
  16.              dialogPlugin.h
  17.  
  18.      SOURCES +=    directory.cpp \
  19.              file.cpp \
  20.              dialogPlugin.cpp

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

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

  1.  Во dialogPlugin.h:
  2.  
  3.      #include <QtDeclarative/QDeclarativeExtensionPlugin>
  4.  
  5.      class DialogPlugin : public QDeclarativeExtensionPlugin
  6.      {
  7.          Q_OBJECT
  8.  
  9.          public:
  10.          void registerTypes(const char *uri);
  11.  
  12.      };

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

  1. DialogPlugin.cpp:
  2.  
  3.      #include "dialogPlugin.h"
  4.      #include "directory.h"
  5.      #include "file.h"
  6.      #include <QtDeclarative/qdeclarative.h>
  7.  
  8.      void DialogPlugin::registerTypes(const char *uri){
  9.  
  10.          qmlRegisterType<Directory>(uri, 1, 0, "Directory");
  11.          qmlRegisterType<File>(uri, 1, 0,"File");
  12.      }
  13.  
  14.      Q_EXPORT_PLUGIN2(FileDialog, DialogPlugin);

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

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

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

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

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

  1.      class Directory : public QObject{
  2.  
  3.          Q_OBJECT
  4.  
  5.          Q_PROPERTY(int filesCount READ filesCount CONSTANT)
  6.          Q_PROPERTY(QString filename READ filename WRITE setFilename NOTIFY filenameChanged)
  7.          Q_PROPERTY(QString fileContent READ fileContent WRITE setFileContent NOTIFY fileContentChanged)
  8.          Q_PROPERTY(QDeclarativeListProperty<File> files READ files CONSTANT )
  9.  
  10.          ...

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

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

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

  1. Q_PROPERTY(QDeclarativeListProperty<File> files READ files CONSTANT )

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

  1.      class File : public QObject{
  2.  
  3.          Q_OBJECT
  4.          Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
  5.  
  6.          ...
  7.      };

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

  1.      Directory{
  2.          id: directory
  3.  
  4.          filesCount
  5.          filename
  6.          fileContent
  7.          files
  8.  
  9.          files[0].name
  10.      }

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

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

  1.  Во Directory.h:
  2.  
  3.      Q_INVOKABLE void saveFile();
  4.      Q_INVOKABLE void loadFile();

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

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

Конструкторот на QDeclarativeListProperty [doc.qt.nokia.com] и Directory имплементацијата:

  1.      QDeclarativeListProperty  ( QObject * object, void * data, AppendFunction append, CountFunction count = 0, AtFunction at = 0, ClearFunction clear = 0 )
  2.      QDeclarativeListProperty<File>( this, &m_fileList, &appendFiles, &filesSize, &fileAt,  &clearFilesPtr );

Конструкторот ги предава поинтерите кон функции кои ќе додаваат на листата, бројат на листата, да се добие елемент со користење на индекс, и да се испразни листата. Само функцијата за додавање е мандаторна. Да се примети дека функциските поинтери мора да одговара на дефиницијата на AppendFunction [doc.qt.nokia.com], CountFunction [doc.qt.nokia.com], AtFunction [doc.qt.nokia.com], или ClearFunction [doc.qt.nokia.com].

  1.      void appendFiles(QDeclarativeListProperty<File> * property, File * file)
  2.      File* fileAt(QDeclarativeListProperty<File> * property, int index)
  3.      int filesSize(QDeclarativeListProperty<File> * property)
  4.      void clearFilesPtr(QDeclarativeListProperty<File> *property)

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

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

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

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

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

  1.  Во qmldir:
  2.  
  3.      Button ./Button.qml
  4.      FileDialog ./FileDialog.qml
  5.      TextArea ./TextArea.qml
  6.      TextEditor ./TextEditor.qml
  7.      EditMenu ./EditMenu.qml
  8.  
  9.      plugin FileDialog plugins

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

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

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

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

  1. Во FileMenu.qml:
  2.  
  3.      Directory{
  4.          id:directory
  5.          filename: textInput.text
  6.          onDirectoryChanged: fileDialog.notifyRefresh()
  7.      }

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

  1. Во FileDialog.qml:
  2.  
  3.      signal notifyRefresh()
  4.      onNotifyRefresh: dirView.model = directory.files

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

  1. В FileMenu.qml:
  2.  
  3.      Button{
  4.          id: newButton
  5.          label: "New"
  6.          onButtonClick:{
  7.              textArea.textContent = ""
  8.          }
  9.      }
  10.      Button{
  11.          id: loadButton
  12.          label: "Load"
  13.          onButtonClick:{
  14.              directory.filename = textInput.text
  15.              directory.loadFile()
  16.              textArea.textContent = directory.fileContent
  17.          }
  18.      }
  19.      Button{
  20.          id: saveButton
  21.          label: "Save"
  22.          onButtonClick:{
  23.              directory.fileContent = textArea.textContent
  24.              directory.filename = textInput.text
  25.              directory.saveFile()
  26.          }
  27.      }
  28.      Button{
  29.          id: exitButton
  30.          label: "Exit"
  31.          onButtonClick:{
  32.              Qt.quit()
  33.          }
  34.      }

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

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

File Menu

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

New File

Апликацијата може да функционира како едноставен текст едитор, може да прифати текст и да го сними текстот во фајл. Текст едиторот исто така може да прочита фајл и да извршува манипулации со текстот.