Created
June 2, 2011 05:41
-
-
Save segphault/1003989 to your computer and use it in GitHub Desktop.
This is the original source code of GDeskStatus, a simple PyGtk+ utility that eventually became Gwibber
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import gtk, pango, gobject | |
import urllib2, base64, time, datetime, os | |
try: | |
import simplejson | |
def loadjson(content): return simplejson.loads(content) | |
except: | |
def loadjson(content): | |
false = False; true = True; null = None | |
return eval(content.replace("\/", "/")) | |
class Twitter: | |
def __init__(self): | |
# Auth information will eventually be stored in the GNOME keyring | |
self.user, passwd = "segphault", "XXXXXXXXXXXXXX" | |
self.auth = "Basic %s" % base64.encodestring("%s:%s" % (self.user, passwd)) | |
def connect(self, url, data = None): | |
return urllib2.urlopen(urllib2.Request( | |
url, data, {"Authorization": self.auth})).read() | |
def get_timeline(self, kind): | |
for status in loadjson(self.connect( | |
"http://twitter.com/statuses/%s" % kind)): | |
yield status["user"], status | |
def public_timeline(self, args = None): | |
return self.get_timeline("public_timeline.json?%s" % args) | |
def friends_timeline(self, args = None): | |
return self.get_timeline("friends_timeline.json?%s" % args) | |
def user_timeline(self, args = None): | |
return self.get_timeline("user_timeline.json?%s" % args) | |
def friends(self, args = None): | |
for user in loadjson(self.connect( | |
"http://twitter.com/statuses/friends.json?%s" % args)): | |
yield user, user["status"] | |
@classmethod | |
def parse_time(self, t): | |
# Time format was changed, used to be: '%a %b %d %H:%M:%S +0000 %Y' | |
try: | |
t = time.strptime(t, "%m/%d/%Y %H:%M:%S %Z") | |
d = datetime.datetime(*time.gmtime()[0:6]) - datetime.datetime(*t[0:6]) | |
except: return t | |
if d.seconds < 60: return "%d seconds ago" % d.seconds | |
elif d.seconds < (60 * 60): return "%d minutes ago" % (d.seconds / 60) | |
elif d.seconds < (60 * 60 * 2): return "1 hour ago" | |
elif d.days < 1: return "%d hours ago" % (d.seconds / 60 / 60) | |
elif d.days == 1: return "1 day ago" | |
elif d.days > 0: return "%d days ago" % d.days | |
else: return "BUG: %s" % str(d) | |
class UIStatusMessage(gtk.TextView): | |
def __init__(self, name, message, created_at): | |
gtk.TextView.__init__(self) | |
self.set_wrap_mode(gtk.WRAP_WORD) | |
self.set_editable(False) | |
self.modify_base(gtk.STATE_NORMAL, | |
gtk.Image().rc_get_style().bg[gtk.STATE_NORMAL]) | |
self.new_tag("name", weight=pango.WEIGHT_BOLD, scale=pango.SCALE_LARGE) | |
self.new_tag("time", scale=pango.SCALE_SMALL) | |
self.new_tag("text", pixels_above_lines=4) | |
self.add_text(name, "name") | |
self.add_text(" (%s)" % Twitter.parse_time(created_at), "time") | |
self.add_text("\n%s" % message, "text") | |
def add_text(self, text, tag=None): | |
if tag: | |
self.get_buffer().insert_with_tags_by_name( | |
self.get_buffer().get_bounds()[1], text, tag) | |
else: self.get_buffer().insert(self.get_buffer().get_bounds()[1], text) | |
def new_tag(self, name, **props): | |
tag = gtk.TextTag(name) | |
for k, v in props.items(): tag.set_property(k, v) | |
self.get_buffer().get_tag_table().add(tag) | |
class UIUserIcon(gtk.Image): | |
def __init__(self, user): | |
gtk.Image.__init__(self) | |
self.set_from_file(self.user_image_path(user)) | |
@classmethod | |
def user_image_path(self, user): | |
timestamp = user["profile_image_url"].split("/")[-1].split("?")[-1] | |
img_path = os.path.join("imgcache", "%s-%s" % (user["id"], timestamp)) | |
if not os.path.exists(img_path): | |
output = open(img_path, "w+") | |
output.write(urllib2.urlopen(user["profile_image_url"]).read()) | |
output.close() | |
return img_path | |
class UIStatusList(gtk.VBox): | |
def __init__(self, status_generator, args = None): | |
gtk.VBox.__init__(self) | |
self.set_spacing(5); self.set_border_width(5) | |
self.status_generator = status_generator | |
self.status_args = args | |
self.update() | |
def update(self): | |
try: | |
gen = self.status_generator(self.status_args) | |
self.populate(gen) | |
except: print "Update failed" | |
def populate(self, generator): | |
for w in self.get_children(): self.remove(w) | |
for user, status in generator: | |
hb = gtk.HBox(); hb.set_border_width(5); hb.set_spacing(10) | |
hb.pack_start(UIUserIcon(user), False, False) | |
hb.pack_start(UIStatusMessage( | |
user["name"], status["text"], status["created_at"])) | |
frame = gtk.Frame(); frame.add(hb) | |
self.pack_start(frame, False, False) | |
self.show_all() | |
class DeskStatusClient(gtk.Window): | |
def __init__(self, refresh = None): | |
gtk.Window.__init__(self); self.resize(350, 250) | |
self.status_list = UIStatusList(Twitter().friends_timeline) | |
scroll = gtk.ScrolledWindow() | |
scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) | |
scroll.add_with_viewport(self.status_list) | |
""" | |
input = gtk.Entry(140); input_count = gtk.Label("140") | |
inputbox = gtk.HBox(); inputbox.set_spacing(5) | |
inputbox.pack_start(input, True, True) | |
split = gtk.VBox(); split.set_border_width(5); split.set_spacing(5) | |
split.pack_start(scroll, True); split.pack_start(inputbox, False, True) | |
""" | |
input = gtk.TextView(); input.set_wrap_mode(gtk.WRAP_WORD) | |
inputbox = gtk.Frame(); inputbox.add(input) | |
split = gtk.VPaned(); split.set_border_width(5) | |
split.pack1(scroll, True); split.pack2(inputbox, False, True) | |
ui_text = """<ui> | |
<menubar name="Menubar"> | |
<menu name="File" action="FileMenu"> | |
<menuitem action="Refresh" /> | |
<menuitem action="Preferences" /> | |
<menuitem action="Quit" /> | |
</menu> | |
<menu name="View" action="ViewMenu"> | |
<menuitem action="ToggleEdit" /> | |
<menuitem action="ToggleCondensed" /> | |
<menuitem action="ToggleMenu" /> | |
<menu name="Display" action="DisplayMenu"> | |
<menuitem action="Public" /> | |
<menuitem action="Friends" /> | |
<menuitem action="User" /> | |
</menu> | |
</menu> | |
</menubar> | |
<toolbar name="Toolbar"> | |
<toolitem action="Refresh" /> | |
<separator /> | |
<toolitem action="ToggleEdit" /> | |
<toolitem action="Display" /> | |
<separator /> | |
<toolitem action="Refresh" /> | |
<toolitem action="Preferences" /> | |
<toolitem action="Quit" /> | |
</toolbar> | |
</ui>""" | |
ag = gtk.ActionGroup("Actions") | |
ag.add_actions([ | |
("FileMenu", None, "_Status", None, None, None), | |
("ViewMenu", None, "_View", None, None, None), | |
("DisplayMenu", gtk.STOCK_INDEX, "_Display", None, None, None), | |
("Display", gtk.STOCK_INDEX, "_Display", None, None, self.display_menu), | |
("Refresh", gtk.STOCK_REFRESH, "_Refresh", "<control>R", "Refresh statuses", self.button_pressed), | |
("Preferences", gtk.STOCK_PREFERENCES, "_Preferences", "<control>P", "Edit preferences", self.button_pressed), | |
("Quit", gtk.STOCK_QUIT, "_Quit", "<control>Q", "Quit the program", self.button_pressed)]) | |
ag.add_toggle_actions([ | |
("ToggleEdit", gtk.STOCK_EDIT, "_Edit Pane", "<control>E", "Toggle editor visibility", self.button_pressed), | |
("ToggleCondensed", None, "_Condensed", "<control>C", "Toggle condensed view", self.button_pressed), | |
("ToggleMenu", gtk.STOCK_INDEX, "_Menu", "<control>M", "Toggle menu visibility", self.button_pressed)]) | |
ag.add_radio_actions([ | |
("Public", None, "_Public", "<control>P", "Toggle view", 0), | |
("Friends", None, "_Friends", "<control>F", "Toggle view", 1), | |
("User", None, "_User", "<control>U", "Toggle view", 2)]) | |
ui = gtk.UIManager() | |
ui.insert_action_group(ag, 0) | |
ui.add_ui_from_string(ui_text) | |
self.add_accel_group(ui.get_accel_group()) | |
toolbar = ui.get_widget("/Toolbar") | |
menubar = ui.get_widget("/Menubar") | |
self.view_menu = ui.get_widget("/Menubar/View/Display") | |
vbox = gtk.VBox() | |
vbox.pack_start(menubar, False) | |
vbox.pack_start(toolbar, False) | |
vbox.pack_start(split) | |
self.add(vbox) | |
self.refresh_interval = refresh or (1000 * 60 * 5) | |
gobject.timeout_add(self.refresh_interval, self.update) | |
def display_menu(self, action): | |
self.view_menu.get_submenu().popup(None, None, None, 1, 0) | |
def button_pressed(self, action): | |
print action | |
def update(self): | |
self.status_list.update() | |
return True | |
c = DeskStatusClient() | |
c.connect("destroy", gtk.main_quit) | |
c.show_all() | |
gtk.main() | |
#for user, status in Twitter().friends_timeline(): | |
# print user, status |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment