Added thread support for the background poller.

Leonardo Robol [2011-10-23 16:07]
Added thread support for the background poller.
Filename
Larss.pro
feedmodel.h
feedpoller.cpp
feedpoller.h
mainwindow.cpp
mainwindow.h
rssparser.cpp
rssparser.h
diff --git a/Larss.pro b/Larss.pro
index 5c684a3..74bcf7e 100644
--- a/Larss.pro
+++ b/Larss.pro
@@ -13,10 +13,12 @@ TEMPLATE = app
 SOURCES += main.cpp\
         mainwindow.cpp \
     feedmodel.cpp \
-    rssparser.cpp
+    rssparser.cpp \
+    feedpoller.cpp

 HEADERS  += mainwindow.h \
     feedmodel.h \
-    rssparser.h
+    rssparser.h \
+    feedpoller.h

 FORMS    += mainwindow.ui
diff --git a/feedmodel.h b/feedmodel.h
index 232c771..0e6923d 100644
--- a/feedmodel.h
+++ b/feedmodel.h
@@ -4,6 +4,7 @@
 #include <QObject>
 #include <QAbstractItemModel>
 #include <QSqlDatabase>
+#include <QtXml>

 namespace Larss {

@@ -70,8 +71,6 @@ public:
      */
     bool addFeed (QString name, QString url, quint32 category_id);

-
-
 signals:

 public slots:
diff --git a/feedpoller.cpp b/feedpoller.cpp
new file mode 100644
index 0000000..94f40a5
--- /dev/null
+++ b/feedpoller.cpp
@@ -0,0 +1,134 @@
+#include "feedpoller.h"
+#include <QtXml>
+#include "feedmodel.h"
+
+using namespace Larss;
+
+FeedPoller::FeedPoller(QObject *parent, RssParser *parser, FeedModel *model) :
+    QThread(parent)
+{
+    this->parser = parser;
+    this->model = model;
+
+    workQueue = new QList<QModelIndex> ();
+    nowLoading = 0;
+
+    rssContent = new QHash<quint32, QString>();
+    poll_active = true;
+
+    // Create the QNetworkAccessManager and connect the loaded signal
+    // with our handler.
+    manager = new QNetworkAccessManager ();
+    manager->connect(manager, SIGNAL(finished(QNetworkReply*)),
+                     this, SLOT(networkManagerReplyFinished(QNetworkReply*)));
+}
+
+void
+FeedPoller::run()
+{
+
+    // Create the timer that will call the function every second.
+    QTimer *timer = new QTimer ();
+    timer->setInterval(800);
+    timer->connect(timer, SIGNAL(timeout()),
+                   this, SLOT(poll()));
+    timer->start();
+
+    QThread::exec();
+}
+
+bool
+FeedPoller::poll()
+{
+    // Poll indefinitely until we are requested to exit.
+    if (nowLoading == 0)
+    {
+        if (workQueue->isEmpty())
+            return false;
+        else
+        {
+            QModelIndex next_item = workQueue->takeFirst();
+            nowLoading = next_item.internalId();
+            manager->get(QNetworkRequest(QUrl(model->getUrl(next_item))));
+            return true;
+        }
+    }
+}
+
+void
+FeedPoller::stopPolling ()
+{
+    poll_active = false;
+}
+
+void
+FeedPoller::queueWork(const QModelIndex &index)
+{
+    workQueue->append(index);
+}
+
+void
+FeedPoller::networkManagerReplyFinished(QNetworkReply *reply)
+{
+    rssContent->insert (nowLoading, reply->readAll());
+
+    // Now update the database with the new data obtained.
+    QDomDocument doc;
+    if (doc.setContent(rssContent->value(nowLoading)))
+    {
+        QDomElement doc_el = doc.documentElement();
+        QDomNodeList items = doc_el.elementsByTagName("item");
+
+        for (quint32 i = 0; i < items.length(); i++)
+        {
+            // Get the i-th news
+            QDomNode item = items.item(i);
+            QDomElement element = item.toElement();
+
+            // Get the data in it
+            QString link = element.elementsByTagName("link").item(0).firstChild().nodeValue();
+            QString title = element.elementsByTagName("title").item(0).firstChild().nodeValue();
+            QString description = element.elementsByTagName("description").item(0).firstChild().nodeValue();
+
+            // We should enable this for RSS 2.0
+            // QString guid = element.elementsByTagName("guid").item(0).firstChild().nodeValue();
+            // QString pubDate = element.elementsByTagName("pubDate").item(0).firstChild().nodeValue();
+
+            // Try to catch other news_feed with the same link
+            QSqlQuery query(parser->db);
+            query.prepare ("SELECT id from news WHERE feed=:feed AND link=:link;");
+            query.bindValue("feed", nowLoading);
+            query.bindValue("link", link);
+            if (query.exec())
+            {
+                if (!query.first())
+                {
+                    // That means that no results were found, so let's insert this one.
+                    QSqlRecord record = parser->record();
+                    record.setValue("time", 0);
+                    record.setValue("read", 0);
+                    record.setValue("title", title);
+                    record.setValue("link", link);
+                    record.setValue("description", description);
+                    record.setValue("feed", nowLoading - FEEDMODEL_MAX_CATEGORIES);
+
+                    if (!parser->insertRecord(-1, record))
+                        qDebug () << "Error inserting record";
+                }
+            }
+        }
+    }
+    else
+        qDebug () << "Error parsing the document";
+
+    nowLoading = 0;
+    return;
+
+    // Check if there is work in the queue
+    if (!workQueue->isEmpty())
+    {
+        QModelIndex next_item = workQueue->takeFirst();
+        nowLoading = next_item.internalId();
+        manager->get(QNetworkRequest(QUrl(model->getUrl(next_item))));
+    }
+}
diff --git a/feedpoller.h b/feedpoller.h
new file mode 100644
index 0000000..4494249
--- /dev/null
+++ b/feedpoller.h
@@ -0,0 +1,74 @@
+#ifndef FEEDPOLLER_H
+#define FEEDPOLLER_H
+
+#include <QObject>
+#include <QThread>
+#include <QtSql>
+#include <QtNetwork>
+#include "rssparser.h"
+#include "feedmodel.h"
+
+namespace Larss {
+
+    class FeedPoller : public QThread
+    {
+        Q_OBJECT
+    public:
+        explicit FeedPoller(QObject *parent, RssParser* parser, FeedModel *model);
+        void queueWork (const QModelIndex& index);
+        void stopPolling ();
+
+    signals:
+
+    public slots:
+        void networkManagerReplyFinished(QNetworkReply* reply);
+        bool poll();
+
+    private:
+        /**
+         * @brief The parser of this instance of Larss.
+         */
+        RssParser *parser;
+
+        /**
+         * @brief The FeedModel of this instance of Larss.
+         */
+        FeedModel *model;
+
+        /**
+         * @brief The content of the rss loaded from the various
+         * items in the feedmodel.
+         */
+        QHash<quint32, QString> *rssContent;
+
+        /**
+         * @brief The NetworkAccessManager that will be used to retrieve
+         * the data from sourceUrl.
+         */
+        QNetworkAccessManager *manager;
+
+        /**
+         * @brief An index of the RSS that is currently being loaded, or 0
+         * if there is nothing in the queue.
+         */
+        quint32 nowLoading;
+
+        /**
+         * @brief Queue of item that needs to be refreshed.
+         */
+        QList<QModelIndex> *workQueue;
+
+        /**
+         * @brief If the thread is requested to polling or not.
+         */
+        bool poll_active;
+
+        /**
+         * @brief Real work of the thread.
+         */
+        void run();
+
+    };
+}
+
+#endif // FEEDPOLLER_H
diff --git a/mainwindow.cpp b/mainwindow.cpp
index 3174d4a..86878b6 100644
--- a/mainwindow.cpp
+++ b/mainwindow.cpp
@@ -30,6 +30,9 @@ MainWindow::MainWindow(QWidget *parent) :
     ui->newsTableView->setColumnHidden(4, true); // Description
     ui->newsTableView->setColumnHidden(6, true); // Read state
     ui->newsTableView->verticalHeader()->setHidden(true);
+
+    poller = new FeedPoller (this, rssParser, feedModel);
+    poller->start();
 }

 MainWindow::~MainWindow()
