added two markets more and improved fremantle support
[stockthis] / stockthis.py
1 #!/usr/bin/env python2.5
2 # -*- coding: UTF8 -*-
3 # Copyright (C) 2008 by Daniel Martin Yerga
4 # <dyerga@gmail.com>
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 #
19 # StocksPy: Application to get stocks data from Yahoo Finance.
20 # Version 0.1
21 #
22
23 import urllib2
24 import gtk, gobject
25 try:
26     import hildon
27     HILDON = True
28 except:
29     HILDON = False
30     
31 try:
32     import osso
33     OSSO = True
34     osso_c = osso.Context("net.yerga.stockthis", "0.1", False)
35 except:
36     OSSO = False    
37
38 from marketdata import markets, idmarket, localmarkets, localids
39
40 #TODO: detect if running in Fremantle
41 FREMANTLE=True
42
43 #detect if is ran locally or not
44 import sys
45 runningpath = sys.path[0]
46
47 if '/usr/share' in runningpath:
48     running_locally = False
49 else:
50     running_locally = True
51     
52 if running_locally:
53     imgdir = 'pixmaps/'
54 else:
55     imgdir = '/usr/share/stockthis/pixmaps/'
56
57 loading_img = imgdir + 'loading.gif'
58
59 gtk.gdk.threads_init()
60
61 class StocksPy:
62
63     def __init__(self):
64         if HILDON:
65             self.program = hildon.Program()
66             self.program.__init__()
67             gtk.set_application_name('')
68             if FREMANTLE:
69                 self.window = hildon.StackableWindow()
70             else:
71                 self.window = hildon.Window()                
72             self.program.add_window(self.window) 
73         else:
74             self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
75
76         self.window.set_default_size(800, 480)  
77         self.window.set_title('StocksPy')
78         self.window.connect("destroy", gtk.main_quit)
79         self.window.connect("key-press-event", self.on_key_press)
80         self.window.connect("window-state-event", self.on_window_state_change)
81         self.window_in_fullscreen = False
82         
83         if HILDON:
84             if FREMANTLE:
85                 menu = hildon.AppMenu()
86                 self.window.set_main_menu(menu)
87                 about_menu = gtk.Button("About")
88             else:
89                 menu = gtk.Menu()
90                 self.window.set_menu(menu)
91                 about_menu = gtk.MenuItem("About")
92                 about_menu.set_size_request(-1, 55)
93             
94             about_menu.connect("activate", self.on_about)
95             menu.append(about_menu)            
96             menu.show_all()
97         else:
98             #TODO: create a gtk.menubar
99             pass
100         
101         
102         vbox = gtk.VBox()
103         
104         self.toolbar = gtk.HBox()
105         self.toolbar.set_property("no-show-all", True)
106         self.toolbar.hide()
107         self.toolbar.set_homogeneous(True)
108         self.toolbar.set_size_request(-1, 55)
109         
110         self.markets_btn = gtk.Button('Markets')
111         self.markets_btn.connect("clicked", self.create_markets_view)
112         self.markets_btn.set_property("can_focus", False)
113         
114         self.components_btn = gtk.Button('Components')
115         self.components_btn.connect("clicked", self.show_components_view)
116         self.components_btn.set_property("can_focus", False)          
117         
118         self.graph_btn = gtk.Button('Graphs')
119         self.graph_btn.connect("clicked", self.create_graphs_view)
120         self.graph_btn.set_property("can_focus", False)
121         
122         self.refresh_btn = gtk.Button('Refresh')
123         self.refresh_btn.connect("clicked", self.refresh_stock_data)
124         self.refresh_btn.set_property("can_focus", False)
125         
126         self.quotes_btn = gtk.Button('Quotes')
127         self.quotes_btn.connect("clicked", self.show_quotes_view)
128         self.quotes_btn.set_property("can_focus", False)        
129         
130         self.toolbar.pack_start(self.markets_btn)        
131         self.toolbar.pack_start(self.components_btn)
132         self.toolbar.pack_start(self.graph_btn)
133         self.toolbar.pack_start(self.quotes_btn) 
134         self.toolbar.pack_start(self.refresh_btn)        
135         
136         self.mainbox = gtk.VBox()
137
138
139         if FREMANTLE:
140             self.swin = hildon.PannableArea()
141         else:
142             self.swin = gtk.ScrolledWindow()
143             if HILDON:
144                 hildon.hildon_helper_set_thumb_scrollbar(self.swin, True)
145             self.swin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
146         self.swin.add_with_viewport(self.mainbox)
147
148         vbox.pack_start(self.swin, True, True, 0)
149         vbox.pack_start(gtk.HSeparator(), False, False, 5)
150         vbox.pack_start(self.toolbar, False, False, 0)
151
152         self.create_markets_view(self.mainbox)
153         self.window.add(vbox)
154
155         self.window.show_all()
156
157     def create_markets_view(self, widget): 
158         names = markets
159         ids = idmarket
160         self.toolbar.hide()
161         
162         actual_view = self.mainbox.get_children()
163         if len(actual_view) > 0:
164             for a_widget in actual_view:
165                 a_widget.destroy()
166
167         self.marketsbox = gtk.VBox()        
168         lnames = len(names)
169         
170         for i in range(lnames):
171             button = gtk.Button(names[i])
172             button.connect("clicked", self.create_components_view, ids[i])
173             button.set_size_request(-1, 65)
174             button.set_property("can_focus", False)
175             self.marketsbox.pack_start(button, True, True, 2)
176         
177         self.mainbox.pack_start(self.marketsbox, True, True, 2)
178         self.mainbox.show_all()
179
180     def show_components_view(self, widget):
181         kind = self.market_id
182         self.create_components_view(widget, kind)
183
184     def create_components_view(self, widget, kind):    
185         actual_view = self.mainbox.get_children()
186         for i in actual_view:
187             i.destroy()
188     
189         self.market_id = kind
190         self.toolbar.show()
191         self.components_btn.hide()
192         self.markets_btn.show()
193         self.refresh_btn.hide()
194         self.graph_btn.hide()
195         self.quotes_btn.hide()
196         
197         names = localmarkets[idmarket.index(kind)]
198         ids = localids[idmarket.index(kind)]
199         
200         self.compbox = gtk.VBox()
201         
202         self.components_trv = gtk.TreeView()
203         
204         if not FREMANTLE:
205             selection = self.components_trv.get_selection()
206             selection.connect("changed", self.changed_selection, ids)
207         else:
208             self.components_trv.connect("row-activated", self.select_component, ids)
209             
210         self.components_trv.set_rubber_banding(True)
211         self.components_trv.set_headers_visible(False)        
212         self.components_model = self.__create_components_model()
213         self.components_trv.set_model(self.components_model)
214         self._components_trv_columns(self.components_trv)   
215         self.add_initial_components(names, ids)
216         
217         self.compbox.pack_start(self.components_trv, True, True, 0) 
218
219         self.mainbox.pack_start(self.compbox, True, True, 0)
220         self.mainbox.show_all()
221
222     def select_component(self, widget, path, column, ids):
223         self.create_quotes_view(widget,self.components_model[path][1] )
224    
225     def changed_selection(self, widget, ids):
226         n, i, m = self.get_selected_from_treeview(self.components_trv, self.components_model, 1)
227         if n is not None:
228             self.create_quotes_view(widget, n)
229             
230     def __create_components_model(self):
231         lstore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
232         return lstore
233
234     def _components_trv_columns(self, treeview): 
235         renderer = gtk.CellRendererText()
236         renderer.set_property('scale', 1.6)
237         #renderer.set_property("background", 'lightgray')
238         renderer.set_property("xpad", 15)
239         column = gtk.TreeViewColumn('Name', renderer, text=0)
240         column.set_expand(True)
241         treeview.append_column(column) 
242         
243         renderer = gtk.CellRendererText()
244         column = gtk.TreeViewColumn('IDS', renderer, text=1)
245         column.set_visible(False)
246         treeview.append_column(column)         
247         
248     def add_initial_components(self, kind, ids):
249         for i in range(len(kind)):
250             niter = self.components_model.append() 
251             self.components_model.set(niter, 0, kind[i], 1, ids[i])    
252
253     def get_selected_from_treeview(self, treeview, model, setting):
254         selection = treeview.get_selection()
255         selected_model, selected_iter = selection.get_selected()
256         if selected_iter:
257             selected_name = model.get_value(selected_iter, setting)
258         else:
259             selected_name = None
260         return selected_name, selected_iter, selected_model
261
262     def show_quotes_view(self, widget):
263         kind = self.stocks_id
264         self.create_quotes_view(widget, kind)
265
266     def create_quotes_view(self, widget, kind):
267         self.stocks_id = kind
268         self.toolbar.show()
269         self.components_btn.show()
270         self.markets_btn.show()
271         self.refresh_btn.show()
272         self.graph_btn.show() 
273         self.quotes_btn.hide() 
274     
275         #actual_view = self.mainbox.get_children()
276         #actual_view[0].destroy()
277         
278         try:
279             self.graphbox.destroy()
280         except:
281             pass
282         
283         for i in range(len(localids)):
284             if kind in localids[i]:
285                 ind1, ind2 = i, localids[i].index(kind)  
286
287         self.quotesbox = gtk.VBox()
288
289         label1 = gtk.Label('')
290         label1.set_markup('<b><big>'+localmarkets[ind1][ind2]+'</big></b>')
291         color = gtk.gdk.color_parse("#03A5FF")          
292         label1.modify_fg(gtk.STATE_NORMAL, color)
293
294         self.databox = gtk.VBox()
295         
296         self.imagebox = gtk.VBox()
297         self.dataimg = gtk.Image()        
298         self.imagebox.pack_start(self.dataimg)
299         
300         self.show_data(kind)        
301         
302         hbox1 = gtk.HBox()
303         
304         label2 = gtk.Label('')
305         label2.set_markup('<b><big>Price:</big></b>')
306         self.lprice = gtk.Label('')
307         
308         hbox1.pack_start(label2, False, False, 50)
309         hbox1.pack_start(self.lprice, False, False, 185)
310         
311         hbox2 = gtk.HBox()
312
313         label4 = gtk.Label('')
314         label4.set_markup('<b><big>Change:</big></b>')
315         self.lchange = gtk.Label('')
316         self.lpercent = gtk.Label('')
317         
318         hbox2.pack_start(label4, False, False, 50)
319         hbox2.pack_start(self.lchange  , False, False, 145)             
320         hbox2.pack_start(self.lpercent, False, False, 0)
321                 
322         hbox3 = gtk.HBox()
323         
324         label7 = gtk.Label('')
325         label7.set_markup('<b><big>Volume:</big></b>')
326         self.lvolume = gtk.Label('')
327         
328         hbox3.pack_start(label7, False, False, 50)
329         hbox3.pack_start(self.lvolume  , False, False, 145)    
330         
331         hbox4 = gtk.HBox()
332         
333         label9 = gtk.Label('')
334         label9.set_markup('<b><big>52 week high:</big></b>')
335         self.l52whigh = gtk.Label('')
336         
337         hbox4.pack_start(label9, False, False, 50)
338         hbox4.pack_start(self.l52whigh , False, False, 55)             
339         
340         hbox5 = gtk.HBox()
341         
342         label11 = gtk.Label('')
343         label11.set_markup('<b><big>52 week low:</big></b>')
344         self.l52wlow = gtk.Label('')
345         
346         hbox5.pack_start(label11, False, False, 50)
347         hbox5.pack_start(self.l52wlow , False, False, 70)
348     
349         self.databox.pack_start(hbox1, True, True, 0)
350         self.databox.pack_start(hbox2, True, True, 0)
351         self.databox.pack_start(hbox3, True, True, 0)
352         self.databox.pack_start(hbox4, True, True, 0)
353         self.databox.pack_start(hbox5, True, True, 0)
354                
355         self.quotesbox.pack_start(label1, False, False, 0)
356         self.quotesbox.pack_start(gtk.HSeparator(), False, False, 0)     
357         self.quotesbox.pack_start(self.databox, True, True, 0)     
358         self.quotesbox.pack_start(self.imagebox, True, True, 0)    
359
360         self.mainbox.pack_start(self.quotesbox, True, True, 0)
361         self.mainbox.show_all()
362         self.compbox.hide()
363         self.databox.hide()
364
365     def show_data(self, kind):
366         import thread
367         self.dataimg.set_from_file(loading_img)
368         self.imagebox.show()
369         self.databox.hide()
370         thread.start_new_thread(self.get_data, (kind,))
371
372     def get_data(self, kind):    
373         self.graph_btn.show()
374         self.refresh_btn.show()
375         self.quotes_btn.hide() 
376         
377         from ystockquote import ystockquote as yt
378         data = yt.get_all(kind)
379         
380         try:
381             ch_percent = 100.0 * float(data['change'])/float(data['price'])
382         except ValueError:
383             ch_percent = 0.0
384
385         self.lprice.set_label(data['price'])
386         self.lchange.set_label(data['change'])
387         self.lpercent.set_label('%6.2f %%' % ch_percent)
388         
389         if '-' in data['change']:
390             color = gtk.gdk.color_parse("#FF0000")
391         else:
392             color = gtk.gdk.color_parse("#16EB78")   
393                      
394         self.lpercent.modify_fg(gtk.STATE_NORMAL, color)
395         self.lchange.modify_fg(gtk.STATE_NORMAL, color) 
396                    
397         self.lvolume.set_label(data['volume'])
398         self.l52whigh.set_label(data['52_week_high'])
399         self.l52wlow.set_label(data['52_week_low'])
400                
401         self.databox.show_all()  
402         self.imagebox.hide()  
403
404     def refresh_stock_data(self, widget):
405         self.show_data(self.stocks_id)
406
407     def create_graphs_view(self, widget):        
408         self.graph_btn.hide()
409         self.refresh_btn.hide()
410         self.quotes_btn.show()
411         
412         self.graph = gtk.Image()
413         
414         kind = self.stocks_id
415         
416         self.show_graph(None, '1d', kind)
417         
418         for i in range(len(localids)):
419             if kind in localids[i]:
420                 ind1, ind2 = i, localids[i].index(kind)
421
422         #self.mainbox.destroy()  
423         #self.mainbox = gtk.VBox()
424         #self.swin.add_with_viewport(self.mainbox) 
425         
426         actual_view = self.mainbox.get_children()
427         for a_widget in actual_view:
428             a_widget.destroy() 
429
430         self.graphbox = gtk.VBox()
431
432         label1 = gtk.Label(localmarkets[ind1][ind2])
433         color = gtk.gdk.color_parse("#03A5FF")
434         label1.modify_fg(gtk.STATE_NORMAL, color)
435         
436         hbox1 = gtk.HBox()
437         hbox1.set_size_request(-1, 55)
438         hbox1.set_homogeneous(True)
439         
440         self.btn1d = gtk.Button('1d')
441         self.btn1d.set_property("can_focus", False)
442         self.btn1d.connect("clicked", self.show_graph, '1d', kind)
443         self.btn5d = gtk.Button('5d')
444         self.btn5d.set_property("can_focus", False)
445         self.btn5d.connect("clicked", self.show_graph, '5d', kind)
446         self.btn3m = gtk.Button('3m')
447         self.btn3m.set_property("can_focus", False)
448         self.btn3m.connect("clicked", self.show_graph, '3m', kind)
449         self.btn6m = gtk.Button('6m')
450         self.btn6m.set_property("can_focus", False)
451         self.btn6m.connect("clicked", self.show_graph, '6m', kind)
452         self.btn1y = gtk.Button('1y')
453         self.btn1y.set_property("can_focus", False)
454         self.btn1y.connect("clicked", self.show_graph, '1y', kind)
455         self.btn2y = gtk.Button('2y')
456         self.btn2y.set_property("can_focus", False)
457         self.btn2y.connect("clicked", self.show_graph, '2y', kind)
458         self.btn5y = gtk.Button('5y')
459         self.btn5y.set_property("can_focus", False)
460         self.btn5y.connect("clicked", self.show_graph, '5y', kind)
461         self.btnmax = gtk.Button('max')
462         self.btnmax.set_property("can_focus", False)
463         self.btnmax.connect("clicked", self.show_graph, 'max', kind)
464         
465         hbox1.pack_start(self.btn1d)
466         hbox1.pack_start(self.btn5d)        
467         hbox1.pack_start(self.btn3m)        
468         hbox1.pack_start(self.btn6m)        
469         hbox1.pack_start(self.btn1y) 
470         hbox1.pack_start(self.btn2y)        
471         hbox1.pack_start(self.btn5y)        
472         hbox1.pack_start(self.btnmax)      
473            
474         self.graphbox.pack_start(label1, False, False, 2)
475         self.graphbox.pack_start(hbox1, False, False, 0)     
476         self.graphbox.pack_start(self.graph, True, True, 5)
477         
478         self.mainbox.pack_start(self.graphbox, False, False, 2)
479         self.mainbox.show_all()   
480
481     def show_graph(self, widget, option, kind):
482         import thread
483         self.graph.set_from_file(loading_img)
484         thread.start_new_thread(self.get_graph_data, (option, kind))
485
486     def get_graph_data(self, option, kind):
487         if option == '1d':
488             url = 'http://uk.ichart.yahoo.com/b?s=%s' % kind
489         elif option == '5d':  
490             url = 'http://uk.ichart.yahoo.com/w?s=%s' % kind
491         elif option == '3m':  
492             url = 'http://chart.finance.yahoo.com/c/3m/s/%s' % kind.lower()
493         elif option == '6m':  
494             url = 'http://chart.finance.yahoo.com/c/6m/s/%s' % kind.lower()
495         elif option == '1y':  
496             url = 'http://chart.finance.yahoo.com/c/1y/s/%s' % kind.lower()
497         elif option == '2y':  
498             url = 'http://chart.finance.yahoo.com/c/2y/s/%s' % kind.lower()
499         elif option == '5y':  
500             url = 'http://chart.finance.yahoo.com/c/5y/s/%s' % kind.lower()
501         elif option == 'max':  
502             url = 'http://chart.finance.yahoo.com/c/my/s/%s' % kind.lower()
503                 
504         myimg = urllib2.urlopen(url)
505         imgdata=myimg.read()
506
507         pbl = gtk.gdk.PixbufLoader()
508         pbl.write(imgdata)
509
510         pbuf = pbl.get_pixbuf()
511         pbl.close()
512         
513         self.graph.set_from_pixbuf(pbuf)
514
515     #Functions for fullscreen
516     def on_window_state_change(self, widget, event, *args):           
517         if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
518             self.window_in_fullscreen = True
519         else:
520             self.window_in_fullscreen = False
521
522     #F6 fullscreen, F7 bigger font, F8 smaller font
523     def on_key_press(self, widget, event, *args):  
524         if event.keyval == gtk.keysyms.F6:
525             if self.window_in_fullscreen:
526                 self.window.unfullscreen ()
527             else:
528                 self.window.fullscreen () 
529
530     def on_about(self, widget):
531         dialog = gtk.AboutDialog()
532         dialog.set_name("StockThis")
533         dialog.set_version("0.1")
534         dialog.set_copyright("Copyright © 2009")
535         dialog.set_website("http://stockthis.garage.maemo.org")
536         dialog.set_authors(["Daniel Martin Yerga <dyerga@gmail.com>"])
537         logo = gtk.gdk.pixbuf_new_from_file(imgdir + "stockthis.png")
538         dialog.set_logo(logo)
539         dialog.set_license("This program is released under the GNU\nGeneral Public License. Please visit \nhttp://www.gnu.org/copyleft/gpl.html\nfor details.")
540         dialog.set_artists(["Logo by Daniel Martin Yerga"])
541         dialog.run()
542         dialog.destroy()
543           
544 if __name__ == "__main__":
545     stockspy = StocksPy()
546     gtk.gdk.threads_enter()
547     gtk.main()
548     gtk.gdk.threads_leave()
549