Getting Started Programming with QML/sq

From Qt Wiki
Jump to: navigation, 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.


Fillimi i programimit në QML

Mirë se vjen në botën e QML, gjuhë deklarative për krijimin e ndërfaqeve (UI). Në këtë udhërrëfyes ne do të krijojmë një program i cili shërben si editues teksti. Pasi që ta keni lexuar këtë udhërrëfyes, ju do të jeni gati të shkruani aplikacionet tuaja duke shfrytëzuar QML dhe Qt C+.


QML për të ndërtuar ndërfaqe për përdorues

Aplikacioni të cilin jemi duke e ndërtuar është një editues teksti i cili do të ngarkojë, ruaj dhe do të manipulojë tekst. Ky udhërrëfyes përmban dy pjesë. Në pjesën e parë do të bëhet konstruktimi i faqosjeve dhe sjelljeve duke shfrytëzuar gjuhën deklarative në QML. Për pjesën e dytë, ngarkimi dhe ruajtja e skedarit do të implementohet duke shfrytëzuar C+ dhe Qt. Duke shfrytëzuar sistemin e Qt për meta-objekte, ne mund ti vëmë në dukje funksionet e C++ si veti (apo në gjuhën angleze, property) të cilat elementet e QML mund ti përdorin. Duke shfrytëzuar QML dhe Qt C++ ne në mënyrë efikase mund ta ndajmë logjikën e ndërfaqes nga logjika e aplikacionit.

pamja1

Për ta ekzekutuar QML kodin, thjesht përdoreni veglën e qujtur qmlviewer me QML skedarin sikur argument. Pjesa e C++ supozon që lexuesi posedon njohuri bazike mbi procedurat e përpilimit (kompajllimit) të Qt.

Kapitujt e udhëzimit

  1. Përcaktimi i butonit dhe menysë
  2. Zbatimi i shiritit të menysë
  3. Ndërtimi i edituesit të tekstit
  4. Dekorimi i edituesit të tekstit
  5. Zgjerimi i QML duke shfrytëzuar C++ dhe Qt

Komponenta themelore – një buton

Ne fillojmë zhvillimin e edituesit të tekstit duke ndërtuar një buton. Funksionalisht, butoni ka një zonë të ndjeshme e cila mund të preket nga klikimi I miut dhe një etiketë (Ang. Label) e cila mban tekst. Butonët kryejnë veprime pasi që përdoruesi ta shtypë butonin.

Elementi vizual themelor në QML është një elementi drejtkëndësh i shkruar në gjuhën angleze si Rectangle. Elementi drejtkëndësh ka veti për të kontrolluar paraqitjen dhe pozicionin e elementit.

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

Pjesa më e lartë e quajtur import Qt 4.7 lejon veglën qmlviewer ti importoj QML elementet të cilat do ti përdorim më vonë. Kjo linjë duhet të ekzistojë në çdo QML skedar. Vëmendje sepse versioni i Qt moduleve është i përfshirë në deklaratën për import. Ky drejtkëndësh i thjeshtë ka një identifikues unik, i quajtur simplebutton, i cili është i lidhur me vetinë id. Vetitë e elementit drejtkëndësh janë të lidhura me vlera duke listuar vetinë e ndjekur nga dy pika dhe më pas vlera. Në shembull është paraqitur që ngjyra grey është i lidhur me vetinë color të drejtkëndëshit. Në mënyrë të ngjashme janë lidhur edhe vetitë tjera të drejtkëndëshit siç është gjerësia dhe lartësia (width dhe height).

Elementi Text është fushë e tekstit e cila nuk mund të ndryshohet. Ne e quajmë këtë element teksti si buttonLabel. Për ta vendosur përmbajtjen e stringut së fushës për tekst, ne lidhim vlerën me vetinë text. Etiketa është e përmbajtur brenda drejtkëndëshit dhe në mënyrë që ta vendosim atë në mes në e caktojmë anchor vetinë e Text elementit që të jetë në prindin e tij, i cili quhet simplebutton. Ankorët mund të lidhin edhe ankorë të elementeve të tjera duke bërë faqosjen më të lehtë.

Ne do ta ruajmë këtë kod si SimpleButton.qml. Ekzekutimi i qmlviewer me emrin e skedarit si argument do bëjë shfaqjen e një drejtkëndëshi me një etiketë me tekst brenda.

pamja2

Për ta zbatuar funksionalitetin e klikimit të butonit, ne mund ta përdorim trajtimin e ngjarjeve që na jep QML. Trajtimi I ngjarjeve në QML është shumë i ngjashëm me mekanizmin e sinjaleve dhe slotëve që gjindet në Qt. Sinjalet emetohet dhe slot-ët që janë të lidhur për atë sinjal thirren.

 Rectangle{
 id:simplebutton
 

MouseArea{
 id: buttonMouseArea

anchors.fill: parent //vendos të gjitha anët e zonës së miut te anët e drejtkëndëshit
 //onClicked trajton klikimet valide të butonit
 onClicked: console.log(buttonLabel.text + " clicked" )
 }
 }

