__author__ = "Aaron Straup Cope" __url__ = "http://www.mirrorproject.com/widget/" __version__ = "1.0" __cvsversion__ = "$Revision: 1.2 $" __date__ = "$Date: 2004/04/19 02:39:20 $" __copyright__ = "%s\n%s\n%s\n\n%s\n%s" % \ ("This application is licensed under the", "Creative Commons Attribution-NonCommercial License", "http://creativecommons.org/licenses/by-nc/1.0/", "Individual photographs are copyright The Mirror Project", "and their respective photographers, all rights reserved.") __license__ = "This application is licensed under the Creative \ Commons Attribution-NonCommercial License. To view a copy of this \ license, visit http://creativecommons.org/licenses/by-nc/1.0/ or send \ a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, \ California 94305, USA. Individual photographs are copyright The Mirror \ Project and their respective photographers, all rights reserved." from wxPython.wx import * from cStringIO import StringIO from time import sleep from xmlrpclib import Server from os import name # this is an unfortunate hack to # deal with some questionable design # decisions on the server side... from urllib import unquote_plus URL_MIRRORPROJECT = "http://www.mirrorproject.com/xml/" SECONDS_DELAY = 30 SECONDS_SLEEP = 0.01 COUNTER_DELAY = int(1/SECONDS_SLEEP) * SECONDS_DELAY # Insert hand-waving about internationalization # here. Patches welcome... TITLE_MP = "The Mirror Project" TITLE_APP = "Random Image Widget" TITLE_ABOUT = "About" TITLE_INFO = "Image Info" MENUBAR_FILE = "&File" MENUBAR_TOOLS = "&Tools" MENUBAR_HELP = "&Help" MENU_ABOUT = "&About" MENU_PAUSE = "&Pause" MENU_RESUME = "&Resume" MENU_INFO = "Image &Info" MENU_QUIT = "&Quit" DESCRIBE_PAUSE = "Pause" DESCRIBE_RESUME = "Resume" DESCRIBE_INFO = "Info" DESCRIBE_ABOUT = "More information about this program" DESCRIBE_QUIT = "Quit the program" MSG_PAUSED = "paused, press Ctrl-R to resume" MSG_POLLING = "asking for a new image" MSG_PROCESSING = "" ERR_POLLING = "failed to get image from server" ERR_STRINGIFY = "failed to read image from server" ERR_STREAMIFY = "failed to munge image" ERR_DISPLAY = "failed to set image" ERR_IMAGEINFO = "no image data" DIALOG_ABOUT = "%s\n%s\nversion %s\n\n%s" % \ (TITLE_MP, TITLE_APP, __version__, __copyright__) # These need better names... STATUSTEXT = 0.8 BORDER = 4 # ID_APP = wxNewId() ID_PAUSE = wxNewId() ID_RESUME = wxNewId() ID_INFO = wxNewId() ID_ABOUT = wxNewId() ID_EXIT = wxNewId() class mpApp(wxApp): def OnInit(self): wxInitAllImageHandlers() self.Server = Server(URL_MIRRORPROJECT) frame = mpFrame(NULL, ID_APP, TITLE_MP) frame.Show(true) self.SetTopWindow(frame) frame.Panel.SetFocus() return true def MainLoop(self): frame = self.GetTopWindow() while frame and frame.Panel : if frame.Paused == true: wxSafeYield(frame,true) else: frame.DisplayRandom(self.Server) for i in range(COUNTER_DELAY): sleep(SECONDS_SLEEP) if frame and frame.Panel: if frame.Paused == true: break else: wxYield() else: break class mpFrame(wxFrame): def __init__(self, parent, ID, title): wxFrame.__init__(self, parent, ID, title, wxDefaultPosition, wxSize(300,300)) self.CreateStatusBar() FileMenu = wxMenu() FileMenu.Append(ID_PAUSE, MENU_PAUSE, DESCRIBE_PAUSE) FileMenu.Append(ID_RESUME, MENU_RESUME, DESCRIBE_RESUME) FileMenu.AppendSeparator() FileMenu.Append(ID_EXIT, MENU_QUIT, DESCRIBE_QUIT) FileMenu.Enable(ID_RESUME,false) ToolsMenu = wxMenu() ToolsMenu.Append(ID_INFO, MENU_INFO, DESCRIBE_INFO) HelpMenu = wxMenu() HelpMenu.Append(ID_ABOUT, MENU_ABOUT, DESCRIBE_ABOUT) menuBar = wxMenuBar() menuBar.Append(FileMenu, MENUBAR_FILE); menuBar.Append(ToolsMenu, MENUBAR_TOOLS); menuBar.Append(HelpMenu, MENUBAR_HELP); self.SetMenuBar(menuBar) self.Panel = mpPanel(self, -1) self.Image = None self.Caption = None self.Data = {} self.Paused = false self.Die = false EVT_MENU(self, ID_PAUSE, self.Pause) EVT_MENU(self, ID_RESUME, self.Resume) EVT_MENU(self, ID_INFO, self.Info) EVT_MENU(self, ID_ABOUT, self.About) EVT_MENU(self, ID_EXIT, self.Quit) EVT_CLOSE(self,self.Quit) # Doesn't appear to work under # py-wxPython (FreeBSD) EVT_MENU_CLOSE(self,self.SetCaption) EVT_KEY_DOWN(self.Panel, self.KeyDown) def Yield(self): # This causes Windows to hang # FreeBSD hangs without it... if name == "nt": pass else: wxSafeYield(self,true) def ReportError(self,msg): if msg: self.SetStatusText("[%s]" % self.PrepareStatusText(msg)) else: self.SetStatusText("") self.Yield() def ReportActivity(self,msg): if msg: self.SetStatusText("[%s]" % self.PrepareStatusText(msg)) else: self.SetStatusText("") self.Yield() def Pause(self,event): if self.Paused == true: return true self.Paused = true self.ReportActivity(MSG_PAUSED) # http://wiki.wxpython.org/index.cgi/ \ # wxPython_20Platform_20Inconsistencies menu = self.GetMenuBar().FindItemById(-1).GetMenu() menu.Enable(ID_PAUSE,false) menu.Enable(ID_RESUME,true) def Resume(self,event): if self.Paused == false: return true self.Paused = false # http://wiki.wxpython.org/index.cgi/ \ # wxPython_20Platform_20Inconsistencies menu = self.GetMenuBar().FindItemById(-1).GetMenu() menu.Enable(ID_PAUSE,true) menu.Enable(ID_RESUME,false) def Info(self,event): if len(self.Data) == 0: msg = ERR_IMAGEINFO else: msg = "%s\n" % (self.Data["who"]) if self.Data["what"]: msg = "%s \"%s\"\n" % (msg,self.Data["what"]) msg = "%s\n" % (msg) # god help me, there # *must* be a better # way... if self.Data["where"]: msg = "%s%s" % (msg,self.Data["where"]) if self.Data["when"]: if self.Data["where"]: msg = "%s, %s" % (msg,self.Data["when"]) else: msg = "%s%s" % (msg,self.Data["when"]) dlg = wxMessageDialog(self, msg, TITLE_INFO, wxOK | wxICON_INFORMATION) dlg.ShowModal() dlg.Destroy() def KeyDown(self,event): if event.ControlDown(): keycode = event.GetKeyCode() else: event.Skip() return true if keycode <= 256: keyname = chr(keycode) else: event.Skip() return true if keyname == "A": self.About(event) elif keyname == "I": self.Info(event) elif keyname == "P": self.Pause(event) elif keyname == "R": self.Resume(event) elif keyname == "Q": self.Quit(event) else: event.Skip() return true def About(self, event): dlg = wxMessageDialog(self, DIALOG_ABOUT, TITLE_ABOUT, wxOK | wxICON_INFORMATION) dlg.ShowModal() dlg.Destroy() def Quit(self, event): # this is required by "nt" # and, I think, "os x" self.Panel.Destroy() self.Destroy() def DisplayRandom(self,server): self.ReportActivity(MSG_POLLING) try: resp = server.mirror.RandomImage() except: self.ReportError(ERR_POLLING) return false self.ReportActivity(MSG_PROCESSING) try: data = StringIO(resp["image"].data) except: self.ReportError(ERR_STRINGIFY) return false try: image = wxImageFromStream(data) except: self.ReportError(ERR_STREAMIFY) return false try: self.Panel.Display(image) except: self.ReportError(ERR_DISPLAY) return false self.SetWindow(image) # the unquote_plus stuff is a hack; # see notes in import block above self.Data["who"] = unquote_plus(resp["who"]) self.Data["what"] = unquote_plus(resp["what"]) self.Data["where"] = unquote_plus(resp["where"]) self.Data["when"] = unquote_plus(resp["when"]) if self.Data["what"]: caption = "%s, %s" % (self.Data["who"],self.Data["what"]) else: caption = self.Data["who"] # caption = self.PrepareStatusText(caption, int(image.GetWidth() * STATUSTEXT)) # self.Caption = caption self.SetCaption() return true def SetCaption(self,event=None): if self.Caption: self.SetStatusText(self.Caption) else: self.SetStatusText("") def PrepareStatusText(self,txt,max=0): # Okay, now we have to make sure that the # text will fit in the status bar (txt_x,txt_y) = self.GetTextExtent(txt) if max == 0: max = int(self.GetSize()[0] * STATUSTEXT) if txt_x > max: while txt_x > max: # please make the python keeners # shut the fuck up about how great # their language is. my kingdom for # a substring function... txt = txt[0:len(txt) - 1] # just in case if len(txt) == 1: txt = "" break (txt_x,txt_y) = self.GetTextExtent(txt) txt = "%s..." % txt # return txt def SetWindow(self,image): scr_x = wxSystemSettings_GetMetric(wxSYS_SCREEN_X) scr_y = wxSystemSettings_GetMetric(wxSYS_SCREEN_Y) (sb_x,sb_y) = self.GetStatusBar().GetSize() (mb_x,mb_y) = self.GetMenuBar().GetSize() (pos_x,pos_y) = self.GetPosition() # must find a way to measure # the width of the various # windowing border elements count_border_offset = 2.25 width_border_side = 3 if name == "nt": count_border_offset *= 2 width_border_side *= 2.75 # windows returns something # cracked like (999334,0) # this is a bug... mb_y = 25 # img_x = image.GetWidth() img_x += (BORDER * count_border_offset) img_y = image.GetHeight() img_y += (BORDER * width_border_side) # no comment if name == "nt": img_x -= 1 # win_x = int(img_x) win_y = int(img_y + sb_y + mb_y) new_x = pos_x new_y = pos_y move = false # if (pos_x + win_x) > scr_x: new_x = scr_x - (win_x + (BORDER * count_border_offset)) move = true if (pos_y + win_y) > scr_y: new_y = scr_y - (win_y + (BORDER * count_border_offset)) move = true if move: self.SetPosition((new_x,new_y)) # self.SetSize(wxSize(win_x,win_y)) class mpPanel(wxPanel): def __init__(self, parent, id): wxPanel.__init__(self, parent, id) self.Image = None EVT_PAINT(self, self.OnPaint) def Display(self, image): self.Image = image self.Refresh(True) def OnPaint(self, evt): if self.Image: img_x = self.Image.GetWidth() img_y = self.Image.GetHeight() dc = wxPaintDC(self) dc.SetBrush(wxBrush("#000000")) # 3/7 should be computed somewhere... dc.DrawRectangle(0,3, img_x + (BORDER * 2), img_y + (BORDER * 2)) dc.DrawBitmap(self.Image.ConvertToBitmap(), BORDER,7) # Make it so! app = mpApp(0) app.MainLoop()