Commits

Atsushi Odagiri committed a366f53

feedreader

Comments (0)

Files changed (3)

+# -*- coding:utf-8 -*-
+import os
+from PyQt4 import QtGui, QtCore, uic, QtWebKit
+import feedparser
+from feeds import *
+
+uifile = os.path.join(os.path.dirname(__file__), 'mainwindow.ui')
+Form, Base = uic.loadUiType(uifile)
+
+class MainWindow(Base, Form):
+    def __init__(self):
+        super(Base, self).__init__()
+        Form.setupUi(self, self)
+        self.filename = None
+        self.webView = QtWebKit.QWebView(self.feedEntryViewContainer)
+        self.webView.setHtml(u"<html><body><h1>Feed Reader</h1><p>This is contents.</p></body></html>")
+
+        self.setupFeedsTree()
+        self.setupEntryList()
+
+
+    def setupEntryList(self):
+        self.entryModel = EntryListModel(self)
+        self.entriesView.setModel(self.entryModel)
+
+    def setupFeedsTree(self):
+        urls = [
+            (u'Aodag Blog', 'http://feeds.feedburner.jp/AodagBlog'),
+            (u'aodagの日記', 'http://d.hatena.ne.jp/aodag/rss'),
+            ]
+        blog = FeedCategory('blog', [Feed(n, u) for n, u in urls])
+        urls = [
+            (u'Bicking Blog', 'http://blog.ianbicking.org/feed/atom/'),
+            ]
+        python = FeedCategory('python', [Feed(n, u) for n, u in urls])
+        
+        self.categoryTreeModel = FeedListModel([blog, python], self.categoryTreeView)
+        self.categoryTreeView.setModel(self.categoryTreeModel)
+
+    def on_categoryTreeView_activated(self, index):
+        entries = index.internalPointer().entries
+        self.entryModel.set_entries(list(entries))
+
+    def on_entriesView_activated(self, index):
+        entry = index.internalPointer()
+        self.webView.setHtml('<h1>%s</h1><div>%s</div>' % (entry.title, entry.content[0].value))
+
+def main():
+    import sys
+    app = QtGui.QApplication(sys.argv)
+
+
+    win = MainWindow()
+    win.show()
+    sys.exit(app.exec_())
+
+if __name__ == '__main__':
+    main()
+# -*- coding:utf-8 -*-
+
+from PyQt4 import QtGui, QtCore
+
+import feedparser
+import itertools
+
+class EntryListModel(QtCore.QAbstractItemModel):
+    headers = (QtCore.QVariant(u'Title'), QtCore.QVariant(u'Date'))
+    def __init__(self, parent):
+        super(EntryListModel, self).__init__(parent)
+        self._entries = []
+
+    def columnCount(self, parent):
+        return len(self.headers)
+
+    def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
+        if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
+            return self.headers[section]
+
+        return QtCore.QVariant()
+            
+    def rowCount(self, parent):
+        if parent.isValid():
+            return 0
+        return len(self._entries)
+
+    def index(self, row, column, parent):
+        return self.createIndex(row, column, self._entries[row])
+
+    def data(self, index, role=QtCore.Qt.DisplayRole):
+        if not index.isValid():
+            return QtCore.QVariant()
+        if role != QtCore.Qt.DisplayRole:
+            return QtCore.QVariant()
+        entry = self._entries[index.row()]
+        if index.column() == 0:
+            return QtCore.QVariant(entry.title)
+        elif index.column() == 1:
+            return QtCore.QVariant(entry.date)
+        else:
+            return QtCore.QVariant()
+
+    def parent(self, item):
+        return QtCore.QModelIndex()
+
+
+    def set_entries(self, entries):
+        self._entries = entries
+        self.emit(QtCore.SIGNAL('modelReset()'))
+
+    def get_entries(self):
+        return self._entries
+
+
+
+class Feed(object):
+    def __init__(self, name=None, url=None):
+        self.name = name
+        self.url = url
+        self.category = None
+
+    @property
+    def entries(self):
+        d = feedparser.parse(self.url)
+        return d.entries
+
+    @property
+    def children(self):
+        return []
+
+
+    @property
+    def index(self):
+        for i in xrange(len(self.category.children)):
+            if self.category.children[i] == self:
+                return i
+
+    @property
+    def level(self):
+        return self.category.level + 1
+
+class FeedCategory(object):
+    def __init__(self, name=None, feeds=None):
+        self.name = name
+        self.category = None
+        if not feeds:
+            self.feeds = []
+        else:
+            self.feeds = feeds
+            for f in self.feeds:
+                f.category = self
+
+    @property
+    def index(self):
+        if not self.category:
+            return 0
+        for i in xrange(len(self.category.children)):
+            if self.category.children[i] == self:
+                return i
+
+    @property
+    def children(self):
+        return self.feeds
+
+    @property
+    def url(self):
+        return u''
+
+    @property
+    def entries(self):
+        return itertools.chain(*[c.entries for c in self.children])
+
+    @property
+    def level(self):
+
+        if not self.category:
+            return 0
+        return self.category.level + 1
+
+
+class FeedListModel(QtCore.QAbstractItemModel):
+    def __init__(self, feeds, parent=None):
+        super(FeedListModel, self).__init__(parent)
+        self.headers = [QtCore.QVariant('Name'),
+                        QtCore.QVariant('URL')]
+        self.feeds = feeds
+
+
+    def columnCount(self, parent):
+        return 2
+
+    def index(self, row, column, parent=QtCore.QModelIndex()):
+
+        if not parent.isValid():
+            return self.createIndex(row, column, self.feeds[row])
+        else:
+            parentItem = parent.internalPointer()
+            if len(parentItem.children) > row:
+                return self.createIndex(row, column, parentItem.children[row])
+            else:
+                return self.createIndex(row, column)
+                
+            
+
+    def parent(self, index):
+        if index.internalPointer() in self.feeds:
+            
+            return QtCore.QModelIndex()
+
+        item = index.internalPointer()
+        if item is None:
+            return QtCore.QModelIndex()
+        if item.category is None:
+            return QtCore.QModelIndex()
+            
+        parent = item.category
+        
+        return self.createIndex(parent.index, max(parent.level, 0), parent)
+
+    def rowCount(self, parent):
+        item = parent.internalPointer()
+        if item:
+            return len(item.children)
+
+        return len(self.feeds)
+
+    def data(self, index, role=QtCore.Qt.DisplayRole):
+
+        if role != QtCore.Qt.DisplayRole:
+            return QtCore.QVariant()
+
+        item = index.internalPointer()
+        if item is None:
+            return QtCore.QVariant()
+
+        if index.column() == 0:
+            return QtCore.QVariant(item.name)
+        elif index.column() == 1:
+            return QtCore.QVariant(item.url)
+        else:
+            return QtCore.QVariant()
+
+    def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
+        if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
+            return self.headers[section]
+
+        return QtCore.QVariant()
+            
+        
+
+if __name__ == '__main__':
+    import sys, os
+    app = QtGui.QApplication(sys.argv)
+    v = QtGui.QTreeView()
+    urls = [
+        (u'Aodag Blog', 'http://feeds.feedburner.jp/AodagBlog'),
+        (u'aodagの日記', 'http://d.hatena.ne.jp/aodag/rss'),
+        ]
+    blog = FeedCategory('blog', [Feed(n, u) for n, u in urls])
+    root = FeedCategory('root', [blog])
+
+    model = FeedListModel(root, v)
+    v.setModel(model)
+    v.show()
+    
+    sys.exit(app.exec_())
+<ui version="4.0" >
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow" >
+  <property name="geometry" >
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>800</width>
+    <height>600</height>
+   </rect>
+  </property>
+  <property name="windowTitle" >
+   <string>MainWindow</string>
+  </property>
+  <widget class="QWidget" name="centralwidget" >
+   <layout class="QVBoxLayout" name="verticalLayout_2" >
+    <item>
+     <widget class="QSplitter" name="splitter" >
+      <property name="orientation" >
+       <enum>Qt::Vertical</enum>
+      </property>
+      <widget class="QTreeView" name="entriesView" />
+      <widget class="QWidget" native="1" name="feedEntryViewContainer" />
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar" >
+   <property name="geometry" >
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>800</width>
+     <height>18</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="menuFeed" >
+    <property name="title" >
+     <string>Feed</string>
+    </property>
+    <addaction name="separator" />
+    <addaction name="actionClose" />
+   </widget>
+   <addaction name="menuFeed" />
+  </widget>
+  <widget class="QStatusBar" name="statusbar" />
+  <widget class="QDockWidget" name="dockWidget" >
+   <property name="floating" >
+    <bool>false</bool>
+   </property>
+   <attribute name="dockWidgetArea" >
+    <number>1</number>
+   </attribute>
+   <widget class="QWidget" name="dockWidgetContents" >
+    <layout class="QVBoxLayout" name="verticalLayout" >
+     <item>
+      <widget class="QTreeView" name="categoryTreeView" />
+     </item>
+    </layout>
+   </widget>
+  </widget>
+  <action name="actionClose" >
+   <property name="text" >
+    <string>Close</string>
+   </property>
+  </action>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>actionClose</sender>
+   <signal>triggered()</signal>
+   <receiver>MainWindow</receiver>
+   <slot>close()</slot>
+   <hints>
+    <hint type="sourcelabel" >
+     <x>-1</x>
+     <y>-1</y>
+    </hint>
+    <hint type="destinationlabel" >
+     <x>399</x>
+     <y>299</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>