Ne e përfshijmë një element të quajtur MouseArea në elementin simplebutton. Elementi MouseArea përshkruan një zonë interaktive ku zbluohen lëvizjet e miut. Për butonin tonë ne e zmadhojmë të gjithë MouseArea sa është elementi prind i tij, i cili në rastin tonë është simplebutton.

Sintaksa anchors.fill është njëra nga mënyrat për qasjen në një veti të quajtur fill e cila gjindet nën grupin e vetive anchors. QML përdor faqosjen e bazuar në ankor (spirancë) ku një element mund të jetë ankor i një elementi tjeter dhe kështu krijon faqosje të fuqishme.

MouseArea ka plot mbajtës të sinjaleve thirrja e të cilëve bëhet gjatë lëvizjeve të miut brenda kufijve të caktuar të MouseArea. Njëri nga ta është onClicked dhe thirrja e të cilit bëhet në momentin kur klikohet me butonin e pranueshëm të miut, ku fillimisht klikimi i pranueshëm është klikimi me butonin e majtë të miut, kjo mund të ndërrohet. Ne mund të lidhim veprime në mbajtësin e quajtur onClicked. Në shembullin tonë, console.log() nxjerr rezultat në formë teksti sa herë që zona e miut (MouseArea) klikohet. Funksioni console.log() është vegël e dobishme për qëllim të rregullimeve (debug-im) dhe për të nxjerrë tekst në ekran.

Kodi në SimpleButton.qml është i mjaftueshëm për shfaqjen e një butoni në ekran dhe për të nxjerrë tekst sa herë që ai të preket me miun e kompjuterit.

 Rectangle {
 id:Button
 

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

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

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

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

Butoni plotësisht funksional është në Button.qml. Disa copa të kodit në këtë artikull janë hequr dhe janë zëvendësuar me shenja që tregojn heqjen e tekstit (elips) sepse ato copëza të kodit janë paraqitur në seksione të mëparshme apo janë të parëndësishme për diskutimin aktual të kodit. Veti të ndërtuara sipas nevojes janë deklaruar me sintaksen vetija tipi emri (Ang. Property type name). Në kod vetija buttonColor e tipit color është deklaruar dhe i lidhur me vlerën "lightblue". Vetija buttonColor përdoret më vonë në operactionin kushtëzues për të vendosur ngjyrën e butonit. Vëmendje në caktimin e vlerës së vetisë sepse është i mundur përdorimi i shenjës së barazimit (=) krahas lidhjes së vlerës duke shfrytëzuar karakterin dy pika ( : ). Vetitë e ndërtuara sipas nevojës lejojnë qasjen nga jashtë të elementeve të brendshme të cilat gjinden jashtë fushës së drejtkëndëshit. Në QML ka tipe të të dhënave bazike si int, string, real si dhe tipi i të dhënave variant.

Duke lidhur trajtuesit e sinjaleve onEntered dhe onExited me ngjyrat, kufijtë e butonit do të kthehet në ngjyrë të verdhë kur miu afrohet sipër butonit dhe kthen ngjyrën kur miu largohet nga zona e miut (MouseArea).

Sinjali buttonClick() është deklaruar në Button.qml duke vendosur fjalën kyçe signal para emrit të sinjalit. Trajtuesit e të gjitha sinjaleve krijohen automatikisht, emrat e tyre fillojnë me on. Si rezultat onButtonClick është trajtuesi i buttonClick. onButtonClick atëherë i caktohet një veprim të cilin duhet ta kryej. Në shembullin tonë të butonit, onClicked trajtuesi i miut thjesht thërret onButtonClick, i cili shfaq një tekst.

onButtonClick lejon që objektet e jashtme të kenë qasje të lehtë te zona e miut të butonit. Për shembull elementet mund të kenë më shumë se një zonë të miut (MouseArea) të deklaruar dhe një buttonClick sinjal mund të bëjë më mirë dallime në mes të disa MouseArea trajtuesve.

Ne tani kemi njohuri bazike për ti zbatuar elementet në QML të cilët mund të trajtojnë lëvizje bazike të miut. Ne krijuam një etiketë teksti (Text elementi) brenda një drejtkëndëshi (Rectangle elementi) i përshtatëm vetitë dhe zbatuam sjelljet të cilat reagojnë në lëvizjet e miut. Kjo ide për të krijuar elemente brenda elementeve përsëritet gjatë gjithë aplikacionit për editim të tekstit. Ky buton nuk është i dobishëm përderisa nuk përdoret si përbërës që të kryej ndonjë veprim. Në pjesën tjetër ne do të krijojmë një meny e cila përmbanë disa prej ketyre butonëve.

pamja3

Krijimi i një faqe meny

Deri në këtë fazë kemi mbuluar si të krijojmë elemente dhe të caktojmë sjellje brenda një QML skedari të vetëm. Në këtë seksion ne do të mbulojmë se si të importojmë QML elementet dhe si ti ripërdorim disa përbërës të krijuar më heret si bazë për të ndërtuar përbërës të tjerë. Menytë shfaqin përmbajtjet e një liste, secili element ka aftësi të kryerjes së një veprimi. Në QML ne mund të krijojmë një meny në disa mënyra. Së pari ne krijojmë një meny e cila përmbanë butonë të cilat përfundimisht do të kryejnë veprime të ndryshme. Kodi për meny është në FileMenu.qml.

 import Qt 4.7 importimi i modulit kryesor të Qt QML
 import "folderName" importimi i përmbajtjes së direktoriumit
 import "script.js" as Script importimi i skedarit të JavaScript dhe emërto atë si Script

Sintaksa e shfaqur më sipër tregon si mund ta përdorni fjalën kyçe import. Kjo është e nevojshme për të përdorur skedarë të JavaScript apo QML skedarë të cilët nuk janë në direktoriumin e njëjtë. Pasi që Button.qml është në direktoriumin e njëjtë sikur FileMenu.qml ne nuk kemi nevojë të importojmë skedarin Button.qml për të përdorim atë. Ne mund drejtpërdrejt të krijojmë një Button element duke e deklaruar si Button{}, ngjashëm si deklarimi i një drejtkëndëshi, Rectangle{}.

 Në skedarin 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()
 }
 }

