git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@7748 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
		
			
				
	
	
		
			360 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			360 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #!/usr/bin/python
 | |
| """This is wxSlash 1.1
 | |
| 
 | |
|     It's the obligatory Slashdot.org headlines reader that any modern
 | |
| widget set/library must have in order to be taken seriously :-)
 | |
| 
 | |
|     Usage is quite simple; wxSlash attempts to download the 'ultramode.txt'
 | |
| file from http://slashdot.org, which contains the headlines in a computer
 | |
| friendly format. It then displays said headlines in a wxWindows list control.
 | |
| 
 | |
|     You can read articles using either Python's html library or an external
 | |
| browser. Uncheck the 'browser->internal' menu item to use the latter option.
 | |
| Use the settings dialog box to set which external browser is started.
 | |
| 
 | |
|     This code is available under the wxWindows license, see elsewhere. If you
 | |
| modify this code, be aware of the fact that slashdot.org's maintainer,
 | |
| CmdrTaco, explicitly asks 'ultramode.txt' downloaders not to do this
 | |
| automatically more than twice per hour. If this feature is abused, CmdrTaco
 | |
| may remove the ultramode file completely and that will make a *lot* of people
 | |
| unhappy.
 | |
| 
 | |
|     I want to thank Alex Shnitman whose slashes.pl (Perl/GTK) script gave me
 | |
| the idea for this applet.
 | |
| 
 | |
|     Have fun with it,
 | |
| 
 | |
|     Harm van der Heijden (H.v.d.Heijden@phys.tue.nl)
 | |
| """
 | |
| 
 | |
| from wxPython.wx import *
 | |
| from httplib import HTTP
 | |
| from htmllib import HTMLParser
 | |
| import os
 | |
| import re
 | |
| import formatter
 | |
| 
 | |
| class HTMLTextView(wxFrame):
 | |
|     def __init__(self, parent, id, title='HTMLTextView', url=None):
 | |
|         wxFrame.__init__(self, parent, id, title, wxPyDefaultPosition,
 | |
|                          wxSize(600,400))
 | |
| 
 | |
|         self.mainmenu = wxMenuBar()
 | |
| 
 | |
|         menu = wxMenu()
 | |
|         menu.Append(201, '&Open URL...', 'Open URL')
 | |
|         EVT_MENU(self, 201, self.OnFileOpen)
 | |
|         menu.Append(209, 'E&xit', 'Exit viewer')
 | |
|         EVT_MENU(self, 209, self.OnFileExit)
 | |
| 
 | |
|         self.mainmenu.Append(menu, '&File')
 | |
|         self.SetMenuBar(self.mainmenu)
 | |
|         self.CreateStatusBar(1)
 | |
| 
 | |
|         self.text = wxTextCtrl(self, -1, "", wxPyDefaultPosition,
 | |
|                                wxPyDefaultSize, wxTE_MULTILINE | wxTE_READONLY)
 | |
| 
 | |
|         if (url):
 | |
|             self.OpenURL(url)
 | |
| 
 | |
|     def logprint(self, x):
 | |
|         self.SetStatusText(x)
 | |
| 
 | |
|     def OpenURL(self, url):
 | |
|         self.url = url
 | |
|         m = re.match('file:(\S+)\s*', url)
 | |
|         if m:
 | |
|             f = open(m.groups()[0],'r')
 | |
|         else:
 | |
|             m = re.match('http://([^/]+)(/\S*)\s*', url)
 | |
|             if m:
 | |
|                 host = m.groups()[0]
 | |
|                 path = m.groups()[1]
 | |
|             else:
 | |
|                 m = re.match('http://(\S+)\s*', url)
 | |
|                 if not m:
 | |
|                     # Invalid URL
 | |
|                     self.logprint("Invalid or unsupported URL: %s" % (url))
 | |
|                     return
 | |
|                 host = m.groups()[0]
 | |
|                 path = ''
 | |
|             f = RetrieveAsFile(host,path,self.logprint)
 | |
|         if not f:
 | |
|             self.logprint("Could not open %s" % (url))
 | |
|             return
 | |
|         self.logprint("Receiving data...")
 | |
|         data = f.read()
 | |
|         tmp = open('tmphtml.txt','w')
 | |
|         fmt = formatter.AbstractFormatter(formatter.DumbWriter(tmp))
 | |
|         p = HTMLParser(fmt)
 | |
|         self.logprint("Parsing data...")
 | |
