1. Yuki KODAMA
  2. feedeater

Commits

Yuki KODAMA  committed 9787bf0

rename extension to 'pyw'

  • Participants
  • Parent commits 56f173a
  • Branches default

Comments (0)

Files changed (2)

File feedeater.py

-# Feed Eater - A simple RSS/ATOM feeds monitor.
-#
-# Copyright 2010 Yuki KODAMA <endflow.net@gmail.com>
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2, incorporated herein by reference.
-
-import sys
-import time
-import gntp
-import socket
-import iniparse
-import feedparser
-
-from PyQt4.QtCore import Qt, QString, QStringList, QSettings, QDir
-from PyQt4.QtGui import QDialog, QVBoxLayout, QHBoxLayout, QPushButton
-from PyQt4.QtGui import QApplication, QTreeView, QStandardItemModel
-from PyQt4.QtGui import QDialogButtonBox, QFormLayout, QLineEdit, QSpinBox
-from PyQt4.QtGui import QAbstractItemView, QStandardItem, QLabel
-
-__version__ = '0.0.1'
-
-def u(s):
-    return unicode(str(s), 'utf-8')
-
-class FeedItem(object):
-
-    def __init__(self, name='', url='', interval=15 * 60, enabled=True,
-                 last=0, hist=[]):
-        self.set('name', name)
-        self.set('url', url)
-        self.set('interval', interval)
-        self.set('enabled', enabled)
-        self.set('last', last)
-        self.set('hist', hist)
-
-    def set(self, attr, raw):
-        if attr in ('name', 'url'):
-            value = u(raw)
-        elif attr in ('interval', 'last'):
-            value = int(raw)
-        elif attr == 'enabled':
-            if isinstance(raw, basestring):
-                value = True
-                if raw.lower() in ('false', '0'):
-                    value = False
-            else:
-                value = raw
-        elif attr == 'hist':
-            if isinstance(raw, basestring):
-                value = [u(t).strip() for t in raw.split(',')]
-            else:
-                value = [u(t).strip() for t in raw]
-        else:
-            raise AttributeError, 'invalid attribute name: %s' % attr
-        setattr(self, attr, value)
-
-MIN_INTERVAL = 10
-
-class FeedItemDialog(QDialog):
-
-    def __init__(self, parent, item=None):
-        QDialog.__init__(self, parent)
-
-        if item is None:
-            self.setWindowTitle('Add Feed Item')
-            self.item = FeedItem()
-        else:
-            self.setWindowTitle('Edit Feed Item')
-            self.item = item
-
-        base = QVBoxLayout()
-
-        # feed item properties
-        form = QFormLayout()
-        form.setFieldGrowthPolicy(QFormLayout.FieldsStayAtSizeHint)
-        base.addLayout(form)
-
-        self.name_text = QLineEdit()
-        self.name_text.setText(self.item.name)
-        self.name_text.setMinimumWidth(180)
-        form.addRow('Name', self.name_text)
-        self.url_text = QLineEdit()
-        self.url_text.setText(self.item.url)
-        self.url_text.setMinimumWidth(320)
-        form.addRow('URL', self.url_text)
-
-        self.interval_spin = QSpinBox()
-        self.interval_spin.setMinimumWidth(120)
-        self.interval_spin.setRange(MIN_INTERVAL, 24 * 60 * 60)
-        self.interval_spin.setSuffix(' seconds')
-        self.interval_spin.setValue(self.item.interval)
-        form.addRow('Interval', self.interval_spin)
-
-        bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
-        base.addWidget(bbox)
-        bbox.accepted.connect(self.accepted_handler)
-        bbox.rejected.connect(self.rejected_handler)
-
-        self.setLayout(base)
-        if item is not None:
-            self.interval_spin.setFocus()
-
-    def accepted_handler(self):
-        self.item.name = self.name_text.text()
-        self.item.url = self.url_text.text()
-        self.item.interval = self.interval_spin.value()
-        self.accept()
-
-    def rejected_handler(self):
-        self.item = None
-        self.reject()
-
-COL_INDEX = 0
-COL_NAME = 1
-COL_URL = 2
-COL_INTETVAL = 3
-
-MAX_HIST = 200
-
-TEMPLATE = '<span style="color: %s; font-weight: bold; font-size: 14px;">%s</span>'
-RUN = TEMPLATE % ('green', 'Running')
-STOP = TEMPLATE % ('red', 'Stopped')
-
-class MainDialog(QDialog):
-
-    def __init__(self):
-        QDialog.__init__(self, None)
-
-        self.setWindowTitle('Feed Eater')
-
-        mainbox = QVBoxLayout()
-
-        # control box
-        bbox = QHBoxLayout()
-        mainbox.addLayout(bbox)
-
-        self.run_btn = QPushButton('Start')
-        self.run_btn.clicked.connect(self.run_clicked)
-        bbox.addWidget(self.run_btn)
-
-        self.status_text = QLabel(STOP)
-        bbox.addWidget(self.status_text)
-        bbox.addStretch(0)
-
-        # feed manager
-        fbox = QHBoxLayout()
-        mainbox.addLayout(fbox)
-
-        ## feed list
-        self.tree = QTreeView()
-        fbox.addWidget(self.tree)
-        self.tree.setRootIsDecorated(False)
-        self.tree.setSelectionMode(QAbstractItemView.ExtendedSelection)
-        self.tree.setEditTriggers(QAbstractItemView.NoEditTriggers)
-
-        self.model = QStandardItemModel()
-        self.tree.setModel(self.model)
-
-        self.headers = QStringList()
-        self.headers.append('Index')
-        self.headers.append('Name')
-        self.headers.append('URL')
-        self.headers.append('Interval')
-
-        ## command buttons
-        cbox = QVBoxLayout()
-        fbox.addLayout(cbox)
-
-        self.add_btn = QPushButton('Add...')
-        self.add_btn.clicked.connect(self.add_clicked)
-        cbox.addWidget(self.add_btn)
-        self.remove_btn = QPushButton('Remove')
-        self.remove_btn.clicked.connect(self.remove_clicked)
-        cbox.addWidget(self.remove_btn)
-        self.edit_btn = QPushButton('Edit...')
-        self.edit_btn.clicked.connect(self.edit_clicked)
-        cbox.addWidget(self.edit_btn)
-        cbox.addStretch(0)
-
-        self.timer = None
-        self.setLayout(mainbox)
-        self.load_settings()
-        self.refresh()
-        self.register()
-
-        self.rejected.connect(self.store_settings)
-
-    def run_clicked(self):
-        if self.run_btn.text() == 'Start':
-            text = 'Stop'
-            status = RUN
-            self.check()
-            self.timer = self.startTimer(MIN_INTERVAL * 1000)
-        else:
-            text = 'Start'
-            status = STOP
-            self.killTimer(self.timer)
-        self.run_btn.setText(text)
-        self.status_text.setText(status)
-
-    def timerEvent(self, event):
-        self.check()
-
-    def add_clicked(self):
-        dlg = FeedItemDialog(self)
-        dlg.show()
-        dlg.exec_()
-        if dlg.result() == QDialog.Accepted:
-            self.feeds.append(dlg.item)
-            self.refresh()
-
-    def remove_clicked(self):
-        num = len(self.feeds)
-        sel = self.tree.selectionModel()
-        rows = [i.row() for i in sel.selectedRows()]
-        removes = [int(self.model.item(i, COL_INDEX).text()) for i in rows]
-        removes.sort()
-        removes.reverse()
-        for index in removes:
-            del self.feeds[index]
-        if len(self.feeds) < num:
-            self.refresh()
-
-    def edit_clicked(self):
-        sel = self.tree.selectionModel()
-        cur = sel.currentIndex().row()
-        if cur < 0:
-            return
-        item = self.feeds[int(self.model.item(cur, COL_INDEX).text())]
-        dlg = FeedItemDialog(self, item)
-        dlg.show()
-        dlg.exec_()
-        if dlg.result() == QDialog.Accepted:
-            self.refresh()
-
-    def store_settings(self):
-        # config file
-        if len(self.feeds) > 0 and 'feeds' not in self.cfg:
-            self.cfg._new_namespace('feeds')
-        for index, item in enumerate(self.feeds):
-            for attr in ('name', 'url', 'interval', 'enabled'):
-                key = '%s.%s' % (index, attr)
-                self.cfg.feeds[key] = getattr(item, attr)
-        try:
-            f = open(self.rcpath, 'w')
-            f.write(u(self.cfg).encode('utf-8'))
-            f.close()
-        except IOError, e:
-            print 'Unable to write config file', e
-
-        # history file
-        settings = QSettings()
-        settings.beginWriteArray('feeds')
-        for index, item in enumerate(self.feeds):
-            settings.setArrayIndex(index)
-            settings.setValue('url', item.url)
-            settings.setValue('last', item.last)
-            hist = QStringList()
-            for entry_id in item.hist:
-                hist.append(entry_id)
-            settings.setValue('hist', hist)
-        settings.endArray()
-
-    def load_settings(self):
-        self.feeds = []
-        self.rcpath = unicode(QDir.home().filePath('.feedeaterrc'))
-        try:
-            # config file
-            feeds = {}
-            self.cfg = iniparse.INIConfig(file(self.rcpath))
-            if 'feeds' in self.cfg:
-                for key in self.cfg.feeds:
-                    group, attr = key.split('.')
-                    try:
-                        item = feeds[group]
-                    except KeyError:
-                        item = feeds[group] = FeedItem()
-                    item.set(attr, self.cfg.feeds[key])
-                for item in feeds.values():
-                    self.feeds.append(item)
-            else:
-                return
-
-            # history file
-            settings = QSettings()
-            size = settings.beginReadArray('feeds')
-            for row in range(size):
-                settings.setArrayIndex(row)
-                url = u(settings.value('url').toString())
-                try:
-                    item = [f for f in self.feeds if f.url == url][0]
-                except IndexError:
-                    continue
-                last, ok = settings.value('last').toInt()
-                item.set('last', last)
-                hist = settings.value('hist').toStringList()
-                item.set('hist', hist)
-            settings.endArray()
-        except IOError, e:
-            print 'No config file:', e
-
-    def refresh(self):
-        def wrap(*args, **kargs):
-            items = []
-            for index, val in enumerate(args):
-                item = QStandardItem(str(val))
-                if index == COL_NAME:
-                    item.setCheckable(True)
-                    item.setCheckState(kargs['enabled'] and Qt.Checked \
-                                                         or Qt.Unchecked)
-                items.append(item)
-            return items
-        self.model.clear()
-        self.model.setHorizontalHeaderLabels(self.headers)
-        self.tree.setColumnHidden(COL_INDEX, True)
-        for index, item in enumerate(self.feeds):
-            self.model.appendRow(wrap(index, item.name, item.url,
-                                      item.interval, enabled=item.enabled))
-
-    def check(self):
-        def record(item, parsed):
-            hist = set(item.hist)
-            diff = []
-            for entry in parsed.entries:
-                if entry.id not in hist:
-                    diff.append(entry)
-                    item.hist.insert(0, entry.id)
-            if MAX_HIST < len(item.hist):
-                item.hist = item.hist[:MAX_HIST]
-            return diff
-
-        for item in self.feeds:
-            next = item.last + item.interval
-            if next < int(time.time()):
-                parsed = feedparser.parse(str(item.url))
-                item.last = int(time.time())
-                diff = record(item, parsed)
-                self.notify(diff)
-
-    def send(self, data):
-        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-        sock.connect(('localhost', 23053))
-        sock.send(data)
-        res = gntp.parse_gntp(sock.recv(1024))
-        sock.close()
-
-    def register(self):
-        register = gntp.GNTPRegister()
-        register.add_header('Application-Name', 'Feed Eater')
-        register.add_notification('New Feed Item', True)
-        self.send(register.encode())
-
-    def notify(self, entries):
-        def divide(text):
-            tokens = text.split('\n')
-            return tokens[0], tokens[1:]
-        for entry in entries:
-            notice = gntp.GNTPNotice()
-            notice.add_header('Application-Name', 'Feed Eater')
-            notice.add_header('Notification-Name', 'New Feed Item')
-            title = entry.title.split('\n')[0]
-            desc = entry.author
-            notice.add_header('Notification-Title', title)
-            notice.add_header('Notification-Text', desc)
-            self.send(notice.encode())
-
-QSettings.setDefaultFormat(QSettings.IniFormat)
-
-app = QApplication(sys.argv)
-app.setApplicationName('FeedEater')
-app.setApplicationVersion(__version__)
-app.setOrganizationName('FeedEater')
-app.setOrganizationDomain('endflow.net')
-dlg = MainDialog()
-dlg.show()
-sys.exit(app.exec_())

