Skip to content

Instantly share code, notes, and snippets.

@segphault
Created June 2, 2011 05:41
Show Gist options
  • Save segphault/1003989 to your computer and use it in GitHub Desktop.
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
#!/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