]> git.saurik.com Git - wxWidgets.git/blob - utils/wxPython/demo/SlashDot.py
Final tweaks for 2.1b1
[wxWidgets.git] / utils / wxPython / demo / SlashDot.py
1 #!/usr/bin/python
2 """This is SlashDot 1.2
3
4 It's the obligatory Slashdot.org headlines reader that
5 any modern widget set/library must have in order to be taken
6 seriously :-)
7
8 Usage is quite simple; wxSlash attempts to download the
9 'ultramode.txt' file from http://slashdot.org, which
10 contains the headlines in a computer friendly format. It
11 then displays said headlines in a wxWindows list control.
12
13 You can read articles using either Python's html library
14 or an external browser. Uncheck the 'browser->internal' menu
15 item to use the latter option. Use the settings dialog box
16 to set which external browser is started.
17
18 This code is available under the wxWindows license, see
19 elsewhere. If you modify this code, be aware of the fact
20 that slashdot.org's maintainer, CmdrTaco, explicitly asks
21 'ultramode.txt' downloaders not to do this automatically
22 more than twice per hour. If this feature is abused,
23 CmdrTaco may remove the ultramode file completely and that
24 will make a *lot* of people unhappy.
25
26 I want to thank Alex Shnitman whose slashes.pl
27 (Perl/GTK) script gave me the idea for this applet.
28
29 Have fun with it,
30
31 Harm van der Heijden (H.v.d.Heijden@phys.tue.nl)
32 """
33
34 from wxPython.wx import *
35 from httplib import HTTP
36 from htmllib import HTMLParser
37 import os
38 import re
39 import formatter
40
41 class HTMLTextView(wxFrame):
42 def __init__(self, parent, id, title='HTMLTextView', url=None):
43 wxFrame.__init__(self, parent, id, title, wxPyDefaultPosition,
44 wxSize(600,400))
45
46 self.mainmenu = wxMenuBar()
47
48 menu = wxMenu()
49 menu.Append(201, '&Open URL...', 'Open URL')
50 EVT_MENU(self, 201, self.OnFileOpen)
51 menu.Append(209, 'E&xit', 'Exit viewer')
52 EVT_MENU(self, 209, self.OnFileExit)
53
54 self.mainmenu.Append(menu, '&File')
55 self.SetMenuBar(self.mainmenu)
56 self.CreateStatusBar(1)
57
58 self.text = wxTextCtrl(self, -1, "", wxPyDefaultPosition,
59 wxPyDefaultSize, wxTE_MULTILINE | wxTE_READONLY)
60
61 if (url):
62 self.OpenURL(url)
63
64 def logprint(self, x):
65 self.SetStatusText(x)
66
67 def OpenURL(self, url):
68 self.url = url
69 m = re.match('file:(\S+)\s*', url)
70 if m:
71 f = open(m.groups()[0],'r')
72 else:
73 m = re.match('http://([^/]+)(/\S*)\s*', url)
74 if m:
75 host = m.groups()[0]
76 path = m.groups()[1]
77 else:
78 m = re.match('http://(\S+)\s*', url)
79 if not m:
80 # Invalid URL
81 self.logprint("Invalid or unsupported URL: %s" % (url))
82 return
83 host = m.groups()[0]
84 path = ''
85 f = RetrieveAsFile(host,path,self.logprint)
86 if not f:
87 self.logprint("Could not open %s" % (url))
88 return
89 self.logprint("Receiving data...")
90 data = f.read()
91 tmp = open('tmphtml.txt','w')
92 fmt = formatter.AbstractFormatter(formatter.DumbWriter(tmp))
93 p = HTMLParser(fmt)
94 self.logprint("Parsing data...")
95 p.feed(data)
96 p.close()
97 tmp.close()
98 tmp = open('tmphtml.txt', 'r')
99 self.text.SetValue(tmp.read())
100 self.SetTitle(url)
101 self.logprint(url)
102
103 def OnFileOpen(self, event):
104 dlg = wxTextEntryDialog(self, "Enter URL to open:", "")
105 if dlg.ShowModal() == wxID_OK:
106 url = dlg.GetValue()
107 else:
108 url = None
109 if url:
110 self.OpenURL(url)
111
112 def OnFileExit(self, event):
113 self.Close()
114
115 def OnCloseWindow(self, event):
116 self.Destroy()
117
118
119 def ParseSlashdot(f):
120 art_sep = re.compile('%%\r?\n')
121 line_sep = re.compile('\r?\n')
122 data = f.read()
123 list = art_sep.split(data)
124 art_list = []
125 for i in range(1,len(list)-1):
126 art_list.append(line_sep.split(list[i]))
127 return art_list
128
129 def myprint(x):
130 print x
131
132 def RetrieveAsFile(host, path='', logprint = myprint):
133 try:
134 h = HTTP(host)
135 except:
136 logprint("Failed to create HTTP connection to %s... is the network available?" % (host))
137 return None
138 h.putrequest('GET',path)
139 h.putheader('Accept','text/html')
140 h.putheader('Accept','text/plain')
141 h.endheaders()
142 errcode, errmsg, headers = h.getreply()
143 if errcode != 200:
144 logprint("HTTP error code %d: %s" % (errcode, errmsg))
145 return None
146 f = h.getfile()
147 # f = open('/home/harm/ultramode.txt','r')
148 return f
149
150
151 class AppStatusBar(wxStatusBar):
152 def __init__(self, parent):
153 wxStatusBar.__init__(self,parent, -1)
154 self.SetFieldsCount(2)
155 self.SetStatusWidths([-1, 100])
156 self.but = wxButton(self, 1001, "Refresh")
157 EVT_BUTTON(self, 1001, parent.OnViewRefresh)
158 self.OnSize(None)
159
160 def logprint(self,x):
161 self.SetStatusText(x,0)
162
163 def OnSize(self, event):
164 rect = self.GetFieldRect(1)
165 self.but.SetPosition(wxPoint(rect.x+2, rect.y+2))
166 self.but.SetSize(wxSize(rect.width-4, rect.height-4))
167
168 # This is a simple timer class to start a function after a short delay;
169 class QuickTimer(wxTimer):
170 def __init__(self, func, wait=100):
171 wxTimer.__init__(self)
172 self.callback = func
173 self.Start(wait); # wait .1 second (.001 second doesn't work. why?)
174 def Notify(self):
175 self.Stop();
176 apply(self.callback, ());
177
178 class AppFrame(wxFrame):
179 def __init__(self, parent, id, title):
180 wxFrame.__init__(self, parent, id, title, wxPyDefaultPosition,
181 wxSize(650, 250))
182
183 # if the window manager closes the window:
184 EVT_CLOSE(self, self.OnCloseWindow);
185
186 # Now Create the menu bar and items
187 self.mainmenu = wxMenuBar()
188
189 menu = wxMenu()
190 menu.Append(209, 'E&xit', 'Enough of this already!')
191 EVT_MENU(self, 209, self.OnFileExit)
192 self.mainmenu.Append(menu, '&File')
193 menu = wxMenu()
194 menu.Append(210, '&Refresh', 'Refresh headlines')
195 EVT_MENU(self, 210, self.OnViewRefresh)
196 menu.Append(211, '&Slashdot Index', 'View Slashdot index')
197 EVT_MENU(self, 211, self.OnViewIndex)
198 menu.Append(212, 'Selected &Article', 'View selected article')
199 EVT_MENU(self, 212, self.OnViewArticle)
200 self.mainmenu.Append(menu, '&View')
201 menu = wxMenu()
202 menu.Append(220, '&Internal', 'Use internal text browser',TRUE)
203 menu.Check(220, true)
204 self.UseInternal = 1;
205 EVT_MENU(self, 220, self.OnBrowserInternal)
206 menu.Append(222, '&Settings...', 'External browser Settings')
207 EVT_MENU(self, 222, self.OnBrowserSettings)
208 self.mainmenu.Append(menu, '&Browser')
209 menu = wxMenu()
210 menu.Append(230, '&About', 'Some documentation');
211 EVT_MENU(self, 230, self.OnAbout)
212 self.mainmenu.Append(menu, '&Help')
213
214 self.SetMenuBar(self.mainmenu)
215
216 if wxPlatform == '__WXGTK__':
217 # I like lynx. Also Netscape 4.5 doesn't react to my cmdline opts
218 self.BrowserSettings = "xterm -e lynx %s &"
219 elif wxPlatform == '__WXMSW__':
220 # netscape 4.x likes to hang out here...
221 self.BrowserSettings = '\\progra~1\\Netscape\\Communicator\\Program\\netscape.exe %s'
222 else:
223 # a wild guess...
224 self.BrowserSettings = 'netscape %s'
225
226 # A status bar to tell people what's happening
227 self.sb = AppStatusBar(self)
228 self.SetStatusBar(self.sb)
229
230 self.list = wxListCtrl(self, 1100)
231 self.list.SetSingleStyle(wxLC_REPORT)
232 self.list.InsertColumn(0, 'Subject')
233 self.list.InsertColumn(1, 'Date')
234 self.list.InsertColumn(2, 'Posted by')
235 self.list.InsertColumn(3, 'Comments')
236 self.list.SetColumnWidth(0, 300)
237 self.list.SetColumnWidth(1, 150)
238 self.list.SetColumnWidth(2, 100)
239 self.list.SetColumnWidth(3, 100)
240
241 EVT_LIST_ITEM_SELECTED(self, 1100, self.OnItemSelected)
242 EVT_LEFT_DCLICK(self.list, self.OnLeftDClick)
243
244 self.logprint("Connecting to slashdot... Please wait.")
245 # wxYield doesn't yet work here. That's why we use a timer
246 # to make sure that we see some GUI stuff before the slashdot
247 # file is transfered.
248 self.timer = QuickTimer(self.DoRefresh, 1000)
249
250 def logprint(self, x):
251 self.sb.logprint(x)
252
253 def OnFileExit(self, event):
254 self.Destroy()
255
256 def DoRefresh(self):
257 f = RetrieveAsFile('slashdot.org','/ultramode.txt',self.sb.logprint)
258 art_list = ParseSlashdot(f)
259 self.list.DeleteAllItems()
260 self.url = []
261 self.current = -1
262 i = 0;
263 for article in art_list:
264 self.list.InsertStringItem(i, article[0])
265 self.list.SetStringItem(i, 1, article[2])
266 self.list.SetStringItem(i, 2, article[3])
267 self.list.SetStringItem(i, 3, article[6])
268 self.url.append(article[1])
269 i = i + 1
270 self.logprint("File retrieved OK.")
271
272 def OnViewRefresh(self, event):
273 self.logprint("Connecting to slashdot... Please wait.");
274 wxYield()
275 self.DoRefresh()
276
277 def DoViewIndex(self):
278 if self.UseInternal:
279 self.view = HTMLTextView(self, -1, 'slashdot.org',
280 'http://slashdot.org')
281 self.view.Show(true)
282 else:
283 self.logprint(self.BrowserSettings % ('http://slashdot.org'))
284 #os.system(self.BrowserSettings % ('http://slashdot.org'))
285 wxExecute(self.BrowserSettings % ('http://slashdot.org'))
286 self.logprint("OK")
287
288 def OnViewIndex(self, event):
289 self.logprint("Starting browser... Please wait.")
290 wxYield()
291 self.DoViewIndex()
292
293 def DoViewArticle(self):
294 if self.current<0: return
295 url = self.url[self.current]
296 if self.UseInternal:
297 self.view = HTMLTextView(self, -1, url, url)
298 self.view.Show(true)
299 else:
300 self.logprint(self.BrowserSettings % (url))
301 os.system(self.BrowserSettings % (url))
302 self.logprint("OK")
303
304 def OnViewArticle(self, event):
305 self.logprint("Starting browser... Please wait.")
306 wxYield()
307 self.DoViewArticle()
308
309 def OnBrowserInternal(self, event):
310 if self.mainmenu.Checked(220):
311 self.UseInternal = 1
312 else:
313 self.UseInternal = 0
314
315 def OnBrowserSettings(self, event):
316 dlg = wxTextEntryDialog(self, "Enter command to view URL.\nUse %s as a placeholder for the URL.", "", self.BrowserSettings);
317 if dlg.ShowModal() == wxID_OK:
318 self.BrowserSettings = dlg.GetValue()
319
320 def OnAbout(self, event):
321 dlg = wxMessageDialog(self, __doc__, "wxSlash", wxOK | wxICON_INFORMATION)
322 dlg.ShowModal()
323
324 def OnItemSelected(self, event):
325 self.current = event.m_itemIndex
326 self.logprint("URL: %s" % (self.url[self.current]))
327
328 def OnLeftDClick(self, event):
329 (x,y) = event.Position();
330 # Actually, we should convert x,y to logical coords using
331 # a dc, but only for a wxScrolledWindow widget.
332 # Now wxGTK derives wxListCtrl from wxScrolledWindow,
333 # and wxMSW from wxControl... So that doesn't work.
334 #dc = wxClientDC(self.list)
335 ##self.list.PrepareDC(dc)
336 #x = dc.DeviceToLogicalX( event.GetX() )
337 #y = dc.DeviceToLogicalY( event.GetY() )
338 id = self.list.HitTest(wxPoint(x,y))
339 #print "Double click at %d %d" % (x,y), id
340 # Okay, we got a double click. Let's assume it's the current selection
341 wxYield()
342 self.OnViewArticle(event)
343
344 def OnCloseWindow(self, event):
345 self.Destroy()
346
347
348 #---------------------------------------------------------------------------
349 # if running standalone
350
351 if __name__ == '__main__':
352 class MyApp(wxApp):
353 def OnInit(self):
354 frame = AppFrame(NULL, -1, "Slashdot Breaking News")
355 frame.Show(true)
356 self.SetTopWindow(frame)
357 return true
358
359 app = MyApp(0)
360 app.MainLoop()
361
362
363
364 #---------------------------------------------------------------------------
365 # if running as part of the Demo Framework...
366
367 def runTest(frame, nb, log):
368 win = AppFrame(NULL, -1, "Slashdot Breaking News")
369 frame.otherWin = win
370 win.Show(true)
371
372
373 overview = __doc__
374
375
376 #----------------------------------------------------------------------------
377
378