Në FileMenu.qml kemi deklaruar tri Button elemente. Ata janë të deklaruar brenda një Roë elementit, i cili do ti pozicionojë fëmijët e tij së bashku në një rresht vertical. Deklarimi i Button është në Button.qml i cili është I njëjtë Button.qml që përdorëm në seksionin e mëparshëm. Lidhjet e reja të vetive mund të deklarohen brenda butonëve të sapo krijuar, në mënyrë efektive i mbishkruan vetitë e dhëna në Button.qml. Butoni i quajtur exitButton pasi që të shtypet do ta përfundojë aplikacionin dhe ta mbyllë dritaren ku aplikacioni ishte i hapur. Vini re se thirrja e trajtuesit onButtonClick në Button.qml do të bëhet në vend të trajtuesit onButtonClick në exitButton.

pamja4

Elementi Row është i deklaruar në drejtkëndësh (në Rectangle element), kështu duke krijuar një konteiner për rresht të butonëve. Ky drejtkëndësh shtesë në mënyrë indirekte krijon një mënyrë për organizimin e rreshtit të butonëve brenda në meny. Deklarimi i menyja për redaktim është shumë i ngjashëm në këtë fazë. Menyja ka butonë të cilat kanë etiketat: Copy, Paste dhe Select All.

pamja5

I pajisur me njohuritë tona për importimin dhe përvetësimin e përbërësve të krijuar më parë, ne tani mund të kombinojmë këto faqe të menyve dhe të krijojmë një shirit menyje të përbërë nga butonët për të përzgjedhur menynë dhe të vështrojmë se si ne mund të strukturojmë të dhëna duke shfrytëzuar QML.

Zbatimi i një shiriti për meny

Aplikacionit tonë për editim të tekstit i duhet një mënyrë e shfaqjes së menyve duke shfrytëzuar shiritin për meny. Shiriti i menysë do të ndryshoj menytë e ndryshme dhe përdoruesi do të bën përzgjedhjen se cilën meny do ta shfaq. Ndryshimi i menyve nënkupton se menyve ju duhet më shumë strukturë se sa thjesht ti shfaq ato në një rresht. QML përdor modelet dhe shikimet (models dhe vieës) për ti strukturuar të dhënat dhe ti shfaq ato të dhëna të strukturuara.

Përdorimi i modeleve të të dhënave dhe shikimeve

QML ka disa shikime të të dhënave që shfaqin modele të të dhënave. Shiriti jonë i menysë do të shfaqë menytë në një listë me një shirit kryesues (Ang. Header) i cili shfaq rreshtin e emrave të menysë. Lista e menyve është deklaruar brenda një VisualItemModel. VisualItemModel elementi përmban elementet të cilat tashmë kanë shikime (Ang. Vieës) siç është elementi drejtkëndësh (Rectangle elementi) dhe ndërfaqet për përdorues të cilat janë të importuara. Tipeve të modeleve tjera siç është elementi ListModel, i duhet një delegues (Ang. Delegate) për ti shfaqur të dhënat e tyre. Deklarojmë dy elemente vizuele në menuListModel të quajtura FileMenu dhe EditMenu. Pastaj përshtatim dy menytë tona dhe shfaqim ato duke shfrytëzuar ListView. Skedari MenuBar.qml përmban QML deklarimet dhe një meny e thjeshtë për redaktim është e definuar në EditMenu.qml.

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

Elementi ListView do të shfaq modelin sipas deleguesit. Deleguesi mund të deklarojë modelin e elementeve për ti shfaqur ato në një Roë element apo ti shfaqë elementet në një grid. menuListModel tashmë ka elemente të dukshme prandaj ne nuk kemi nevojë të deklarojmë një delegues.

 ListView{
 id: menuListView

//Ankorët janë vendosur për të reaguar në ankorët e dritares
 anchors.fill:parent
 anchors.bottom: parent.bottom
 width:parent.width
 height: parent.height

//modeli i cili përmban të dhënat
 model: menuListModel

//kontrollo lëvizjen e ndryshimeve të menysë
 snapMode: ListView.SnapOneItem
 orientation: ListView.Horizontal
 boundsBehavior: Flickable.StopAtBounds
 flickDeceleration: 5000
 highlightFollowsCurrentItem: true
 highlightMoveDuration:240
 highlightRangeMode: ListView.StrictlyEnforceRange
 }