@@ -40,7 +43,7 @@ MainWindow::~MainWindow()

 void MainWindow::do_exit()
 {
-    // Some cleanup will be need here in the future
+    poller->stopPolling();
     QApplication::exit();
 }

@@ -53,7 +56,7 @@ void MainWindow::on_actionExit_activated()
 void Larss::MainWindow::on_feedTreeView_clicked(const QModelIndex &index)
 {
     // Trigger refresh of selected item
-    rssParser->loadItem(index);
+    poller->queueWork(index);

     // Set the active filter
     quint64 feed_id;
diff --git a/mainwindow.h b/mainwindow.h
index 40338d0..11d1003 100644
--- a/mainwindow.h
+++ b/mainwindow.h
@@ -4,6 +4,7 @@
 #include <QMainWindow>
 #include "rssparser.h"
 #include "feedmodel.h"
+#include "feedpoller.h"
 #include <QSqlDatabase>

 namespace Ui {
@@ -34,6 +35,7 @@ private:
     QSqlDatabase db;
     FeedModel *feedModel;
     RssParser *rssParser;
+    FeedPoller *poller;
 };

 }
diff --git a/rssparser.cpp b/rssparser.cpp
index 04630c5..a9a1cbf 100644
--- a/rssparser.cpp
+++ b/rssparser.cpp
@@ -26,20 +26,7 @@ Larss::RssParser::RssParser(QSqlDatabase db, FeedModel *model, QObject *parent)

     // Select manual submit so user cannot modify content directly
     setEditStrategy(QSqlTableModel::OnRowChange);
