Utilisation de fichiers et de signaux séparés
Séparer le code difficile à manier dans différents fichiers et attacher des signaux à vos composants.
Mais pourquoi ?
Pour la première fois, nous allons séparer certains de nos composants dans leurs propres fichiers QML. Si nous continuons à ajouter des choses au fichier « main.qml », il va rapidement devenir difficile de dire ce qui fait quoi et nous risquons de rendre notre code illisible.
D'abord, nous devons ajouter nos nouveaux fichiers dans notre fichier « resources.qrc » que nous avons créé dans la première partie de ce tutoriel.
<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>
Utilisation de nos nouveaux fichiers
Nous devons trouver certains moyens pour utiliser nos nouveaux fichiers dans « main.qml ». Heureusement, tout ce que nous avons à faire est d'inclure une déclaration de ces composants dans notre fichier « main.qml » comme ceci :
AddEditSheet {
id: addEditSheet
}
Extension de notre feuille d'ajout en une feuille d'ajout / modification
Alors que dans le dernier tutoriel, nous avons fait en sorte que notre bouton d'ajout de compte à rebours fasse quelque chose, le bouton d'édition de nos cartes de compte à rebours est toujours inactif. Nous avons également créé une feuille d'ajout que nous pouvons maintenant utiliser aussi comme feuille d'édition... Mais avant cela, nous devons ajouter quelques éléments supplémentaires à notre fichier « main.qml ».
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
| import QtQuick 2.15
import QtQuick.Controls 2.15 as Controls
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.20 as Kirigami
Kirigami.ApplicationWindow {
id: root
title: i18nc("@title:window", "Day Kountdown")
globalDrawer: Kirigami.GlobalDrawer {
isMenu: true
actions: [
Kirigami.Action {
text: i18n("Quit")
icon.name: "gtk-quit"
shortcut: StandardKey.Quit
onTriggered: Qt.quit()
}
]
}
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 openPopulatedSheet(mode, index = -1, listName = "", listDesc = "", listDate = "") {
addEditSheet.mode = mode
addEditSheet.index = index;
addEditSheet.name = listName
addEditSheet.description = listDesc
addEditSheet.kdate = listDate
addEditSheet.open()
}
pageStack.initialPage: Kirigami.ScrollablePage {
title: i18nc("@title", "Kountdown")
// Kirigami.Action encapsulates a UI action. Inherits from Controls.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: openPopulatedSheet("add")
}
Kirigami.CardsListView {
id: layout
model: kountdownModel
delegate: KountdownDelegate {}
}
}
}
|
The key changes we have made involve the addition of our component definition AddEditSheet
(and KountdownDelegate
further down) and a new function called openPopulatedSheet()
.
Passons en revue notre définition de « AddEditSheet » :
AddEditSheet {
id: addEditSheet
onEdited: kountdownModel.set(index, {
name,
description,
date,
});
onAdded: kountdownModel.append({
name,
description,
date,
});
}
onAdded
and onEdited
are signal handlers. Just like onTriggered
is called when we click an action, we can use handlers that respond to our custom signals.
AddEditSheet.qml
Looking at our new AddEditSheet.qml
—our repurposed adding sheet—we can see how these signals work:
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.15
import QtQuick.Controls 2.15 as Controls
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.20 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();
}
}
}
}
|
Signals invoke their handlers when they are called. In this case, we have created two signals, added
and edited
, that we can invoke with different outcomes, and to which we can attach information about the countdown we are adding or creating. A neat thing about signals is that they expose the variables defined in them to the functions that are listening to them, which is why we can just call those variable names in our onEdited
and onAdded
handlers in main.qml
. Our signals are invoked by the "Done" button depending on what the mode
property, defined at the top of our AddEditSheet, is set to.
La propriété « mode » contrôle également plusieurs autres choses : principalement le titre de notre feuille et le texte devant être inclus dans nos champs de texte. Cependant, par défaut, notre propriété « mode » est juste définie pour ajouter...
Which brings us back to main.qml
and our new openPopulatedSheet()
function. You might have noticed that this is what it is called now when the countdown-adding action is triggered. This function takes in several arguments which have been provided with defaults. This is helpful when we simply want to add a new countdown, because we can have the concise function call openPopulatedSheet("add")
. More importantly, this function sets all the relevant properties in AddEditSheet.
function openPopulatedSheet(mode, index = -1, listName = "", listDesc = "", listDate = "") {
addEditSheet.mode = mode
addEditSheet.index = index;
addEditSheet.name = listName
addEditSheet.description = listDesc
addEditSheet.kdate = listDate
addEditSheet.open()
}
- Le paramètre « mode » modifie la feuille d'ajout / modification selon que cet argument est défini comme « add » ou « edit ».
index
is needed so that when we save our edited countdown, the correct one is modified- Les objets « listName », « listDesc » et « listDate » sont les détails pertinents du compte à rebours, devant être placés dans les champs de la feuille.
Of course, to actually use our sheet for anything besides adding countdowns first we need to make the edit button on our cards work. But if you look at our
Kirigami.CardsListView
in main.qml
...
Kirigami.CardsListView {
id: layout
model: kountdownModel
delegate: KountdownDelegate {}
}
KountdownDelegate.qml
We've replaced our
Kirigami.AbstractCard
with a delegate component definition from 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
| import QtQuick 2.15
import QtQuick.Controls 2.15 as Controls
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.20 as Kirigami
Kirigami.AbstractCard {
id: kountdownDelegate
contentItem: Item {
implicitWidth: delegateLayout.implicitWidth
implicitHeight: delegateLayout.implicitHeight
GridLayout {
id: delegateLayout
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 {
Layout.fillHeight: true
level: 1
text: i18n("%1 days", Math.round((date-Date.now())/86400000))
}
ColumnLayout {
Kirigami.Heading {
Layout.fillWidth: true
level: 2
text: name
}
Kirigami.Separator {
Layout.fillWidth: true
visible: description.length > 0
}
Controls.Label {
Layout.fillWidth: true
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: openPopulatedSheet("edit", index, name, description, new Date(date).toISOString().slice(0,10))
}
}
}
}
|
The
onClicked
property of the "Edit" button on our cards now calls the openPopulatedSheet()
function, with the card's fetched list element properties set as the arguments for this function. With these, the sheet can be populated with the correct text.
Avec cela, nous avons une feuille entièrement fonctionnelle où nous pouvons ajouter et modifier nos comptes à rebours !
!