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