Qt Quick Demostración - Noticias RSS
Un lector de noticias RSS QML que utiliza los tipos QML personalizados XmlListModel y XmlListModelRole para descargar datos XML, ListModel y ListElement para crear una lista de categorías, y ListView para mostrar los datos.

RSS News muestra las siguientes Qt Quick funciones:
- Uso de tipos QML personalizados.
- Uso de modelos de lista y elementos de lista para representar datos.
- Uso de modelos de lista XML para descargar datos XML.
- Uso de vistas de lista para mostrar datos.
- Uso del tipo Component para crear un pie de página para la vista de lista de noticias.
- Uso del tipo Image para crear un botón para cerrar la aplicación.
Ejecución del ejemplo
Para ejecutar el ejemplo desde Qt Creatorabra el modo Welcome y seleccione el ejemplo de Examples. Para más información, consulte Qt Creator: Tutorial: Construir y ejecutar.
Uso de tipos personalizados
En la aplicación Noticias RSS, utilizamos los siguientes tipos personalizados, cada uno de ellos definido en un archivo .qml independiente:
BusyIndicator.qmlCategoryDelegate.qmlNewsDelegate.qmlRssFeeds.qmlScrollBar.qml
Creación de la ventana principal
En Main.qml, utilizamos un tipo Rectangle con propiedades personalizadas para crear la ventana principal de la aplicación:
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
Utilizaremos las propiedades personalizadas más adelante para cargar datos XML y para ajustar el diseño de la pantalla en función de su orientación.
Creación de una lista de categorías
En Main.qml, utilizamos el tipo personalizado RssFeeds que especificamos en RssFeeds.qml para crear una lista de categorías de feeds:
RssFeeds { id: rssFeeds }
En RssFeeds.qml, utilizamos un tipo ListModel con un tipo ListElement para crear una lista de categorías en la que los elementos de lista representan categorías de fuentes:
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" } }
Los elementos de lista se definen como otros tipos QML, salvo que contienen una colección de definiciones de roles en lugar de propiedades. Los roles definen cómo se accede a los datos e incluyen los propios datos.
Para cada elemento de lista, utilizamos el rol name para especificar el nombre de la categoría, el rol feed para especificar la URL desde la que cargar los datos y el rol image para mostrar una imagen de la categoría.
En Main.qml, utilizamos un tipo ListView para mostrar la lista de categorías:
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 } }
Para disponer la lista de categorías horizontalmente en la parte superior de la ventana en orientación vertical y verticalmente en el lado izquierdo en orientación horizontal, utilizamos la propiedad orientation. En función de la orientación, vinculamos la anchura o la altura de la lista a un valor fijo (itemWidth).
Utilizamos la propiedad anchors.top para posicionar la vista de lista en la parte superior de la pantalla en ambas orientaciones.
Utilizamos la propiedad model para cargar los datos XML del modelo rssFeeds, y CategoryDelegate como delegado para instanciar cada elemento de la lista.
Creación de elementos de lista
En CategoryDelegate.qml, utilizamos el tipo Rectangle con propiedades personalizadas para crear elementos de lista:
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.isCurrentItem
Usamos required para declarar las propiedades que esperamos del modelo.
Establecemos la propiedad selected a la propiedad adjunta ListView.isCurrentItem para especificar que selected es true si delegate es el elemento actual.
Usamos la propiedad Image type source para mostrar la imagen, centrada en el delegado, especificada para el elemento de lista por el rol image en el modelo de lista rssFeeds:
Image { anchors.centerIn: parent source: delegate.image }
Utilizamos el tipo Text para añadir títulos a los elementos de la lista:
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 } }
Utilizamos la propiedad anchors para colocar el título en la parte superior del elemento de lista, con un margen de 20 píxeles. Utilizamos las propiedades font para ajustar el tamaño de la fuente y el formato del texto.
Utilizamos las propiedades color y scale para dar brillo al texto y ampliarlo ligeramente cuando el elemento de la lista es el elemento actual. Aplicando un Behavior a la propiedad, animamos las acciones de seleccionar y deseleccionar elementos de la lista.
Utilizamos un tipo MouseArea para descargar datos XML cuando los usuarios tocan un elemento de la lista de categorías:
MouseArea { anchors.fill: delegate onClicked: { delegate.ListView.view.currentIndex = delegate.index delegate.clicked() } }
La propiedad anchors.fill se establece en delegate para permitir a los usuarios tocar en cualquier lugar dentro del elemento de la lista.
Para cargar los datos XML de la lista de categorías en un evento clicked, establecemos la currentIndex de la propiedad adjunta ListView y emitimos la clicked signal() en onClicked.
Descarga de datos XML
En Main.qml, utilizamos un tipo XmlListModel como fuente de datos para que los elementos ListView muestren las noticias de la categoría seleccionada:
XmlListModel { id: feedModel source: "https://" + window.currentFeed query: "/rss/channel/item"
Utilizamos la propiedad source y la propiedad personalizada window.currentFeed para obtener las noticias de la categoría seleccionada.
La propiedad query especifica que XmlListModel genera un elemento modelo para cada <item> en el documento XML.
Utilizamos el tipo XmlListModelRole para especificar los atributos del elemento modelo. Cada elemento del modelo tiene los atributos title, content, link, y pubDate que coinciden con los valores del correspondiente <item> en el documento 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: "" } }
Utilizamos el modelo feedModel en un tipo ListView para mostrar los datos:
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 {} }
Para listar las noticias debajo de la lista de categorías en orientación vertical y a su derecha en orientación horizontal, utilizamos la propiedad personalizada isPortrait para anclar la parte superior de la lista de noticias a la izquierda de window e inferior de categories en orientación vertical y a la derecha de categories e inferior de window en orientación horizontal.
Utilizamos la propiedad anchors.bottom para anclar la parte inferior de la vista de lista a la parte inferior de la ventana en ambas orientaciones.
En la orientación vertical, recortamos la pintura de las noticias al rectángulo delimitador de la vista de lista para evitar artefactos gráficos cuando las noticias se desplazan sobre otros elementos. En la orientación horizontal, esto no es necesario, porque la lista ocupa toda la pantalla verticalmente.
Utilizamos la propiedad model para cargar datos XML del modelo feedModel, y utilizamos NewsDelegate como delegado para instanciar cada elemento de la lista.
En NewsDelegate.qml, utilizamos un tipo Column para disponer los datos 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: 8
Dentro de la columna, utilizamos una Fila y otra columna para colocar las imágenes y el texto del título:
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 } }
Generamos una representación textual de cuánto tiempo hace que se publicó el elemento utilizando la función 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) } }
Utilizamos el gestor de señales onLinkActivated para abrir la URL en un navegador externo cuando los usuarios seleccionan el enlace.
Información a los usuarios
En CategoryDelegate.qml, utilizamos el tipo personalizado BusyIndicator para indicar la actividad mientras se cargan los datos XML:
BusyIndicator { scale: 0.8 visible: delegate.ListView.isCurrentItem && delegate.isLoading anchors.centerIn: parent }
Utilizamos la propiedad scale para reducir el tamaño del indicador a 0.8. Vinculamos la propiedad visible a la propiedad adjunta isCurrentItem de la vista de lista delegate y a la propiedad loading de la ventana principal para mostrar la imagen del indicador cuando un elemento de la lista de categorías es el elemento actual y se están cargando datos XML.
Definimos el tipo BusyIndicator en BusyIndicator.qml. Utilizamos un tipo Image para mostrar una imagen y aplicamos un NumberAnimation a su propiedad rotation para rotar la imagen en un bucle infinito:
Image { id: container source: "images/busy.png"; NumberAnimation on rotation { running: container.visible from: 0; to: 360; loops: Animation.Infinite; duration: 1200 } }
En sus aplicaciones, también puede utilizar el tipo BusyIndicator del módulo Qt Quick Controls módulo.
Creación de barras de desplazamiento
En Main.qml, utilizamos nuestro propio tipo personalizado ScrollBar para crear barras de desplazamiento en las vistas de lista de categorías y noticias. En sus aplicaciones, también puede utilizar el tipo ScrollView del módulo Qt Quick Controls del módulo.
En primer lugar, creamos una barra de desplazamiento en la vista de lista de categorías. Vinculamos la propiedad orientation a la propiedad isPortrait y al valor Horizontal del tipo enum Qt::Orientation para mostrar una barra de desplazamiento horizontal en orientación vertical y al valor Vertical para mostrar una barra de desplazamiento vertical en orientación horizontal:
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 }
Al igual que con la vista de lista categories, ajustamos la anchura y la altura de la barra de desplazamiento en función de la propiedad isPortrait.
Utilizamos la propiedad scrollArea para mostrar la barra de desplazamiento en la vista de lista categories.
Utilizamos la propiedad anchors.right para anclar la barra de desplazamiento al lado derecho de la lista de categorías.
ScrollBar { scrollArea: list width: 8 anchors.right: window.right anchors.top: window.isPortrait ? categories.bottom : window.top anchors.bottom: window.bottom }
En segundo lugar, creamos otra barra de desplazamiento en la vista de lista de noticias. Queremos que aparezca una barra de desplazamiento vertical en el lado derecho de la vista independientemente de la orientación de la pantalla, por lo que podemos establecer la propiedad width a 8 y vincular la propiedad anchors.right a la propiedad window.right. Utilizamos la propiedad anchors.top para anclar la barra de desplazamiento en la parte inferior de la lista de categorías en orientación vertical y en la parte superior de la lista de noticias en orientación horizontal. Utilizamos la propiedad anchors.bottom para anclar la parte inferior de la barra de desplazamiento a la parte inferior de la vista de lista en ambas orientaciones.
Definimos el tipo ScrollBar en ScrollBar.qml. Utilizamos un tipo Item con propiedades personalizadas para crear un contenedor para la barra de desplazamiento:
Utilizamos un tipo BorderImage para mostrar el pulgar de la barra de desplazamiento en la posición x e y que calculamos utilizando la función 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 }
Utilizamos la función size para calcular la anchura y altura del pulgar dependiendo de la orientación de la pantalla.
Utilizamos states para hacer visible la barra de desplazamiento cuando los usuarios mueven el área de desplazamiento:
states: State { name: "visible" when: container.orientation == Qt.Vertical ? container.scrollArea.movingVertically : container.scrollArea.movingHorizontally PropertyChanges { container { opacity: 1.0 } } }
Utilizamos transitions para aplicar un NumberAnimation a la propiedad opacity cuando el estado cambia de "visible" al estado por defecto:
transitions: Transition { from: "visible"; to: "" NumberAnimation { properties: "opacity"; duration: 600 } } }
Creación de pies de página
En Main.qml, utilizamos un tipo Component con un tipo Rectangle para crear un pie de página para la vista de lista de noticias:
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: ""}
Vinculamos el width del pie de página a la anchura del componente y el height a la altura del botón cerrar para alinearlos cuando no se muestren noticias.
Creación de botones
En Main.qml, utilizamos un tipo Image para crear un simple botón que los usuarios pueden tocar para cerrar la aplicación:
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() } } }
Utilizamos anchors para situar el botón de cierre en la esquina superior derecha de la vista de la lista de noticias, con márgenes de 4 píxeles. Como el botón de cierre se superpone a la lista de categorías en orientación vertical, animamos la propiedad opacity para que el botón sea casi totalmente transparente cuando los usuarios se desplacen por la lista de categorías.
Utilizamos el controlador de señal onClicked dentro de MouseArea para llamar a la función quit() cuando los usuarios seleccionan el botón de cierre.
Ver también Aplicaciones 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.