|         p.feed(data)
 | |
|         p.close()
 | |
|         tmp.close()
 | |
|         tmp = open('tmphtml.txt', 'r')
 | |
|         self.text.SetValue(tmp.read())
 | |
|         self.SetTitle(url)
 | |
|         self.logprint(url)
 | |
| 
 | |
|     def OnFileOpen(self, event):
 | |
|         dlg = wxTextEntryDialog(self, "Enter URL to open:", "")
 | |
|         if dlg.ShowModal() == wxID_OK:
 | |
|             url = dlg.GetValue()
 | |
|         else:
 | |
|             url = None
 | |
|         if url:
 | |
|             self.OpenURL(url)
 | |
| 
 | |
|     def OnFileExit(self, event):
 | |
|         self.Close()
 | |
| 
 | |
|     def OnCloseWindow(self, event):
 | |
|         self.Destroy()
 | |
| 
 | |
| 
 | |
| def ParseSlashdot(f):
 | |
|     art_sep = re.compile('%%\r?\n')
 | |
|     line_sep = re.compile('\r?\n')
 | |
|     data = f.read()
 | |
|     list = art_sep.split(data)
 | |
|     art_list = []
 | |
|     for i in range(1,len(list)-1):
 | |
|         art_list.append(line_sep.split(list[i]))
 | |
|     return art_list
 | |
| 
 | |
| def myprint(x):
 | |
|     print x
 | |
| 
 | |
| def RetrieveAsFile(host, path='', logprint = myprint):
 | |
|     try:
 | |
|         h = HTTP(host)
 | |
|     except:
 | |
|         logprint("Failed to create HTTP connection to %s... is the network available?" % (host))
 | |
|         return None
 | |
|     h.putrequest('GET',path)
 | |
|     h.putheader('Accept','text/html')
 | |
|     h.putheader('Accept','text/plain')
 | |
|     h.endheaders()
 | |
|     errcode, errmsg, headers = h.getreply()
 | |
|     if errcode != 200:
 | |
|         logprint("HTTP error code %d: %s" % (errcode, errmsg))
 | |
|         return None
 | |
|     f = h.getfile()
 | |
| #    f = open('/home/harm/ultramode.txt','r')
 | |
|     return f
 | |
| 
 | |
| 
 | |
| class AppStatusBar(wxStatusBar):
 | |
|     def __init__(self, parent):
 | |
|         wxStatusBar.__init__(self,parent, -1)
 | |
|         self.SetFieldsCount(2)
 | |
|         self.SetStatusWidths([-1, 100])
 | |
|         self.but = wxButton(self, 1001, "Refresh")
 | |
|         EVT_BUTTON(self, 1001, parent.OnViewRefresh)
 | |
|         self.OnSize(None)
 | |
| 
 | |
|     def logprint(self,x):
 | |
|         self.SetStatusText(x,0)
 | |
| 
 | |
|     def OnSize(self, event):
 | |
|         rect = self.GetFieldRect(1)
 | |
|         self.but.SetPosition(wxPoint(rect.x+2, rect.y+2))
 | |
|         self.but.SetSize(wxSize(rect.width-4, rect.height-4))
 | |
| 
 | |
| # This is a simple timer class to start a function after a short delay;
 | |
| class QuickTimer(wxTimer):
 | |
|     def __init__(self, func, wait=100):
 | |
| 	wxTimer.__init__(self)
 | |
| 	self.callback = func
 | |
| 	self.Start(wait); # wait .1 second (.001 second doesn't work. why?)
 | |
|     def Notify(self):
 | |
| 	self.Stop();
 | |
| 	apply(self.callback, ());
 | |
| 
 | |
| class AppFrame(wxFrame):
 | |
|     def __init__(self, parent, id, title):
 | |
|         wxFrame.__init__(self, parent, id, title, wxPyDefaultPosition,
 | |
|                          wxSize(650, 250))
 | |
| 
 | |
| 	# if the window manager closes the window:
 | |
| 	EVT_CLOSE(self, self.OnCloseWindow);
 | |
| 
 | |
| 	# Now Create the menu bar and items
 | |
|         self.mainmenu = wxMenuBar()
 | |
| 
 | |
|         menu = wxMenu()
 | |
|         menu.Append(209, 'E&xit', 'Enough of this already!')
 | |
|         EVT_MENU(self, 209, self.OnFileExit)
 | |
|         self.mainmenu.Append(menu, '&File')
 | |
