How to use streaming api



  • I am now making Mastodon client application.
    However, I do not know how to use Mastodon's streaming api from Qt.
    I wrote following code.
    And manager->get(req) in MainWindow::MainWindow() seems to be connected to server.
    But I do not know how to acquire data asynchronously.

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    
    #include <QLabel>
    #include <QFrame>
    
    #include <QNetworkAccessManager>
    #include <QNetworkReply>
    
    #include <QJsonObject>
    #include <QJsonDocument>
    #include <QJsonParseError>
    #include <QJsonArray>
    
    #include <vector>
    
    #include "IconWidget.h"
    #include <QHBoxLayout>
    
    #include "defineconstants.h"
    
    #include <QUrlQuery>
    #include <QSystemTrayIcon>
    
    #include <QTextCodec>
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    
        manager = new QNetworkAccessManager(this);
        connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
    
        ui->menuStartLocalTimeline->setEnabled(false);
        ui->menuStopLocalTimeline->setEnabled(true);
        //Temporarily comment out
        //mLocalTimelineTimer = startTimer(LOCAL_TIMELINE_UPDATE_INTERVAL);
    
        connect(ui->menuStartLocalTimeline, SIGNAL(triggered(bool)), this, SLOT(startLocalTimelineTimer()));
        connect(ui->menuStopLocalTimeline, SIGNAL(triggered(bool)), this, SLOT(stopLocalTimelineTimer()));
    
        //Temporarily comment out
        //manager->get(QNetworkRequest(QUrl("https://mstdn.jp/api/v1/timelines/public?local=\"true\"")));
    
        trayIcon = new QSystemTrayIcon(this);
    
        QFile file("your_usercred.txt");
        if (!file.open(QIODevice::ReadOnly)) {
            if (DEBUG_MODE) {
                qDebug() << "file could not open.";
            }
        }
        QTextStream in(&file);
        mAccessToken = in.readLine();
    
        //QString urlStr = "https://streaming.mstdn.jp/api/v1/streaming/public?access_token" + mAccessToken + "&stream=public";
        QString urlStr = "https://streaming.mstdn.jp/api/v1/streaming/public";
        QNetworkRequest req(urlStr);
        QString headerText = "Bearer " + mAccessToken;
        req.setRawHeader("Authorization", headerText.toUtf8());
        manager->get(req);
    
        qDebug() << QSslSocket::supportsSsl();
    }
    
    void MainWindow::timerEvent(QTimerEvent *e)
    {
        if (e->timerId() == mLocalTimelineTimer) {
            manager->get(QNetworkRequest(QUrl("https://mstdn.jp/api/v1/timelines/public?local=\"true\"")));
        }
    }
    
    void MainWindow::startLocalTimelineTimer()
    {
        mLocalTimelineTimer = startTimer(LOCAL_TIMELINE_UPDATE_INTERVAL);
        ui->menuStartLocalTimeline->setEnabled(false);
        ui->menuStopLocalTimeline->setEnabled(true);
    }
    
    void MainWindow::stopLocalTimelineTimer()
    {
        killTimer(mLocalTimelineTimer);
        ui->menuStartLocalTimeline->setEnabled(true);
        ui->menuStopLocalTimeline->setEnabled(false);
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::on_pushButton_2_clicked()
    {
        manager->get(QNetworkRequest(QUrl("https://mstdn.jp/api/v1/timelines/public?local=\"true\"")));
    }
    
    void MainWindow::replyFinished(QNetworkReply *reply)
    {
        if (DEBUG_MODE) {
            qDebug() << "MainWindow::replyFinished() called.";
            qDebug() << "url" << reply->url();
            qDebug() << reply->errorString();
        }
    
        if (reply->url().toString() == "https://mstdn.jp/api/v1/statuses") {
            return;
        }
    
        if (reply->error() == QNetworkReply::NoError) {
            QByteArray byteArray = reply->readAll();
            //ui->textEdit->setPlainText(byteArray);
    
            QJsonParseError error;
            QJsonDocument jsonDocument = QJsonDocument::fromJson(byteArray, &error);
            qDebug() << error.errorString();
    
            /*
            qDebug() << "Is Object? " << jsonDocument.isObject();
            qDebug() << "Is Array? " << jsonDocument.isArray();
            */
    
            QJsonArray jsonArray = jsonDocument.array();
            //qDebug() << jsonArray.size();
            QStringList keys = jsonArray[0].toObject().keys();
            qDebug() << keys;
            foreach (QString key, keys) {
                qDebug() << "key =" << key;
                QString value = jsonArray[0].toObject().value(key).toString();
                qDebug() << "value =" << value;
            }
    
            //std::vector<QHBoxLayout*> tootLayouts;
            static QVBoxLayout *timelineLayout = new QVBoxLayout();
            //for (int i=0; i<jsonArray.size(); i++) {
            for (int i=jsonArray.size()-1; i>=0; i--) {
                if (i == 0) {
                    QString *title = new QString("タイトルは長いんだよー");
                    trayIcon->show();
                    trayIcon->showMessage(*title, jsonArray[i].toObject().value("content").toString());
                }
    
                QFrame *tootFrame = new QFrame();
                tootFrame->setObjectName("tootFrame");
                tootFrame->setStyleSheet("#tootFrame { border: 1px solid; background-color: white; }");
                //tootFrame->maximumWidth = ui->scrollAreaWidgetContents->width();
                //tootFrame->setMaximumWidth(ui->scrollAreaWidgetContents->width());
                QHBoxLayout *tootLayout = new QHBoxLayout();
    
                QVBoxLayout *tootContentsLayout = new QVBoxLayout();
                QLabel *name = new QLabel(jsonArray[i].toObject().value("account").toObject().value("display_name").toString(), this);
    
                QString tootHtml = jsonArray[i].toObject().value("content").toString();
                QRegExp br("<br />");
                tootHtml.replace(br, "\n");
                //QRegExp tag("<[^>]*>");
                //tootHtml.remove(tag);
                QLabel *toot = new QLabel(tootHtml);
                toot->setWordWrap(true);
                toot->setTextFormat(Qt::PlainText);
    
                QUrl *iconUrl = new QUrl(jsonArray[i].toObject().value("account").toObject().value("avatar").toString());
                //QUrl *iconUrl = new QUrl("https://media.mstdn.jp/images/accounts/avatars/000/006/953/original/b0a6986188b3fdf7.png");
                IconWidget *iconWidget = new IconWidget(iconUrl, nullptr);
                tootLayout->addWidget(iconWidget);
    
                tootContentsLayout->addWidget(name);
                tootContentsLayout->addWidget(toot);
                tootLayout->addLayout(tootContentsLayout);
                tootFrame->setLayout(tootLayout);
    
                //timelineLayout->addLayout(tootLayout);
                //timelineLayout->addWidget(tootFrame);
                timelineLayout->insertWidget(0, tootFrame);
            }
            //ui->tootList->setLayout(timelineLayout);
            //ui->scrollAreaWidgetContents->setLayout(timelineLayout);
            ui->localTimelineContents->setLayout(timelineLayout);
        }
    }
    
    void MainWindow::on_tootButton_clicked()
    {
        QUrlQuery params;
        QNetworkRequest req(QUrl("https://mstdn.jp/api/v1/statuses"));
    
        /*
        QFile file("your_usercred.txt");
        if (!file.open(QIODevice::ReadOnly)) {
            if (DEBUG_MODE) {
                qDebug() << "file could not open.";
            }
        }
        QTextStream in(&file);
        QString usercred = in.readLine();
        */
        QString usercred = mAccessToken;
    
        QString header_text = "Bearer " + usercred;
        req.setRawHeader("Authorization", header_text.toUtf8());
        params.addQueryItem("visibility", "");
        params.addQueryItem("status", ui->tootText->toPlainText());
    
        manager->post(req, params.toString(QUrl::FullyEncoded).toUtf8());
    }
    

  • Moderators

    @wabisuke2718 Please take a look at documentation: http://doc.qt.io/qt-5/qnetworkaccessmanager.html
    You will find there this example:

    QNetworkRequest request;
    request.setUrl(QUrl("http://qt-project.org"));
    request.setRawHeader("User-Agent", "MyOwnBrowser 1.0");
    
    QNetworkReply *reply = manager->get(request);
    connect(reply, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
    connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
            this, SLOT(slotError(QNetworkReply::NetworkError)));
    connect(reply, SIGNAL(sslErrors(QList<QSslError>)),
            this, SLOT(slotSslErrors(QList<QSslError>)));
    

    So, to get the data asynchronously you connect a slot to QNetworkReply::readyRead() signal and in that signal you can get the data currently available using readAll(). Keep in mind that you most probably will not get all the data at once but in chunks - that means your slot will be called several times.



  • I solved this problem.
    I should have used QWebSocket.


Log in to reply