1 #!/usr/bin/env python2.5
3 # Copyright (C) 2008 by Daniel Martin Yerga
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.
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.
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.
19 # StocksPy: Application to get stocks data from Yahoo Finance.
29 #osso_c = osso.Context("net.yerga.stockthis", "0.3", False)
35 #detect if is ran locally or not
37 runningpath = sys.path[0]
39 if '/usr/share' in runningpath:
40 runninglocally = False
44 HOME = os.path.expanduser("~")
46 settingsdb, imgdir, configdir, logfile = \
47 settings.define_paths(runninglocally, HOME)
50 fhsize = gtk.HILDON_SIZE_FINGER_HEIGHT
51 horbtn = hildon.BUTTON_ARRANGEMENT_HORIZONTAL
52 ui_normal = gtk.HILDON_UI_MODE_NORMAL
53 ui_edit = gtk.HILDON_UI_MODE_EDIT
54 winprogind = hildon.hildon_gtk_window_set_progress_indicator
56 gtk.gdk.threads_init()
61 self.program = hildon.Program()
62 self.program.__init__()
63 gtk.set_application_name("StockThis")
64 self.window = hildon.StackableWindow()
65 self.window.set_default_size(800, 480)
66 self.program.add_window(self.window)
67 self.window.connect("destroy", gtk.main_quit)
69 menu = hildon.AppMenu()
70 self.window.set_app_menu(menu)
71 button = gtk.Button("About")
72 button.connect("clicked", About)
77 toolbar = self.main_toolbar(False, False, None, '', '')
79 parea = hildon.PannableArea()
80 tv = hildon.GtkTreeView(ui_normal)
81 inmodel = self.__create_model(marketdata.main, marketdata.idmain)
82 tv.connect("row-activated", self.show_instrument_view, inmodel,
83 marketdata.localmarkets, marketdata.localids,
89 vbox.pack_start(parea, True, True, 0)
90 vbox.pack_start(gtk.HSeparator(), False, False, 5)
91 vbox.pack_start(toolbar, False, False, 0)
94 self.window.show_all()
96 def show_instrument_view(self, widget, path, column, inmodel, names,
98 market = inmodel[path][0]
99 names = names[mindex.index(market)]
100 ids = ids[mindex.index(market)]
102 window = hildon.StackableWindow()
103 window.set_title("StockThis - " + inmodel[path][1])
106 toolbar = self.main_toolbar(False, False, None, '', '')
108 parea = hildon.PannableArea()
109 tv = hildon.GtkTreeView(ui_normal)
110 model = self.__create_model(names, ids)
111 tv.connect("row-activated", self.show_quotes_view, model, False)
116 vbox.pack_start(parea, True, True, 0)
117 vbox.pack_start(gtk.HSeparator(), False, False, 5)
118 vbox.pack_start(toolbar, False, False, 0)
123 def show_quotes_view(self, widget, path, column, model, portfolio):
124 quote = model[path][0], model[path][1]
125 print "quote:", quote[0]
126 #('EURUSD=X', 'EUR/USD')
128 #Currencies and ETFs should show the list now -> view = True
129 #Other items show a new list with options
131 for i in marketdata.localids[(len(marketdata.localids)-2):]:
138 if quote[0] in marketdata.idindexes:
139 self.show_instrument_view(widget, path, column, model,
140 marketdata.wnamesindexes,
141 marketdata.widsindexes,
142 marketdata.idindexes)
144 if quote[0] in marketdata.idotmarkets:
145 self.show_instrument_view(widget, path, column, model,
147 marketdata.omsymbols,
148 marketdata.idotmarkets)
150 if quote[0] in marketdata.ideumarkets:
151 self.show_instrument_view(widget, path, column, model,
153 marketdata.eusymbols,
154 marketdata.ideumarkets)
156 if quote[0] in marketdata.idusmarkets:
157 self.show_instrument_view(widget, path, column, model,
159 marketdata.ussymbols,
160 marketdata.idusmarkets)
164 win = hildon.StackableWindow()
165 win.set_title("StockThis - Quotes View - " + quote[1])
169 ltitle = gtk.Label('')
170 ltitle.set_markup('<b><big>' + quote[1].replace('&', '') +
172 color = gtk.gdk.color_parse("#03A5FF")
173 ltitle.modify_fg(gtk.STATE_NORMAL, color)
175 parea = hildon.PannableArea()
180 label = gtk.Label('')
181 label.set_markup('<b><big>Price:</big></b>')
182 lprice = gtk.Label('')
183 hbox.pack_start(label, False, False, 20)
184 hbox.pack_start(lprice, False, False, 245)
185 vbox1.pack_start(hbox, True, True, 0)
188 label = gtk.Label('')
189 label.set_markup('<b><big>Change:</big></b>')
190 lchange = gtk.Label('')
191 lpercent = gtk.Label('')
192 hbox.pack_start(label, False, False, 20)
193 hbox.pack_start(lchange, False, False, 205)
194 hbox.pack_start(lpercent, False, False, 0)
195 vbox1.pack_start(hbox, True, True, 0)
198 label = gtk.Label('')
199 label.set_markup('<b><big>Volume:</big></b>')
200 lvolume = gtk.Label('')
201 hbox.pack_start(label, False, False, 20)
202 hbox.pack_start(lvolume, False, False, 207)
203 vbox1.pack_start(hbox, True, True, 0)
206 label = gtk.Label('')
207 label.set_markup('<b><big>52 week high:</big></b>')
208 l52whigh = gtk.Label('')
209 hbox.pack_start(label, False, False, 20)
210 hbox.pack_start(l52whigh, False, False, 110)
211 vbox1.pack_start(hbox, True, True, 0)
214 label = gtk.Label('')
215 label.set_markup('<b><big>52 week low:</big></b>')
216 l52wlow = gtk.Label('')
217 hbox.pack_start(label, False, False, 20)
218 hbox.pack_start(l52wlow, False, False, 125)
219 vbox1.pack_start(hbox, True, True, 0)
222 button1 = hildon.PickerButton(fhsize, horbtn)
223 data = ["50", "100", "200", "300", "400", "500", "600", "700", "800",
225 selector = self.create_selector(data, True)
226 button1.set_selector(selector)
227 button1.set_title("Your shares")
228 #FIXME: Improve as it's shown you have a component in your portfolio
229 shares = self.get_shares_from_symbol(quote[0])
230 button1.set_value(shares)
231 hbox.pack_start(button1, True, True, 0)
233 button = hildon.Button(fhsize, horbtn)
234 button.set_title("Add to Portfolio")
235 button.connect("clicked", self.add_to_portfolio, button1, quote[0], quote[1])
236 hbox.pack_start(button, True, True, 0)
239 label = gtk.Label('')
240 label.set_markup('<b><big>Shares:</big></b>')
241 lshares = gtk.Label(shares)
242 hbox1.pack_start(label, False, False, 20)
243 hbox1.pack_start(lshares, False, False, 215)
246 label = gtk.Label('')
247 label.set_markup('<b><big>Holdings Value:</big></b>')
248 holdingsvalue = gtk.Label("")
249 hbox2.pack_start(label, False, False, 20)
250 hbox2.pack_start(holdingsvalue, False, False, 85)
253 label = gtk.Label('')
254 label.set_markup("<b><big>Day's Value Change:</big></b>")
255 dayvaluechange = gtk.Label("")
256 hbox3.pack_start(label, False, False, 20)
257 hbox3.pack_start(dayvaluechange, False, False, 10)
260 vbox1.pack_start(hbox, False, False, 0)
262 vbox1.pack_start(hbox1, True, True, 0)
263 vbox1.pack_start(hbox2, True, True, 0)
264 vbox1.pack_start(hbox3, True, True, 0)
266 parea.add_with_viewport(vbox1)
268 widgets = [win, ltitle, lprice, lchange, lpercent, lvolume, l52whigh, l52wlow, lshares, holdingsvalue, dayvaluechange]
270 toolbar = self.main_toolbar(True, portfolio, widgets, quote[0], quote[1])
272 vbox.pack_start(ltitle, False, False, 0)
273 vbox.pack_start(gtk.HSeparator(), False, False, 0)
274 vbox.pack_start(parea, True, True, 0)
275 vbox.pack_start(gtk.HSeparator(), False, False, 5)
276 vbox.pack_start(toolbar, False, False, 0)
281 self.show_data(quote[0], widgets, shares)
283 def get_shares_from_symbol(self, symbol):
284 portfolio_data = settings.load_portfolio(settingsdb)
286 for item in portfolio_data :
291 def add_to_portfolio(self, widget, button, symbol, name):
292 shares = button.get_value()
294 portfolio = settings.load_portfolio(settingsdb)
296 for item in portfolio:
298 index = portfolio.index(item)
300 item = [symbol, name, shares, '-']
303 settings.insert_new_item_to_portfolio(settingsdb, item)
305 settings.delete_item_from_portfolio(settingsdb, symbol)
306 settings.insert_new_item_to_portfolio(settingsdb, item)
308 self.show_info_banner(widget, "Added to portfolio")
311 def create_selector(self, data, entry):
313 selector = hildon.TouchSelectorEntry(text=True)
315 selector = hildon.hildon_touch_selector_new_text()
316 for i in range(len(data)):
317 selector.append_text(data[i])
321 def show_data(self, symbol, widgets, shares):
323 winprogind(widgets[0], 1)
324 thread.start_new_thread(self.get_data, (symbol, widgets, shares))
326 def get_data(self, symbol, widgets, shares):
327 from ystockquote import ystockquote as yt
328 win, ltitle, lprice, lchange, lpercent, lvolume, l52whigh, l52wlow, lshares, holdingsvalue, dayvaluechange = widgets
331 data = yt.get_all(symbol)
333 print 'Failed to get internet data'
334 data = {'price': 'N/A', 'change': 'N/A', 'volume':'N/A',
335 '52_week_high': 'N/A', '52_week_low': 'N/A'}
336 ltitle.set_markup('<b><big>Failed to get data</big></b>')
340 100.0 * float(data['change'])/(float(data['price']) - \
341 float(data['change']))
345 lprice.set_label(data['price'])
346 lchange.set_label(data['change'])
347 lpercent.set_label('%6.2f %%' % ch_percent)
349 if '-' in data['change']:
350 color = gtk.gdk.color_parse("#FF0000")
352 color = gtk.gdk.color_parse("#16EB78")
354 lpercent.modify_fg(gtk.STATE_NORMAL, color)
355 lchange.modify_fg(gtk.STATE_NORMAL, color)
357 lvolume.set_label(data['volume'])
358 l52whigh.set_label(data['52_week_high'])
359 l52wlow.set_label(data['52_week_low'])
362 daychange = float(shares)*float(data['change'])
366 holdvalue = float(shares)*float(data['price'])
370 dayvaluechange.set_label(str(daychange))
371 holdingsvalue.set_label(str(holdvalue))
375 def refresh_stock_data(self, widget, portfolio, widgets, symbol):
377 shares = self.get_shares_from_symbol(symbol)
381 self.show_data(symbol, widgets, shares)
383 def show_graph_view(self, widget, symbol, name):
384 win = hildon.StackableWindow()
385 win.set_title("StockThis - Graph View - " + name)
388 toolbar = self.main_toolbar(False, True, None, '', '')
390 self.graphs_title = gtk.Label(name)
391 color = gtk.gdk.color_parse("#03A5FF")
392 self.graphs_title.modify_fg(gtk.STATE_NORMAL, color)
394 parea = hildon.PannableArea()
397 hbox.set_homogeneous(True)
399 button = hildon.Button(fhsize, horbtn)
400 button.set_label('1d')
401 button.connect("clicked", self.show_graph, '1d', win, symbol)
402 hbox.pack_start(button)
404 button = hildon.Button(fhsize, horbtn)
405 button.set_label('5d')
406 button.connect("clicked", self.show_graph, '5d', win, symbol)
407 hbox.pack_start(button)
409 button = hildon.Button(fhsize, horbtn)
410 button.set_label('3m')
411 button.connect("clicked", self.show_graph, '3m', win, symbol)
412 hbox.pack_start(button)
414 button = hildon.Button(fhsize, horbtn)
415 button.set_label('6m')
416 button.connect("clicked", self.show_graph, '6m', win, symbol)
417 hbox.pack_start(button)
419 button = hildon.Button(fhsize, horbtn)
420 button.set_label('1y')
421 button.connect("clicked", self.show_graph, '1y', win, symbol)
422 hbox.pack_start(button)
424 button = hildon.Button(fhsize, horbtn)
425 button.set_label('2y')
426 button.connect("clicked", self.show_graph, '2y', win, symbol)
427 hbox.pack_start(button)
429 button = hildon.Button(fhsize, horbtn)
430 button.set_label('5y')
431 button.connect("clicked", self.show_graph, '5y', win, symbol)
432 hbox.pack_start(button)
434 button = hildon.Button(fhsize, horbtn)
435 button.set_label('Max')
436 button.connect("clicked", self.show_graph, 'max', win, symbol)
437 hbox.pack_start(button)
440 vbox1.pack_start(hbox, False, False, 0)
442 self.graph = gtk.Image()
443 vbox1.pack_start(self.graph, True, True, 0)
445 parea.add_with_viewport(vbox1)
447 vbox.pack_start(self.graphs_title, False, False, 0)
448 vbox.pack_start(gtk.HSeparator(), False, False, 0)
449 vbox.pack_start(parea, True, True, 0)
450 vbox.pack_start(gtk.HSeparator(), False, False, 5)
451 vbox.pack_start(toolbar, False, False, 0)
456 self.show_graph(None, '1d', win, symbol)
458 def show_graph(self, widget, option, win, symbol):
461 thread.start_new_thread(self.get_graph_data, (option, win, symbol))
463 def get_graph_data(self, option, win, symbol):
465 url = 'http://uk.ichart.yahoo.com/b?s=%s' % symbol
467 url = 'http://uk.ichart.yahoo.com/w?s=%s' % symbol
469 url = 'http://chart.finance.yahoo.com/c/3m/s/%s' % symbol.lower()
471 url = 'http://chart.finance.yahoo.com/c/6m/s/%s' % symbol.lower()
473 url = 'http://chart.finance.yahoo.com/c/1y/s/%s' % symbol.lower()
475 url = 'http://chart.finance.yahoo.com/c/2y/s/%s' % symbol.lower()
477 url = 'http://chart.finance.yahoo.com/c/5y/s/%s' % symbol.lower()
478 elif option == 'max':
479 url = 'http://chart.finance.yahoo.com/c/my/s/%s' % symbol.lower()
482 myimg = urllib2.urlopen(url)
483 imgdata = myimg.read()
485 pbl = gtk.gdk.PixbufLoader()
488 pbuf = pbl.get_pixbuf()
490 self.graph.set_from_pixbuf(pbuf)
494 self.graphs_title.set_label('Failed to get data')
497 def _tv_columns(self, treeview):
498 column = gtk.TreeViewColumn('ID', gtk.CellRendererText(), text=0)
499 column.set_visible(False)
500 treeview.append_column(column)
502 column = gtk.TreeViewColumn('Name', gtk.CellRendererText(), text=1)
503 treeview.append_column(column)
505 def __create_model(self, names, ids):
506 lstore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
507 for item in range(len(names)):
508 iter = lstore.append()
509 lstore.set(iter, 0, ids[item], 1, names[item])
512 def main_toolbar(self, quotesview, portfolio, widgets, symbol, name):
514 toolbar.set_homogeneous(True)
516 portfolio_btn = hildon.Button(fhsize, horbtn)
517 portfolio_btn.set_title("Portfolio")
518 portfolio_btn.connect("clicked", self.show_portfolio_view)
520 graph_btn = hildon.Button(fhsize, horbtn)
521 graph_btn.set_title("Graph")
522 graph_btn.connect("clicked", self.show_graph_view, symbol, name)
524 refresh_btn = hildon.Button(fhsize, horbtn)
525 refresh_btn.set_title("Refresh")
526 refresh_btn.connect("clicked", self.refresh_stock_data, portfolio,
530 toolbar.pack_start(portfolio_btn)
532 toolbar.pack_start(graph_btn)
533 toolbar.pack_start(refresh_btn)
539 def show_portfolio_view(self, widget):
540 data = settings.load_portfolio(settingsdb)
542 win = hildon.StackableWindow()
543 win.set_title("StockThis - Portfolio")
547 parea = hildon.PannableArea()
548 tv = hildon.GtkTreeView(ui_normal)
549 tv.set_headers_visible(True)
550 self.portfolio_model = self._create_portfolio_model(data)
551 tv.connect("row-activated", self.show_quotes_view, self.portfolio_model, True)
552 tv.set_model(self.portfolio_model)
553 self._tv_portfolio_columns(tv)
557 button = hildon.Button(fhsize, horbtn)
558 button.set_title("Refresh All")
559 button.connect("clicked", self.refresh_portfolio, tv, win)
560 hbox.pack_start(button, True, True, 0)
562 button = hildon.Button(fhsize, horbtn)
563 button.set_title("Remove")
564 button.connect("clicked", self.remove_item)
565 hbox.pack_start(button, True, True, 0)
567 vbox.pack_start(parea, True, True, 0)
568 vbox.pack_start(hbox, False, False, 0)
572 def remove_item(self, widget):
573 win = hildon.StackableWindow()
575 toolbar = hildon.EditToolbar("Choose items to delete", "Delete")
576 win.set_edit_toolbar(toolbar)
579 parea = hildon.PannableArea()
580 tv = hildon.GtkTreeView(ui_edit)
581 selection = tv.get_selection()
582 selection.set_mode(gtk.SELECTION_MULTIPLE)
583 tv.set_model(self.portfolio_model)
584 self._tv_remove_portfolio_columns(tv)
587 toolbar.connect("button-clicked", self.delete_from_portfolio, win, tv,
589 toolbar.connect_object("arrow-clicked", gtk.Window.destroy, win)
591 vbox.pack_start(parea, True, True, 0)
595 def delete_from_portfolio(self, widget, win, tv, selection):
596 if not self.is_treeview_selected(tv):
599 conf = self.show_confirmation(win, "Delete items?")
602 selmodel, selected = selection.get_selected_rows()
603 iters = [selmodel.get_iter(path) for path in selected]
605 symbol = selmodel.get_value(i, 0)
606 settings.delete_item_from_portfolio(settingsdb, symbol)
609 def _tv_remove_portfolio_columns(self, treeview):
610 column = gtk.TreeViewColumn('ID', gtk.CellRendererText(), text=0)
611 column.set_visible(False)
612 treeview.append_column(column)
614 column = gtk.TreeViewColumn('Name', gtk.CellRendererText(), text=1)
615 column.set_property("expand", True)
616 treeview.append_column(column)
618 column = gtk.TreeViewColumn('Shares', gtk.CellRendererText(), text=2)
619 column.set_visible(False)
620 treeview.append_column(column)
622 column = gtk.TreeViewColumn('Price', gtk.CellRendererText(), text=3)
623 column.set_visible(False)
624 treeview.append_column(column)
626 def refresh_portfolio(self, widget, tv, win):
627 data = settings.load_portfolio(settingsdb)
630 thread.start_new_thread(self._do_refresh_portfolio, (data, tv, win))
632 def _do_refresh_portfolio(self, data, tv, win):
634 item[3] = self.get_price(item[0])
636 self.portfolio_model = self._create_portfolio_model(data)
637 tv.set_model(self.portfolio_model)
640 def get_price(self, symbol):
641 from ystockquote import ystockquote as yt
642 price = yt.get_price(symbol)
646 def _create_portfolio_model(self, data):
647 lstore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING,
648 gobject.TYPE_STRING, gobject.TYPE_STRING)
650 iter = lstore.append()
651 lstore.set(iter, 0, item[0], 1, item[1], 2, item[2], 3, item[3])
654 def _tv_portfolio_columns(self, treeview):
655 column = gtk.TreeViewColumn('ID', gtk.CellRendererText(), text=0)
656 column.set_visible(False)
657 treeview.append_column(column)
659 column = gtk.TreeViewColumn('Name', gtk.CellRendererText(), text=1)
660 column.set_property("expand", True)
661 treeview.append_column(column)
663 column = gtk.TreeViewColumn('Shares', gtk.CellRendererText(), text=2)
664 treeview.append_column(column)
666 column = gtk.TreeViewColumn('Price', gtk.CellRendererText(), text=3)
667 treeview.append_column(column)
669 def show_confirmation(self, window, msg):
670 dialog = hildon.hildon_note_new_confirmation(window, msg)
672 result = dialog.run()
673 if result == gtk.RESPONSE_OK:
680 def show_info_banner(self, widget, msg):
681 hildon.hildon_banner_show_information(widget, 'qgn_note_infoprint', msg)
683 def is_treeview_selected(self, treeview):
684 selection = treeview.get_selection()
685 if selection.count_selected_rows() == 0:
686 self.show_info_banner(treeview, 'No selected item')
693 def __init__(self, widget):
694 dialog = gtk.Dialog(title='About', parent=None, flags=0)
695 dialog.set_has_separator(False)
696 dialog.set_size_request(-1, 400)
698 self.info_lb = gtk.Label()
699 self.info_lb.set_line_wrap(True)
703 button = hildon.Button(fhsize, horbtn)
704 button.set_title('Description')
705 button.connect("clicked", self.show_info, 'description')
706 hbox1.pack_start(button, True, True, 0)
708 button = hildon.Button(fhsize, horbtn)
709 button.set_title('Credits')
710 button.connect("clicked", self.show_info, 'credits')
711 hbox1.pack_start(button, True, True, 0)
713 button = hildon.Button(fhsize, horbtn)
714 button.set_title('License')
715 button.connect("clicked", self.show_info, 'license')
716 hbox1.pack_start(button, True, True, 0)
718 button = hildon.Button(fhsize, horbtn)
719 button.set_title('Donate')
720 button.connect("clicked", self.show_info, 'donate')
721 hbox1.pack_start(button, True, True, 0)
723 button = hildon.Button(fhsize, horbtn)
724 button.set_title('Report ')
725 button.connect("clicked", self.show_info, 'report')
726 hbox1.pack_start(button, True, True, 0)
728 button = hildon.Button(fhsize, horbtn)
729 button.set_title(' Vote ')
730 button.connect("clicked", self.show_info, 'vote')
731 hbox1.pack_start(button, True, True, 0)
733 self.action_btn = hildon.Button(fhsize, horbtn)
734 self.image = gtk.Image()
736 self.show_info(None, 'description')
738 dialog.vbox.pack_start(self.action_btn, False, False, 0)
739 dialog.vbox.pack_start(self.image, False, False, 5)
740 dialog.vbox.pack_start(self.info_lb, True, True, 0)
741 dialog.vbox.pack_start(hbox1, False, False, 0)
744 self.action_btn.hide()
749 def do_action(self, widget, action):
751 if action == "donate":
752 url = "http://stockthis.garage.maemo.org/donate.html"
753 elif action == "report":
754 url = "http://stockthis.garage.maemo.org/reporting.html"
755 elif action == "vote":
756 url = "http://maemo.org/downloads/product/stockthis"
757 webbrowser.open_new(url)
759 def show_info(self, widget, kind):
760 if kind == 'license':
761 self.action_btn.hide()
763 info = """<small><b>StockThis</b> is free software. It's using a GPL version 2 license or at your election any later version.
765 Logo by Daniel Martin Yerga.
767 elif kind == 'credits':
768 self.action_btn.hide()
770 info = """<small><b>Written by</b> Daniel Martin Yerga (dyerga@gmail.com)
772 <b>Thanks</b> to everyone who has reported bugs, suggestions, giving spirits, critiques, writing blog articles about StockThis, and so on. Like always the list is extremely big and for not forget anybody, THANKS TO ALL!</small>"""
773 elif kind == 'description':
774 self.action_btn.hide()
776 info = """<b><big>StockThis 0.3</big></b>
778 <i>StockThis is a stocks application for Maemo</i>
781 stockthis.garage.maemo.org"""
783 elif kind == 'donate':
784 self.action_btn.show()
786 self.action_btn.set_title('I want donate')
787 self.action_btn.connect("clicked", self.do_action, "donate")
788 info = """<small><b>StockThis</b> is a free (and gratis) software application.
789 Developing good software takes time and hard work.
791 <b>StockThis's author</b> develops the program in him spare time.
792 If you like the program and it's helpful, consider donating a small amount of money.
793 Donations are a great incentive and help to feel that the hard work is appreciated.</small>
796 elif kind == 'report':
797 self.action_btn.show()
799 self.action_btn.set_title('Report bug')
800 self.action_btn.connect("clicked", self.do_action, "report")
801 info = """<small>StockThis is being improved thanks to bug reports. The author appreciates very much all these reports.
802 If the application is raising an error when you're using it, you have two choices to report this error:
803 1) Send the log from the application menu (if there's an error in the log).
804 2) Write a bug report in the bugtracker of StockThis with as much information as possible (especially the log from the menu).</small>"""
807 self.action_btn.show()
809 self.image.set_from_file(imgdir + "maemoorg.png")
810 self.action_btn.set_title('Vote for StockThis')
811 self.action_btn.connect("clicked", self.do_action, "vote")
812 info = """<small>The downloads section in maemo.org has a nice system where you can rate applications.
813 If you consider StockThis a good application (or a bad one too), you could rate it in maemo.org site.</small>"""
815 self.info_lb.set_markup(info)
818 if __name__ == "__main__":
819 stockspy = StocksPy()
820 gtk.gdk.threads_enter()
822 gtk.gdk.threads_leave()