Si informacion shtesë, ListView trashëgohet nga Flickable elementi, e cila ndihmon që lista të reagojë në tërheqje të miut dhe gjesteve të tjera. Pjesa e fundit e kodit sipër vendos vetitë e Flickable elementit për të krijuar lëvizje të lehta në shikimin tonë. Veçanërisht, vetija highlightMoveDuration ndryshon kohëzgjatjen e tranzicionit të lëvizjes së lehtë. Vlera më e lartë në highlightMoveDuration, rezulton në ndryshim më të ngadalshëm të menysë. ListView mirëmban elementet e modelit përmes indeksit dhe çdo element vizuel në model ka qasje përmes indeksit, në QML thirrja e indeksit mundësohet nga variabla index. Ndryshimin e currentIndex në mënyrë efektive ndryshon elementin e përzgjedhur në ListVieë. Pjesa kryesore e menysë ilustron me shembull këtë efekt. Ka dy butonë në një rresht, që të dyja ndryshojnë menynë e njëjtë pasi që të klikohen. fileButton ndryshon menynë aktuale në menynë për skedar pasi që të klikohet, në këtë rast indeksi është 0 sepse FileMenu është krijuar i pari në menuListModel. Në mënyrë të ngjashme, editButton do të ndryshojë menynë aktuale në EditMenu pasi që të klikohet. Drejtkëndëshi labelList ka vlerën 1 në z variablën, kjo do të thotë që është paraqitur në pjesën e përparme të shiritit të menysë. Elementet me vlerë më të madhe të variablës z, shfaqen në pjesën e përparme të elementeve që vlerën e variablës z e kanë më të vogel. Vlera e variablës z fillimisht është 0.

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

Në shiritin e menysë që sapo krijuam, qasja në menu mund të bëhet duke rrëshqitur (Ang. Flicking) apo duke klikuar në emrat e tyre në krye. Kalimi nëpër ekranet e menysë ka ndjeshmëri intuitive dhe ka reagim të lehtë.

pamja6

Ndërtimi i edituesit të tekstit

Deklarimi i TextArea

Edituesi jonë i tekstit nuk mund të jetë një editues i tekstit nëse i mungon zona e cila përdoret për ta edituar tekstin. TextEdit elementi në QML lejon deklarimin e një zone për editim te tekstit e cila ka më shumë se një linjë. TextEdit është një formë tjetër e Text elementit, i cili nuk lejon përdoruesin që në mënyrë direkte ta ndryshojë tekstin.

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

wrapMode: TextEdit.Wrap

onCursorRectangleChanged: flickArea.ensureVisible(cursorRectangle)
 }

Edituesi ka të caktuar vetinë e ngjyrës së fontit dhe po ashtu ka të caktuar një veti për ta mbështjellur (Ang. Wrap) tekstin. Zona TextEdit gjindet brenda zonës e cila ka të aplikuar lëvizjet e lehta (Ang. Flickable) e cila ndihmon për ta rrëshqitur tekstin nëse kursori është jashtë zonës së dukshme. Funksioni ensureVisible() do të kontrollojë nëse drejtkëndëshi i kursorit është jashtë kufijve të dukshëm dhe do ta lëviz zonën e tekstit në përputhje me rrethanat. QML përdor sintaksë të Javascript për skriptat e tij, dhe siç përmendëm më heret Javascript skedarët mund po ashtu të importohen brenda skedarit të 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;
 }

Kombinimi i përbërësve për edituesin e tekstit

Tani jemi gati për ta krijuar një faqosje të edituesit tonë të tekstit duke shfrytëzuar QML. Edituesi i tekstit ka dy komponente, shiritin e menysë të cilën e krijuam më parë dhe zonën e tekstit. QML na lejon ripërdorimin e përbërësve duke i importuar dhe ndryshuar ato sipas nevojës, në këtë formë edhe kodi jonë do të jetë më i thjeshtë. Edituesi i tekstit ndan dritarën në dysh, një e treta e ekranit është i dedikuar shiritit të menysë, dhe dy të tretat tjera janë dedikuar ekranit për shfaqjen e zonës për tekst. Shiriti i menysë shfaqet përpara çdo elementi tjetër.

 Rectangle{

id: screen
 width: 1000; height: 1000

//ekrani është ndarë në MenuBar dhe TextArea. 1/3 e ekranit i është caktuar MenuBar elementit
 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
 }
 }

Kështu duke importuar përbërës të ripërdorshëm, kodi për TextEditor duket shumë më i thjeshtë. Ne mund ta përshtatim aplikacionin tonë pa u shqetësuar për vetitë të cilat tashmë kanë sjellje. Duke shfrytëzuar këtë qasje, faqosjen e aplikacioneve dhe komponentet për ndërfaqe të përdoruesve mund të krijohen shumë lehtë.

pamja7

Dekorimi i edituesit të tekstit

Zbatimi i ndërfaqes – sirtar