|         menu = wxMenu()
 | |
|         menu.Append(210, '&Refresh', 'Refresh headlines')
 | |
|         EVT_MENU(self, 210, self.OnViewRefresh)
 | |
|         menu.Append(211, '&Slashdot Index', 'View Slashdot index')
 | |
|         EVT_MENU(self, 211, self.OnViewIndex)
 | |
|         menu.Append(212, 'Selected &Article', 'View selected article')
 | |
|         EVT_MENU(self, 212, self.OnViewArticle)
 | |
|         self.mainmenu.Append(menu, '&View')
 | |
|         menu = wxMenu()
 | |
|         menu.Append(220, '&Internal', 'Use internal text browser',TRUE)
 | |
|         menu.Check(220, true)
 | |
|         self.UseInternal = 1;
 | |
|         EVT_MENU(self, 220, self.OnBrowserInternal)
 | |
|         menu.Append(222, '&Settings...', 'External browser Settings')
 | |
|         EVT_MENU(self, 222, self.OnBrowserSettings)
 | |
|         self.mainmenu.Append(menu, '&Browser')
 | |
| 	menu = wxMenu()
 | |
| 	menu.Append(230, '&About', 'Some documentation');
 | |
| 	EVT_MENU(self, 230, self.OnAbout)
 | |
| 	self.mainmenu.Append(menu, '&Help')
 | |
| 
 | |
|         self.SetMenuBar(self.mainmenu)
 | |
| 
 | |
| 	if wxPlatform == '__WXGTK__':
 | |
| 	    # I like lynx. Also Netscape 4.5 doesn't react to my cmdline opts
 | |
| 	    self.BrowserSettings = "xterm -e lynx %s &"
 | |
| 	elif wxPlatform == '__WXMSW__':
 | |
| 	    # netscape 4.x likes to hang out here...
 | |
| 	    self.BrowserSettings = '\\progra~1\\Netscape\\Communicator\\Program\\netscape.exe %s'
 | |
| 	else:
 | |
| 	    # a wild guess...
 | |
| 	    self.BrowserSettings = 'netscape %s'
 | |
| 	    
 | |
| 	# A status bar to tell people what's happening
 | |
| 	self.sb = AppStatusBar(self)
 | |
|         self.SetStatusBar(self.sb)
 | |
| 
 | |
|         self.list = wxListCtrl(self, 1100)
 | |
| 	self.list.SetSingleStyle(wxLC_REPORT)
 | |
| 	self.list.InsertColumn(0, 'Subject')
 | |
| 	self.list.InsertColumn(1, 'Date')
 | |
| 	self.list.InsertColumn(2, 'Posted by')
 | |
| 	self.list.InsertColumn(3, 'Comments')
 | |
|         self.list.SetColumnWidth(0, 300)
 | |
|         self.list.SetColumnWidth(1, 150)
 | |
|         self.list.SetColumnWidth(2, 100)
 | |
|         self.list.SetColumnWidth(3, 100)
 | |
| 
 | |
|         EVT_LIST_ITEM_SELECTED(self, 1100, self.OnItemSelected)
 | |
| 	EVT_LEFT_DCLICK(self.list, self.OnLeftDClick)
 | |
| 
 | |
| 	self.logprint("Connecting to slashdot... Please wait.")
 | |
| 	# wxYield doesn't yet work here. That's why we use a timer
 | |
| 	# to make sure that we see some GUI stuff before the slashdot
 | |
| 	# file is transfered.
 | |
| 	self.timer = QuickTimer(self.DoRefresh, 1000)
 | |
| 
 | |
|     def logprint(self, x):
 | |
|         self.sb.logprint(x)
 | |
| 
 | |
|     def OnFileExit(self, event):
 | |
|         self.Destroy()
 | |
| 
 | |
|     def DoRefresh(self):
 | |
|         f = RetrieveAsFile('slashdot.org','/ultramode.txt',self.sb.logprint)
 | |
|         art_list = ParseSlashdot(f)
 | |
|         self.list.DeleteAllItems()
 | |
|         self.url = []
 | |
|         self.current = -1
 | |
|         i = 0;
 | |
|         for article in art_list:
 | |
|             self.list.InsertStringItem(i, article[0])
 | |
|             self.list.SetStringItem(i, 1, article[2])
 | |
|             self.list.SetStringItem(i, 2, article[3])
 | |
|             self.list.SetStringItem(i, 3, article[6])
 | |
