wxWidgets/utils/wxPython/tests/wxSlash.py

360 lines
11 KiB
Python
Raw Normal View History

#!/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()