Edituesi jonë i tekstit duket shumë i thjeshtë dhe duhet ta dekorojmë. Duke shfrytëzuar QML ne mund të deklarojmë tranzicione dhe ta gjallërojmë aplikacionin tonë duke shfrytëzuar animacione. Shiriti i menysë është duke e zënë një të tretën e ekranit dhe do të ishte mirë ta shfaqim atë kur të na duhet. Për ta arritur atë ne do të shtojmë një ndërfaqe – sirtar e cila do ta shfaq apo ta fsheh shiritin e menysë pasi që të klikohet. Në zbatimin tonë ne kemi një drejtkëndësh të hollë e cila i përgjigjet klikimeve të miut. Elementi draëer po ashtu edhe aplikacioni jonë ka dy gjendje: gjendja kur "sirtari është i hapur" dhe gjendja kur "sirtari është i mbyllur". Elementi draëer është një drejtkëndësh me lartësi të vogël. Brenda është një Image element i ndërthurur e cila deklaron që një ikonë shigjete do të jetë e përqëndruar brenda dhe në mes të sirtarit. Sirtari cakton gjendjen e gjithë aplikacionit me identifikuesin screen sa herë që përdoruesi klikon mbi zonën e miut.

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

Gjendja është thjesht një koleksion i konfigurimeve dhe është i deklaruar në elementin e quajtur State. Lista e gjendjeve mund të listohet dhe të lidhet me vetinë states. Në aplikacionin tonë dy gjendje janë të quajtura DRAWER_CLOSED dhe DRAWER_OPEN. Konfiguracionet e elementeve janë të deklaruara në PropertyChanges elementet. Në gjendjën DRAWER_OPEN janë katër elemente të cilat pranojnë njoftimin e ndryshimit të vetive. Objektivi i parë është që elementi menuBar të ndërrojë vetinë y në vlerën 0, ngjashëm vlenë edhe për textArea i cili do të ulë pozicionin kur gjendja është DRAWER_OPEN. Elementet textArea, drawer dhe ikona e sirtarit do ti nënshtrohen njoftimeve të ndryshimeve të vetive për të takuar gjendjën aktuale.

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

Ndryshimet e gjendjeve janë të papritura dhe iu nevoiten tranzicione të lëmuara. Tranzicionet në mes të gjendjeve janë të definuara duke shfrytëzuar elementin e quajtur Transition i cili pastaj mund të lidhet në vetinë transitions të elementit. Edituesi jonë i tekstit ka tranzicion të gjendjeve sa herë që gjendja të ndryshohet cilido nga dy gjendjet tona DRAWER_OPEN apo DRAWER_CLOSED. E rëndësishme është që tranzicionit i nevojiten dy gjendje të përcaktuara nga dhe deri (from dhe to) gjendjet, që QML të na ndihmojë për rrjedhinë e tranzicioneve që do të pason dhe i duhen këto të dyja të jenë të përcaktuara, por për tranzicionet tona ne mund ta përdorim shenjën * në gjendjen to e cila paraqet që tranzicioni vlen për të gjitha ndryshimet e gjendjeve.

Elementi jonë menuBar ndërron pozicionin nga y:0 në y:-partition dhe ne mund ta animojmë këtë tranzicion duke shfrytëzuar NumerAnimation elementin. Ne e deklarojmë që vetitë e objektivave do të animohen për një kohë të caktuar dhe duke shfrytëzuar një easing curve të caktuar. Easing curve i kontrollon normat e animacionit dhe sjelljet e mbushjeve së kufijve brenda kufijve bazë (Ang. Interpolation) gjatë tranzicionit të gjendjeve. Easing curve të cilin ne e kemi zgjedhur është Easing.OutQuint i cili ngadalëson lëvizjen pranë fundit të animacionit. Ju lutemi lexojeni artikullin për animacione në 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 }
 }
 ]

Një mënyrë tjetër për të animuar ndryshime të vetive është duke deklaruar një element të quajtur Behavior. Tranzicioni punon vetëm gjatë ndryshimeve të gjendjeve kurse Behavior mund të caktojë animacion për ndryshim gjeneral të vetive. Në edituesin e tekstit, shigjeta ka një NumberAnimation element të definuar i cili bën animimin e vetisë rotation sa herë që vetija ndërron.

 Në skedarin TextEditor.qml:

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

Kthehemi prapa tek përbërësit tonë me njohuritë mbi gjendjet dhe animacionet, ne tani mund ta përmirsojmë dukjen e komponenteve. Në skedarin Button.qml ne mund të shtojmë njoftimet mbi ndryshimet e vetive color dhe scale kur butoni është klikuar. Tipet e ngjyrave animohen duke shfrytëzuar ColorAnimation dhe numrat animohen duke shfrytëzuar NumberAnimation. Sintaksa on propertyName e cila shfaqet më poshtë na ndihmon kur kemi për qëllim ta animojmë vetëm një veti.

Në skedarin 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} }

Si shtesë ne mund ta përmirsojmë dukjen e QML komponenteve tona duke shtuar efekte të ngjyrave siç janë gradientët dhe efektin për tejdukshmëri. Duke deklaruar një element të quajtur Gradient ne shkelim vetinë fillestare color të elementit dhe kështu duke deklaruar një gradient. Ju mund të deklaroni një ngjyrë brenda gradientit duke shfrytëzuar elementin GradientStop. Gradienti është i pozicionuar duke shfrytëzuar shkallë (Ang. Scale) në mes të 0.0 dhe 1.0.