|             self.url.append(article[1])
 | |
|             i = i + 1
 | |
| 	self.logprint("File retrieved OK.")
 | |
| 
 | |
|     def OnViewRefresh(self, event):
 | |
| 	self.logprint("Connecting to slashdot... Please wait.");
 | |
| 	wxYield()
 | |
| 	self.DoRefresh()
 | |
| 
 | |
|     def DoViewIndex(self):
 | |
|         if self.UseInternal:
 | |
|             self.view = HTMLTextView(self, -1, 'slashdot.org',
 | |
|                                      'http://slashdot.org')
 | |
|             self.view.Show(true)
 | |
|         else:
 | |
|             self.logprint(self.BrowserSettings % ('http://slashdot.org'))
 | |
|             os.system(self.BrowserSettings % ('http://slashdot.org'))
 | |
| 	self.logprint("OK")
 | |
| 
 | |
|     def OnViewIndex(self, event):
 | |
| 	self.logprint("Starting browser... Please wait.")
 | |
| 	wxYield()
 | |
| 	self.DoViewIndex()
 | |
| 
 | |
|     def DoViewArticle(self):
 | |
|         if self.current<0: return
 | |
|         url = self.url[self.current]
 | |
|         if self.UseInternal:
 | |
|             self.view = HTMLTextView(self, -1, url, url)
 | |
|             self.view.Show(true)
 | |
|         else:
 | |
|             self.logprint(self.BrowserSettings % (url))
 | |
|             os.system(self.BrowserSettings % (url))
 | |
| 	self.logprint("OK")
 | |
| 
 | |
|     def OnViewArticle(self, event):
 | |
| 	self.logprint("Starting browser... Please wait.")
 | |
| 	wxYield()
 | |
| 	self.DoViewArticle()
 | |
| 
 | |
|     def OnBrowserInternal(self, event):
 | |
|         if self.mainmenu.Checked(220):
 | |
|             self.UseInternal = 1
 | |
|         else:
 | |
|             self.UseInternal = 0
 | |
| 
 | |
|     def OnBrowserSettings(self, event):
 | |
|         dlg = wxTextEntryDialog(self, "Enter command to view URL.\nUse %s as a placeholder for the URL.", "", self.BrowserSettings);
 | |
|         if dlg.ShowModal() == wxID_OK:
 | |
|             self.BrowserSettings = dlg.GetValue()
 | |
| 
 | |
|     def OnAbout(self, event):
 | |
| 	dlg = wxMessageDialog(self, __doc__, "wxSlash", wxOK | wxICON_INFORMATION)
 | |
| 	dlg.ShowModal()
 | |
| 
 | |
|     def OnItemSelected(self, event):
 | |
|         self.current = event.m_itemIndex
 | |
|         self.logprint("URL: %s" % (self.url[self.current]))
 | |
| 
 | |
|     def OnLeftDClick(self, event):
 | |
| 	(x,y) = event.Position();
 | |
| 	# Actually, we should convert x,y to logical coords using
 | |
| 	# a dc, but only for a wxScrolledWindow widget.
 | |
| 	# Now wxGTK derives wxListCtrl from wxScrolledWindow,
 | |
| 	# and wxMSW from wxControl... So that doesn't work.
 | |
| 	#dc = wxClientDC(self.list)
 | |
| 	##self.list.PrepareDC(dc)
 | |
| 	#x = dc.DeviceToLogicalX( event.GetX() )
 | |
| 	#y = dc.DeviceToLogicalY( event.GetY() )
 | |
| 	id = self.list.HitTest(wxPoint(x,y))
 | |
| 	#print "Double click at %d %d" % (x,y), id
 | |
| 	# Okay, we got a double click. Let's assume it's the current selection
 | |
| 	wxYield()
 | |
| 	self.OnViewArticle(event)
 | |
| 
 | |
|     def OnCloseWindow(self, event):
 | |
|         self.Destroy()
 | |
| 
 | |
| class MyApp(wxApp):
 | |
|     def OnInit(self):
 | |
|         frame = AppFrame(NULL, -1, "Slashdot Breaking News")
 | |
|         frame.Show(true)
 | |
|         self.SetTopWindow(frame)
 | |
|         return true
 | |
| 
 | |
| #
 | |
| # main thingy
 | |
| #
 | |
| if __name__ == '__main__':
 | |
|     app = MyApp(0)
 | |
|     app.MainLoop()
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 |