#!/usr/bin/python
# -*- coding: utf-8 -*-
+import gobject
import gtk
import hildon
import hildondesktop
import os
from subprocess import *
import cairo
+import time
+from threading import *
+import re
-supports_alpha = False
+class pHelpDialog(gtk.Dialog):
+ def __init__(self, heading, text):
+ gtk.Dialog.__init__(self, heading, None,
+ gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR,
+ ("OK", gtk.RESPONSE_OK))
+ self.vbox.add(gtk.Label(text))
+ self.show_all()
+ self.parent
class UssdConfigDialog(gtk.Dialog):
def __init__(self, config):
self.ussdNumber.set_text(config[0])
self.parser = hildon.Entry(gtk.HILDON_SIZE_AUTO)
self.parser.set_text(config[1])
- self.vbox.add(gtk.Label("USSD number"))
- self.vbox.add(self.ussdNumber)
- self.vbox.add(gtk.Label("Parser"))
- self.vbox.add(self.parser)
+ self.chain = hildon.Entry(gtk.HILDON_SIZE_AUTO)
+ self.chain.set_text(config[2])
+ self.update_interval = hildon.Entry(gtk.HILDON_SIZE_AUTO)
+ self.update_interval.set_text(config[3])
+ self.regexp = hildon.Entry(gtk.HILDON_SIZE_AUTO)
+ self.regexp.set_text(config[4])
+
+ phelp = gtk.Button("?")
+ phelp.connect("clicked", on_show_phelp)
+
+ chelp = gtk.Button("?")
+ chelp.connect("clicked", on_show_chelp)
+
+ reghelp = gtk.Button("?")
+ reghelp.connect("clicked", on_show_reghelp)
+
+ numberBox = gtk.HBox()
+ numberBox.add(gtk.Label("USSD number"))
+ numberBox.add(self.ussdNumber)
+ self.vbox.add(numberBox)
+
+ parserBox = gtk.HBox()
+ parserBox.add(gtk.Label("Parser"))
+ parserBox.add(phelp)
+ parserBox.add(self.parser)
+ self.vbox.add(parserBox)
+
+ chainBox = gtk.HBox()
+ chainBox.add(gtk.Label("Chain"))
+ chainBox.add(chelp)
+ chainBox.add(self.chain)
+ self.vbox.add(chainBox)
+
+ updateBox = gtk.HBox()
+ updateBox.add(gtk.Label("Update every "))
+ updateBox.add(self.update_interval)
+ updateBox.add(gtk.Label(" minutes (BROKEN)"))
+ self.vbox.add(updateBox)
+
+ regexpBox = gtk.HBox()
+ regexpBox.add(gtk.Label("RegExp"))
+ regexpBox.add(reghelp)
+ regexpBox.add(self.regexp)
+ self.vbox.add(regexpBox)
+
self.show_all()
self.parent
+def smart_split_string (str, query) :
+ word = ""
+ result = []
+ # Is simbol backslashed?
+ bs = 0
+ # Quotes: 1 - ", 2 - ', 0 - no quotes
+ qs = 0
+ for i in range(len(str)) :
+ if bs == 0 and (str[i] == '"' and qs == 1 or str[i] == "'" and qs == 2) :
+ qs = 0
+ elif bs == 0 and qs == 0 and (str[i] == '"' or str[i] == "'") :
+ if str[i] == '"':
+ qs = 1
+ else :
+ qs = 2
+ elif bs == 0 and str[i] == '\\' :
+ bs = 1
+ elif bs == 0 and str[i] == '%' :
+ word += query
+ ws = 0
+ else :
+ if bs == 1 and str[i] != '\\' and str[i] != '"' and str[i] != "'" :
+ word += "\\"
+ if qs == 0 and (str[i] == " " or str[i] == "\t") :
+ if word != "" :
+ result.append(word)
+ word = ""
+ else :
+ word += str[i]
+ bs = 0
+ if word != "" :
+ result.append(word)
+ return result
+
def get_config():
try :
config = open(os.getenv("HOME")+"/.ussdWidget.conf","r")
number = config.readline().strip()
config.readline()
parser = config.readline().strip()
+ config.readline()
+ chain = config.readline().strip()
+ config.readline()
+ interval = config.readline().strip()
+ config.readline()
+ regexp = config.readline().strip()
config.close()
- return [number, parser]
+ return [number, parser, chain, interval, regexp]
except IOError:
return None
fconfig = open(os.getenv("HOME")+"/.ussdWidget.conf","w")
fconfig.writelines(["# Parameters are taken by line number, do not move them\n", "# USSD query to be run by widget\n", config[0], "\n"])
fconfig.writelines(["Parser command\n", config[1], "\n"])
+ fconfig.writelines(["Chain command\n", config[2], "\n"])
+ fconfig.writelines(["Update interval in minutes\n", config[3], "\n"])
+ fconfig.writelines(["RegExp pattern\n", config[4], "\n"])
fconfig.close()
+def check_regexp(regexp):
+ try :
+ re.compile( regexp )
+ except Exception, e:
+ on_error_regexp( str( e ) )
+ return 1
+ return 0
+
+#def timed_renewer(Thread):
+# def __init__ (self, widget, period):
+# self.widget = widget
+# self.period = period
+# self.version = widget.timerversion
+#
+# def run (self):
+# while widget.timerversion == version:
+# ussd_renew(widget, None)
+# time.sleep(period)
+
def ussd_renew(widget, event):
- config = get_config()
- widget.processing = 1
- widget.label.set_text("Processing")
- widget.queue_draw()
- gtk.main_iteration ()
-# widget.send_expose(gtk.gdk.EXPOSE)
- if config :
- p = Popen(['python', '/usr/bin/ussdquery.py', config[0]], stdout=PIPE)
- reply = p.communicate()[0].strip()
- if reply == "" :
- reply = " Error "
-# else :
-# if config[1] != "":
-# p = Popen([config[1]], stdout=PIPE)
-# reply = p.communicate(reply)[0].strip()
- else :
- reply = " Bad config "
- widget.processing = 0
- widget.label.set_text(reply)
+ if widget.process == None or widget.process.isAlive() == False :
+ widget.process = ussd_renewer (widget)
+ # See bug https://bugs.maemo.org/show_bug.cgi?id=7809
+ widget.process.run()
+
+class ussd_renewer ():#Thread):
+ def __init__ (self, widget):
+# Thread.__init__ (self)
+ self.widget = widget
+ self.daemon = True
+
+# stub while thread but is not fixed
+ def isAlive(self):
+ return False
+
+ def run(self):
+ widget = self.widget
+ config = get_config()
+ widget.processing = 1
+ last_text = widget.label.get_text()
+ widget.label.set_text("Processing")
+ widget.queue_draw()
+ gtk.main_iteration ()
+
+ if config :
+ p = Popen(['/usr/bin/ussdquery.py', config[0]], stdout=PIPE)
+ reply = p.communicate()[0].strip()
+ if reply == "" :
+ reply = " Error "
+ widget.error = 1
+ # Show previous text in 5 seconds
+ widget.timer = gobject.timeout_add (5000, error_return, widget, last_text)
+ else :
+ widget.error = 0
+ if config[1] != "":
+ p = Popen(smart_split_string(config[1], reply), stdout=PIPE)
+ reply = p.communicate(reply+"\n")[0].strip()
+ if config[2] != "":
+ p = Popen(smart_split_string(config[2], reply))
+ if config[4] != "":
+ try :
+ r = re.match( config[4], reply ).group( 1 )
+ except Exception, e:
+ r = "Regexp Error: " + str( e )
+
+ if r :
+ reply = r
+ else :
+ reply = " Bad config "
+ widget.processing = 0
+ widget.label.set_text(reply)
+ widget.queue_draw()
+ gtk.main_iteration ()
+
+def error_return(widget, text) :
+ if widget.processing == 0 and widget.error == 1:
+ widget.label.set_text(text)
+ return False
def on_show_settings(widget):
config = get_config()
if config == None :
- config = ["", ""]
+ config = ["", "", "", "", ""]
dialog = UssdConfigDialog(config)
dialog.run()
- set_config ([dialog.ussdNumber.get_text(), dialog.parser.get_text()])
-
+ if check_regexp( dialog.regexp.get_text() ) :
+ return
+
+ set_config ([dialog.ussdNumber.get_text(), dialog.parser.get_text(), dialog.chain.get_text(), dialog.update_interval.get_text(), dialog.regexp.get_text()])
+
+# Doesn't work in hildon-home
+# widget.timerversion += 1
+# if dialog.update_interval.get_text() != "" :
+# ussd_renewer (widget, 60000*int(dialog.update_interval.get_text()))
+
dialog.destroy()
- if config == ["", ""] :
+ if config == ["", "", "", "", ""] :
widget.label.set_text("Click to update")
+def on_show_phelp(widget):
+ dialog = pHelpDialog("Format help", "Reply would be passed to specified utility, output\n of utility would be shown to you.\n Format:\n% would be replaced by reply\n\\ invalidates special meaming of following symbol\n\" and ' work as usual\nspace delimits command line parameters of utility")
+ dialog.run()
+ dialog.destroy()
+
+def on_show_chelp(widget):
+ dialog = pHelpDialog("Format help", "Reply would be passed to specified utility after\nparser utility. May be used for logging, statistics etc.\n Format:\n% would be replaced by reply\n\\ invalidates special meaming of following symbol\n\" and ' work as usual\nspace delimits command line parameters of utility")
+ dialog.run()
+ dialog.destroy()
+
+def on_show_reghelp(widget):
+ dialog = pHelpDialog("Format help", "standard python regexps")
+ dialog.run()
+ dialog.destroy()
+
+def on_error_regexp(error):
+ dialog = pHelpDialog( "Regexp syntax error", error )
+ dialog.run()
+ dialog.destroy()
+
def get_color(logicalcolorname):
settings = gtk.settings_get_default()
color_style = gtk.rc_get_style_by_paths(settings, 'GtkButton', 'osso-logical-colors', gtk.Button)
def __init__(self):
hildondesktop.HomePluginItem.__init__(self)
+ self.process = None
+ self.timerversion = 0 # Because threads in pyton are crap
self.processing = 0
+ self.error = 0
colormap = self.get_screen().get_rgba_colormap()
self.set_colormap (colormap)
- query = get_config()
- if query :
- self.label = gtk.Label("Click to update")
- else :
- self.label = gtk.Label("Configure me")
-
+ config = get_config()
+
self.connect("button-press-event", ussd_renew)
+ self.label = gtk.Label()
self.label.set_padding(15, 10)
self.label.set_size_request(-1,-1)
self.set_size_request(-1,-1)
self.vbox.show_all()
+ if config :
+ self.label.set_label("Data not available")
+# Should be uncommented only after thread bug fixed
+# ussd_renew (self, None)
+# Doesn't work in hildon-home
+# if config[3] != "" :
+# timed_renewer (self, 60000*int(config[3]))
+ else :
+ self.label.set_label("Configure me")
+
def _expose(self, event):
cr = self.window.cairo_create()
cr.set_source_rgba (bg_color.red / 65535.0, bg_color.green/65535.0, bg_color.blue/65535.0, 0.7)
cr.fill_preserve ()
- cr.set_source_rgba (fg_color.red / 65535.0, fg_color.green / 65535.0, fg_color.blue / 65535.0, 0.5)
+ if self.error :
+ cr.set_source_rgba (1.0, 0.0, 0.0, 0.5)
+ else :
+ cr.set_source_rgba (fg_color.red / 65535.0, fg_color.green / 65535.0, fg_color.blue / 65535.0, 0.7)
cr.stroke ()
def do_expose_event(self, event):
self.vbox.do_expose_event (self, event)
hd_plugin_type = UssdWidgetPlugin
+gtk.gdk.threads_init()
# The code below is just for testing purposes.
# It allows to run the widget as a standalone process.
if __name__ == "__main__":
- import gobject
gobject.type_register(hd_plugin_type)
obj = gobject.new(hd_plugin_type, plugin_id="plugin_id")
obj.show_all()
+ gtk.gdk.threads_init()
gtk.main()