File feedeater.pyw

View file
+# Feed Eater - A simple RSS/ATOM feeds monitor.
+#
+# Copyright 2010 Yuki KODAMA <endflow.net@gmail.com>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2, incorporated herein by reference.
+
+import sys
+import time
+import gntp
+import socket
+import iniparse
+import feedparser
+
+from PyQt4.QtCore import Qt, QString, QStringList, QSettings, QDir
+from PyQt4.QtGui import QDialog, QVBoxLayout, QHBoxLayout, QPushButton
+from PyQt4.QtGui import QApplication, QTreeView, QStandardItemModel
+from PyQt4.QtGui import QDialogButtonBox, QFormLayout, QLineEdit, QSpinBox
+from PyQt4.QtGui import QAbstractItemView, QStandardItem, QLabel
+
+__version__ = '0.0.1'
+
+def u(s):
+    return unicode(str(s), 'utf-8')
+
+class FeedItem(object):
+
+    def __init__(self, name='', url='', interval=15 * 60, enabled=True,
+                 last=0, hist=[]):
+        self.set('name', name)
+        self.set('url', url)
+        self.set('interval', interval)
+        self.set('enabled', enabled)
+        self.set('last', last)
+        self.set('hist', hist)
+
+    def set(self, attr, raw):
+        if attr in ('name', 'url'):
+            value = u(raw)
+        elif attr in ('interval', 'last'):
+            value = int(raw)
+        elif attr == 'enabled':
+            if isinstance(raw, basestring):
+                value = True
+                if raw.lower() in ('false', '0'):
+                    value = False
+            else:
+                value = raw
+        elif attr == 'hist':
+            if isinstance(raw, basestring):
+                value = [u(t).strip() for t in raw.split(',')]
+            else:
+                value = [u(t).strip() for t in raw]
+        else:
+            raise AttributeError, 'invalid attribute name: %s' % attr
+        setattr(self, attr, value)
+
+MIN_INTERVAL = 10
+
+class FeedItemDialog(QDialog):
+
+    def __init__(self, parent, item=None):
+        QDialog.__init__(self, parent)
+
+        if item is None:
+            self.setWindowTitle('Add Feed Item')
+            self.item = FeedItem()
+        else:
+            self.setWindowTitle('Edit Feed Item')
+            self.item = item
+
+        base = QVBoxLayout()
+
+        # feed item properties
+        form = QFormLayout()
+        form.setFieldGrowthPolicy(QFormLayout.FieldsStayAtSizeHint)
+        base.addLayout(form)
+
+        self.name_text = QLineEdit()
+        self.name_text.setText(self.item.name)
+        self.name_text.setMinimumWidth(180)
+        form.addRow('Name', self.name_text)
+        self.url_text = QLineEdit()
+        self.url_text.setText(self.item.url)
+        self.url_text.setMinimumWidth(320)
+        form.addRow('URL', self.url_text)
+
+        self.interval_spin = QSpinBox()
+        self.interval_spin.setMinimumWidth(120)
+        self.interval_spin.setRange(MIN_INTERVAL, 24 * 60 * 60)
+        self.interval_spin.setSuffix(' seconds')
+        self.interval_spin.setValue(self.item.interval)
+        form.addRow('Interval', self.interval_spin)
+
+        bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+        base.addWidget(bbox)
+        bbox.accepted.connect(self.accepted_handler)
+        bbox.rejected.connect(self.rejected_handler)
+
+        self.setLayout(base)
+        if item is not None:
+            self.interval_spin.setFocus()
+
+    def accepted_handler(self):
+        self.item.name = self.name_text.text()
+        self.item.url = self.url_text.text()
+        self.item.interval = self.interval_spin.value()
+        self.accept()
+
+    def rejected_handler(self):
+        self.item = None
+        self.reject()
+
+COL_INDEX = 0
+COL_NAME = 1
+COL_URL = 2
+COL_INTETVAL = 3
+
+MAX_HIST = 200
+
+TEMPLATE = '<span style="color: %s; font-weight: bold; font-size: 14px;">%s</span>'
+RUN = TEMPLATE % ('green', 'Running')
+STOP = TEMPLATE % ('red', 'Stopped')
+
+class MainDialog(QDialog):
+
+    def __init__(self):
+        QDialog.__init__(self, None)
+
+        self.setWindowTitle('Feed Eater')
+
+        mainbox = QVBoxLayout()
+
+        # control box
+        bbox = QHBoxLayout()
+        mainbox.addLayout(bbox)
+
+        self.run_btn = QPushButton('Start')
+        self.run_btn.clicked.connect(self.run_clicked)
+        bbox.addWidget(self.run_btn)
+
+        self.status_text = QLabel(STOP)
+        bbox.addWidget(self.status_text)
+        bbox.addStretch(0)
+
+        # feed manager
+        fbox = QHBoxLayout()
+        mainbox.addLayout(fbox)
+
+        ## feed list
+        self.tree = QTreeView()
+        fbox.addWidget(self.tree)
+        self.tree.setRootIsDecorated(False)
+        self.tree.setSelectionMode(QAbstractItemView.ExtendedSelection)
+        self.tree.setEditTriggers(QAbstractItemView.NoEditTriggers)
+
+        self.model = QStandardItemModel()
+        self.tree.setModel(self.model)
+
+        self.headers = QStringList()
+        self.headers.append('Index')
+        self.headers.append('Name')
+        self.headers.append('URL')
+        self.headers.append('Interval')
+
+        ## command buttons
+        cbox = QVBoxLayout()
+        fbox.addLayout(cbox)
+
+        self.add_btn = QPushButton('Add...')
+        self.add_btn.clicked.connect(self.add_clicked)
+        cbox.addWidget(self.add_btn)
+        self.remove_btn = QPushButton('Remove')
+        self.remove_btn.clicked.connect(self.remove_clicked)
+        cbox.addWidget(self.remove_btn)
+        self.edit_btn = QPushButton('Edit...')
+        self.edit_btn.clicked.connect(self.edit_clicked)
+        cbox.addWidget(self.edit_btn)
+        cbox.addStretch(0)
+
+        self.timer = None
+        self.setLayout(mainbox)
+        self.load_settings()
+        self.refresh()
+        self.register()
+
+        self.rejected.connect(self.store_settings)
+
+    def run_clicked(self):
+        if self.run_btn.text() == 'Start':
+            text = 'Stop'
+            status = RUN
+            self.check()
+            self.timer = self.startTimer(MIN_INTERVAL * 1000)
+        else:
+            text = 'Start'
+            status = STOP
+            self.killTimer(self.timer)
+        self.run_btn.setText(text)
+        self.status_text.setText(status)
+
+    def timerEvent(self, event):
+        self.check()
+
+    def add_clicked(self):
+        dlg = FeedItemDialog(self)
+        dlg.show()
+        dlg.exec_()
+        if dlg.result() == QDialog.Accepted:
+            self.feeds.append(dlg.item)
+            self.refresh()
+
+    def remove_clicked(self):
+        num = len(self.feeds)
+        sel = self.tree.selectionModel()
+        rows = [i.row() for i in sel.selectedRows()]
+        removes = [int(self.model.item(i, COL_INDEX).text()) for i in rows]
+        removes.sort()
+        removes.reverse()
+        for index in removes:
+            del self.feeds[index]
+        if len(self.feeds) < num:
+            self.refresh()
+
+    def edit_clicked(self):
+        sel = self.tree.selectionModel()
+        cur = sel.currentIndex().row()
+        if cur < 0:
+            return
+        item = self.feeds[int(self.model.item(cur, COL_INDEX).text())]
+        dlg = FeedItemDialog(self, item)
+        dlg.show()
+        dlg.exec_()
+        if dlg.result() == QDialog.Accepted:
+            self.refresh()
+
+    def store_settings(self):
+        # config file
+        if len(self.feeds) > 0 and 'feeds' not in self.cfg:
+            self.cfg._new_namespace('feeds')
+        for index, item in enumerate(self.feeds):
+            for attr in ('name', 'url', 'interval', 'enabled'):
+                key = '%s.%s' % (index, attr)
+                self.cfg.feeds[key] = getattr(item, attr)
+        try:
+            f = open(self.rcpath, 'w')
+            f.write(u(self.cfg).encode('utf-8'))
+            f.close()
+        except IOError, e:
+            print 'Unable to write config file', e
+
+        # history file
+        settings = QSettings()
+        settings.beginWriteArray('feeds')
+        for index, item in enumerate(self.feeds):
+            settings.setArrayIndex(index)
+            settings.setValue('url', item.url)
+            settings.setValue('last', item.last)
+            hist = QStringList()
+            for entry_id in item.hist:
+                hist.append(entry_id)
+            settings.setValue('hist', hist)
+        settings.endArray()
+
+    def load_settings(self):
+        self.feeds = []
+        self.rcpath = unicode(QDir.home().filePath('.feedeaterrc'))
+        try:
+            # config file
+            feeds = {}
+            self.cfg = iniparse.INIConfig(file(self.rcpath))
+            if 'feeds' in self.cfg:
+                for key in self.cfg.feeds:
+                    group, attr = key.split('.')
+                    try:
+                        item = feeds[group]
+                    except KeyError:
+                        item = feeds[group] = FeedItem()
+                    item.set(attr, self.cfg.feeds[key])
+                for item in feeds.values():
+                    self.feeds.append(item)
+            else:
+                return
+
+            # history file
+            settings = QSettings()
+            size = settings.beginReadArray('feeds')
+            for row in range(size):
+                settings.setArrayIndex(row)
+                url = u(settings.value('url').toString())
+                try:
+                    item = [f for f in self.feeds if f.url == url][0]
+                except IndexError:
+                    continue
+                last, ok = settings.value('last').toInt()
+                item.set('last', last)
+                hist = settings.value('hist').toStringList()
+                item.set('hist', hist)
+            settings.endArray()
+        except IOError, e:
+            print 'No config file:', e
+
+    def refresh(self):
+        def wrap(*args, **kargs):
+            items = []
+            for index, val in enumerate(args):
+                item = QStandardItem(str(val))
+                if index == COL_NAME:
+                    item.setCheckable(True)
+                    item.setCheckState(kargs['enabled'] and Qt.Checked \
+                                                         or Qt.Unchecked)
+                items.append(item)
+            return items
+        self.model.clear()
+        self.model.setHorizontalHeaderLabels(self.headers)
+        self.tree.setColumnHidden(COL_INDEX, True)
+        for index, item in enumerate(self.feeds):
+            self.model.appendRow(wrap(index, item.name, item.url,
+                                      item.interval, enabled=item.enabled))
+
+    def check(self):
+        def record(item, parsed):
+            hist = set(item.hist)
+            diff = []
+            for entry in parsed.entries:
+                if entry.id not in hist:
+                    diff.append(entry)
+                    item.hist.insert(0, entry.id)
+            if MAX_HIST < len(item.hist):
+                item.hist = item.hist[:MAX_HIST]
+            return diff
+
+        for item in self.feeds:
+            next = item.last + item.interval
+            if next < int(time.time()):
+                parsed = feedparser.parse(str(item.url))
+                item.last = int(time.time())
+                diff = record(item, parsed)
+                self.notify(diff)
+
+    def send(self, data):
+        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        sock.connect(('localhost', 23053))
+        sock.send(data)
+        res = gntp.parse_gntp(sock.recv(1024))
+        sock.close()
+
+    def register(self):
+        register = gntp.GNTPRegister()
+        register.add_header('Application-Name', 'Feed Eater')
+        register.add_notification('New Feed Item', True)
+        self.send(register.encode())
+
+    def notify(self, entries):
+        def divide(text):
+            tokens = text.split('\n')
+            return tokens[0], tokens[1:]
+        for entry in entries:
+            notice = gntp.GNTPNotice()
+            notice.add_header('Application-Name', 'Feed Eater')
+            notice.add_header('Notification-Name', 'New Feed Item')
+            title = entry.title.split('\n')[0]
+            desc = entry.author
+            notice.add_header('Notification-Title', title)
+            notice.add_header('Notification-Text', desc)
+            self.send(notice.encode())
+
+QSettings.setDefaultFormat(QSettings.IniFormat)
+
+app = QApplication(sys.argv)
+app.setApplicationName('FeedEater')
+app.setApplicationVersion(__version__)
+app.setOrganizationName('FeedEater')
+app.setOrganizationDomain('endflow.net')
+dlg = MainDialog()
+dlg.show()
+sys.exit(app.exec_())