-
-    // Set the source to an empty string, that means no source
-    rssContent = new QHash<quint32, QString>();
-    workQueue = new QList<QModelIndex> ();
     this->model = model;
-    nowLoading = 0;
-    activeItem = 0;
-
-    // Create the QNetworkAccessManager and connect the loaded signal
-    // with our handler.
-    manager = new QNetworkAccessManager (this);
-    manager->connect(manager, SIGNAL(finished(QNetworkReply*)),
-                     this, SLOT(networkManagerReplyFinished(QNetworkReply*)));
-
     select();
 }

@@ -81,10 +68,6 @@ Larss::RssParser::headerData(int section, Qt::Orientation orientation, int role)

 Larss::RssParser::~RssParser()
 {
-    db.close();
-    delete manager;
-    delete rssContent;
-    delete workQueue;
 }

 QVariant
@@ -105,95 +88,6 @@ Larss::RssParser::data(const QModelIndex &idx, int role) const
     return QSqlTableModel::data(idx, role);
 }

-void
-Larss::RssParser::loadItem(const QModelIndex &index)
-{
-    if (index.internalId() < FEEDMODEL_MAX_CATEGORIES)
-        return;
-
-    if (nowLoading != 0)
-    {
-        // We should queue this work and wait till there is
-        // the networkmanager free to do it.
-        workQueue->append(index);
-    }
-    else
-    {
-        nowLoading = index.internalId();
-        manager->get(QNetworkRequest(QUrl(model->getUrl(index))));
-    }
-}
-
-void
-Larss::RssParser::networkManagerReplyFinished(QNetworkReply *reply)
-{
-    rssContent->insert (nowLoading, reply->readAll());
-
-    // Now update the database with the new data obtained.
-    QDomDocument doc;
-    if (doc.setContent(rssContent->value(nowLoading)))
-    {
-        QDomElement doc_el = doc.documentElement();
-        QDomNodeList items = doc_el.elementsByTagName("item");
-
-        for (quint32 i = 0; i < items.length(); i++)
-        {
-            // Get the i-th news
-            QDomNode item = items.item(i);
-            QDomElement element = item.toElement();
-
-            // Get the data in it
-            QString link = element.elementsByTagName("link").item(0).firstChild().nodeValue();
-            QString title = element.elementsByTagName("title").item(0).firstChild().nodeValue();
-            QString description = element.elementsByTagName("description").item(0).firstChild().nodeValue();
-
-            // We should enable this for RSS 2.0
-            // QString guid = element.elementsByTagName("guid").item(0).firstChild().nodeValue();
-            // QString pubDate = element.elementsByTagName("pubDate").item(0).firstChild().nodeValue();
-
-            // Try to catch other news_feed with the same link
-            QSqlQuery query(db);
-            query.prepare ("SELECT id from news WHERE feed=:feed AND link=:link;");
-            query.bindValue("feed", nowLoading);
-            query.bindValue("link", link);
-            if (query.exec())
-            {
-                if (!query.first())
-                {
-                    // That means that no results were found, so let's insert this one.
-                    QSqlRecord record = this->record();
-                    record.setValue("time", 0);
-                    record.setValue("read", 0);
-                    record.setValue("title", title);
-                    record.setValue("link", link);
-                    record.setValue("description", description);
-                    record.setValue("feed", nowLoading - FEEDMODEL_MAX_CATEGORIES);
-
-                    if (!insertRecord(-1, record))
-                        qDebug () << "Error inserting record";
-                }
-            }
-        }
-    }
-    else
-        qDebug () << "Error parsing the document";
-
-
-    if (activeItem == nowLoading)
-    {
-        // We shall call something like dataChanged in here
-    }
-    nowLoading = 0;
-
-    // Check if there is work in the queue
-    if (!workQueue->isEmpty())
-    {
-        QModelIndex next_item = workQueue->takeFirst();
-        nowLoading = next_item.internalId();
-        manager->get(QNetworkRequest(QUrl(model->getUrl(next_item))));
-    }
-}
-
 QString
 Larss::RssParser::getLink(const QModelIndex &index)
 {
diff --git a/rssparser.h b/rssparser.h
index 1a2a732..4b42c93 100644
--- a/rssparser.h
+++ b/rssparser.h
@@ -24,11 +24,6 @@ namespace Larss {
         ~RssParser();

         /**
-         * @brief Reload the feed from a given NewsFeed.
-         */
-        void loadItem (const QModelIndex& index);
-
-        /**
          * @brief Function that tells the views that use this model
          * what to display in the title of the columns.
          */
@@ -61,50 +56,21 @@ namespace Larss {
          */
         void selectActiveFeed (quint64 feed_id);

-
-    signals:
-
-    public slots:
-        void networkManagerReplyFinished (QNetworkReply* reply);
-
-    private:
         /**
          * @brief Database where all the news will be loaded and saved.
          */
         QSqlDatabase db;

-        /**
-         * @brief The url of the current rss feed.
-         */
-        FeedModel *model;
-
-        /**
-         * @brief The content of the rss loaded from the various
-         * items in the feedmodel.
-         */
-        QHash<quint32, QString> *rssContent;
-
-        /**
-         * @brief The NetworkAccessManager that will be used to retrieve
-         * the data from sourceUrl.
-         */
-        QNetworkAccessManager *manager;
+    signals:

-        /**
-         * @brief An index of the RSS that is currently being loaded, or 0
-         * if there is nothing in the queue.
-         */
-        quint32 nowLoading;
+    public slots:

-        /**
-         * @brie A pointer to the active news feed.
-         */
-        quint32 activeItem;
+    private:

         /**
-         * @brief Queue of item that needs to be refreshed.
+         * @brief The FeedModel
          */
-        QList<QModelIndex> *workQueue;
+        FeedModel *model;
     };

 }
ViewGit