]> git.saurik.com Git - wxWidgets.git/blob - utils/wxPython/demo/SlashDot.py
Added a demo showing how to use wxPostEvent
[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, style=wxLC_REPORT)
231 self.list.InsertColumn(0, 'Subject')
232 self.list.InsertColumn(1, 'Date')
233 self.list.InsertColumn(2, 'Posted by')
234 self.list.InsertColumn(3, 'Comments')
235 self.list.SetColumnWidth(0, 300)
236 self.list.SetColumnWidth(1, 150)
237 self.list.SetColumnWidth(2, 100)
238 self.list.SetColumnWidth(3, 100)
239
240 EVT_LIST_ITEM_SELECTED(self, 1100, self.OnItemSelected)
241 EVT_LEFT_DCLICK(self.list, self.OnLeftDClick)
242
243 self.logprint("Connecting to slashdot... Please wait.")
244 # wxYield doesn't yet work here. That's why we use a timer
245 # to make sure that we see some GUI stuff before the slashdot
246 # file is transfered.
247 self.timer = QuickTimer(self.DoRefresh, 1000)
248
249 def logprint(self, x):
250 self.sb.logprint(x)
251
252 def OnFileExit(self, event):
253 self.Destroy()
254
255 def DoRefresh(self):
256 f = RetrieveAsFile('slashdot.org','/ultramode.txt',self.sb.logprint)
257 art_list = ParseSlashdot(f)
258 self.list.DeleteAllItems()
259 self.url = []
260 self.current = -1
261 i = 0;
262 for article in art_list:
263 self.list.InsertStringItem(i, article[0])
264 self.list.SetStringItem(i, 1, article[2])
265 self.list.SetStringItem(i, 2, article[3])
266 self.list.SetStringItem(i, 3, article[6])
267 self.url.append(article[1])
268 i = i + 1
269 self.logprint("File retrieved OK.")
270
271 def OnViewRefresh(self, event):
272 self.logprint("Connecting to slashdot... Please wait.");
273 wxYield()
274 self.DoRefresh()
275
276 def DoViewIndex(self):
277 if self.UseInternal:
278 self.view = HTMLTextView(self, -1, 'slashdot.org',
279 'http://slashdot.org')
280 self.view.Show(true)
281 else:
282 self.logprint(self.BrowserSettings % ('http://slashdot.org'))
283 #os.system(self.BrowserSettings % ('http://slashdot.org'))
284 wxExecute(self.BrowserSettings % ('http://slashdot.org'))
285 self.logprint("OK")
286
287 def OnViewIndex(self, event):
288 self.logprint("Starting browser... Please wait.")
289 wxYield()
290 self.DoViewIndex()
291
292 def DoViewArticle(self):
293 if self.current<0: return
294 url = self.url[self.current]
295 if self.UseInternal:
296 self.view = HTMLTextView(self, -1, url, url)
297 self.view.Show(true)
298 else:
299 self.logprint(self.BrowserSettings % (url))
300 os.system(self.BrowserSettings % (url))
301 self.logprint("OK")
302
303 def OnViewArticle(self, event):
304 self.logprint("Starting browser... Please wait.")
305 wxYield()
306 self.DoViewArticle()
307
308 def OnBrowserInternal(self, event):
309 if self.mainmenu.Checked(220):
310 self.UseInternal = 1
311 else:
312 self.UseInternal = 0
313
314 def OnBrowserSettings(self, event):
315 dlg = wxTextEntryDialog(self, "Enter command to view URL.\nUse %s as a placeholder for the URL.", "", self.BrowserSettings);
316 if dlg.ShowModal() == wxID_OK:
317 self.BrowserSettings = dlg.GetValue()
318
319 def OnAbout(self, event):
320 dlg = wxMessageDialog(self, __doc__, "wxSlash", wxOK | wxICON_INFORMATION)
321 dlg.ShowModal()
322
323 def OnItemSelected(self, event):
324 self.current = event.m_itemIndex
325 self.logprint("URL: %s" % (self.url[self.current]))
326
327 def OnLeftDClick(self, event):
328 (x,y) = event.Position();
329 # Actually, we should convert x,y to logical coords using
330 # a dc, but only for a wxScrolledWindow widget.
331 # Now wxGTK derives wxListCtrl from wxScrolledWindow,
332 # and wxMSW from wxControl... So that doesn't work.
333 #dc = wxClientDC(self.list)
334 ##self.list.PrepareDC(dc)
335 #x = dc.DeviceToLogicalX( event.GetX() )
336 #y = dc.DeviceToLogicalY( event.GetY() )
337 id = self.list.HitTest(wxPoint(x,y))
338 #print "Double click at %d %d" % (x,y), id
339 # Okay, we got a double click. Let's assume it's the current selection
340 wxYield()
341 self.OnViewArticle(event)
342
343 def OnCloseWindow(self, event):
344 self.Destroy()
345
346
347 #---------------------------------------------------------------------------
348 # if running standalone
349
350 if __name__ == '__main__':
351 class MyApp(wxApp):
352 def OnInit(self):
353 frame = AppFrame(NULL, -1, "Slashdot Breaking News")
354 frame.Show(true)
355 self.SetTopWindow(frame)
356 return true
357
358 app = MyApp(0)
359 app.MainLoop()
360
361
362
363 #---------------------------------------------------------------------------
364 # if running as part of the Demo Framework...
365
366 def runTest(frame, nb, log):
367 win = AppFrame(NULL, -1, "Slashdot Breaking News")
368 frame.otherWin = win
369 win.Show(true)
370
371
372 overview = __doc__
373
374
375 #----------------------------------------------------------------------------
376
377