#!/usr/bin/python # # Custom twitter client... mostly for learning Python import sys, twitter, wx, ConfigParser, os, tkMessageBox, datetime, dateutil.tz class TwitWindow(wx.Frame): """ Display Tweets, post to twitter """ def __init__(self, parent, title): wx.Frame.__init__(self, parent, title=title, size=(300,600)) config = ConfigParser.ConfigParser() config.read(os.path.expanduser("~/.mytwitter")) self.username = config.get('global', 'username') self.password = config.get('global', 'password') self.num_entries = int(config.get('global', 'entries')) self.refresh_time = int(config.get('global', 'refreshtime')) self.labels = [] self.texts = [] self.init_widgets() def init_widgets(self): main_sizer = wx.BoxSizer(wx.VERTICAL) # Create the scrolled frame that will hold the tweets tweet_view = wx.ScrolledWindow(self) tweet_sizer = wx.BoxSizer(wx.VERTICAL) # Status bar self.CreateStatusBar() # Menu bar filemenu = wx.Menu() filemenu.Append(wx.ID_ABOUT, "&About", "About MyTwitter") menu_exit = filemenu.Append(wx.ID_EXIT, "E&xit", "Close MyTwitter") self.Bind(wx.EVT_MENU, self.on_exit, menu_exit) menu_bar = wx.MenuBar() menu_bar.Append(filemenu, "&File") self.SetMenuBar(menu_bar) # Create labels and text widgets for i in range(0, self.num_entries): self.labels.append(wx.TextCtrl(tweet_view, style=wx.TE_READONLY)) self.texts.append(wx.TextCtrl(tweet_view, style=wx.TE_MULTILINE | wx.TE_READONLY)) self.labels[i].SetDefaultStyle(wx.TextAttr('DARK_BLUE')) self.texts[i].SetDefaultStyle(wx.TextAttr('LIGHT_BLUE')) tweet_sizer.Add(self.labels[i]) tweet_sizer.Add(self.texts[i]) # Layout the tweet view widget width,height=tweet_view.GetSizeTuple() tweet_view.SetSizerAndFit(tweet_sizer) tweet_view.SetScrollbars(0, 20, 0, height/20) tweet_view.SetSize((300,400)) # A button to refresh manually button_sizer = wx.BoxSizer(wx.HORIZONTAL) refresh_button = wx.Button(self, label="Refresh") refresh_button.Bind(wx.EVT_BUTTON, self.update_window) button_sizer.Add(refresh_button) # Create an update box at the bottom of the window update_sizer = wx.BoxSizer(wx.HORIZONTAL) self.update_entry = wx.TextCtrl(self) self.update_entry.Bind(wx.EVT_TEXT, self.char_counter) self.update_entry.Bind(wx.EVT_TEXT_ENTER, self.update_status_callback) update_button = wx.Button(self, label="Update") update_button.Bind(wx.EVT_BUTTON, self.update_status) self.update_count = wx.StaticText(self, label="0/140") update_sizer.Add(self.update_entry, 1, wx.EXPAND) update_sizer.Add(update_button) update_sizer.Add(self.update_count) ### Set up bindings # Bind scrollwheel to move the tweets, as well as page up/down # fixme: convert this to wxPython # self.tkroot.bind_all("", self.scroll_wheel) # self.tkroot.bind_all("", self.scroll_wheel) # self.tkroot.bind_all("", self.line_up) # self.tkroot.bind_all("", self.line_down) # self.tkroot.bind_all("", self.page_up) # self.tkroot.bind_all("", self.page_down) # Pack the top level together main_sizer.Add(tweet_view, 1, wx.EXPAND) main_sizer.Add(button_sizer) main_sizer.Add(update_sizer) self.SetSizerAndFit(main_sizer) # Init the twitter API self.api = twitter.Api(username=self.username, password=self.password) # Updates! self.timer = wx.Timer(self) self.timer.Bind(wx.EVT_TIMER, self.update_window_callback) self.timer.Start(self.refresh_time * 1000) self.update_window() # end of init_widgets def update_window(self): print "DEBUG: update_window()" timezone = dateutil.tz.gettz() time_format = "%Y.%m.%d %H:%M:%S %Z" statuses = self.api.GetFriendsTimeline(self.username, count=self.num_entries) for i in range(0, self.num_entries): if i < len(statuses): # Update the label with the user's name and screen name user = statuses[i].user timestamp = datetime.datetime.strptime(statuses[i].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) labeltext = user.name + " (" + user.screen_name + ") " + timestring self.labels[i].Clear() self.labels[i].AppendText(labeltext) # Display the text of the tweet self.texts[i].Clear() self.texts[i].AppendText(statuses[i].text) def update_window_callback(self, event): self.update_window() def update_status(self): text = self.update_entry.GetValue() if len(text) > 140: d = wx.MessageDialog(self, 'Tweet too long', 'Tweets must be <= 140 characters'); d.ShowModal() d.Destroy() return self.update_entry.Clear() self.api.PostUpdate(text) self.update_window() # Just calls update_status, here so that things # that pass an event can be used def update_status_callback(self, event): self.update_status() # fixme: convert all of the below to wxPython # def scroll_wheel(self, event): # if event.num == 4: # self.tweet_view.yview('scroll', -5, 'units'); # if event.num == 5: # self.tweet_view.yview('scroll', 5, 'units'); # def page_up(self, event): # self.tweet_view.yview('scroll', -15, 'units'); # def page_down(self, event): # self.tweet_view.yview('scroll', 15, 'units'); # def line_up(self, event): # self.tweet_view.yview('scroll', -5, 'units'); # def line_down(self, event): # self.tweet_view.yview('scroll', 5, 'units'); def char_counter(self, event): new_count = str(len(self.update_entry.GetValue())) + "/140" self.update_count.SetLabel(new_count) def on_exit(self, event): self.Close(True) ### end class TwitWindow def print_lists(): data = api.GetUserLists() for l in data['lists']: print l.name def print_statuses_list(): statuses = api.GetListStatuses(sys.argv[2]) print_formatted(statuses) def print_statuses(): statuses = api.GetFriendsTimeline(username) print_formatted(statuses) def print_formatted(statuses): for s in statuses: print s.user.name.encode("utf-8"), "(", s.user.screen_name, ") :" print s.text.encode("utf-8") print # main app = wx.App(False) root = TwitWindow(None, "MyTwitter") root.Show(True) app.MainLoop()