Qt Quick Démonstration - Actualités RSS
Un lecteur d'actualités RSS en QML qui utilise les types QML personnalisés XmlListModel et XmlListModelRole pour télécharger des données XML, ListModel et ListElement pour créer une liste de catégories, et ListView pour afficher les données.

RSS News présente les fonctionnalités suivantes Qt Quick :
- Utilisation de types QML personnalisés.
- Utilisation de modèles de liste et d’éléments de liste pour représenter les données.
- Utilisation de modèles de liste XML pour télécharger des données XML.
- Utilisation de vues de liste pour afficher les données.
- Utilisation du type « Component » pour créer un pied de page pour la vue de liste des actualités.
- Utilisation du type « Image » pour créer un bouton permettant de fermer l'application.
Exécution de l'exemple
Vous pouvez exécuter l'exemple à partir de :
- Qt Creator
Ouvrez le mode « Welcome » et sélectionnez l’exemple à l’adresse Examples. Pour plus d’informations, consultez Qt Creator: Tutoriel : Compiler et exécuter.
- Qt Extension for Visual Studio Code
Exécutez la commande « Qt: Open Qt examples » à partir de Command Palette, puis sélectionnez l’exemple dans la liste. Pour plus d’informations, consultez Qt Extension for Visual Studio Code: Tutoriel : Compiler et exécuter.
Utilisation des types personnalisés
Dans l’application RSS News, nous utilisons les types personnalisés suivants, chacun étant défini dans un fichier .qml distinct :
BusyIndicator.qmlCategoryDelegate.qmlNewsDelegate.qmlRssFeeds.qmlScrollBar.qml
Création de la fenêtre principale
Dans le fichier Main.qml, nous utilisons un type Rectangle doté de propriétés personnalisées pour créer la fenêtre principale de l’application :
Rectangle {
id: window
width: 800
height: 480
property string currentFeed: rssFeeds.get(0).feed
property bool loading: feedModel.status === XmlListModel.Loading
property bool isPortrait: Screen.primaryOrientation === Qt.PortraitOrientationNous utiliserons ces propriétés personnalisées ultérieurement pour charger des données XML et pour adapter la mise en page de l'écran en fonction de son orientation.
Création d’une liste de catégories
Dans ` Main.qml`, nous utilisons le type personnalisé ` RssFeeds `, que nous définissons dans ` RssFeeds.qml `, pour créer une liste de catégories de flux :
RssFeeds { id: rssFeeds }Dans ` RssFeeds.qml`, nous utilisons un type ` ListModel ` avec un type ` ListElement ` pour créer une liste de catégories où les éléments de la liste représentent les catégories de flux :
ListModel {
ListElement { name: "Top Stories"; feed: "news.yahoo.com/rss/topstories"; image: "images/TopStories.jpg" }
ListElement { name: "World"; feed: "news.yahoo.com/rss/world"; image: "images/World.jpg" }
ListElement { name: "Europe"; feed: "news.yahoo.com/rss/europe"; image: "images/Europe.jpg" }
ListElement { name: "Asia"; feed: "news.yahoo.com/rss/asia"; image: "images/Asia.jpg" }
ListElement { name: "U.S. National"; feed: "news.yahoo.com/rss/us"; image: "images/USNational.jpg" }
ListElement { name: "Politics"; feed: "news.yahoo.com/rss/politics"; image: "images/Politics.jpg" }
ListElement { name: "Business"; feed: "news.yahoo.com/rss/business"; image: "images/Business.jpg" }
ListElement { name: "Technology"; feed: "news.yahoo.com/rss/tech"; image: "images/Technology.jpg" }
ListElement { name: "Entertainment"; feed: "news.yahoo.com/rss/entertainment"; image: "images/Entertainment.jpg" }
ListElement { name: "Health"; feed: "news.yahoo.com/rss/health"; image: "images/Health.jpg" }
ListElement { name: "Science"; feed: "news.yahoo.com/rss/science"; image: "images/Science.jpg" }
ListElement { name: "Sports"; feed: "news.yahoo.com/rss/sports"; image: "images/Sports.jpg" }
}Les éléments de liste sont définis comme les autres types QML, à la différence qu’ils contiennent un ensemble de définitions de rôles au lieu de propriétés. Les rôles définissent à la fois la manière dont les données sont accessibles et incluent les données elles-mêmes.
Pour chaque élément de la liste, nous utilisons le rôle « name » pour spécifier le nom de la catégorie, le rôle « feed » pour spécifier l’URL à partir de laquelle charger les données, et le rôle « image » pour afficher une image représentant la catégorie.
Dans ` Main.qml`, nous utilisons un type ` ListView ` pour afficher la liste des catégories :
ListView {
id: categories
property int itemWidth: 190
width: window.isPortrait ? parent.width : itemWidth
height: window.isPortrait ? itemWidth : parent.height
orientation: window.isPortrait ? ListView.Horizontal : ListView.Vertical
anchors.top: parent.top
model: rssFeeds
delegate: CategoryDelegate {
itemSize: categories.itemWidth
isLoading: window.loading
onClicked: function () {
if (window.currentFeed == feed)
feedModel.reload()
else
window.currentFeed = feed
}
}Pour disposer la liste des catégories horizontalement en haut de la fenêtre en mode portrait et verticalement à gauche en mode paysage, nous utilisons la propriété orientation. En fonction de l’orientation, nous lions soit la largeur, soit la hauteur de la liste à une valeur fixe (itemWidth).
Nous utilisons la propriété ` anchors.top ` pour positionner la vue de liste en haut de l’écran, quelle que soit l’orientation.
Nous utilisons la propriété ` model ` pour charger les données XML à partir du modèle ` rssFeeds `, et ` CategoryDelegate ` comme délégué pour instancier chaque élément de la liste.
Création d’éléments de liste
Dans ` CategoryDelegate.qml`, nous utilisons le type ` Rectangle ` avec des propriétés personnalisées pour créer des éléments de liste :
Rectangle {
id: delegate
required property real itemSize
required property string name
required property string feed
required property string image
required property int index
required property bool isLoading
property bool selected: ListView.isCurrentItemNous utilisons ` required ` pour déclarer les propriétés que nous attendons du modèle.
Nous définissons la propriété ` selected ` sur la propriété attachée ` ListView.isCurrentItem ` pour spécifier que ` selected ` vaut ` true ` si ` delegate ` est l’élément actuel.
Nous utilisons la propriété de type Image source pour afficher l’image, centrée dans le délégué, spécifiée pour l’élément de liste par le rôle image dans le modèle de liste rssFeeds:
Image {
anchors.centerIn: parent
source: delegate.image
}Nous utilisons un type « Text » pour ajouter des titres aux éléments de la liste :
Text {
id: titleText
anchors {
left: parent.left; leftMargin: 20
right: parent.right; rightMargin: 20
top: parent.top; topMargin: 20
}
font { pixelSize: 18; bold: true }
text: delegate.name
color: delegate.selected ? "#ffffff" : "#ebebdd"
scale: delegate.selected ? 1.15 : 1.0
Behavior on color { ColorAnimation { duration: 150 } }
Behavior on scale { PropertyAnimation { duration: 300 } }Nous utilisons la propriété anchors pour positionner le titre en haut de l’élément de liste, avec une marge de 20 pixels. Nous utilisons les propriétés font pour ajuster la taille de la police et la mise en forme du texte.
Nous utilisons les propriétés ` color ` et ` scale ` pour éclaircir le texte et l'agrandir légèrement lorsque l'élément de la liste est l'élément actif. En appliquant une animation ` Behavior ` à la propriété, nous animons les actions de sélection et de désélection des éléments de la liste.
Nous utilisons un type « MouseArea » pour télécharger des données XML lorsque les utilisateurs appuient sur un élément de la liste des catégories :
MouseArea {
anchors.fill: delegate
onClicked: {
delegate.ListView.view.currentIndex = delegate.index
delegate.clicked()
}
}La propriété anchors.fill est définie sur delegate afin de permettre aux utilisateurs de toucher n’importe où dans l’élément de la liste.
Pour charger les données XML de la liste de catégories lors d’un événement « clicked », nous définissons la valeur « currentIndex » de la propriété attachée « ListView » et émettons le signal « clicked () » dans « onClicked ».
Téléchargement de données XML
Dans Main.qml, nous utilisons un type XmlListModel comme source de données pour les éléments ListView afin d’afficher les actualités de la catégorie sélectionnée :
XmlListModel {
id: feedModel
source: "https://" + window.currentFeed
query: "/rss/channel/item"Nous utilisons la propriété source et la propriété personnalisée window.currentFeed pour récupérer les actualités de la catégorie sélectionnée.
La propriété query spécifie que la propriété XmlListModel génère un élément de modèle pour chaque élément <item> présent dans le document XML.
Nous utilisons le type « XmlListModelRole » pour définir les attributs de l’élément de modèle. Chaque élément de modèle possède les attributs « title », « content », « link » et « pubDate », qui correspondent aux valeurs des éléments « <item> » correspondants dans le document XML :
XmlListModelRole { name: "title"; elementName: "title"; attributeName: ""}
XmlListModelRole { name: "content"; elementName: "content"; attributeName: "url" }
XmlListModelRole { name: "link"; elementName: "link"; attributeName: "" }
XmlListModelRole { name: "pubDate"; elementName: "pubDate"; attributeName: "" }
}Nous utilisons le modèle feedModel de type ListView pour afficher les données :
ListView {
id: list
anchors.left: window.isPortrait ? window.left : categories.right
anchors.right: closeButton.left
anchors.top: window.isPortrait ? categories.bottom : window.top
anchors.bottom: window.bottom
anchors.leftMargin: 30
anchors.rightMargin: 4
clip: window.isPortrait
model: feedModel
footer: footerText
delegate: NewsDelegate {}
}Pour afficher les actualités sous la liste des catégories en mode portrait et à sa droite en mode paysage, nous utilisons la propriété personnalisée « isPortrait » afin d’ancrer le haut de la liste des actualités à gauche de « window » et le bas de « categories » en mode portrait, et à droite de « categories » et le bas de « window » en mode paysage.
Nous utilisons la propriété ` anchors.bottom ` pour ancrer le bas de la vue de liste au bas de la fenêtre, quelle que soit l’orientation.
En mode portrait, nous découpons l’affichage des articles d’actualité selon le rectangle de délimitation de la vue de liste afin d’éviter les artefacts graphiques lorsque les articles défilent par-dessus d’autres éléments. En mode paysage, cela n’est pas nécessaire, car la liste s’étend verticalement sur toute la largeur de l’écran.
Nous utilisons la propriété ` model ` pour charger les données XML à partir du modèle ` feedModel `, et utilisons ` NewsDelegate ` comme délégué pour instancier chaque élément de la liste.
Dans ` NewsDelegate.qml`, nous utilisons un type ` Column ` pour mettre en page les données XML :
Column {
id: delegate
required property string title
required property string content
required property string link
required property string pubDate
width: delegate.ListView.view.width
spacing: 8Au sein de la colonne, nous utilisons une ligne (Row ) et une autre colonne pour positionner les images et le texte du titre :
Row {
width: parent.width
spacing: 8
Column {
Item {
width: 4
height: titleText.font.pixelSize / 4
}
Image {
id: titleImage
source: delegate.content
width: Math.min(delegate.width / 2, sourceSize.width)
fillMode: Image.PreserveAspectFit
}
}
Text {
id: titleText
text: delegate.title.replace(/'/g, "'")
width: delegate.width - titleImage.width
wrapMode: Text.WordWrap
font.pixelSize: 26
font.bold: true
}
}Nous générons une représentation textuelle de la date de publication de l’élément à l’aide de la fonction JavaScript ` timeSinceEvent() ` :
Text {
width: delegate.width
font.pixelSize: 12
textFormat: Text.RichText
font.italic: true
text: delegate.timeSinceEvent(delegate.pubDate) + " (<a href=\"" + delegate.link + "\">Link</a>)"
onLinkActivated: function(link) {
Qt.openUrlExternally(link)
}
}Nous utilisons le gestionnaire d’événements « signal » d’ onLinkActivated pour ouvrir l’URL dans un navigateur externe lorsque les utilisateurs cliquent sur le lien.
Fournir des retours d’information aux utilisateurs
Dans CategoryDelegate.qml, nous utilisons le type personnalisé « BusyIndicator » pour indiquer l’activité pendant le chargement des données XML :
BusyIndicator {
scale: 0.8
visible: delegate.ListView.isCurrentItem && delegate.isLoading
anchors.centerIn: parent
}Nous utilisons la propriété ` scale ` pour réduire la taille de l'indicateur à ` 0.8`. Nous lions la propriété ` visible ` à la propriété associée ` isCurrentItem ` de la vue de liste ` delegate ` et à la propriété ` loading ` de la fenêtre principale afin d'afficher l'image de l'indicateur lorsqu'un élément de la liste des catégories est l'élément actif et que des données XML sont en cours de chargement.
Nous définissons le type ` BusyIndicator ` dans ` BusyIndicator.qml`. Nous utilisons un type ` Image ` pour afficher une image et appliquons un ` NumberAnimation ` à sa propriété ` rotation ` afin de faire pivoter l’image dans une boucle infinie :
Image {
id: container
source: "images/busy.png";
NumberAnimation on rotation {
running: container.visible
from: 0; to: 360;
loops: Animation.Infinite;
duration: 1200
}
}Dans vos applications, vous pouvez également utiliser le type BusyIndicator issu du Qt Quick Controls module.
Création de barres de défilement
Dans Main.qml, nous utilisons notre propre type personnalisé ` ScrollBar ` pour créer des barres de défilement dans les vues de catégorie et de liste d’actualités. Dans vos applications, vous pouvez également utiliser le type ` ScrollView ` issu du Qt Quick Controls module.
Tout d’abord, nous créons une barre de défilement dans la vue de liste des catégories. Nous lions la propriété ` orientation ` à la propriété ` isPortrait ` et à la valeur ` Horizontal ` du type énuméré ` Qt::Orientation ` pour afficher une barre de défilement horizontale en mode portrait, et à la valeur ` Vertical ` pour afficher une barre de défilement verticale en mode paysage :
ScrollBar {
id: listScrollBar
orientation: window.isPortrait ? Qt.Horizontal : Qt.Vertical
height: window.isPortrait ? 8 : categories.height;
width: window.isPortrait ? categories.width : 8
scrollArea: categories;
anchors.right: categories.right
}Comme pour la vue de liste categories, nous ajustons la largeur et la hauteur de la barre de défilement en fonction de la propriété isPortrait.
Nous utilisons la propriété « scrollArea » pour afficher la barre de défilement dans la vue de liste « categories ».
Nous utilisons la propriété « anchors.right » pour ancrer la barre de défilement à droite de la liste des catégories.
ScrollBar {
scrollArea: list
width: 8
anchors.right: window.right
anchors.top: window.isPortrait ? categories.bottom : window.top
anchors.bottom: window.bottom
}Ensuite, nous créons une autre barre de défilement dans la vue de liste des actualités. Nous souhaitons qu’une barre de défilement verticale apparaisse à droite de la vue, quelle que soit l’orientation de l’écran ; nous pouvons donc définir la propriété ` width ` sur ` 8 ` et lier la propriété ` anchors.right ` à la propriété ` window.right `. Nous utilisons la propriété ` anchors.top ` pour ancrer le haut de la barre de défilement au bas de la liste des catégories en mode portrait et au haut de la liste des actualités en mode paysage. Nous utilisons la propriété ` anchors.bottom ` pour ancrer le bas de la barre de défilement au bas de la vue de liste dans les deux orientations.
Nous définissons le type « ScrollBar » dans ` ScrollBar.qml`. Nous utilisons un type « Item » doté de propriétés personnalisées pour créer un conteneur destiné à la barre de défilement :
Nous utilisons un type « BorderImage » pour afficher le curseur de la barre de défilement aux coordonnées x et y que nous calculons à l’aide de la fonction « position() » :
BorderImage {
source: "images/scrollbar.png"
border { left: 1; right: 1; top: 1; bottom: 1 }
x: container.orientation == Qt.Vertical ? 2 : container.position()
y: container.orientation == Qt.Vertical ? container.position() : 2
width: container.orientation == Qt.Vertical ? container.width - 4 : container.size()
height: container.orientation == Qt.Vertical ? container.size() : container.height - 4
}Nous utilisons la fonction size pour calculer la largeur et la hauteur du curseur en fonction de l’orientation de l’écran.
Nous utilisons ` states ` pour rendre la barre de défilement visible lorsque l'utilisateur déplace la zone de défilement :
states: State {
name: "visible"
when: container.orientation == Qt.Vertical ?
container.scrollArea.movingVertically :
container.scrollArea.movingHorizontally
PropertyChanges { container { opacity: 1.0 } }
}Nous utilisons transitions pour appliquer un NumberAnimation à la propriété opacity lorsque l’état passe de « "visible" » à l’état par défaut :
transitions: Transition {
from: "visible"; to: ""
NumberAnimation { properties: "opacity"; duration: 600 }
}
}Création de pieds de page
Dans ` Main.qml`, nous utilisons un type ` Component ` avec un type ` Rectangle ` pour créer un pied de page pour la vue de liste des actualités :
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Window
import QtQml.XmlListModel
Rectangle {
id: window
width: 800
height: 480
property string currentFeed: rssFeeds.get(0).feed
property bool loading: feedModel.status === XmlListModel.Loading
property bool isPortrait: Screen.primaryOrientation === Qt.PortraitOrientation
onLoadingChanged: {
if (feedModel.status == XmlListModel.Ready)
list.positionViewAtBeginning()
}
RssFeeds { id: rssFeeds }
XmlListModel {
id: feedModel
source: "https://" + window.currentFeed
query: "/rss/channel/item"
XmlListModelRole { name: "title"; elementName: "title"; attributeName: ""}Nous lions la propriété ` width ` du pied de page à la largeur du composant et la propriété ` height ` à la hauteur du bouton de fermeture afin de les aligner lorsqu’aucun article n’est affiché.
Création de boutons
Dans ` Main.qml`, nous utilisons un type ` Image ` pour créer un simple bouton sur lequel les utilisateurs peuvent appuyer pour fermer l’application :
XmlListModelRole { name: "content"; elementName: "content"; attributeName: "url" }
XmlListModelRole { name: "link"; elementName: "link"; attributeName: "" }
XmlListModelRole { name: "pubDate"; elementName: "pubDate"; attributeName: "" }
}
ListView {
id: categories
property int itemWidth: 190
width: window.isPortrait ? parent.width : itemWidth
height: window.isPortrait ? itemWidth : parent.height
orientation: window.isPortrait ? ListView.Horizontal : ListView.Vertical
anchors.top: parent.top
model: rssFeeds
delegate: CategoryDelegate {
itemSize: categories.itemWidth
isLoading: window.loading
onClicked: function () {
if (window.currentFeed == feed)
feedModel.reload()
else
window.currentFeed = feed
}
}
spacing: 3
}
ScrollBar {
id: listScrollBar
orientation: window.isPortrait ? Qt.Horizontal : Qt.Vertical
height: window.isPortrait ? 8 : categories.height;
width: window.isPortrait ? categories.width : 8
scrollArea: categories;
anchors.right: categories.right
}
ListView {
id: list
anchors.left: window.isPortrait ? window.left : categories.right
anchors.right: closeButton.left
anchors.top: window.isPortrait ? categories.bottom : window.top
anchors.bottom: window.bottom
anchors.leftMargin: 30
anchors.rightMargin: 4
clip: window.isPortrait
model: feedModel
footer: footerText
delegate: NewsDelegate {}
}
ScrollBar {
scrollArea: list
width: 8
anchors.right: window.right
anchors.top: window.isPortrait ? categories.bottom : window.top
anchors.bottom: window.bottom
}
Component {
id: footerText
Rectangle {
width: parent.width
height: closeButton.height
color: "lightgray"
Text {
text: "RSS Feed from Yahoo News"
anchors.centerIn: parent
font.pixelSize: 14
}
}
}
Image {
id: closeButton
source: "content/images/btn_close.png"
scale: 0.8
anchors.top: parent.top
anchors.right: parent.right
anchors.margins: 4
opacity: (window.isPortrait && categories.moving) ? 0.2 : 1.0
Behavior on opacity {
NumberAnimation { duration: 300; easing.type: Easing.OutSine }
}
MouseArea {
anchors.fill: parent
onClicked: {
Qt.quit()
}
}
}Nous utilisons ` anchors ` pour positionner le bouton de fermeture dans le coin supérieur droit de la vue de la liste des actualités, avec des marges de 4 pixels. Comme le bouton de fermeture chevauche la liste des catégories en mode portrait, nous animons la propriété ` opacity ` pour rendre le bouton presque entièrement transparent lorsque les utilisateurs font défiler la liste des catégories.
Nous utilisons le gestionnaire de signal ` onClicked ` au sein d’un ` MouseArea ` pour appeler la fonction ` quit() ` lorsque l’utilisateur sélectionne le bouton de fermeture.
Voir également Applications QML.
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.