diff --git a/mytwitter.py b/mytwitter.py index 7cb2d5b..488b05a 100755 --- a/mytwitter.py +++ b/mytwitter.py @@ -2,14 +2,16 @@ # # Custom twitter client... mostly for learning Python -import sys, twitter, Tkinter, Pmw, ConfigParser, os, tkMessageBox, datetime, dateutil.tz +import sys, twitter, wx, ConfigParser, os, tkMessageBox, datetime, dateutil.tz -class TwitWindow: +class TwitWindow(wx.Frame): """ Display Tweets, post to twitter """ - def __init__(self): + 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') @@ -20,65 +22,101 @@ class TwitWindow: self.labels = [] self.texts = [] + self.init_widgets() - def create_window(self): - self.tkroot = Tkinter.Tk() - self.tkroot.title("MyTwitter") - Pmw.initialise(self.tkroot) - self.tkroot.grid_columnconfigure(0, weight=1) + + def init_widgets(self): + main_sizer = wx.BoxSizer(wx.VERTICAL) # Create the scrolled frame that will hold the tweets - self.tweet_view = Pmw.ScrolledFrame(self.tkroot, hscrollmode='none', horizflex='elastic') - self.tweet_view.grid(row=0) + 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(Tkinter.Label(self.tweet_view.interior())) - self.labels[i].pack(expand=False, fill=Tkinter.X) - self.texts.append(Tkinter.Label(self.tweet_view.interior())) - self.texts[i].pack(expand=False, fill=Tkinter.X) - self.labels[i].config(bg="#07c", fg="white", anchor=Tkinter.W) - self.texts[i].config(bg="#eff", fg="black", height=3, anchor=Tkinter.NW, justify=Tkinter.LEFT, wraplength=375) + 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 - self.button_box = Tkinter.Frame(self.tkroot) - self.button_box.grid(row=1, sticky=Tkinter.W) - self.refresh_button = Tkinter.Button(self.button_box, text="Refresh", command=self.update_window) - self.refresh_button.grid(row=0, sticky=Tkinter.W) + 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 - self.update_box = Tkinter.Frame(self.tkroot) - self.update_box.grid(row=2, sticky=Tkinter.W) - self.update_entry = Tkinter.Entry(self.update_box) - self.update_entry.grid(row=0, sticky=Tkinter.W) - self.update_box.grid_columnconfigure(0, weight=1) - self.update_button = Tkinter.Button(self.update_box, text="Update", command=self.update_status) - self.update_button.grid(row=0, column=1, sticky=Tkinter.W) - self.update_count = Tkinter.Label(self.update_box, text="0/140") - self.update_count.grid(row=0, column=2, sticky=Tkinter.E) + 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 - 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) + # 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) - # Bind any key in the update_entry to adjust the counter, except return - self.update_entry.bind('', self.char_counter) - self.update_entry.bind('', self.update_status_callback) + # 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 and start up the main loop + # 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() - self.tkroot.mainloop() + + # end of init_widgets - def update_window(self) : + def update_window(self): + print "DEBUG: update_window()" + timezone = dateutil.tz.gettz() time_format = "%Y.%m.%d %H:%M:%S %Z" @@ -88,28 +126,31 @@ class TwitWindow: # Update the label with the user's name and screen name user = statuses[i].user -# fixme: need to find a way to detect local timezone and print in that -# Also need to decide how to format the output... maybe use a config var? 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].config(text=labeltext) + self.labels[i].Clear() + self.labels[i].AppendText(labeltext) # Display the text of the tweet - self.texts[i].config(text=statuses[i].text) + self.texts[i].Clear() + self.texts[i].AppendText(statuses[i].text) - self.timer = self.tkroot.after(self.refresh_time * 1000, self.update_window) + def update_window_callback(self, event): + self.update_window() def update_status(self): - text = self.update_entry.get() + text = self.update_entry.GetValue() if len(text) > 140: - tkMessageBox.showerror('Tweet too long', 'Tweets can only be 140 characters. Write a shorter tweet and try again.') + d = wx.MessageDialog(self, 'Tweet too long', 'Tweets must be <= 140 characters'); + d.ShowModal() + d.Destroy() return - self.update_entry.delete(0, Tkinter.END) + self.update_entry.Clear() self.api.PostUpdate(text) self.update_window() @@ -120,32 +161,37 @@ class TwitWindow: self.update_status() - 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'); + # 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_up(self, event): + # self.tweet_view.yview('scroll', -15, 'units'); - def page_down(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_up(self, event): + # self.tweet_view.yview('scroll', -5, 'units'); - def line_down(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.get())) + "/140" - self.update_count.config(text=new_count) + 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 @@ -172,5 +218,7 @@ def print_formatted(statuses): # main -win = TwitWindow() -win.create_window() +app = wx.App(False) +root = TwitWindow(None, "MyTwitter") +root.Show(True) +app.MainLoop()