diff --git a/hackertray/__init__.py b/hackertray/__init__.py index ac61f6d..a164313 100644 --- a/hackertray/__init__.py +++ b/hackertray/__init__.py @@ -1,10 +1,10 @@ -#!/usr/bin/python +#!/usr/bin/env python import pygtk + pygtk.require('2.0') import gtk -import requests import webbrowser import json import argparse @@ -20,133 +20,149 @@ from appindicator_replacement import get_icon_filename ##This is to get --version to work try: - import pkg_resources - __version = pkg_resources.require("hackertray")[0].version -except ImportError, e: - __version = "Can't read version number." + import pkg_resources + + __version = pkg_resources.require("hackertray")[0].version +except ImportError: + __version = "Can't read version number." from hackernews import HackerNews + class HackerNewsApp: - HN_URL_PREFIX="https://news.ycombinator.com/item?id=" - def __init__(self): - #Load the database - home = expanduser("~") - with open(home+'/.hackertray.json', 'a+') as content_file: - content_file.seek(0) - content = content_file.read() - try: - self.db = set(json.loads(content)) - except: - self.db = set() + HN_URL_PREFIX = "https://news.ycombinator.com/item?id=" - # create an indicator applet - self.ind = appindicator.Indicator ("Hacker Tray", "hacker-tray", appindicator.CATEGORY_APPLICATION_STATUS) - self.ind.set_status (appindicator.STATUS_ACTIVE) - self.ind.set_icon(get_icon_filename("hacker-tray.png")) + def __init__(self): + #Load the database + home = expanduser("~") + with open(home + '/.hackertray.json', 'a+') as content_file: + content_file.seek(0) + content = content_file.read() + try: + self.db = set(json.loads(content)) + except ValueError: + self.db = set() - # create a menu - self.menu = gtk.Menu() + # create an indicator applet + self.ind = appindicator.Indicator("Hacker Tray", "hacker-tray", appindicator.CATEGORY_APPLICATION_STATUS) + self.ind.set_status(appindicator.STATUS_ACTIVE) + self.ind.set_icon(get_icon_filename("hacker-tray.png")) - #The default state is false, and it toggles when you click on it - self.commentState = False - - # create items for the menu - refresh, quit and a separator - menuSeparator = gtk.SeparatorMenuItem() - menuSeparator.show() - self.menu.append(menuSeparator) + # create a menu + self.menu = gtk.Menu() - btnComments = gtk.CheckMenuItem("Show Comments") - btnComments.show() - btnComments.connect("activate", self.toggleComments) - self.menu.append(btnComments) + #The default state is false, and it toggles when you click on it + self.commentState = False - btnAbout = gtk.MenuItem("About") - btnAbout.show() - btnAbout.connect("activate", self.showAbout) - self.menu.append(btnAbout) + # create items for the menu - refresh, quit and a separator + menuSeparator = gtk.SeparatorMenuItem() + menuSeparator.show() + self.menu.append(menuSeparator) - btnRefresh = gtk.MenuItem("Refresh") - btnRefresh.show() - btnRefresh.connect("activate", self.refresh, True) #the last parameter is for not running the timer - self.menu.append(btnRefresh) + btnComments = gtk.CheckMenuItem("Show Comments") + btnComments.show() + btnComments.connect("activate", self.toggleComments) + self.menu.append(btnComments) - btnQuit = gtk.MenuItem("Quit") - btnQuit.show() - btnQuit.connect("activate", self.quit) - self.menu.append(btnQuit) + btnAbout = gtk.MenuItem("About") + btnAbout.show() + btnAbout.connect("activate", self.showAbout) + self.menu.append(btnAbout) - self.menu.show() + btnRefresh = gtk.MenuItem("Refresh") + btnRefresh.show() + #the last parameter is for not running the timer + btnRefresh.connect("activate", self.refresh, True) + self.menu.append(btnRefresh) - self.ind.set_menu(self.menu) - self.refresh() + btnQuit = gtk.MenuItem("Quit") + btnQuit.show() + btnQuit.connect("activate", self.quit) + self.menu.append(btnQuit) - '''Whether comments page is opened or not''' - def toggleComments(self, widget): - self.commentState = not self.commentState + self.menu.show() - '''Handle the about btn''' - def showAbout(self, widget): - webbrowser.open("https://github.com/captn3m0/hackertray/") + self.ind.set_menu(self.menu) + self.refresh() - ''' Handler for the quit button''' - #ToDo: Handle keyboard interrupt properly - def quit(self, widget, data=None): - l=list(self.db) - home = expanduser("~") - #truncate the file - with open(home+'/.hackertray.json', 'w+') as file: - file.write(json.dumps(l)) - gtk.main_quit() + def toggleComments(self, widget): + """Whether comments page is opened or not""" + self.commentState = not self.commentState - def run(self): - signal.signal(signal.SIGINT, self.quit) - gtk.main() - return 0 + def showAbout(self, widget): + """Handle the about btn""" + webbrowser.open("https://github.com/captn3m0/hackertray/") - '''Opens the link in the web browser''' - def open(self, widget, event=None, data=None): - #We disconnect and reconnect the event in case we have - #to set it to active and we don't want the signal to be processed - if(widget.get_active() == False): - widget.disconnect(widget.signal_id) - widget.set_active(True) - widget.signal_id = widget.connect('activate', self.open) - self.db.add(widget.item_id) - webbrowser.open(widget.url) - if(self.commentState): - webbrowser.open(self.HN_URL_PREFIX+widget.hn_id) + #ToDo: Handle keyboard interrupt properly + def quit(self, widget, data=None): + """ Handler for the quit button""" + l = list(self.db) + home = expanduser("~") - '''Adds an item to the menu''' - def addItem(self, item): - if(item['points'] == 0 or item['points'] == None): #This is in the case of YC Job Postings, which we skip - return - i = gtk.CheckMenuItem("("+str(item['points']).zfill(3)+"/"+str(item['comments_count']).zfill(3)+") "+item['title']) - i.set_active(item['id'] in self.db) - i.url = item['url'] - i.signal_id = i.connect('activate', self.open) - i.hn_id = item['id'] - i.item_id = item['id'] - self.menu.prepend(i) - i.show() + #truncate the file + with open(home + '/.hackertray.json', 'w+') as file: + file.write(json.dumps(l)) + + gtk.main_quit() + + def run(self): + signal.signal(signal.SIGINT, self.quit) + gtk.main() + return 0 + + def open(self, widget, event=None, data=None): + """Opens the link in the web browser""" + #We disconnect and reconnect the event in case we have + #to set it to active and we don't want the signal to be processed + if not widget.get_active(): + widget.disconnect(widget.signal_id) + widget.set_active(True) + widget.signal_id = widget.connect('activate', self.open) + + self.db.add(widget.item_id) + webbrowser.open(widget.url) + + if self.commentState: + webbrowser.open(self.HN_URL_PREFIX + widget.hn_id) + + def addItem(self, item): + """Adds an item to the menu""" + #This is in the case of YC Job Postings, which we skip + if item['points'] == 0 or item['points'] is None: + return + + i = gtk.CheckMenuItem( + "(" + str(item['points']).zfill(3) + "/" + str(item['comments_count']).zfill(3) + ") " + item['title']) + + i.set_active(item['id'] in self.db) + i.url = item['url'] + i.signal_id = i.connect('activate', self.open) + i.hn_id = item['id'] + i.item_id = item['id'] + self.menu.prepend(i) + i.show() + + def refresh(self, widget=None, no_timer=False): + """Refreshes the menu """ + data = reversed(HackerNews.getHomePage()[0:20]) + + #Remove all the current stories + for i in self.menu.get_children(): + if hasattr(i, 'url'): + self.menu.remove(i) + + #Add back all the refreshed news + for i in data: + self.addItem(i) + + #Call every 5 minutes + if not no_timer: + gtk.timeout_add(10 * 60 * 1000, self.refresh) - '''Refreshes the menu ''' - def refresh(self, widget=None, no_timer=False): - data = reversed(HackerNews.getHomePage()[0:20]); - #Remove all the current stories - for i in self.menu.get_children(): - if(hasattr(i,'url')): - self.menu.remove(i) - #Add back all the refreshed news - for i in data: - self.addItem(i) - #Call every 5 minutes - if no_timer==False: - gtk.timeout_add(10*60*1000, self.refresh) def main(): - parser = argparse.ArgumentParser(description='Hacker News in your System Tray') - parser.add_argument('--version', action='version', version=__version) - parser.parse_args() - indicator = HackerNewsApp() - indicator.run() + parser = argparse.ArgumentParser(description='Hacker News in your System Tray') + parser.add_argument('--version', action='version', version=__version) + parser.parse_args() + indicator = HackerNewsApp() + indicator.run() diff --git a/hackertray/appindicator_replacement.py b/hackertray/appindicator_replacement.py index 0ce5d70..8ea0171 100644 --- a/hackertray/appindicator_replacement.py +++ b/hackertray/appindicator_replacement.py @@ -14,7 +14,6 @@ import gobject # We also need os and sys import os -import sys from pkg_resources import resource_filename @@ -22,100 +21,91 @@ from pkg_resources import resource_filename CATEGORY_APPLICATION_STATUS = 0 # Status -STATUS_ACTIVE = 0 +STATUS_ACTIVE = 0 STATUS_ATTENTION = 1 # Locations to search for the given icon + def get_icon_filename(icon_name): - - # Determine where the icon is - return os.path.abspath(resource_filename('hackertray.data', 'hacker-tray.png')) + # Determine where the icon is + return os.path.abspath(resource_filename('hackertray.data', 'hacker-tray.png')) + # The main class class Indicator: - - # Constructor - - def __init__ (self,unknown,icon,category): - - # Store the settings - self.inactive_icon = get_icon_filename(icon) - self.active_icon = "" # Blank until the user calls set_attention_icon - - # Create the status icon - self.icon = gtk.StatusIcon() - - # Initialize to the default icon - self.icon.set_from_file(self.inactive_icon) - - # Set the rest of the vars - self.menu = None # We have no menu yet - - def set_menu(self,menu): - - # Save a copy of the menu - self.menu = menu - - # Now attach the icon's signal - # to the menu so that it becomes displayed - # whenever the user clicks it - self.icon.connect("activate", self.show_menu) - - def set_status(self, status): - - # Status defines whether the active or inactive - # icon should be displayed. - if status == STATUS_ACTIVE: - self.icon.set_from_file(self.inactive_icon) - else: - self.icon.set_from_file(self.active_icon) - - def set_label(self, label): - self.icon.set_title(label) - return - - def set_icon(self, icon): - - # Set the new icon - self.icon.set_from_file(get_icon_filename(icon)) - - def set_attention_icon(self, icon): - - # Set the icon filename as the attention icon - self.active_icon = get_icon_filename(icon) - - def show_menu(self, widget): - - # Show the menu - self.menu.popup(None,None,None,0,0) - - # Get the location and size of the window - mouse_rect = self.menu.get_window().get_frame_extents() - - self.x = mouse_rect.x - self.y = mouse_rect.y - self.right = self.x + mouse_rect.width - self.bottom = self.y + mouse_rect.height - - # Set a timer to poll the menu - self.timer = gobject.timeout_add(100, self.check_mouse) - - def check_mouse(self): - - if not self.menu.get_window().is_visible(): - return - - # Now check the global mouse coords - root = self.menu.get_screen().get_root_window() - - x,y,z = root.get_pointer() - - if x < self.x or x > self.right or y < self.y or y > self.bottom: - self.hide_menu() - else: - return True - - def hide_menu(self): - - self.menu.popdown() + # Constructor + def __init__(self, unknown, icon, category): + # Store the settings + self.inactive_icon = get_icon_filename(icon) + self.active_icon = "" # Blank until the user calls set_attention_icon + + # Create the status icon + self.icon = gtk.StatusIcon() + + # Initialize to the default icon + self.icon.set_from_file(self.inactive_icon) + + # Set the rest of the vars + self.menu = None # We have no menu yet + + def set_menu(self, menu): + # Save a copy of the menu + self.menu = menu + + # Now attach the icon's signal + # to the menu so that it becomes displayed + # whenever the user clicks it + self.icon.connect("activate", self.show_menu) + + def set_status(self, status): + # Status defines whether the active or inactive + # icon should be displayed. + if status == STATUS_ACTIVE: + self.icon.set_from_file(self.inactive_icon) + else: + self.icon.set_from_file(self.active_icon) + + def set_label(self, label): + self.icon.set_title(label) + return + + def set_icon(self, icon): + # Set the new icon + self.icon.set_from_file(get_icon_filename(icon)) + + def set_attention_icon(self, icon): + # Set the icon filename as the attention icon + self.active_icon = get_icon_filename(icon) + + def show_menu(self, widget): + # Show the menu + self.menu.popup(None, None, None, 0, 0) + + # Get the location and size of the window + mouse_rect = self.menu.get_window().get_frame_extents() + + self.x = mouse_rect.x + self.y = mouse_rect.y + self.right = self.x + mouse_rect.width + self.bottom = self.y + mouse_rect.height + + # Set a timer to poll the menu + self.timer = gobject.timeout_add(100, self.check_mouse) + + def check_mouse(self): + if not self.menu.get_window().is_visible(): + return + + # Now check the global mouse coords + root = self.menu.get_screen().get_root_window() + + x, y, z = root.get_pointer() + + if x < self.x or x > self.right or y < self.y or y > self.bottom: + self.hide_menu() + else: + return True + + def hide_menu(self): + self.menu.popdown() diff --git a/hackertray/hackernews.py b/hackertray/hackernews.py index 7826d87..9857e45 100644 --- a/hackertray/hackernews.py +++ b/hackertray/hackernews.py @@ -2,16 +2,17 @@ import random import requests urls = [ - 'https://node-hnapi.herokuapp.com/' -]; + 'https://node-hnapi.herokuapp.com/' +] + class HackerNews: - @staticmethod - def getHomePage(): - random.shuffle(urls) - for i in urls: - r = requests.get(i+"news") - try: - return r.json() - except ValueError: - continue; \ No newline at end of file + @staticmethod + def getHomePage(): + random.shuffle(urls) + for i in urls: + r = requests.get(i + "news") + try: + return r.json() + except ValueError: + continue \ No newline at end of file diff --git a/hackertray/hn_test.py b/hackertray/hn_test.py index 90debb3..182631a 100644 --- a/hackertray/hn_test.py +++ b/hackertray/hn_test.py @@ -1,10 +1,12 @@ import unittest from hackernews import HackerNews + class HNTest(unittest.TestCase): - def runTest(self): - data = HackerNews.getHomePage() - self.assertTrue(len(data)>0) + def runTest(self): + data = HackerNews.getHomePage() + self.assertTrue(len(data) > 0) + if __name__ == '__main__': unittest.main() \ No newline at end of file diff --git a/setup.py b/setup.py index 42cfbf1..86d94ec 100644 --- a/setup.py +++ b/setup.py @@ -1,10 +1,12 @@ +import sys + from setuptools import setup from setuptools import find_packages -import sys + requirements = ['requests'] if sys.version_info < (2, 7): - requirements.append('argparse') + requirements.append('argparse') setup(name='hackertray', version='1.9.5', @@ -17,12 +19,12 @@ setup(name='hackertray', license='MIT', packages=find_packages(), package_data={ - 'hackertray.data':['hacker-tray.png'] + 'hackertray.data': ['hacker-tray.png'] }, install_requires=[ - 'requests>=2.0', + 'requests>=2.0', ], entry_points={ - 'console_scripts': ['hackertray = hackertray:main'], + 'console_scripts': ['hackertray = hackertray:main'], }, zip_safe=False)