diff options
author | Øystein Heskestad <oystein.heskestad@qt.io> | 2023-03-03 17:41:43 +0100 |
---|---|---|
committer | Øystein Heskestad <oystein.heskestad@qt.io> | 2023-03-21 14:49:09 +0100 |
commit | 10b4c06b4928d12addc7e55e4a6e5ed537a63942 (patch) | |
tree | 33f521c8fd692bf63618156aabb14dcb6009771c /examples/corelib | |
parent | 3c521e1fb42885e070bf07f36349a1e8cdb39e76 (diff) | |
download | qtbase-10b4c06b4928d12addc7e55e4a6e5ed537a63942.tar.gz |
Move rsslisting example into corelibe/serialization
The implementation of the stream-based XML parser resides in
coreslib/serialization. Moving the rsslisting example there.
Task-number: QTBUG-110647
Pick-to: 6.5
Change-Id: I862909e767301250750b6ee0d8ac7e20d6bad2b1
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
Diffstat (limited to 'examples/corelib')
7 files changed, 346 insertions, 0 deletions
diff --git a/examples/corelib/serialization/CMakeLists.txt b/examples/corelib/serialization/CMakeLists.txt index 9609b0a107..0110eb8fff 100644 --- a/examples/corelib/serialization/CMakeLists.txt +++ b/examples/corelib/serialization/CMakeLists.txt @@ -4,3 +4,6 @@ qt_internal_add_example(cbordump) qt_internal_add_example(convert) qt_internal_add_example(savegame) +if(TARGET Qt6::Network AND TARGET Qt6::Widgets) + qt_internal_add_example(rsslisting) +endif() diff --git a/examples/corelib/serialization/rsslisting/CMakeLists.txt b/examples/corelib/serialization/rsslisting/CMakeLists.txt new file mode 100644 index 0000000000..405a01ce56 --- /dev/null +++ b/examples/corelib/serialization/rsslisting/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(rsslisting LANGUAGES CXX) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/corelib/serialization/rsslisting") + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Network Widgets) + +qt_standard_project_setup() + +qt_add_executable(rsslisting + main.cpp + rsslisting.cpp rsslisting.h +) + +set_target_properties(rsslisting PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(rsslisting PRIVATE + Qt6::Core + Qt6::Gui + Qt6::Network + Qt6::Widgets +) + +install(TARGETS rsslisting + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/examples/corelib/serialization/rsslisting/main.cpp b/examples/corelib/serialization/rsslisting/main.cpp new file mode 100644 index 0000000000..9337e5f352 --- /dev/null +++ b/examples/corelib/serialization/rsslisting/main.cpp @@ -0,0 +1,26 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +/* +main.cpp + +Provides the main function for the RSS news reader example. +*/ + +#include <QtWidgets> + +#include "rsslisting.h" + +/*! + Create an application and a main widget. Open the main widget for + user input, and exit with an appropriate return value when it is + closed. +*/ + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + RSSListing *rsslisting = new RSSListing; + rsslisting->show(); + return app.exec(); +} diff --git a/examples/corelib/serialization/rsslisting/rsslisting.cpp b/examples/corelib/serialization/rsslisting/rsslisting.cpp new file mode 100644 index 0000000000..9269be4080 --- /dev/null +++ b/examples/corelib/serialization/rsslisting/rsslisting.cpp @@ -0,0 +1,211 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +/* +rsslisting.cpp + +Provides a widget for displaying news items from RDF news sources. +RDF is an XML-based format for storing items of information (see +http://www.w3.org/RDF/ for details). + +The widget itself provides a simple user interface for specifying +the URL of a news source, and controlling the downloading of news. + +The widget downloads and parses the XML asynchronously, feeding the +data to an XML reader in pieces. This allows the user to interrupt +its operation, and also allows very large data sources to be read. +*/ + + +#include <QtCore> +#include <QtWidgets> +#include <QtNetwork> + +#include "rsslisting.h" + + +/* + Constructs an RSSListing widget with a simple user interface, and sets + up the XML reader to use a custom handler class. + + The user interface consists of a line edit, a push button, and a + list view widget. The line edit is used for entering the URLs of news + sources; the push button starts the process of reading the + news. +*/ + +RSSListing::RSSListing(QWidget *parent) + : QWidget(parent), currentReply(0) +{ + + lineEdit = new QLineEdit(this); + lineEdit->setText("http://blog.qt.io/feed/"); + + fetchButton = new QPushButton(tr("Fetch"), this); + + treeWidget = new QTreeWidget(this); + connect(treeWidget, &QTreeWidget::itemActivated, + this, &RSSListing::itemActivated); + QStringList headerLabels; + headerLabels << tr("Title") << tr("Link"); + treeWidget->setHeaderLabels(headerLabels); + treeWidget->header()->setSectionResizeMode(QHeaderView::ResizeToContents); + + connect(&manager, &QNetworkAccessManager::finished, + this, &RSSListing::finished); + + connect(lineEdit, &QLineEdit::returnPressed, this, &RSSListing::fetch); + connect(fetchButton, &QPushButton::clicked, this, &RSSListing::fetch); + + QVBoxLayout *layout = new QVBoxLayout(this); + + QHBoxLayout *hboxLayout = new QHBoxLayout; + + hboxLayout->addWidget(lineEdit); + hboxLayout->addWidget(fetchButton); + + layout->addLayout(hboxLayout); + layout->addWidget(treeWidget); + + setWindowTitle(tr("RSS listing example")); + resize(640,480); +} + +/* + Starts the network request and connects the needed signals +*/ +void RSSListing::get(const QUrl &url) +{ + QNetworkRequest request(url); + if (currentReply) { + currentReply->disconnect(this); + currentReply->deleteLater(); + } + currentReply = manager.get(request); + connect(currentReply, &QNetworkReply::readyRead, this, &RSSListing::readyRead); + connect(currentReply, &QNetworkReply::metaDataChanged, this, &RSSListing::metaDataChanged); + connect(currentReply, &QNetworkReply::errorOccurred, this, &RSSListing::error); +} + +/* + Starts fetching data from a news source specified in the line + edit widget. + + The line edit is made read only to prevent the user from modifying its + contents during the fetch; this is only for cosmetic purposes. + The fetch button is disabled, the list view is cleared, and we + define the last list view item to be 0, meaning that there are no + existing items in the list. + + A URL is created with the raw contents of the line edit and + a get is initiated. +*/ + +void RSSListing::fetch() +{ + lineEdit->setReadOnly(true); + fetchButton->setEnabled(false); + treeWidget->clear(); + + xml.clear(); + + QUrl url(lineEdit->text()); + get(url); +} + +void RSSListing::metaDataChanged() +{ + QUrl redirectionTarget = currentReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); + if (redirectionTarget.isValid()) { + get(redirectionTarget); + } +} + +/* + Reads data received from the RDF source. + + We read all the available data, and pass it to the XML + stream reader. Then we call the XML parsing function. +*/ + +void RSSListing::readyRead() +{ + int statusCode = currentReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if (statusCode >= 200 && statusCode < 300) { + QByteArray data = currentReply->readAll(); + xml.addData(data); + parseXml(); + } +} + +/* + Finishes processing an HTTP request. + + The default behavior is to keep the text edit read only. + + If an error has occurred, the user interface is made available + to the user for further input, allowing a new fetch to be + started. + + If the HTTP get request has finished, we make the + user interface available to the user for further input. +*/ + +void RSSListing::finished(QNetworkReply *reply) +{ + Q_UNUSED(reply); + lineEdit->setReadOnly(false); + fetchButton->setEnabled(true); +} + + +/* + Parses the XML data and creates treeWidget items accordingly. +*/ +void RSSListing::parseXml() +{ + while (!xml.atEnd()) { + xml.readNext(); + if (xml.isStartElement()) { + if (xml.name() == u"item") + linkString = xml.attributes().value("rss:about").toString(); + currentTag = xml.name().toString(); + } else if (xml.isEndElement()) { + if (xml.name() == u"item") { + + QTreeWidgetItem *item = new QTreeWidgetItem; + item->setText(0, titleString); + item->setText(1, linkString); + treeWidget->addTopLevelItem(item); + + titleString.clear(); + linkString.clear(); + } + + } else if (xml.isCharacters() && !xml.isWhitespace()) { + if (currentTag == "title") + titleString += xml.text(); + else if (currentTag == "link") + linkString += xml.text(); + } + } + if (xml.error() && xml.error() != QXmlStreamReader::PrematureEndOfDocumentError) { + qWarning() << "XML ERROR:" << xml.lineNumber() << ": " << xml.errorString(); + } +} + +/* + Open the link in the browser +*/ +void RSSListing::itemActivated(QTreeWidgetItem * item) +{ + QDesktopServices::openUrl(QUrl(item->text(1))); +} + +void RSSListing::error(QNetworkReply::NetworkError) +{ + qWarning("error retrieving RSS feed"); + currentReply->disconnect(this); + currentReply->deleteLater(); + currentReply = 0; +} diff --git a/examples/corelib/serialization/rsslisting/rsslisting.h b/examples/corelib/serialization/rsslisting/rsslisting.h new file mode 100644 index 0000000000..81c655f677 --- /dev/null +++ b/examples/corelib/serialization/rsslisting/rsslisting.h @@ -0,0 +1,55 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef RSSLISTING_H +#define RSSLISTING_H + +#include <QNetworkAccessManager> +#include <QNetworkReply> +#include <QWidget> +#include <QBuffer> +#include <QXmlStreamReader> +#include <QUrl> + + +QT_BEGIN_NAMESPACE +class QLineEdit; +class QTreeWidget; +class QTreeWidgetItem; +class QPushButton; +QT_END_NAMESPACE + +class RSSListing : public QWidget +{ + Q_OBJECT +public: + RSSListing(QWidget *widget = nullptr); + +public slots: + void fetch(); + void finished(QNetworkReply *reply); + void readyRead(); + void metaDataChanged(); + void itemActivated(QTreeWidgetItem * item); + void error(QNetworkReply::NetworkError); + +private: + void parseXml(); + void get(const QUrl &url); + + QXmlStreamReader xml; + QString currentTag; + QString linkString; + QString titleString; + + QNetworkAccessManager manager; + QNetworkReply *currentReply; + + QLineEdit *lineEdit; + QTreeWidget *treeWidget; + QPushButton *fetchButton; + +}; + +#endif + diff --git a/examples/corelib/serialization/rsslisting/rsslisting.pro b/examples/corelib/serialization/rsslisting/rsslisting.pro new file mode 100644 index 0000000000..7619755b8f --- /dev/null +++ b/examples/corelib/serialization/rsslisting/rsslisting.pro @@ -0,0 +1,8 @@ +HEADERS += rsslisting.h +SOURCES += main.cpp rsslisting.cpp +QT += network widgets +requires(qtConfig(treewidget)) + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/corelib/serialization/rsslisting +INSTALLS += target diff --git a/examples/corelib/serialization/serialization.pro b/examples/corelib/serialization/serialization.pro index 7651444f19..f36b467783 100644 --- a/examples/corelib/serialization/serialization.pro +++ b/examples/corelib/serialization/serialization.pro @@ -3,3 +3,8 @@ SUBDIRS = \ cbordump \ convert \ savegame + +qtHaveModule(widgets) { + qtHaveModule(network): SUBDIRS += \ + rsslisting +} |