From bf23f6e3fc745b5020f4de7dfd0602b8daa1230f Mon Sep 17 00:00:00 2001 From: Anna Date: Wed, 14 Apr 2010 11:03:20 -0400 Subject: [PATCH] Factored mytwitter into two modules to be cleaner --- mytwitter.py | 429 +--------------------------------------------- twitterwidgets.py | 429 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 431 insertions(+), 427 deletions(-) create mode 100644 twitterwidgets.py diff --git a/mytwitter.py b/mytwitter.py index fb9fbb6..3d0f1e4 100755 --- a/mytwitter.py +++ b/mytwitter.py @@ -2,11 +2,11 @@ # # Custom twitter client... mostly for learning Python -import sys, ConfigParser, os, re, subprocess -import datetime, dateutil.tz +import sys, ConfigParser, os, re import twitter import gtk, gtk.glade, gobject from urllib2 import HTTPError +from twitterwidgets import TweetPane class MyTwitter(): @@ -227,431 +227,6 @@ class MyTwitter(): ### end class MyTwitter -class TweetPane(gtk.ScrolledWindow): - ''' - Box that holds all the TweetBoxes for a given feed - - This box will not update itself, the parent should do that. - - It will connect num_entries listeners to its parent's on_reply() and on_retweet() - - It also gets some data from its parent, including num_entries - ''' - - def __init__(self, list_name, num_entries=20, single_tweet=None): - gtk.ScrolledWindow.__init__(self) - - self.updated_once = False - - self.single_tweet = single_tweet - - self.list_name = list_name - - self.tab_label = CloseTabLabel(self.list_name) - - # These handle determining which tweets are unread - self.last_tweet_read = None - self.latest_tweet = None - self.num_new_tweets = 0 - - self.tweets = [] - - self.num_entries = num_entries - if self.single_tweet is not None: - self.num_entries = 1 - - self.init_widgets() - - - def init_widgets(self): - self.tab_label.connect('label_clicked', self.set_tweets_read_callback) - - tweet_box = gtk.VBox() - viewport = gtk.Viewport() - - # Build us some labels... - for i in range(0, self.num_entries): - self.tweets.append(TweetBox()) - tweet_box.pack_start(self.tweets[i], expand=False) - self.tweets[i].connect('reply', self.on_tweet_reply) - self.tweets[i].connect('retweet', self.on_retweet) - self.tweets[i].connect('in-reply-to', self.on_tweet_reply_to) - - viewport.add(tweet_box) - - # Several different actions should mark the tweets as 'read' - self.connect('focus', self.set_tweets_read_callback) - viewport.connect('button_press_event', self.set_tweets_read_callback) - self.connect('scroll-event', self.set_tweets_read_callback) - self.connect('scroll-child', self.set_tweets_read_callback) - - self.add(viewport) - self.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) - self.show_all() - - - def update_window(self, raw_statuses, using_results=False): - if using_results: - statuses = self.statuses_from_results(raw_statuses) - else: - statuses = raw_statuses - - if self.updated_once is False: - self.updated_once = True - - # If this is our first load of this list, don't treat anything as new! - if self.last_tweet_read is None: - self.last_tweet_read = statuses[0].id - - # Keep count of the new tweets for posting a status message - self.num_new_tweets = 0 - - for i in range(0, self.num_entries): - read = True - if i < len(statuses): - if statuses[i].id > self.last_tweet_read: - self.num_new_tweets += 1 - read = False - self.tweets[i].set_status(statuses[i], read) - else: - self.tweets[i].clear_status() - - self.latest_tweet = statuses[0].id - - self.update_tab_label() - - - # Update the label with the number of unread tweets - def update_tab_label(self): - pane_text = self.list_name - if self.num_new_tweets > 0: - pane_text += ' (' + str(self.num_new_tweets) + ')' - self.tab_label.set_label_text(pane_text) - - - def get_list_name(self): - return self.list_name - - - def set_tweets_read(self): - self.last_tweet_read = self.latest_tweet - self.num_new_tweets = 0 - self.update_tab_label() - - - def set_tweets_read_callback(self, event, arg1=None, arg2=None): - self.set_tweets_read() - - - def get_tab_label(self): - return self.tab_label - - - def get_single_tweet(self): - return self.single_tweet - - - def updated_once(self): - return self.updated_once - - - def on_tweet_reply(self, widget): - self.emit('tweet-reply', {'screen_name': widget.screen_name, 'id': widget.id}) - - - def on_retweet(self, widget): - self.emit('tweet-retweet', {'id': widget.id}) - - - def on_tweet_reply_to(self, widget, data): - self.emit('tweet-in-reply-to', data) - - - # To keep things simple elsewhere and improve code reuse - # we'll build a list of home-cooked Status objects out of results. - # Why is this even necessary? - # Why can't we have more consistency out of the Twitter API? - def statuses_from_results(self, results): - statuses = [] - for result in results.results: - status = Status() - status.id = result.id - status.user = User() - status.user.screen_name = result.from_user - status.user.name = "" - status.in_reply_to_screen_name = result.to_user - # The Twitter Search API has different timestamps than the - # REST API... balls - # fixme: - # Gotta be a cleaner way to do this, but I can't think of it - # right now - created_at = re.sub(',', '', result.created_at) - created_split = re.split(' ', created_at) - status.created_at = created_split[0] + ' ' + created_split[2] + ' ' + created_split[1] + ' ' + created_split[4] + ' ' + created_split[5] + ' ' + created_split[3] - status.text = result.text - statuses.append(status) - return statuses - -### end class TweetPane - -# signals for TweetPane - -gobject.signal_new("tweet-reply", TweetPane, - gobject.SIGNAL_RUN_LAST, - gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)) -gobject.signal_new("tweet-retweet", TweetPane, - gobject.SIGNAL_RUN_LAST, - gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)) -gobject.signal_new("tweet-in-reply-to", TweetPane, - gobject.SIGNAL_RUN_LAST, - gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)) - - - -class TweetBox(gtk.VBox): - - ''' - GUI for displaying one tweet and associated buttons - - Also stores the data necessary for replying or retweeting (id, screen name) - ''' - - def __init__(self): - gtk.VBox.__init__(self) - - self.screen_name = None - self.id = None - self.in_reply_to_id = None - self.in_reply_to_screen_name = None - - self.init_widgets() - - - def init_widgets(self): - ## Build the header - self.header = gtk.Label() - label_eb = gtk.EventBox() - label_eb.add(self.header) - self.pack_start(label_eb) - - # Set the header's properties - label_eb.modify_text(gtk.STATE_NORMAL,gtk.gdk.color_parse("#ffffff")) - label_eb.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse("#8888ff")) - self.header.set_alignment(0.0, 0.0) - self.header.set_selectable(True) - self.header.set_line_wrap(True) - - ## Build the text - self.text = gtk.Label() - text_align = gtk.Alignment() - text_align.add(self.text) - self.text_eb = gtk.EventBox() - self.text_eb.add(text_align) - self.pack_start(self.text_eb) - - # Set the text's properties - text_align.set_padding(2, 5, 10, 5) - self.text.set_alignment(0.0, 0.0) - self.text.set_selectable(True) - self.text.set_line_wrap(True) - if gtk.gtk_version[0] > 2 or (gtk.gtk_version[0] == 2 and gtk.gtk_version[1] >= 18): - self.text.connect('activate-link', self.on_url_clicked) - self.text.connect('button-press-event', self.on_mouse_clicked) - - # Build the buttons - button_box_align = gtk.Alignment() - button_box_align.set_padding(0, 15, 0, 0) - button_box = gtk.HBox() - self.pack_start(button_box) - - self.reply_to_button = gtk.Button("") - self.reply_to_button.set_relief(gtk.RELIEF_NONE) - button_box.pack_start(self.reply_to_button, expand=False) - self.reply_to_button.connect("clicked", self.on_in_reply_to_clicked) - - reply_button = gtk.Button("Reply") - reply_button.set_relief(gtk.RELIEF_HALF) - button_box.pack_end(reply_button, expand=False) - reply_button.connect("clicked", self.on_reply_clicked) - - retweet_button = gtk.Button("Retweet") - retweet_button.set_relief(gtk.RELIEF_HALF) - button_box.pack_end(retweet_button, expand=False) - retweet_button.connect("clicked", self.on_retweet_clicked) - - - def set_status(self, status, read=True): - self.set_read(read) - - timezone = dateutil.tz.gettz() - time_format = "%Y.%m.%d %H:%M:%S %Z" - - # Get the user object - user = status.user - - # Get user's data for retweeting / replying - self.screen_name = user.screen_name - self.id = status.id - self.in_reply_to_id = status.in_reply_to_status_id - self.in_reply_to_screen_name = status.in_reply_to_screen_name - - # ... and a formatted timestamp - timestamp = datetime.datetime.strptime(status.created_at, "%a %b %d %H:%M:%S +0000 %Y") - timestamp = timestamp.replace(tzinfo=dateutil.tz.gettz('UTC')) - timestring = timestamp.astimezone(timezone).strftime(time_format) - - # Set the header - self.header.set_markup(user.name + " (" + user.screen_name + ") " + timestring) - - # and the text - new_text = status.text - new_text = re.sub(r'&([^;]*?)( |$)', r'&\1\2', new_text) - if gtk.gtk_version[0] > 2 or (gtk.gtk_version[0] == 2 and gtk.gtk_version[1] >= 18): - new_text = re.sub(r"(http://.*?)( |$)", r'\1\2', new_text) - self.text.set_markup(new_text) - - # If this is in reply to something, set appropriate label - if self.in_reply_to_screen_name: - self.reply_to_button.set_label('in reply to ' + self.in_reply_to_screen_name) - - - - def clear_status(self): - self.header.set_markup('') - self.text.set_markup('') - self.screen_name = None - self.id = None - self.set_read(True) - - - def set_read(self, read=True): - if read: - self.text_eb.modify_bg(gtk.STATE_NORMAL, - gtk.gdk.color_parse("#f2f1f0")) - else: - self.text_eb.modify_bg(gtk.STATE_NORMAL, - gtk.gdk.color_parse("#dbffdb")) - - - def on_reply_clicked(self, widget): - self.emit('reply') - - - def on_retweet_clicked(self, widget): - self.emit('retweet') - - - def on_in_reply_to_clicked(self, widget): - self.emit('in-reply-to', {'id': self.in_reply_to_id, 'name': self.in_reply_to_screen_name}) - - - def on_mouse_clicked(self, widget, event): - if event.button == 1: - self.set_read(True) - # fixme: call on_url_clicked if there is an active uri - # Apparently, this must wait until pygtk 2.18 - - - def on_url_clicked(self, widget): - # fixme: we're catching this signal just to debug why it doesn't get emitted - # seems to be related to EventBox? - print 'debug: on_url_clicked()' - return True - - -# end class TweetBox - -# signals for TweetBox -gobject.signal_new("reply", TweetBox, - gobject.SIGNAL_RUN_LAST, - gobject.TYPE_NONE, ()) -gobject.signal_new("retweet", TweetBox, - gobject.SIGNAL_RUN_LAST, - gobject.TYPE_NONE, ()) -gobject.signal_new("in-reply-to", TweetBox, - gobject.SIGNAL_RUN_LAST, - gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)) - - - -class CloseTabLabel(gtk.EventBox): - ''' - This class holds a label and a button with an 'I' in it. This button causes the CloseTabLabel - to emit a clicked signal - ''' - - def __init__(self, name=None): - gtk.EventBox.__init__(self) - self.init_widgets(name) - - - # This code is still heinous, but at least it is - # isolated to its own class - def init_widgets(self, name): - #create a custom tab for notebook containing a - #label and a button with STOCK_ICON - tabBox = gtk.HBox(False, 2) - tabButton=gtk.Button() - tabButton.connect('clicked', self.on_clicked) - - self.label = gtk.Label(name) - - #Add a picture on a button - iconBox = gtk.HBox(False, 0) - image = gtk.Image() - image.set_from_stock(gtk.STOCK_CLOSE,gtk.ICON_SIZE_MENU) - gtk.Button.set_relief(tabButton,gtk.RELIEF_NONE) - settings = gtk.Widget.get_settings(tabButton) - (w,h) = gtk.icon_size_lookup_for_settings(settings,gtk.ICON_SIZE_MENU) - gtk.Widget.set_size_request(tabButton, w + 4, h + 4); - iconBox.pack_start(image, True, False, 0) - tabButton.add(iconBox) - - tabBox.pack_start(self.label, False) - tabBox.pack_start(tabButton, False) - - self.connect('button-press-event', self.on_button_press) - - # needed, otherwise even calling show_all on the notebook won't - # make the hbox contents appear. - tabBox.show_all() - self.add(tabBox) - - - def set_label_text(self, new_text): - self.label.set_text(new_text) - - - def on_clicked(self, event): - self.emit('close-clicked') - - - def on_button_press(self, event, direction): - self.emit('label-clicked') - - -### end class CloseTabLabel - -# signals for CloseTabLabel -gobject.signal_new("close-clicked", CloseTabLabel, - gobject.SIGNAL_RUN_LAST, - gobject.TYPE_NONE, ()) -gobject.signal_new("label-clicked", CloseTabLabel, - gobject.SIGNAL_RUN_LAST, - gobject.TYPE_NONE, ()) - - - -class Status(): - def __init__(self): - self.user = User() - self.id = None - self.created_at = None - -class User(): - def __init__(self): - self.screen_name = None - self.name = None # main diff --git a/twitterwidgets.py b/twitterwidgets.py new file mode 100644 index 0000000..d307d2c --- /dev/null +++ b/twitterwidgets.py @@ -0,0 +1,429 @@ +import re +import datetime, dateutil.tz +import gtk, gobject + +class TweetPane(gtk.ScrolledWindow): + ''' + Box that holds all the TweetBoxes for a given feed + + This box will not update itself, the parent should do that. + + It will connect num_entries listeners to its parent's on_reply() and on_retweet() + + It also gets some data from its parent, including num_entries + ''' + + def __init__(self, list_name, num_entries=20, single_tweet=None): + gtk.ScrolledWindow.__init__(self) + + self.updated_once = False + + self.single_tweet = single_tweet + + self.list_name = list_name + + self.tab_label = CloseTabLabel(self.list_name) + + # These handle determining which tweets are unread + self.last_tweet_read = None + self.latest_tweet = None + self.num_new_tweets = 0 + + self.tweets = [] + + self.num_entries = num_entries + if self.single_tweet is not None: + self.num_entries = 1 + + self.init_widgets() + + + def init_widgets(self): + self.tab_label.connect('label_clicked', self.set_tweets_read_callback) + + tweet_box = gtk.VBox() + viewport = gtk.Viewport() + + # Build us some labels... + for i in range(0, self.num_entries): + self.tweets.append(TweetBox()) + tweet_box.pack_start(self.tweets[i], expand=False) + self.tweets[i].connect('reply', self.on_tweet_reply) + self.tweets[i].connect('retweet', self.on_retweet) + self.tweets[i].connect('in-reply-to', self.on_tweet_reply_to) + + viewport.add(tweet_box) + + # Several different actions should mark the tweets as 'read' + self.connect('focus', self.set_tweets_read_callback) + viewport.connect('button_press_event', self.set_tweets_read_callback) + self.connect('scroll-event', self.set_tweets_read_callback) + self.connect('scroll-child', self.set_tweets_read_callback) + + self.add(viewport) + self.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) + self.show_all() + + + def update_window(self, raw_statuses, using_results=False): + if using_results: + statuses = self.statuses_from_results(raw_statuses) + else: + statuses = raw_statuses + + if self.updated_once is False: + self.updated_once = True + + # If this is our first load of this list, don't treat anything as new! + if self.last_tweet_read is None: + self.last_tweet_read = statuses[0].id + + # Keep count of the new tweets for posting a status message + self.num_new_tweets = 0 + + for i in range(0, self.num_entries): + read = True + if i < len(statuses): + if statuses[i].id > self.last_tweet_read: + self.num_new_tweets += 1 + read = False + self.tweets[i].set_status(statuses[i], read) + else: + self.tweets[i].clear_status() + + self.latest_tweet = statuses[0].id + + self.update_tab_label() + + + # Update the label with the number of unread tweets + def update_tab_label(self): + pane_text = self.list_name + if self.num_new_tweets > 0: + pane_text += ' (' + str(self.num_new_tweets) + ')' + self.tab_label.set_label_text(pane_text) + + + def get_list_name(self): + return self.list_name + + + def set_tweets_read(self): + self.last_tweet_read = self.latest_tweet + self.num_new_tweets = 0 + self.update_tab_label() + + + def set_tweets_read_callback(self, event, arg1=None, arg2=None): + self.set_tweets_read() + + + def get_tab_label(self): + return self.tab_label + + + def get_single_tweet(self): + return self.single_tweet + + + def updated_once(self): + return self.updated_once + + + def on_tweet_reply(self, widget): + self.emit('tweet-reply', {'screen_name': widget.screen_name, 'id': widget.id}) + + + def on_retweet(self, widget): + self.emit('tweet-retweet', {'id': widget.id}) + + + def on_tweet_reply_to(self, widget, data): + self.emit('tweet-in-reply-to', data) + + + # To keep things simple elsewhere and improve code reuse + # we'll build a list of home-cooked Status objects out of results. + # Why is this even necessary? + # Why can't we have more consistency out of the Twitter API? + def statuses_from_results(self, results): + statuses = [] + for result in results.results: + status = Status() + status.id = result.id + status.user = User() + status.user.screen_name = result.from_user + status.user.name = "" + status.in_reply_to_screen_name = result.to_user + # The Twitter Search API has different timestamps than the + # REST API... balls + # fixme: + # Gotta be a cleaner way to do this, but I can't think of it + # right now + created_at = re.sub(',', '', result.created_at) + created_split = re.split(' ', created_at) + status.created_at = created_split[0] + ' ' + created_split[2] + ' ' + created_split[1] + ' ' + created_split[4] + ' ' + created_split[5] + ' ' + created_split[3] + status.text = result.text + statuses.append(status) + return statuses + +### end class TweetPane + +# signals for TweetPane + +gobject.signal_new("tweet-reply", TweetPane, + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)) +gobject.signal_new("tweet-retweet", TweetPane, + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)) +gobject.signal_new("tweet-in-reply-to", TweetPane, + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)) + + + +class TweetBox(gtk.VBox): + + ''' + GUI for displaying one tweet and associated buttons + + Also stores the data necessary for replying or retweeting (id, screen name) + ''' + + def __init__(self): + gtk.VBox.__init__(self) + + self.screen_name = None + self.id = None + self.in_reply_to_id = None + self.in_reply_to_screen_name = None + + self.init_widgets() + + + def init_widgets(self): + ## Build the header + self.header = gtk.Label() + label_eb = gtk.EventBox() + label_eb.add(self.header) + self.pack_start(label_eb) + + # Set the header's properties + label_eb.modify_text(gtk.STATE_NORMAL,gtk.gdk.color_parse("#ffffff")) + label_eb.modify_bg(gtk.STATE_NORMAL,gtk.gdk.color_parse("#8888ff")) + self.header.set_alignment(0.0, 0.0) + self.header.set_selectable(True) + self.header.set_line_wrap(True) + + ## Build the text + self.text = gtk.Label() + text_align = gtk.Alignment() + text_align.add(self.text) + self.text_eb = gtk.EventBox() + self.text_eb.add(text_align) + self.pack_start(self.text_eb) + + # Set the text's properties + text_align.set_padding(2, 5, 10, 5) + self.text.set_alignment(0.0, 0.0) + self.text.set_selectable(True) + self.text.set_line_wrap(True) + if gtk.gtk_version[0] > 2 or (gtk.gtk_version[0] == 2 and gtk.gtk_version[1] >= 18): + self.text.connect('activate-link', self.on_url_clicked) + self.text.connect('button-press-event', self.on_mouse_clicked) + + # Build the buttons + button_box_align = gtk.Alignment() + button_box_align.set_padding(0, 15, 0, 0) + button_box = gtk.HBox() + self.pack_start(button_box) + + self.reply_to_button = gtk.Button("") + self.reply_to_button.set_relief(gtk.RELIEF_NONE) + button_box.pack_start(self.reply_to_button, expand=False) + self.reply_to_button.connect("clicked", self.on_in_reply_to_clicked) + + reply_button = gtk.Button("Reply") + reply_button.set_relief(gtk.RELIEF_HALF) + button_box.pack_end(reply_button, expand=False) + reply_button.connect("clicked", self.on_reply_clicked) + + retweet_button = gtk.Button("Retweet") + retweet_button.set_relief(gtk.RELIEF_HALF) + button_box.pack_end(retweet_button, expand=False) + retweet_button.connect("clicked", self.on_retweet_clicked) + + + def set_status(self, status, read=True): + self.set_read(read) + + timezone = dateutil.tz.gettz() + time_format = "%Y.%m.%d %H:%M:%S %Z" + + # Get the user object + user = status.user + + # Get user's data for retweeting / replying + self.screen_name = user.screen_name + self.id = status.id + self.in_reply_to_id = status.in_reply_to_status_id + self.in_reply_to_screen_name = status.in_reply_to_screen_name + + # ... and a formatted timestamp + timestamp = datetime.datetime.strptime(status.created_at, "%a %b %d %H:%M:%S +0000 %Y") + timestamp = timestamp.replace(tzinfo=dateutil.tz.gettz('UTC')) + timestring = timestamp.astimezone(timezone).strftime(time_format) + + # Set the header + self.header.set_markup(user.name + " (" + user.screen_name + ") " + timestring) + + # and the text + new_text = status.text + new_text = re.sub(r'&([^;]*?)( |$)', r'&\1\2', new_text) + if gtk.gtk_version[0] > 2 or (gtk.gtk_version[0] == 2 and gtk.gtk_version[1] >= 18): + new_text = re.sub(r"(http://.*?)( |$)", r'\1\2', new_text) + self.text.set_markup(new_text) + + # If this is in reply to something, set appropriate label + if self.in_reply_to_screen_name: + self.reply_to_button.set_label('in reply to ' + self.in_reply_to_screen_name) + + + + def clear_status(self): + self.header.set_markup('') + self.text.set_markup('') + self.screen_name = None + self.id = None + self.set_read(True) + + + def set_read(self, read=True): + if read: + self.text_eb.modify_bg(gtk.STATE_NORMAL, + gtk.gdk.color_parse("#f2f1f0")) + else: + self.text_eb.modify_bg(gtk.STATE_NORMAL, + gtk.gdk.color_parse("#dbffdb")) + + + def on_reply_clicked(self, widget): + self.emit('reply') + + + def on_retweet_clicked(self, widget): + self.emit('retweet') + + + def on_in_reply_to_clicked(self, widget): + self.emit('in-reply-to', {'id': self.in_reply_to_id, 'name': self.in_reply_to_screen_name}) + + + def on_mouse_clicked(self, widget, event): + if event.button == 1: + self.set_read(True) + # fixme: call on_url_clicked if there is an active uri + # Apparently, this must wait until pygtk 2.18 + + + def on_url_clicked(self, widget): + # fixme: we're catching this signal just to debug why it doesn't get emitted + # seems to be related to EventBox? + print 'debug: on_url_clicked()' + return True + + +# end class TweetBox + +# signals for TweetBox +gobject.signal_new("reply", TweetBox, + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, ()) +gobject.signal_new("retweet", TweetBox, + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, ()) +gobject.signal_new("in-reply-to", TweetBox, + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)) + + + +class CloseTabLabel(gtk.EventBox): + ''' + This class holds a label and a button with an 'I' in it. This button causes the CloseTabLabel + to emit a clicked signal + ''' + + def __init__(self, name=None): + gtk.EventBox.__init__(self) + self.init_widgets(name) + + + # This code is still heinous, but at least it is + # isolated to its own class + def init_widgets(self, name): + #create a custom tab for notebook containing a + #label and a button with STOCK_ICON + tabBox = gtk.HBox(False, 2) + tabButton=gtk.Button() + tabButton.connect('clicked', self.on_clicked) + + self.label = gtk.Label(name) + + #Add a picture on a button + iconBox = gtk.HBox(False, 0) + image = gtk.Image() + image.set_from_stock(gtk.STOCK_CLOSE,gtk.ICON_SIZE_MENU) + gtk.Button.set_relief(tabButton,gtk.RELIEF_NONE) + settings = gtk.Widget.get_settings(tabButton) + (w,h) = gtk.icon_size_lookup_for_settings(settings,gtk.ICON_SIZE_MENU) + gtk.Widget.set_size_request(tabButton, w + 4, h + 4); + iconBox.pack_start(image, True, False, 0) + tabButton.add(iconBox) + + tabBox.pack_start(self.label, False) + tabBox.pack_start(tabButton, False) + + self.connect('button-press-event', self.on_button_press) + + # needed, otherwise even calling show_all on the notebook won't + # make the hbox contents appear. + tabBox.show_all() + self.add(tabBox) + + + def set_label_text(self, new_text): + self.label.set_text(new_text) + + + def on_clicked(self, event): + self.emit('close-clicked') + + + def on_button_press(self, event, direction): + self.emit('label-clicked') + + +### end class CloseTabLabel + +# signals for CloseTabLabel +gobject.signal_new("close-clicked", CloseTabLabel, + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, ()) +gobject.signal_new("label-clicked", CloseTabLabel, + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, ()) + + + +class Status(): + def __init__(self): + self.user = User() + self.id = None + self.created_at = None + +class User(): + def __init__(self): + self.screen_name = None + self.name = None