このページでは

シンプルなRSS一覧表示アプリケーション

ネットワークリソースを取得して表示する方法の実例。

この例では、RSS一覧表示アプリケーションを例に、ユーザーがリクエストしたリソースを取得し、レスポンスに含まれるデータを表示する方法を示します。(RDF Site Summary、またはReally Simple Syndicationは、Webサイトの更新情報を伝達するための標準フォーマットです。 詳細は https://www.rssboard.org/rss-specification を参照してください。)この例の焦点はネットワークの利用方法にあるため、図示されているユーザーインターフェースはシンプルなものですが、本格的なRSSリーダーであれば、当然ながらより洗練されたインターフェースが求められるでしょう。

また、この例では、データの受信と同時に非同期で解析を行う方法も示しています。メンバー変数に状態を保持することで、増分パーサーがネットワーク経由で到着するデータのチャンクを順次処理できるようになります。 解析対象のコンテンツの構成要素は、あるデータチャンクで開始されていても、後のチャンクで完了する場合があり、そのためパーサーは呼び出し間で状態を保持する必要があります。

このアプリケーションは、指定されたURL(この場合はQt Blog)から情報を取得し、利用可能なXMLデータを解析します。

メインプログラムは極めて簡潔です。単に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を指定するためのシンプルなユーザーインターフェースを提供し、利用可能な更新情報が表示された後は、更新された項目のダウンロードを制御します。URLの入力にはQLineEdit が、取得後の結果の表示には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() で実施されているように、コンストラクタを呼び出す側がこれを上書きすることができます。 いずれにせよ、ユーザーはデフォルトのURLを別の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が有効な場合、ネットワークアクセスマネージャーにそのURLへのGETリクエストを実行するよう依頼します。その後、関連するスロットを返答(ある場合)のシグナルに接続し、返答からデータを読み取るようにXMLストリームリーダーを設定します。ネットワークの返答オブジェクトも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") {

                QTreeWidgetItem*item = newQTreeWidgetItem;
                item->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 クラスのメンバ変数です。これは、新しいデータが到着するたびにこのメソッドが繰り返し呼び出される可能性があり、受信したデータの一部が、後のデータが到着するまで完了しない要素の先頭となる場合があるためです。 これにより、パーサーはすべてのデータが到着するのを待つ必要がなく、データが到着するにつれて非同期に動作できるようになります。

サンプルプロジェクト @ code.qt.io

関連項目: QNetworkReply およびQXmlStreamReader 。

© 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.