Në skedarin 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" }
 }

Ky gradient përdoret nga shiriti i menysë për të simuluar thelësinë e gradientit. Ngjyra e parë e gradientit fillon në 0.0 dhe ngjyra e fundit është në 1.0.

Ku të shkojmë nga këtu

Ne kemi mbaruar ndërtimin e ndërfaqës së përdoruesit të edituesit të tekstit tonë të thjeshtë. Ndërfaqja e përdoruesit është kompletuar dhe nga tash mund të vazhdojmë me zbatimin e logjikës së aplikacionit duke shfrytëuar Qt dhe C+. QML punon shumë mirë si një vegël për të ndërtuar prototype kështu duke ndarë logjikën e aplikacionit nga dizajni i ndërfaqes së përdoruesit.

pamja8


Zgjerimi i QML duke shfrytëzuar Qt dhe C+

Tani që kemi faqosjen e edituesit të tekstit ne mund ta zbatojmë funksionalitetin e edituesit të tekstit në C+. Duke shfrytëzuar QML me C+ na mundëson që ta krijojmë logjikën e aplikacionit në Qt. Ne mund të krijojmë QML kontekst në C++ duke shfrytëzuar klasat Declarative të Qt dhe mund ti shfaqim QML elementet duke shfrytëzuar skenë grafike (Ang. Graphics Scene). Ndryshe nga kjo ne mund ta eksportojmë kodin tonë që e kemi shkruar në C++ në një shtese (Ang. Plugin) të cilin vegla e quajtur qmlvieëer mund ta lexojë. Për aplikacionin tonë ne do ti implementojmë funksionalitetet për ruajtje dhe lexim të tekstit në C++ dhe do ta eksportojmë atë si shtesë. Në këtë mënyrë ne na nevojitet vetëm ta ngarkojmë QML skedarin në vend se ta ekzekutojmë një aplikacion të tërë.

Ekspozimi i klasave të C++ në QML

Ne do ta zbatojmë ngarkimin dhe ruajtjen e skedarit duke shfrytëzuar Qt dhe C+. Klasat dhe funksionet e C+ mund të përdoren në QML pasi që ti regjistrojmë ato. Klasa po ashtu duhet të hartohet (Ang. Compiled) sikur shtesë (Ang. Plugin) në Qt dhe QML skedari duhet të di se ku ndodhet shtesa.

Për aplikacionin tonë ne na duhet të krijojmë elementet në vazhdim:

  1. Klasa Directory e cila do të trajtojë operacionet të cilat kanë të bëjnë me direktoriume.
  2. Klasa File e cila është një QObject, e cila simulon listën e skedarëve në një direktorium
  3. Klasa plugin e cila do të regjistrojë klasën në kontekstin e QML
  4. Skedari i projektit i cili do ta hartojë shtesën.
  5. Skedari qmldir i cili do ti tregon qmlviewer veglës se ku mund ta gjejë shtesën.

Ndërtimi i një shtese në Qt

Për të ndërtuar një shtesë në skedarin e projektit të Qt ne duhet të vendosim përmbajtjet në vazhdim. Së pari na duhen skedarët kryesor, skedarët e burimeve dhe modulet e Qt që ti shtojmë në skedarin e projektit. I gjithë C++ kodi dhe skedarët e projektit janë në direktoriumin e quajtur filedialog.

Në skedarin 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

Veçanërisht ne e hartojmë Qt me modulin declarative dhe e konfigurojmë atë sikur shtesë, të cilës i nevojitet lib të jetë në pjesën TEMPLATE. Ne do ta vendosim shtesën e hartuar në direktoriumin plugins.

Regjistrimi i klasës në QML

Në skedarin dialogPlugin.h:

#include <QDeclarativeExtensionPlugin>

class DialogPlugin : public QDeclarativeExtensionPlugin
 {
 Q_OBJECT

public:
 void registerTypes(const char *uri);

};

Klasa jonë e shtesës, DialogPlugin është një nënklasë e QDeclarativeExtensionPlugin klasës. Ne duhet ta implementojmë funksionin e trashëguar registerTypes(). Skedari dialogPlugin.cpp duket sikur në vazhdim:

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

Funksioni i quajtur registerTypes(), bën regjistrimin e dy klasave tona të quajtura File dhe Directory në QML. Këtij funksioni i duhet emri i klasës për shabllonin e tij, numrin madhor të verzionit, numrin e vogël të verzionit dhe emirn për klasët tona. Ne duhet ta eksportojmë shtesën duke shfrytëzuar Q_EXPORT_PLUGIN2 makro funksionin. Vini re se në skedarin dialogPlugin.h ne kemi Q_OBJECT makron në krye të klasës. Si dhe na nevojitet ekzekutimi i qmake në skedarin e projektit për të gjeneruar kodin e nevojshëm të meta-objektit që na kërkon Qt.


Krijimi i QML vetive në një C++ klasë

