diff --git a/apithreads.py b/apithreads.py index 5f4b19b..d2ea080 100644 --- a/apithreads.py +++ b/apithreads.py @@ -209,13 +209,13 @@ class GetFollowing(ApiThread): except (HTTPError, URLError): following = False - self.pane.set_following(following) + self.pane.user_box.set_following(following) ### End class GetFollowing -class GetVerified(ApiThread): +class GetUserInfo(ApiThread): def __init__(self, api, pane, user): ApiThread.__init__(self, api) self.pane = pane @@ -228,13 +228,17 @@ class GetVerified(ApiThread): try: with self.api.lock: user = self.api.GetUser(screen_name) - verified = user.verified except (HTTPError, URLError): verified = False - self.pane.set_verified(verified) + + gtk.gdk.threads_enter() + try: + self.pane.user_box.update_info(user) + finally: + gtk.gdk.threads_leave() -### End class GetVerified +### End class GetUserInfo class GetUserLists(ApiThread): diff --git a/mytwitter.py b/mytwitter.py index 95d70fc..eaaa419 100755 --- a/mytwitter.py +++ b/mytwitter.py @@ -343,8 +343,10 @@ class MyTwitter(): new_pane = TweetPane(name, num_entries=entries, single_tweet=single_tweet, is_user=is_user, conversation=conversation) if is_user: + new_pane.connect('at-clicked', self.on_at_button_clicked) + new_pane.connect('follow-clicked', self.on_follow_button_clicked) apithreads.GetFollowing(api=self.api, pane=new_pane, user=name).start() - apithreads.GetVerified(api=self.api, pane=new_pane, user=name).start() + apithreads.GetUserInfo(api=self.api, pane=new_pane, user=name).start() self.tweet_notebook.append_page_menu(new_pane, new_pane.get_tab_label(), gtk.Label(name)) self.tweet_notebook.set_tab_reorderable(new_pane, True) @@ -354,8 +356,6 @@ class MyTwitter(): new_pane.connect('tweet-in-reply-to', self.on_reply_to) new_pane.connect('tweet-conversation', self.on_conversation) new_pane.connect('show-user', self.show_user_callback) - new_pane.connect('following-set', self.update_follow_button) - new_pane.connect('verified-set', self.update_verified_label) # Special logic for single tweet pane if single_tweet is not None: @@ -381,13 +381,6 @@ class MyTwitter(): # Now get the new page, and set everything up pane = self.tweet_notebook.get_nth_page(page_num) pane.set_tweets_read() - self.update_follow_button(pane) - if pane.get_is_user(): - self.at_button.show() - else: - self.at_button.hide() - - self.update_verified_label(pane) def on_tabs_reordered(self, widget, child, page_num): @@ -415,19 +408,6 @@ class MyTwitter(): self.status_bar.push(self.context_id, text) - def on_following_button_clicked(self, event): - current_pane = self.get_current_pane() - user_name = re.sub('^user: ', '', current_pane.get_list_name()) - if current_pane.get_following(): - follow = False # destroy the friendship - else: - follow = True - - thread = apithreads.ChangeFriendship(self.api, current_pane, user_name, follow) - thread.sig_proxy.connect('friendship-changed', self.on_friendship_changed) - thread.start() - - def on_friendship_changed(self, widget, data): if data['success']: if data['follow']: @@ -441,26 +421,6 @@ class MyTwitter(): self.update_status_bar('Failed to unfollow ' + data['user_name']) - # pane should be the currently active pane - def update_follow_button(self, pane): - if not pane.get_is_user(): - self.following_button.set_label('') - self.following_button.hide() - elif pane.get_following(): - self.following_button.set_label('Unfollow') - self.following_button.show() - else: - self.following_button.set_label('Follow') - self.following_button.show() - - - def update_verified_label(self, pane): - if pane.get_verified(): - self.verified_label.show() - else: - self.verified_label.hide() - - def show_user(self, name): self.add_to_notebook('user: ' + name) @@ -469,9 +429,7 @@ class MyTwitter(): self.show_user(data) - def on_at_button_clicked(self, widget): - current_pane = self.get_current_pane() - user_name = re.sub('^user: ', '', current_pane.get_list_name()) + def on_at_button_clicked(self, widget, user_name): self.add_to_notebook('@' + user_name) diff --git a/twitterwidgets.py b/twitterwidgets.py index ba3437e..de39fb9 100644 --- a/twitterwidgets.py +++ b/twitterwidgets.py @@ -19,8 +19,6 @@ class TweetPane(gtk.ScrolledWindow): def __init__(self, list_name, num_entries=20, single_tweet=None, is_user=False, conversation=False): gtk.ScrolledWindow.__init__(self) - self.data_lock = RLock() - self.list_name = list_name self.single_tweet = single_tweet self.conversation = conversation @@ -54,8 +52,14 @@ class TweetPane(gtk.ScrolledWindow): # Build us some labels... tweet_box.pack_start(self.message) + if self.is_user: + self.user_box = UserBox() + self.user_box.connect('at-clicked', self.on_at_clicked) + self.user_box.connect('follow-clicked', self.on_follow_clicked) + tweet_box.pack_start(self.user_box) + for i in range(0, self.num_entries): - self.tweets.append(TweetBox(self.conversation)) + self.tweets.append(TweetBox(self.conversation, self.is_user)) 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) @@ -126,6 +130,13 @@ class TweetPane(gtk.ScrolledWindow): self.update_tab_label() + # Update the user_box with profile icon, name, etc... + # Thread calling this should have the gtk lock... + def update_user_info(self, user): + if self.is_user: + self.user_box.update_info(user) + + # Update the label with the number of unread tweets def update_tab_label(self): pane_text = self.list_name @@ -175,36 +186,22 @@ class TweetPane(gtk.ScrolledWindow): def on_tweet_conversation(self, widget, data): self.emit('tweet-conversation', data) + def on_show_user(self, widget, data): self.emit('show-user', data) - def get_following(self): - with self.data_lock: - return self.following - - - def get_verified(self): - with self.data_lock: - return self.verified - - - def set_following(self, following): - with self.data_lock: - self.following = following - self.emit("following-set") - - - def set_verified(self, verified): - with self.data_lock: - self.verified = verified - self.emit("verified-set") - - def get_is_user(self): return self.is_user + def on_at_clicked(self, widget, data): + self.emit('at-clicked', data) + + + def on_follow_clicked(self, widget, data): + self.emit('follow-clicked', data) + ### end class TweetPane # signals for TweetPane @@ -224,12 +221,12 @@ gobject.signal_new("tweet-conversation", TweetPane, gobject.signal_new("show-user", TweetPane, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)) -gobject.signal_new("following-set", TweetPane, +gobject.signal_new("follow-clicked", TweetPane, gobject.SIGNAL_RUN_LAST, - gobject.TYPE_NONE, ()) -gobject.signal_new("verified-set", TweetPane, + gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)) +gobject.signal_new("at-clicked", TweetPane, gobject.SIGNAL_RUN_LAST, - gobject.TYPE_NONE, ()) + gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)) @@ -241,7 +238,7 @@ class TweetBox(gtk.HBox): Also stores the data necessary for replying or retweeting (id, screen name) ''' - def __init__(self, conversation=False): + def __init__(self, conversation=False, is_user=False): gtk.HBox.__init__(self) self.screen_name = None @@ -252,15 +249,18 @@ class TweetBox(gtk.HBox): # Lets the tweetbox know if it is part of a conversation or not self.conversation = conversation + self.is_user = is_user + self.init_widgets() def init_widgets(self): # Build the image - self.avatar = gtk.Image() - self.avatar.set_alignment(0.0, 0.0) - self.pack_start(self.avatar, expand=False, fill=False) - self.avatar.hide() + if not self.is_user: + self.avatar = gtk.Image() + self.avatar.set_alignment(0.0, 0.0) + self.pack_start(self.avatar, expand=False, fill=False) + self.avatar.hide() # Everything else goes in a VBox beside the image text_box = gtk.VBox() @@ -279,7 +279,10 @@ class TweetBox(gtk.HBox): self.header.set_alignment(0.0, 0.0) # Handle the header being clicked - self.header.connect('clicked', self.on_user_clicked) + if self.is_user: + self.header.set_sensitive(False) + else: + self.header.connect('clicked', self.on_user_clicked) ## Build the text self.text = gtk.Label() @@ -331,12 +334,13 @@ class TweetBox(gtk.HBox): self.clear_status() # Set avatar - try: - with AvCache().lock: - self.avatar.set_from_pixbuf(AvCache().map[status.user.screen_name]) - self.avatar.show() - except KeyError: - self.avatar.hide() + if not self.is_user: + try: + with AvCache().lock: + self.avatar.set_from_pixbuf(AvCache().map[status.user.screen_name]) + self.avatar.show() + except KeyError: + self.avatar.hide() self.set_read(read) @@ -358,7 +362,11 @@ class TweetBox(gtk.HBox): timestring = timestamp.astimezone(timezone).strftime(time_format) # Set the header - self.header.set_label(user.name + " (" + user.screen_name + ") " + timestring) + if self.is_user: + header_text = timestring + else: + header_text = user.name + " (" + user.screen_name + ") " + timestring + self.header.set_label(header_text) # and the text new_text = status.text @@ -383,7 +391,8 @@ class TweetBox(gtk.HBox): self.set_read(True) self.reply_to_button.set_label('') self.conversation_button.hide() - self.avatar.hide() + if not self.is_user: + self.avatar.hide() def set_read(self, read=True): @@ -456,6 +465,105 @@ gobject.signal_new("show-user", TweetBox, +class UserBox(gtk.VBox): + def __init__(self): + self.data_lock = RLock() + + self.user_name = None + self.following = False + self.verified = False + self.init_widgets() + + + def init_widgets(self): + self.name_label = gtk.Label() + self.avatar = gtk.Image() + self.follow_button = gtk.Button() + at_button = gtk.Button('@') + self.follow_label = gtk.Label('You are following this user') + self.verified_label = gtk.Label('Verified account') + + self.pack_start(self.name_label) + self.pack_start(self.avatar) + self.pack_start(self.follow_label) + self.pack_start(self.verified_label) + button_row = gtk.HBox() + button_row.pack_start(self.follow_button) + button_row.pack_start(at_button) + self.pack_start(self.button_row) + + at_button.connect('clicked', self.on_at_clicked) + follow_button.connect('clicked', self.on_follow_clicked) + + self.show_all() + self.verified_label.hide() + self.follow_label.hide() + + + def update_info(self, user): + self.user_name = user.screen_name + self.name_label.set_text(user.name + ' (' + self.user_name + ')') + + try: + with AvCache().lock: + self.avatar.set_from_pixbuf(AvCache().map[status.user.screen_name]) + self.avatar.show() + except KeyError: + self.avatar.hide() + + with self.data_lock: + self.verified = user.verified + if self.verified: + self.verified_label.show() + else: + self.verified_label.hide() + + + def on_follow_clicked(self, event): + if self.following: + follow = False # destroy the friendship + else: + follow = True + + self.emit('follow-clicked', follow) + + + def on_at_clicked(self, widget): + self.emit('at-clicked', self.user_name) + + + def get_following(self): + with self.data_lock: + return self.following + + + def get_verified(self): + with self.data_lock: + return self.verified + + + def set_following(self, following): + with self.data_lock: + self.following = following + if following: + self.follow_button.set_text('Unfollow') + self.follow_label.show() + else: + self.follow_button.set_text('Follow') + self.follow_label.hide() + +# end class UserBox + +# signals for UserBox +gobject.signal_new("follow-clicked", UserBox, + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)) +gobject.signal_new("at-clicked", UserBox, + 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