Began conversion from Tkinter to wxPython, because Tkinter fails at too many things. Code is currently broken
This commit is contained in:
parent
21fcf25544
commit
50b9ad477b
180
mytwitter.py
180
mytwitter.py
|
@ -2,14 +2,16 @@
|
||||||
#
|
#
|
||||||
# Custom twitter client... mostly for learning Python
|
# 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 """
|
""" 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 = ConfigParser.ConfigParser()
|
||||||
config.read(os.path.expanduser("~/.mytwitter"))
|
config.read(os.path.expanduser("~/.mytwitter"))
|
||||||
self.username = config.get('global', 'username')
|
self.username = config.get('global', 'username')
|
||||||
|
@ -20,65 +22,101 @@ class TwitWindow:
|
||||||
self.labels = []
|
self.labels = []
|
||||||
self.texts = []
|
self.texts = []
|
||||||
|
|
||||||
|
self.init_widgets()
|
||||||
|
|
||||||
def create_window(self):
|
|
||||||
self.tkroot = Tkinter.Tk()
|
def init_widgets(self):
|
||||||
self.tkroot.title("MyTwitter")
|
main_sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
Pmw.initialise(self.tkroot)
|
|
||||||
self.tkroot.grid_columnconfigure(0, weight=1)
|
|
||||||
|
|
||||||
# Create the scrolled frame that will hold the tweets
|
# Create the scrolled frame that will hold the tweets
|
||||||
self.tweet_view = Pmw.ScrolledFrame(self.tkroot, hscrollmode='none', horizflex='elastic')
|
tweet_view = wx.ScrolledWindow(self)
|
||||||
self.tweet_view.grid(row=0)
|
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
|
# Create labels and text widgets
|
||||||
for i in range(0, self.num_entries):
|
for i in range(0, self.num_entries):
|
||||||
self.labels.append(Tkinter.Label(self.tweet_view.interior()))
|
self.labels.append(wx.TextCtrl(tweet_view, style=wx.TE_READONLY))
|
||||||
self.labels[i].pack(expand=False, fill=Tkinter.X)
|
self.texts.append(wx.TextCtrl(tweet_view, style=wx.TE_MULTILINE | wx.TE_READONLY))
|
||||||
self.texts.append(Tkinter.Label(self.tweet_view.interior()))
|
self.labels[i].SetDefaultStyle(wx.TextAttr('DARK_BLUE'))
|
||||||
self.texts[i].pack(expand=False, fill=Tkinter.X)
|
self.texts[i].SetDefaultStyle(wx.TextAttr('LIGHT_BLUE'))
|
||||||
self.labels[i].config(bg="#07c", fg="white", anchor=Tkinter.W)
|
tweet_sizer.Add(self.labels[i])
|
||||||
self.texts[i].config(bg="#eff", fg="black", height=3, anchor=Tkinter.NW, justify=Tkinter.LEFT, wraplength=375)
|
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
|
# A button to refresh manually
|
||||||
self.button_box = Tkinter.Frame(self.tkroot)
|
button_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
self.button_box.grid(row=1, sticky=Tkinter.W)
|
refresh_button = wx.Button(self, label="Refresh")
|
||||||
self.refresh_button = Tkinter.Button(self.button_box, text="Refresh", command=self.update_window)
|
refresh_button.Bind(wx.EVT_BUTTON, self.update_window)
|
||||||
self.refresh_button.grid(row=0, sticky=Tkinter.W)
|
|
||||||
|
button_sizer.Add(refresh_button)
|
||||||
|
|
||||||
# Create an update box at the bottom of the window
|
# Create an update box at the bottom of the window
|
||||||
self.update_box = Tkinter.Frame(self.tkroot)
|
update_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
self.update_box.grid(row=2, sticky=Tkinter.W)
|
|
||||||
self.update_entry = Tkinter.Entry(self.update_box)
|
self.update_entry = wx.TextCtrl(self)
|
||||||
self.update_entry.grid(row=0, sticky=Tkinter.W)
|
self.update_entry.Bind(wx.EVT_TEXT, self.char_counter)
|
||||||
self.update_box.grid_columnconfigure(0, weight=1)
|
self.update_entry.Bind(wx.EVT_TEXT_ENTER, self.update_status_callback)
|
||||||
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)
|
update_button = wx.Button(self, label="Update")
|
||||||
self.update_count = Tkinter.Label(self.update_box, text="0/140")
|
update_button.Bind(wx.EVT_BUTTON, self.update_status)
|
||||||
self.update_count.grid(row=0, column=2, sticky=Tkinter.E)
|
|
||||||
|
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
|
### Set up bindings
|
||||||
|
|
||||||
# Bind scrollwheel to move the tweets, as well as page up/down
|
# Bind scrollwheel to move the tweets, as well as page up/down
|
||||||
self.tkroot.bind_all("<Button-4>", self.scroll_wheel)
|
# fixme: convert this to wxPython
|
||||||
self.tkroot.bind_all("<Button-5>", self.scroll_wheel)
|
# self.tkroot.bind_all("<Button-4>", self.scroll_wheel)
|
||||||
self.tkroot.bind_all("<Up>", self.line_up)
|
# self.tkroot.bind_all("<Button-5>", self.scroll_wheel)
|
||||||
self.tkroot.bind_all("<Down>", self.line_down)
|
# self.tkroot.bind_all("<Up>", self.line_up)
|
||||||
self.tkroot.bind_all("<Prior>", self.page_up)
|
# self.tkroot.bind_all("<Down>", self.line_down)
|
||||||
self.tkroot.bind_all("<Next>", self.page_down)
|
# self.tkroot.bind_all("<Prior>", self.page_up)
|
||||||
|
# self.tkroot.bind_all("<Next>", self.page_down)
|
||||||
|
|
||||||
# Bind any key in the update_entry to adjust the counter, except return
|
# Pack the top level together
|
||||||
self.update_entry.bind('<KeyRelease>', self.char_counter)
|
main_sizer.Add(tweet_view, 1, wx.EXPAND)
|
||||||
self.update_entry.bind('<Return>', self.update_status_callback)
|
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)
|
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.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()
|
timezone = dateutil.tz.gettz()
|
||||||
time_format = "%Y.%m.%d %H:%M:%S %Z"
|
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
|
# Update the label with the user's name and screen name
|
||||||
user = statuses[i].user
|
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 = datetime.datetime.strptime(statuses[i].created_at, "%a %b %d %H:%M:%S +0000 %Y")
|
||||||
timestamp = timestamp.replace(tzinfo=dateutil.tz.gettz('UTC'))
|
timestamp = timestamp.replace(tzinfo=dateutil.tz.gettz('UTC'))
|
||||||
timestring = timestamp.astimezone(timezone).strftime(time_format)
|
timestring = timestamp.astimezone(timezone).strftime(time_format)
|
||||||
|
|
||||||
labeltext = user.name + " (" + user.screen_name + ") " + timestring
|
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
|
# 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):
|
def update_status(self):
|
||||||
text = self.update_entry.get()
|
text = self.update_entry.GetValue()
|
||||||
if len(text) > 140:
|
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
|
return
|
||||||
self.update_entry.delete(0, Tkinter.END)
|
self.update_entry.Clear()
|
||||||
self.api.PostUpdate(text)
|
self.api.PostUpdate(text)
|
||||||
self.update_window()
|
self.update_window()
|
||||||
|
|
||||||
|
@ -120,32 +161,37 @@ class TwitWindow:
|
||||||
self.update_status()
|
self.update_status()
|
||||||
|
|
||||||
|
|
||||||
def scroll_wheel(self, event):
|
# fixme: convert all of the below to wxPython
|
||||||
if event.num == 4:
|
|
||||||
self.tweet_view.yview('scroll', -5, 'units');
|
# def scroll_wheel(self, event):
|
||||||
if event.num == 5:
|
# if event.num == 4:
|
||||||
self.tweet_view.yview('scroll', 5, 'units');
|
# self.tweet_view.yview('scroll', -5, 'units');
|
||||||
|
# if event.num == 5:
|
||||||
|
# self.tweet_view.yview('scroll', 5, 'units');
|
||||||
|
|
||||||
|
|
||||||
def page_up(self, event):
|
# def page_up(self, event):
|
||||||
self.tweet_view.yview('scroll', -15, 'units');
|
# self.tweet_view.yview('scroll', -15, 'units');
|
||||||
|
|
||||||
|
|
||||||
def page_down(self, event):
|
# def page_down(self, event):
|
||||||
self.tweet_view.yview('scroll', 15, 'units');
|
# self.tweet_view.yview('scroll', 15, 'units');
|
||||||
|
|
||||||
|
|
||||||
def line_up(self, event):
|
# def line_up(self, event):
|
||||||
self.tweet_view.yview('scroll', -5, 'units');
|
# self.tweet_view.yview('scroll', -5, 'units');
|
||||||
|
|
||||||
|
|
||||||
def line_down(self, event):
|
# def line_down(self, event):
|
||||||
self.tweet_view.yview('scroll', 5, 'units');
|
# self.tweet_view.yview('scroll', 5, 'units');
|
||||||
|
|
||||||
|
|
||||||
def char_counter(self, event):
|
def char_counter(self, event):
|
||||||
new_count = str(len(self.update_entry.get())) + "/140"
|
new_count = str(len(self.update_entry.GetValue())) + "/140"
|
||||||
self.update_count.config(text=new_count)
|
self.update_count.SetLabel(new_count)
|
||||||
|
|
||||||
|
def on_exit(self, event):
|
||||||
|
self.Close(True)
|
||||||
|
|
||||||
### end class TwitWindow
|
### end class TwitWindow
|
||||||
|
|
||||||
|
@ -172,5 +218,7 @@ def print_formatted(statuses):
|
||||||
|
|
||||||
|
|
||||||
# main
|
# main
|
||||||
win = TwitWindow()
|
app = wx.App(False)
|
||||||
win.create_window()
|
root = TwitWindow(None, "MyTwitter")
|
||||||
|
root.Show(True)
|
||||||
|
app.MainLoop()
|
||||||
|
|
Reference in New Issue
Block a user