Ne mund të krijojmë QML elemente dhe veti duke shfrytëzuar C++ dhe sistemin e Qt për meta-objekte. Ne mund të zbatojmë veti duke shfrytëzuar mekanizmin e sinjaleve dhe slot-ëve, duke bërë Qt të vetëdijshëm për këto veti. Pastaj këto veti mund të përdoren në QML.

Për edituesin e tekstit na nevojitet mundësia për të ngarkuar dhe ruajtur skedarë. Në mënyrë tipike, këto tipare janë të përfshira në dialogun për skedar. Për fat të mirë ne mund të përdorim QDir, QFile dhe QTextStream për të zbatuar leximin e direktoriumeve dhe funksionalitetin hyrës dhe dalës.

 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 )

 


Klasa Directory përdor sistemin meta-objekt të Qt-së për ti regjistruar vetitë të cilat i duhen për të përmbushur trajtimin e skedarëve. Klasa Directory është e eksportuar si shtesë dhe është e përdorshme në QML sikur Directory element. Secila veti e listuar e cila përdor Q_PROPERTY makron është QML veti.

Q_PROPERTY deklaron një veti si dhe funksionet për lexim dhe shkrim në meta-objekt sistemin e Qt. Për shembull vetija filename e tipit QString është e lexueshme duke shfrytëzuar funksionin e quajtur filename() dhe mund të shkruajë duke shfrytëzuar funksionin e quajtur setFilename(). Përveç kësaj, ekziston një sinjal i lidhur me vetinë për emër të skedarit (filename) i cili quhet filenameChanged(), i cili emetohet sa herë që vetija ndërron. Funksionet për lexim dhe shkrim janë të deklaruara sikur funksione publike në skedarin kryesues (Ang. Header file).

Ngjashëm kemi edhe veti të tjera të deklaruara sipas përdorueshmërisë së tyre. Vetija filesCount tregon numrin e skedarëve në direktorium. Vetija filename caktohet kur kemi një skedar të zgjedhur dhe ngarkimi dhe ruajtja e përmbajtjes së skedarit ruhet në vetinë fileContent.

 Q_PROPERTY(QDeclarativeListProperty<File> files READ files CONSTANT )


Vetija files është një veti listë e cila përmban listën e të gjithë skedarët e filtruar në një direktorium. Klasa Directory është zbatuar në atë mënyrë që ti filtrojë dhe ti heq skedarët që nuk janë tekst skedarë, vetëm skedarët me prapashtesë .txt janë të vlefshëm. Më tej, QList-at mund të përdoren në QML skedarë duke deklaruar ato sikur QDeclarativeListProperty në C+. Objekti i cili është shabllon duhet të trashëgohet nga QObject. Në klasën Directory, lista e objekteve të quajtura File është e ruajtur në një QList e quajtur m_fileList.

 class File : public QObject{

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

 
 };

Dhe tani vetitë mund të përdoren në QML sikur pjesë e vetive të elementit Directory. Vini re se ne nuk kemi nevojë të krijojmë një identifikues (veti të quajtur id) në kodin tonë në C.

 Directory{
 id: directory

 filesCount
 filename
 fileContent
 files

 files[0].name
 }


Ngaqë QML përdor sintaksë dhe strukturë të Javascript ne mund të kalojmë nëpër listë të skedarëve dhe të marrim vetitë e tyre. Për të marrë vetinë e cila shfrytëzohet për emer të skedarit të parë ne mund të bëjmë thirrjen në këtë formë, files[0].name.

Funksionet e rregullta ne C+ mund po ashtu të jenë të arritshme nga QML. Funksionet për ngarkim dhe ruajtje janë të zbatuara në C++ dhe janë të deklaruara duke shfrytëzuar Q_INVOKABLE makron. Në mënyrë alternative ne mund të deklarojmë funksionet sikur slot-ë dhe funksionet mund të kenë qasje nga QML.

Në skedarin Directory.h:

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

Klasa Directory po ashtu ka për të njoftuar objektet tjera sa herë që përmbajtja e direktoriumeve të ndryshojë. Ky tipar kryhet duke shfrytëzuar një sinjal. Siç e përmendem më heret QML sinjalet kanë trajtuesin përkatës ku emrat e tyre janë të parashtruara me on. Sinjali quhet directoryChanged dhe emetohet pasi që direktoriumi të ndryshojë përmbajtjen apo të rifreskohet. Rifreskimi i direktoriumit thjesht ringarkon përmbajtjen e direktoriumit dhe freskon listen e skedarëve valid në atë direktorium. QML elementet mund të njoftohen duke bashkangjitur një veprim në onDirectoryChanged trajtuesin e sinjalit.

Vetitë e listave duhet hulumtuar më tepër. Kjo është sepse vetitë listë përdorin prapa-thirrjet (Ang. Callbacks) për të patur qasje dhe modifikoj përmbajtjen e listës. Vetija e listës është e tipit QDeclarativeListProperty<File>. Sa herë që list aka qasje, funksioni i cili i qasjet listës duhet të kthejë QDeclarativeListProperty<File>. Shabllonit File i nevojitet që të jetë derivate I QObject. Më shumë për të krijuar një QDeclarativeListProperty, aksesori dhe modifikuesi duhet kaluar në konstruktor si tregues në funksione (Ang. Function pointers). Listës, në rastin tonë është QList, po ashtu i nevojitet të jetë një listë e File pointerëve.

