最小限のRSSリストアプリケーション
ネットワークリソースを取得して表示する方法のデモ。
この例では、ユーザーが要求したリソースをフェッチし、レスポンスに含まれるデータを表示する方法を、RSSリスト・アプリケーションで説明します。(RDFサイトサマリー、またはReally Simple Syndicationは、Webサイトの更新を伝達するための標準フォーマットです。詳しくは https://www.rssboard.org/rss-specification を参照)。この例ではネットワークの使い方に焦点を当てているため、図のユーザーインターフェイスはシンプルですが、本格的なRSSリーダーには当然もっと洗練されたインターフェイスが必要です。
また、この例では、受信したデータを非同期で解析し、メンバ変数に状態を保存して、インクリメンタル・パーサがネットワーク経由で到着したデータのチャンクを消費できるようにする方法を示している。構文解析されたコンテンツの構成要素は、あるデータのチャンクで開始されるかもしれないが、後のチャンクまで完了しないかもしれない。
メイン・プログラムはかなり最小限のものである。QApplication とRSSListing
ウィジェットをインスタンス化し、後者を表示し、前者に制御を渡すだけである。説明のために、チェックするリソースのデフォルト値としてQtブログのURLをウィジェットに与えます。
int main(int argc, char **argv) { QApplication app(argc, argv); RSSListing rsslisting(u"https://www.qt.io/blog/rss.xml"_s); rsslisting.show(); return app.exec(); }
RSSListingクラス
class RSSListing : public QWidget { Q_OBJECT public: explicit RSSListing(const QString &url = QString(), QWidget *widget = nullptr); public slots: void fetch(); void finished(QNetworkReply *reply); void consumeData(); void error(QNetworkReply::NetworkError); private: void parseXml(); void get(const QUrl &url); // Parser state: QXmlStreamReader xml; QString currentTag; QString linkString; QString titleString; // Network state: QNetworkAccessManager manager; QNetworkReply *currentReply; // UI elements: QLineEdit *lineEdit; QTreeWidget *treeWidget; QPushButton *fetchButton; };
ウィジェット自体は、取得するURLを指定し、利用可能な更新が表示されたら、更新されたアイテムのダウンロードを制御するためのシンプルなユーザーインターフェイスを提供します。QLineEdit 、URLの入力を行い、QTreeWidget 、取得した結果を表示します。
ウィジェットはRSS(XMLの一種)を非同期でダウンロード・解析し、到着したデータをXMLリーダーに供給する。これにより、非常に大きなデータソースの読み込みをサポートします。データはネットワークからXMLリーダーを通してストリーミングされるため、XMLの全文をメモリに保持する必要はない。他のコンテキストでは、同様のアプローチにより、ユーザーがこのようなインクリメンタルな読み込みを中断することができる。
構築
RSSListing::RSSListing(const QString &url, QWidget *parent) : QWidget(parent), currentReply(0) { connect(&manager, &QNetworkAccessManager::finished, this, &RSSListing::finished); lineEdit = new QLineEdit(this); lineEdit->setText(url); connect(lineEdit, &QLineEdit::returnPressed, this, &RSSListing::fetch); fetchButton = new QPushButton(tr("Fetch"), this); connect(fetchButton, &QPushButton::clicked, this, &RSSListing::fetch); treeWidget = new QTreeWidget(this); connect(treeWidget, &QTreeWidget::itemActivated, // Open the link in the browser: this, [](QTreeWidgetItem *item) { QDesktopServices::openUrl(QUrl(item->text(1))); }); treeWidget->setHeaderLabels(QStringList { tr("Title"), tr("Link") }); treeWidget->header()->setSectionResizeMode(QHeaderView::ResizeToContents); QHBoxLayout *hboxLayout = new QHBoxLayout; hboxLayout->addWidget(lineEdit); hboxLayout->addWidget(fetchButton); QVBoxLayout *layout = new QVBoxLayout(this); layout->addLayout(hboxLayout); layout->addWidget(treeWidget); setWindowTitle(tr("RSS listing example")); resize(640, 480); }
コンストラクタは、ウィジェットの様々なコンポーネントをセットアップし、それらの様々なシグナルを、それらを処理するために使用するスロットに接続する。
ユーザーインターフェースは、ラインエディット、プッシュボタン、リストビューウィジェットで構成されます。ラインエディットはフェッチするURLを入力するために使われ、プッシュボタンは更新のフェッチプロセスを開始します。ラインエディットはデフォルトでは空ですが、今回のmain()
のように、コンストラクタの呼び出し側でオーバーライドできます。いずれにせよ、ユーザーはデフォルトを別のRSSフィードのURLに置き換えることができます。
リストビューには、RSSフィードで更新された項目が表示されます。そのうちのひとつをダブルクリックすると、QDesktopServices::openUrl() を使ってユーザーのブラウザやその他のユーザーエージェントにその URL が送信されます。
スロット
voidRSSListing::fetch() { lineEdit->setReadOnly(true); fetchButton->setEnabled(false); treeWidget->clear(); get(QUrl(lineEdit->text())); }voidRSSListing::consumeData() {intstatusCode= currentReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();if(statusCode>= 200 &&statusCode< 300) parseXml(); }voidRSSListing::error(QNetworkReply::NetworkError){ qWarning("error retrieving RSS feed"); xml.clear(); currentReply->disconnect(this); currentReply->deleteLater(); currentReply=nullptr; }voidRSSListing::finished(QNetworkReply*reply) { Q_UNUSED(reply); lineEdit->setReadOnly(false); fetchButton->setEnabled(true); }
すべてのスロットは、難しい処理をプライベート・メソッドに委譲することでシンプルに保たれている。
ユーザが "Fetch "ボタンをクリックするか、ラインエディットでリターンキーを押すかしてURLの入力を完了すると、fetch()
スロットは "Fetch "ボタンを無効にし、ラインエディットのさらなる編集を無効にします。これは利用可能な更新の表示を消去し、HTTP GETリクエストの開始をget()
。
データが受信されると、ネットワーク・リプライはreadyRead() シグナルをトリガーし、get()
はconsumeData()
スロットに接続する。このシグナルは、レスポンスのステータスコードが正常かどうかをチェックし、正常であればparseXml()
。
ネットワーク・リプライにエラーが発生した場合、このエラーはerror()
スロットに送られる。 スロットはエラーを報告し、XMLストリーム・リーダーをクリアしてから、リプライとの接続を切断して削除する。
ネットワーク・リプライが完了すると(成功したかどうかにかかわらず)、finished()
スロットは、行編集と "Fetch" ボタンを再度有効にして、フェッチする新しい URL を受け入れる準備ができるように UI を復元します。
get()メソッド
void RSSListing::get(const QUrl &url) { if (currentReply) { currentReply->disconnect(this); currentReply->deleteLater(); } currentReply = url.isValid() ? manager.get(QNetworkRequest(url)) : nullptr; if (currentReply) { connect(currentReply, &QNetworkReply::readyRead, this, &RSSListing::consumeData); connect(currentReply, &QNetworkReply::errorOccurred, this, &RSSListing::error); } xml.setDevice(currentReply); // Equivalent to clear() if currentReply is null. }
get()
メソッドは、fetch()
スロットが HTTP GET リクエストを開始するために使用します。最初にXMLストリームリーダーをクリアし、応答が現在アクティブであれば、それを切断して削除する。渡されたURLが有効であれば、ネットワークアクセスマネージャにGETを要求する。ネットワーク・リプライ・オブジェクトはQIODevice
でもあり、そこからデータを読み取ることができます。
parseXml() メソッド
voidRSSListing::parseXml() {while(!xml.atEnd()) { xml.readNext();if(xml.isStartElement()) {if(xml.name()==u"item") { linkString=xml.attributes().value("rss:about").toString(); titleString.clear(); } currentTag=xml.name().toString(); }else if(xml.isEndElement()) {if(xml.name()==u"item") { *item = new item. QTreeWidgetItem*item = newQTreeWidgetItemitem->setText(0,titleString); item->setText(1,linkString); treeWidget->addTopLevelItem(item); } }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(); }
データが受信され、XMLストリームリーダーが利用できるようになると、parseXml()
はXMLストリームから読み込み、item
要素とその中にあるtitle
要素とlink
要素をチェックする。item
のrss:about
属性をツリービューの Link 列の URL として使用し、link
要素の内容を使用します。title
要素の内容をツリービューの Title 列に使用します。各item
要素が閉じると、その詳細がツリーウィジェットの新しい行になり、抽出されたタイトルと URL が Title 列と Link 列に表示されます。
linkString
titleString
currentTag
RSSListing
新しいデータが到着すると、このメソッドが繰り返し呼び出され、受信したデータの 1 つのチャンクが、後のチャンクが到着するまで完了しない要素を開始する可能性があるためです。これによってパーサーは、すべてのデータが到着するまで待たずに、データが到着するたびに非同期で操作できるようになります。
© 2025 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.