Perl regexp from irmin. Code adoped for threads.
authorkibergus <kibergus@gmail.com>
Mon, 11 Jan 2010 22:06:11 +0000 (22:06 +0000)
committerkibergus <kibergus@gmail.com>
Mon, 11 Jan 2010 22:06:11 +0000 (22:06 +0000)
git-svn-id: file:///svnroot/ussd-widget/trunk@15 d197f4d6-dc93-42ad-8354-0da1f58e353f

ussd-widget/build_ussd-widget.py
ussd-widget/src/usr/lib/hildon-desktop/ussd-widget.py

index 9b33a52..7df6871 100644 (file)
@@ -23,7 +23,7 @@ if __name__ == "__main__":
     p.description="Widget, that executes USSD query and displays response text\nThe main purpose is viewing your balance. In Russia all operators provide balace information via USSD queries andmost part of contracts are prepaid. Ability to see your balance on desktop can be useful in such case.\nAnyway, you can configure widget to any other USSD query."
     p.author="Alexey Guseynov"
     p.mail="kibergus@gmail.com"
-    p.depends = "python2.5, ussd-common, python-hildondesktop, hildon-desktop-python-loader, python-gtk2, python-gobject, python-hildon, python-cairo"
+    p.depends = "python2.5, ussd-common, python-hildondesktop (>=0.1.0-1maemo2), hildon-desktop-python-loader (>=0.1.0-1maemo2), python-gtk2, python-gobject, python-hildon, python-cairo"
     p.section="user/desktop"
     p.icon = "./ussd-widget.png"
     p.arch="all"                #should be all for python, any for all arch
@@ -34,9 +34,9 @@ if __name__ == "__main__":
 #    p.postinstall="""#!/bin/sh
 #""" #Set here your post install script
 
-    version = "0.0.1"
+    version = "0.0.3"
     build = "0" 
-    changeloginformation = "Transpared widget, more debug support, initial parser support" 
+    changeloginformation = "Perl regexp from irmin. Code adoped for threads." 
    
     dir_name = "src"     
 
index 23fc4fd..ec501bb 100755 (executable)
@@ -1,13 +1,24 @@
 #!/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):
@@ -18,13 +29,88 @@ class UssdConfigDialog(gtk.Dialog):
         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")
@@ -33,8 +119,14 @@ def get_config():
         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
 
@@ -42,44 +134,133 @@ def set_config(config):
     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)
@@ -89,19 +270,19 @@ class UssdWidgetPlugin(hildondesktop.HomePluginItem):
     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)
@@ -115,6 +296,16 @@ class UssdWidgetPlugin(hildondesktop.HomePluginItem):
 
         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()
 
@@ -151,7 +342,10 @@ class UssdWidgetPlugin(hildondesktop.HomePluginItem):
         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):
@@ -160,12 +354,13 @@ class UssdWidgetPlugin(hildondesktop.HomePluginItem):
         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()