]> git.saurik.com Git - wxWidgets.git/blame - wxPython/demo/SlashDot.py
support Show() in wxFlexGridSizer (patch 737850)
[wxWidgets.git] / wxPython / demo / SlashDot.py
CommitLineData
cf694132
RD
1#!/usr/bin/python
2"""This is SlashDot 1.2
3
4 It's the obligatory Slashdot.org headlines reader that
5any modern widget set/library must have in order to be taken
6seriously :-)
7
8 Usage is quite simple; wxSlash attempts to download the
9'ultramode.txt' file from http://slashdot.org, which
10contains the headlines in a computer friendly format. It
11then displays said headlines in a wxWindows list control.
12
13 You can read articles using either Python's html library
14or an external browser. Uncheck the 'browser->internal' menu
15item to use the latter option. Use the settings dialog box
16to set which external browser is started.
17
18 This code is available under the wxWindows license, see
19elsewhere. If you modify this code, be aware of the fact
20that slashdot.org's maintainer, CmdrTaco, explicitly asks
21'ultramode.txt' downloaders not to do this automatically
22more than twice per hour. If this feature is abused,
23CmdrTaco may remove the ultramode file completely and that
24will 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
34from wxPython.wx import *
35from httplib import HTTP
36from htmllib import HTMLParser
37import os
38import re
39import formatter
40
41class 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
f6bcfd97 46 EVT_CLOSE(self, self.OnCloseWindow)
cf694132
RD
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
120def 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
130def myprint(x):
131 print x
132
133def 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
152class 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)
f6bcfd97 159 EVT_SIZE(self, self.OnSize)
cf694132
RD
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;
171class QuickTimer(wxTimer):
172 def __init__(self, func, wait=100):
1e4a197e
RD
173 wxTimer.__init__(self)
174 self.callback = func
175 self.Start(wait); # wait .1 second (.001 second doesn't work. why?)
cf694132 176 def Notify(self):
1e4a197e
RD
177 self.Stop();
178 apply(self.callback, ());
cf694132
RD
179
180class AppFrame(wxFrame):
181 def __init__(self, parent, id, title):
182 wxFrame.__init__(self, parent, id, title, wxPyDefaultPosition,
183 wxSize(650, 250))
184
1e4a197e
RD
185 # if the window manager closes the window:
186 EVT_CLOSE(self, self.OnCloseWindow);
cf694132 187
1e4a197e 188 # Now Create the menu bar and items
cf694132
RD
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()
1e4a197e
RD
204 menu.Append(220, '&Internal', 'Use internal text browser',True)
205 menu.Check(220, True)
cf694132
RD
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')
1e4a197e
RD
211 menu = wxMenu()
212 menu.Append(230, '&About', 'Some documentation');
213 EVT_MENU(self, 230, self.OnAbout)
214 self.mainmenu.Append(menu, '&Help')
cf694132
RD
215
216 self.SetMenuBar(self.mainmenu)
217
1e4a197e
RD
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)
cf694132
RD
230 self.SetStatusBar(self.sb)
231
06c0fba4 232 self.list = wxListCtrl(self, 1100, style=wxLC_REPORT)
1e4a197e
RD
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')
cf694132
RD
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)
1e4a197e 243 EVT_LEFT_DCLICK(self.list, self.OnLeftDClick)
cf694132 244
1e4a197e
RD
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)
cf694132
RD
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
1e4a197e 271 self.logprint("File retrieved OK.")
cf694132
RD
272
273 def OnViewRefresh(self, event):
1e4a197e
RD
274 self.logprint("Connecting to slashdot... Please wait.");
275 wxYield()
276 self.DoRefresh()
cf694132
RD
277
278 def DoViewIndex(self):
279 if self.UseInternal:
280 self.view = HTMLTextView(self, -1, 'slashdot.org',
281 'http://slashdot.org')
1e4a197e 282 self.view.Show(True)
cf694132
RD
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'))
1e4a197e 287 self.logprint("OK")
cf694132
RD
288
289 def OnViewIndex(self, event):
1e4a197e
RD
290 self.logprint("Starting browser... Please wait.")
291 wxYield()
292 self.DoViewIndex()
cf694132
RD
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)
1e4a197e 299 self.view.Show(True)
cf694132
RD
300 else:
301 self.logprint(self.BrowserSettings % (url))
302 os.system(self.BrowserSettings % (url))
1e4a197e 303 self.logprint("OK")
cf694132
RD
304
305 def OnViewArticle(self, event):
1e4a197e
RD
306 self.logprint("Starting browser... Please wait.")
307 wxYield()
308 self.DoViewArticle()
cf694132
RD
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):
1e4a197e
RD
322 dlg = wxMessageDialog(self, __doc__, "wxSlash", wxOK | wxICON_INFORMATION)
323 dlg.ShowModal()
cf694132
RD
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):
1e4a197e
RD
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)
cf694132
RD
344
345 def OnCloseWindow(self, event):
346 self.Destroy()
347
348
349#---------------------------------------------------------------------------
350# if running standalone
351
352if __name__ == '__main__':
353 class MyApp(wxApp):
354 def OnInit(self):
493f1553 355 frame = AppFrame(None, -1, "Slashdot Breaking News")
1e4a197e 356 frame.Show(True)
cf694132 357 self.SetTopWindow(frame)
1e4a197e 358 return True
cf694132
RD
359
360 app = MyApp(0)
361 app.MainLoop()
362
363
364
365#---------------------------------------------------------------------------
366# if running as part of the Demo Framework...
367
368def runTest(frame, nb, log):
493f1553 369 win = AppFrame(None, -1, "Slashdot Breaking News")
cf694132 370 frame.otherWin = win
1e4a197e 371 win.Show(True)
cf694132
RD
372
373
374overview = __doc__
375
376
377#----------------------------------------------------------------------------
378
379