diff --git a/apithreads.py b/apithreads.py index ba4dc06..0ea9c78 100644 --- a/apithreads.py +++ b/apithreads.py @@ -6,7 +6,7 @@ from threading import Thread,RLock import twitter_pb2 from oauthtwitter import OAuthApi from urllib2 import HTTPError,URLError -import avcache +import usercache import webbrowser from time import sleep @@ -107,10 +107,11 @@ class GetTweets(ApiThread): except (HTTPError, URLError): statuses = None - # For each user id present, populate the AvCache with an image + # For each user id present, populate the UserCaches if statuses: for status in statuses: - avcache.add_to_cache(status.user) + usercache.add_to_av_cache(status.user) + usercache.add_to_name_cache(status.user, self.api) gtk.gdk.threads_enter() try: @@ -140,7 +141,8 @@ class GetSingleTweet(ApiThread): # In case we've never seen this user, grab their profile image for status in statuses: - avcache.add_to_cache(status.user) + usercache.add_to_av_cache(status.user) + usercache.add_to_name_cache(status.user) gtk.gdk.threads_enter() try: @@ -184,7 +186,8 @@ class GetConversation(ApiThread): # In case we've never seen some of these users, grab their profile images and cache them for status in statuses: - avcache.add_to_cache(status.user) + usercache.add_to_av_cache(status.user) + usercache.add_to_name_cache(status.user) statuses.reverse() @@ -235,7 +238,8 @@ class GetUserInfo(ApiThread): try: with self.api.lock: user = self.api.GetUser(screen_name) - avcache.add_to_cache(user) + usercache.add_to_av_cache(user) + usercache.add_to_name_cache(user) except (HTTPError, URLError): verified = False user = None diff --git a/avcache.py b/avcache.py deleted file mode 100644 index 18da168..0000000 --- a/avcache.py +++ /dev/null @@ -1,65 +0,0 @@ -from threading import RLock -from urllib2 import URLError,urlopen -import gtk - - -class AvCache: - """ - Store a cache of avatar images we've already downloaded. - This cache will be accessed by a number of threads, so it includes - a lock as well. - """ - - class __impl: - """ Implementation of the singleton interface """ - - def __init__(self): - self.lock = RLock() - self.map = {} - - - # storage for the instance reference - __instance = None - - def __init__(self): - """ Create singleton instance """ - # Check whether we already have an instance - if AvCache.__instance is None: - # Create and remember instance - AvCache.__instance = AvCache.__impl() - - # Store instance reference as the only member in the handle - self.__dict__['_AvCache__instance'] = AvCache.__instance - - def __getattr__(self, attr): - """ Delegate access to implementation """ - return getattr(self.__instance, attr) - - def __setattr__(self, attr, value): - """ Delegate access to implementation """ - return setattr(self.__instance, attr, value) - -# end class AvCache - - -def add_to_cache(user): - """ - This helping function takes a python-twitter User object, grabs the image - data from the image_url and adds it to the AvCache with the screen_name as - the key. - """ - with AvCache().lock: - hit = AvCache().map.has_key(user.screen_name) - if not hit: - try: - url = urlopen(user.profile.image_url) - data = url.read() - loader = gtk.gdk.PixbufLoader() - loader.write(str(data)) - image = loader.get_pixbuf() - loader.close() - - with AvCache().lock: - AvCache().map[user.screen_name] = image - except (URLError, ValueError): - pass # Nothing needs be done, just catch & ignore diff --git a/twitterwidgets.py b/twitterwidgets.py index 4a7c9a8..d1d1c1b 100644 --- a/twitterwidgets.py +++ b/twitterwidgets.py @@ -2,7 +2,7 @@ import re import datetime, dateutil.tz import gtk, gobject from threading import RLock -from avcache import AvCache +from usercache import AvCache,NameCache import webbrowser @@ -377,11 +377,18 @@ class TweetBox(gtk.HBox): if not self.is_user: try: with AvCache().lock: - self.avatar.set_from_pixbuf(AvCache().map[status.user.screen_name]) + image = AvCache().map[status.user.screen_name] + self.avatar.set_from_pixbuf(image) self.avatar.show() except KeyError: self.avatar.hide() - + + try: + with NameCache().lock: + name = NameCache().map[status.user.screen_name] + except KeyError: + name = status.user.name + self.set_read(read) timezone = dateutil.tz.gettz() @@ -424,7 +431,7 @@ class TweetBox(gtk.HBox): if self.is_user: self.user_button.set_label('') else: - self.user_button.set_label(user.name + " (" + user.screen_name + ") ") + self.user_button.set_label(name + " (" + user.screen_name + ") ") # and the text new_text = status.text @@ -591,15 +598,24 @@ class UserBox(gtk.VBox): def update_info(self, user): self.user_name = user.screen_name - self.name_label.set_text(user.name + ' (' + self.user_name + ')') + name = '' try: with AvCache().lock: - self.avatar.set_from_pixbuf(AvCache().map[user.screen_name]) + image = AvCache().map[user.screen_name] + self.avatar.set_from_pixbuf(image) self.avatar.show() except KeyError: self.avatar.hide() + try: + with NameCache().lock: + name = NameCache().map[user.screen_name] + except KeyError: + name = user.name + + self.name_label.set_text(name + ' (' + self.user_name + ')') + with self.data_lock: self.verified = user.verified if self.verified: diff --git a/usercache.py b/usercache.py new file mode 100644 index 0000000..2b436de --- /dev/null +++ b/usercache.py @@ -0,0 +1,131 @@ +from threading import RLock +from urllib2 import URLError,urlopen +import gtk + + +class AvCache: + """ + Store a cache of user data we've already downloaded. + This cache will be accessed by a number of threads, so it includes + a lock as well. + """ + + class __impl: + """ Implementation of the singleton interface """ + + def __init__(self): + self.lock = RLock() + self.map = {} + + + # storage for the instance reference + __instance = None + + def __init__(self): + """ Create singleton instance """ + # Check whether we already have an instance + if AvCache.__instance is None: + # Create and remember instance + AvCache.__instance = AvCache.__impl() + + # Store instance reference as the only member in the handle + self.__dict__['_AvCache__instance'] = AvCache.__instance + + def __getattr__(self, attr): + """ Delegate access to implementation """ + return getattr(self.__instance, attr) + + def __setattr__(self, attr, value): + """ Delegate access to implementation """ + return setattr(self.__instance, attr, value) + +# end class AvCache + + +class NameCache: + """ + Store a cache of user names we've already downloaded. + This cache will be accessed by a number of threads, so it includes + a lock as well. + """ + + class __impl: + """ Implementation of the singleton interface """ + + def __init__(self): + self.lock = RLock() + self.map = {} + + + # storage for the instance reference + __instance = None + + def __init__(self): + """ Create singleton instance """ + # Check whether we already have an instance + if NameCache.__instance is None: + # Create and remember instance + NameCache.__instance = NameCache.__impl() + + # Store instance reference as the only member in the handle + self.__dict__['_NameCache__instance'] = NameCache.__instance + + def __getattr__(self, attr): + """ Delegate access to implementation """ + return getattr(self.__instance, attr) + + def __setattr__(self, attr, value): + """ Delegate access to implementation """ + return setattr(self.__instance, attr, value) + +# end class NameCache + + + +def add_to_av_cache(user): + """ + This helping function takes a python-twitter User object, grabs the image + and from the image_url and adds it to the cache, along with the user's + full name, with the screen_name as the key. + """ + with AvCache().lock: + hit = AvCache().map.has_key(user.screen_name) + + if not hit: + try: + url = urlopen(user.profile.image_url) + data = url.read() + loader = gtk.gdk.PixbufLoader() + loader.write(str(data)) + image = loader.get_pixbuf() + loader.close() + with AvCache().lock: + AvCache().map[user.screen_name] = image + except (URLError, ValueError): + pass + + +def add_to_name_cache(user, api=None): + """ + This helping function takes a python-twitter User object and saves the + user's full name to the cache. If an API is passed in and the name in + 'user' is false, we can grab the UserInfo for the user and get the name + directly from that. + """ + with NameCache().lock: + hit = NameCache().map.has_key(user.screen_name) + if not hit: + NameCache().map[user.screen_name] = user.name + name = NameCache().map[user.screen_name] + + # Now let's look at the name we've got, and see if we need a better one + if not name: + with api.lock: + try: + new_user = api.GetUser(user.screen_name) + except (HTTPError, URLError): + new_user = None + + if new_user is not None: + with NameCache().lock: + NameCache().map[user.screen_name] = new_user.name