Uporaba ločenih datotek in signalov

Ločevanje neusmiljene kode v različne datoteke in spajanje signalov komponentam.

Ampak zakaj?

Prvič bomo nekatere naše komponente ločili v lastne QML datoteke. Če bomo še naprej dodajali stvari na "main.qml", bo hitro postalo težko določljivo, kaj vse to počne in tvegamo blatenje naše kode.

Najprej moramo dodati naše nove datoteke v naše `resources.qrc' ki smo jih ustvarili v prvem delu tega učbenika.

<RCC>
	<qresource prefix="/">
		<file alias="main.qml">contents/ui/main.qml</file>
		<file alias="AddEditSheet.qml">contents/ui/AddEditSheet.qml</file>
		<file alias="KountdownDelegate.qml">contents/ui/KountdownDelegate.qml</file>
	</qresource>
</RCC>

Uporaba naših novih datotek

Najti moramo način uporabe naših novih datotek v "main.qml". na srečo moramo le vključiti izjavo o teh komponentah v naš 'main.qml' kot je ta:

AddEditSheet {
	id: addEditSheet
}

Razširitev lista z dodajanjem v list za dodajanje/urejanje

Če smo v zadnji vaji naredili, da je naš gumb za dodajanje odštevanja nekaj opravil, je gumb za urejanje na naših karticah odštevanja še vedno nedejaven. Ustvarili smo tudi seštevalni list, ki bi ga lahko zdaj prenamenili, da bi služil tudi kot list za urejanje … ampak preden pridemo do tega, moramo dodati nekaj reči našemu »main.qml«.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import QtQuick 2.6
import QtQuick.Controls 2.0 as Controls
import QtQuick.Layouts 1.2
import org.kde.kirigami 2.13 as Kirigami

// Base element, provides basic features needed for all kirigami applications
Kirigami.ApplicationWindow {
	// ID provides unique identifier to reference this element
	id: root
	
	// Window title
	title: i18nc("@title:window", "Day Kountdown")

	// Global drawer element with app-wide actions
	globalDrawer: Kirigami.GlobalDrawer {
		// Makes drawer a small menu rather than sliding pane
		isMenu: true
		actions: [
			Kirigami.Action {
				text: i18n("Quit")
				icon.name: "gtk-quit"
				shortcut: StandardKey.Quit
				onTriggered: Qt.quit()
			}
		]
	}
	
	// ListModel needed for ListView, contains elements to be displayed
	ListModel {
		id: kountdownModel
	}
	
	// Fetches item from addEditSheet.qml and does action on signal
	AddEditSheet { 
		id: addEditSheet
		onAdded: kountdownModel.append({
			"name": name, 
			"description": description, 
			"date": Date.parse(kdate)
		});
		onEdited: kountdownModel.set(index, {
			"name": name, 
			"description": description, 
			"date": Date.parse(kdate) 
		});
		onRemoved: kountdownModel.remove(index, 1)
	}

	// Function called by 'edit' button on card and by 'add'-Action
	function openPopulateSheet(mode, index = -1, listName = "", listDesc = "", listDate = "") {
		addEditSheet.mode = mode
		addEditSheet.index = index;
		addEditSheet.name = listName
		addEditSheet.description = listDesc
		addEditSheet.kdate = listDate

		addEditSheet.open()
	}


	// Initial page to be loaded on app load
	pageStack.initialPage: Kirigami.ScrollablePage {
		// Title for the current page, placed on the toolbar
		title: i18nc("@title", "Kountdown")

		// Kirigami.Action encapsulates a UI action. Inherits from QQC2 Action
		actions.main: Kirigami.Action {
			id: addAction
			// Name of icon associated with the action
			icon.name: "list-add"
			// Action text, i18n function returns translated string
			text: i18nc("@action:button", "Add kountdown")
			// What to do when triggering the action
			onTriggered: openPopulateSheet("add")
		}
		
		// List view for card elements
		Kirigami.CardsListView {
			id: layout
			// Model contains info to be displayed
			model: kountdownModel
			// Delegate is how the information will be presented in the ListView
			delegate: KountdownDelegate {}
		}
	}
}

Ključne spremembe, ki smo jih naredili, vključujejo dodajanje definicije naše komponente 'AddEditSheet' (in naprej spodaj "KountdownDelegate") in novo funkcijo, imenovano 'openPopulateSheet'.

Pojdimo čez definicijo »AddEditSheet«:

AddEditSheet { 
	id: addEditSheet
	onEdited: kountdownModel.set(index, {
		name, 
		description, 
		date, 
	});
	onAdded: kountdownModel.append({
		name, 
		description, 
		date, 
	});
}

'onAdded' in 'onEdited' so krmilniki signala. Tako kot se imenuje 'onTriggered', ko kliknemo dejanje, lahko uporabimo tudi krmilnike, ki se odzivajo na naše signale po meri. Če pogledamo naš novi 'AddEditSheet.qml' -- naš dodan list s spremenjenim namenom -- lahko vidimo, kako ti signali delujejo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import QtQuick 2.6
import QtQuick.Controls 2.3 as Controls
import QtQuick.Layouts 1.2
import org.kde.kirigami 2.13 as Kirigami

// Overlay sheets appear over a part of the window
Kirigami.OverlaySheet {
	id: addEditSheet
	
	// Sheet mode
	property string mode: "add"
	
	property int index: -1
	property alias name: nameField.text
	property alias description: descriptionField.text
	property alias kdate: dateField.text
	
	// Signals can be read and certain actions performed when these happen
	signal added (string name, string description, var kdate)
	signal edited(int index, string name, string description, var kdate)
	signal removed(int index)
	
	header: Kirigami.Heading {
		// i18nc is useful for adding context for translators
		text: mode === "add" ? i18nc("@title:window", "Add kountdown") : 
			i18nc("@title:window", "Edit kountdown")
	}
	// Form layouts help align and structure a layout with several inputs
	Kirigami.FormLayout {
		// Textfields let you input text in a thin textbox
		Controls.TextField {
			id: nameField
			// Provides label attached to the textfield
			Kirigami.FormData.label: i18nc("@label:textbox", "Name:")
			// Placeholder text is visible before you enter anything
			placeholderText: i18n("Event name (required)")
			// What to do after input is accepted (i.e. pressed enter)
			// In this case, it moves the focus to the next field
			onAccepted: descriptionField.forceActiveFocus()
		}
		Controls.TextField {
			id: descriptionField
			Kirigami.FormData.label: i18nc("@label:textbox", "Description:")
			placeholderText: i18n("Optional")
			onAccepted: dateField.forceActiveFocus()
		}
		Controls.TextField {
			id: dateField
			Kirigami.FormData.label: i18nc("@label:textbox", "Date:")
			inputMask: "0000-00-00"
			placeholderText: i18n("YYYY-MM-DD")
		}
		// This is a button.
		Controls.Button {
			id: deleteButton
			Layout.fillWidth: true
			text: i18nc("@action:button", "Delete")
			visible: mode === "edit"
			onClicked: {
				addEditSheet.removed(addEditSheet.index)
				close();
			}
		}
		Controls.Button {
			id: doneButton
			Layout.fillWidth: true
			text: i18nc("@action:button", "Done")
			// Button is only enabled if the user has entered something into the nameField
			enabled: nameField.text.length > 0
			onClicked: {
				// Add a listelement to the kountdownModel ListModel
				if(mode === "add") {
					addEditSheet.added(
						nameField.text, 
						descriptionField.text, 
						dateField.text
					);
				}
				else {
					addEditSheet.edited(
						index, 
						nameField.text, 
						descriptionField.text, 
						dateField.text 
					);
				}
				close();
			}
		}
	}
}

Signali prikličejo svoje rokovalce, ko so klicani. V tem primeru smo ustvarili dva signala, ki ju lahko pritkličemo z različnimi izidi in katerim lahko priložimo informacije o odštevanje, ki ga dodajamo ali ustvarjamo. Pri signalih je uporabno, da spremenljivke, opredeljene v njih, izpostavijo funkcijam, ki jih poslušajo, zato lahko te spremenljivke kličemo v naših rokovalcih »onEdited« in »onAdded« v »main.qml«. Naše signale prijkliče gumb »Končano« (»Done«) glede na to, na kaj je nastavljena lastnost »mode« na vrhu našega AddEditSheet.

Lastnost 'mode' nadzira tudi več drugih stvari: predvsem na kakšen naslov našega lista je nastavljen, in kakšno besedilo naj bi bilo vključeno v naša besedilna polja. Vendar je privzeto naša lastnost 'mode' nastavljena tako, da doda...

Kar nas pripelje nazaj do 'main.qml' in naše nove funkcije 'openPopulateSheet'. Morda ste opazili, da se to imenuje tedaj, ko se sproži dejanje dodajanja odštevanja. Ta funkcija vključuje več argumentov, ki so bili zagotovljeni s privzetimi vrednostmi. To je v pomoč, ko želimo preprosto dodati novo odštevanje, saj lahko imamo jedrnato funkcijo klic 'openPopulateSheet("add")'. Še pomembneje je, da ta funkcija določa vse ustrezne lastnosti v AddEditSheet.

function openPopulateSheet(mode, index = -1, listName = "", listDesc = "", listDate = "") {
	addEditSheet.mode = mode
	addEditSheet.index = index;
	addEditSheet.name = listName
	addEditSheet.description = listDesc
	addEditSheet.kdate = listDate
	
	addEditSheet.open()
}
  • 'mode' spremeni list z dodajanjem/urejanjem, odvisno od tega, ali je ta argument nastavljen na "add" ali na "edit"
  • argument 'index' je potreben, tako da se pri shranjevanje urejenega odštevanja spremeni pravega
  • 'listName', 'listDesc' in 'listDate' so ustrezne podrobnosti odštevanja, ki jih je treba vnesti v polja lista

Seveda, da lahko dejansko uporabite naš list za vse, moramo poleg dodajanja odštevanja najprej narediti gumb urejanja, da na naših karticah to deluje. Če pa pogledate naš 'Kirigami.CardsListView' v 'main.qml'...

Kirigami.CardsListView {
	id: layout
	model: kountdownModel
	delegate: KountdownDelegate {}
}

Smo zamenjali našo 'Kirigami.AbstractCard' z opredelitvijo delegirane komponente iz 'KountdownDelegate.qml'.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import QtQuick 2.6
import QtQuick.Controls 2.0 as Controls
import QtQuick.Layouts 1.2
import org.kde.kirigami 2.13 as Kirigami

Kirigami.AbstractCard {
	id: kountdownDelegate
	// contentItem property includes the content to be displayed on the card
	contentItem: Item {
    	// implicitWidth/Height define the natural width/height of an item if no width or height is specified
		// The setting below defines a component's preferred size based on its content
		implicitWidth: delegateLayout.implicitWidth
		implicitHeight: delegateLayout.implicitHeight
		GridLayout {
			id: delegateLayout
			// QtQuick anchoring system allows quick definition of anchor points for positioning
			anchors {
				left: parent.left
				top: parent.top
				right: parent.right
			}
			rowSpacing: Kirigami.Units.largeSpacing
			columnSpacing: Kirigami.Units.largeSpacing
			columns: root.wideScreen ? 4 : 2
				
			Kirigami.Heading {
				// Heading will be as tall as possible while respecting constraints
				Layout.fillHeight: true
				// Level determines the size of the heading
				level: 1
				text: i18n("%1 days", Math.round((date-Date.now())/86400000))
			}
			
			// Layout for positioning elements vertically
			ColumnLayout {
				Kirigami.Heading {
					Layout.fillWidth: true
					level: 2
					text: name
				}
				// Horizontal rule
				Kirigami.Separator {
					Layout.fillWidth: true
					visible: description.length > 0
				}
				// Labels contain text
				Controls.Label {
					Layout.fillWidth: true
					// Word wrap makes text stay within box and shift with size
					wrapMode: Text.WordWrap
					text: description
					visible: description.length > 0
				}
			}
			Controls.Button {
				Layout.alignment: Qt.AlignRight
				// Column spanning within grid layout (vertically in this case)
				Layout.columnSpan: 2
				text: i18n("Edit")
				onClicked: openPopulateSheet("edit", index, name, description, new Date(date).toISOString().slice(0,10))
			}
		}
	}
}

Lastnost gumba 'onClicked' 'Edit' na naših karticah zdaj kliče funkcijo 'openPopulateSheet', pri tem pa so lastnosti elementov seznama kartice nastavljene kot argumenti za to funkcijo. S temi se lahko list napolni s pravilnim besedilom.

S tem imamo popolnoma funkcionalni list, na katerem lahko dodamo in uredimo naše odštevanje!