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