Konstruktori i QDeclarativeListProperty dhe konstruktori i zbatimit të 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 );

Konstruktori kalon pointerët në funksionet të cilët do të shtojnë në listë, numrojnë listën dhe do të marrin elemente nga lista duke shfrytëzuar indeksin dhe listën e zbrazët. Vetëm funksioni për shtim në listë është i detyrueshëm. Vini re se pointerët në funksione duhet të përputhen me definicionin e AppendFunction, CountFunction, AtFunction apo 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)

Për ta thjeshtuar dialogun tonë për skedar, klasa Directory filtron tekst skedarët jo valid, skedarë të cilët nuk kanë prapashtesën .txt. Nëse emri i skedarit nuk ka prapashtesën .txt atëherë ajo nuk do të shfaqet në dialogun për skedarë. Gjithashtu zbatimi i këtij funksionaliteti sigurohet që skedarët posedojnë prapashtesën .txt në emer të skedarit. Direktoriumi përdorë QTextStream klasën për të lexuar skedarin dhe për të shkruar përmbajtjen e edituesit të tekstit në skedar.

Me elementin Directory ne mund të marrim skedarë si listë, mund të dijmë se sa skedarë tekstesh ka në direktoriumin e aplikacionit, mund të marrim emrat e skedarëve dhe përmbajtjet si string dhe mund të njoftohemi sa herë që kemi ndryshime në përmbajtje te direktoriumeve.

Për ta ndërtuar shtesën, ekzekuto qmake mbi cppPlugins.pro skedarin dhe më pas ekzekuto make për ta ndërtuar shtesën dhe për ta transferuar atë në direktoriumin e quajtur plugins.

Importimi i shtesës në QML

Vegla e quajtur qmlviewer importon skedarët të cilët janë në direktoriumin e njëjtë me aplikacionin. Ne po ashtu mund të krijojmë një qmldir skedar i cili përmban lokacionet e QML skedarëve të cilët ne dëshirojmë ti importojmë. Skedari qmldir po ashtu mund të ruajë lokacione të shtesave dhe resurseve të tjera.

Në skedarin qmldir:

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

plugin FileDialog plugins

Shtesa që sapo krijuam, quhet FileDialog, dhe është treguar nga fusha TARGET në skedarin e projektit. Shtesa e hartuar është në direktoriumin plugins.

Integrimi i File Dialog në File Menu

Elementi FileMenu duhet të shfaq FileDialog elementin i cili përmban listën e tekst skedarëve në një direktorium kështu lejon përdoruesin për të zgjedhur skedarin duke klikuar mbi listë. Ne po ashtu duhet që caktojmë veprimet e butonëve për ruajtje, ngarkim dhe skedar të ri (save, load dhe new butonët). FileMenu elementi posedon një hyrje e cila ka përmbajtje të tekstit i cili mund të editohet për të lejuar përdoruesin ta shkruaj emrin e skedarit duke shfrytëzuar tastieren.

Elementi Directory është përdorë në skedarin FileMenu.qml dhe njofton FileDialog elementin që directory elementi ka rifreskuar përmbajtjen e tij. Ky njoftim kryhet nëpërmes trajtuesit të sinjalit i cili quhet onDirectoryChanged.

Në skedarin FileMenu.qml:

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

Për të mbajtur thjeshtësinë e aplikacionit tonë, dialogu për skedarë do të jetë përherë i dukshëm dhe nuk do të bëjë shfaqjen e tekst skedarëve jo valid, të cilët nuk do të kenë prapashtesën .txt.

Në skedarin FileDialog.qml:

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

Elementi i quajtur FileDialog do të bëjë shfaqjen e përmbajtjes së direktoriumit duke lexuar vetinë listë të quajtur files. Vetija e quajtur files do të përdoret si model i një GridVieë elementi i cili shfaq të dhënat e elementeve në një grid sipas një delegati të definuar. Delegati trajton dukjen e modelit dhe dialogu për skedar thjesht do të krijojë një grid me tekst të përqëndruar në mes. Klikimi në emër të skedarit do të rezultojë që në dukjen e drejtkëndëshit të theksohet emri i skedarit. Pastaj FileDialog do të njoftohet sa herë që notifyRefresh sinjali emetohet dhe kështu rezulton duke ringarkuar skedarët në atë direktorium.

Në skedarin 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()
 }
 }

Elementi FileMenu tani mund të lidhet me veprimet e tyre përkatëse. saveButton do të transferojë tekstin nga TextEdit elementi në vetinë fileContent të elementit Directory, pastaj do të bëjë kopje emrin e skedarit nga një vend për futje të tekstit. Më në fund butoni bën thirrjen e funksionit saveFile() duke rezultuar në ruajtje të skedarit. sloadButton ka kryerje të ngjashme. Gjithashtu veprimi Neë do të zbrazë përmbajtjen aktuale të TextEdit elementit.

Butonët e EditMenu elementit janë të lidhur me funksionet për kopjim, ngjitje dhe për selektim të gjithë tekstit (Ang. Copy, paste, select all) në editues të tekstit.

pamja9

Kompletimi i Edituesit të Tekstit

pamja10