
HabraKarma - writing a Python application for s60

This time we’ll do something useful, namely write an application that will show karma. Not just to show, but to display on top of all the “windows” a plate on which the karma value and the difference with the previous value will be. In addition, the program will publish a winning gong when karma rises and a cry of despair when it drops. In addition, we will launch it as a server, that is, it will not be displayed in the list of running programs.
For those who are more interested in the result than the process, you are welcome to the end of the article, where you are expectedfinished application HabraKarma 1.0 !!!
This time, I will not comment on every line in the program. I mean that you are at least a little familiar with python, can read the documentation , and at least understand something from my previous article .
General principles of work
The program itself will consist of 2 parts: the server itself, which does all the work, and the GUI, in which we will configure the server, start it and stop it.
The server itself is started by the start_server function from the e32 module. The function has only one parameter - the full path to the Python script, which should be run as a server. Yes, yes, everything is so simple, but with some limitations.
Since the server is running in the background, it cannot use the appuifw module, which is responsible for the UI. But we don’t need it either, as we will demonstrate the result of the work using the topwindow module, which allows you to display images on top of all “windows”. In addition, we have access to another module, called globalui, which also allows us to display things such as different requests, warnings, notifications, etc. on top of all “windows”. We will also use this, demonstrating the fact of starting and stopping the server.
Another limitation is the inability to access files located in the application folder (x: / private / UID). Therefore, if we want to use various multimedia files, settings files, we must put this in some neutral place, for example, in the x: / system / data / AppName folder. This folder has the “hidden” attribute, therefore the pictures placed there will not be visible in galleries, but audio files in the library.
Another problem is the "isolation" of the server from the main program. That is, we can start it, but we can’t find out if it is running, or stop it using standard modules. In our program, we will proceed as follows:
The server, at startup, creates a file called habra_flag in the root of drive D. When the server is shut down, this file will be deleted. Now, when you run the shell, check to see if the file you are looking for exists. If yes, then we consider the server running. In addition, you need the ability to stop the server. To do this, we’ll write something into this file, but on the server, in a loop, we will check. If the file length is greater than zero, we stop work.
Some may ask what kind of drive D we are going to write the habra_flag file to. This is a ram disk, of small volume, which is cleared upon reboot. Using it, we kill two birds with one stone: when the phone is rebooted, our server is manually terminated, the file with the flag disappears, and the file system on the ram disk has great performance. For more complex interaction with the server, you can use sockets.
Writing UI
The face of the application, which will be launched by "clicking" on the program icon, will be the Form class, which is described almost completely and completely in this wiki article . It looks like this:

And here is the program code itself with my comments:
- #coding: utf-8
- from appuifw import *
- import e32
- import os
- # the folder in which the settings files, media files, etc.
- data_path = os.getcwd () [0] + u ': \\ System \\ data \\ HabraKarma \\'
- class Main:
- def __init __ (self):
- app.screen = 'normal'
- # read the settings file, which is a dictionary,
- # stupidly saved via repr :)
- self.settings = eval (open (data_path + 'set.dat'). read ())
- # List of fields for our form, insert the saved values into its fields.
- self.fields = [
- (u'Habrauser ',' text ', self.settings [' user ']),
- (u "Auto-update (min.)", 'number', self.settings ['time']),
- (u "Turn on sound?", 'combo', ([u'Yes ', u'No'], int (self.settings ['sound']))),
- (u "Count the difference", 'combo', ([u'S the beginning of work ', u'S the last update'], int (self.settings ['diff']))),
- (u "Position X", 'number', self.settings ['X']),
- (u "Position Y", 'number', self.settings ['Y']),
- ]
- # create a form with flags
- self.form = Form (self.fields, flags = FFormDoubleSpaced | FFormEditModeOnly)
- # define a function that will save values from the form
- self.form.save_hook = self.save
- # if the server is already running, then the menu should be “stop” and vice versa
- if os.path.exists (u'd: \\ habra_flag '):
- self.form.menu = [(u'Stop ', self.stop)]
- else:
- self.form.menu = [(u'Start ', self.start_server)]
- # "execute" the form, that is, it will be displayed on the screen until
- # we won’t get out of it.
- self.form.execute ()
- # after exiting the form, check if the server is running and offer to start it
- if not os.path.exists (u'd: \\ habra_flag '):
- if query (u 'Run?', 'query'):
- self.start_server ()
- def start_server (self):
- # Actually the server start function
- self.form.menu = [(u'Stop ', self.stop)]
- e32.start_server (data_path + 'server.py')
- def stop (self):
- # server stop function.
- self.form.menu = [(u'Start ', self.start_server)]
- open (u'd: \\ habra_flag ',' w '). write (' stop ')
- def save (self, arg = None):
- # take the values from the form and save them to the settings file.
- self.settings ['user'] = arg [0] [2]
- self.settings ['time'] = arg [1] [2]
- self.settings ['sound'] = arg [2] [2] [1]
- self.settings ['diff'] = arg [3] [2] [1]
- self.settings ['X'] = arg [4] [2]
- self.settings ['Y'] = arg [5] [2]
- open (data_path + 'set.dat', 'w'). write (repr (self.settings))
- return true
- a = Main ()
It remains to write a server :)
- # - * - coding: utf-8 - * -
- import os
- import e32
- import sys
- # redirect errors to a file.
- sys.stderr = open ('d: \\ err.txt', 'w')
- import globalui as ui
- import topwindow
- from graphics import *
- import urllib
- import audio
- open (u'd: \\ habra_flag ',' w '). write (' ')
- data_path = os.getcwd () [0] + u ': \\ System \\ data \\ HabraKarma \\'
- # showing that we started.
- ui.global_note (u'Habrastart! ')
- class Habra:
- def __init __ (self):
- # open images
- self.habr_img = Image.open (data_path + 'habr.png')
- self.update_img = Image.open (data_path + 'update.png')
- # read the settings
- self.settings = eval (open (data_path + 'set.dat'). read ())
- self.last_karma = eval (open (data_path + 'karma.dat'). read ())
- # open sound
- self.bad_sound = audio.Sound.open (data_path + 'bad.wav')
- self.good_sound = audio.Sound.open (data_path + 'good.wav')
- self.loop = True
- # create a TopWindow window
- self.top = topwindow.TopWindow ()
- # indicate rounding
- self.top.corner_type = 'corner5'
- self.top.size = (170.35)
- # set the position from the settings file
- self.top.position = (self.settings ['X'], self.settings ['Y'])
- self.top.show ()
- self.karma_screen ()
- self.mainloop ()
- def stop (self):
- self.loop = False
- self.top.hide ()
- def karma_screen (self):
- # function called to update
- self.update_screen ()
- e32.ao_sleep (0.2)
- # pull info with harba api
- data = urllib.urlopen ('http://habrahabr.ru/api/profile/'+self.settings [' user ']) .read ()
- # I’m probably stumbled, but I don’t want to pull in this
- # case library for parsing xml
- karma = float (data.split ('
') [1] .split (' ') [0])- # compare the new karma with the old and
- # depending on the situation, collect the text, specify the color
- # and play sound, if set in the settings
- if self.last_karma
- karma_diff = u '(+' + str (abs (self.last_karma-karma)) + u ')'
- karma_color = 0x009f31
- if not self.settings ['sound']:
- self.good_sound.play ()
- elif self.last_karma> karma:
- karma_diff = u '(-' + str (abs (self.last_karma-karma)) + u ')'
- karma_color = 0xc30202
- if not self.settings ['sound']:
- self.bad_sound.play ()
- else:
- karma_diff = u ''
- karma_color = 0xffffff
- if self.settings ['diff']:
- self.last_karma = karma
- open (data_path + 'karma.dat', 'w'). write (repr (karma))
- # here we will find out how many pixels in width will occupy text
- karma_width = self.img.measure_text (unicode (karma)) [0] [2]
- diff_width = self.img.measure_text (karma_diff) [0] [2]
- # and then a matter of technology :)
- self.img = Image.new ((45 + karma_width + diff_width, 35))
- self.top.size = (self.img.size [0], 35)
- self.img.text ((35.25), unicode (karma))
- self.img.text ((35 + karma_width + 5.25), karma_diff, fill = karma_color)
- self.img.blit (self.habr_img)
- self.top.add_image (self.img, (0,0))
- def update_screen (self):
- # this will be displayed during the update
- self.img = Image.new ((165.35))
- self.top.size = (self.img.size [0], 35)
- self.img.blit (self.update_img)
- self.img.text ((35.25), u'Updated ')
- self.top.add_image (self.img, (0,0))
- def mainloop (self):
- # and start the main server loop
- interval = self.settings ['time'] * 60
- count = 0
- while self.loop:
- # if so ordered, stop the server
- if open (u'd: \\ habra_flag '). read ():
- os.remove (u'd: \\ habra_flag ')
- ui.global_note (u'Habrastop! ')
- self.loop = False
- count + = 1
- # if it’s time, we expose karma.
- if count> interval:
- count = 0
- self.karma_screen ()
- e32.ao_sleep (1)
- habr = Habra ()
It remains to collect the whole thing in sis and watch karma :)
Sources, along with pictures, sounds, an icon can be taken HERE . In ensymble, in the assembly options, write:
--extrasdir = data --lang = RU --icon = icon_path
And take the result ...
So, I present to your attention the HabraKarma application for symbain 9.x
Install the application, enter your username, configure the update time, window position. Now you can start the server, which will display your karma on top of all windows. After that, the program can be closed, the window will remain until you turn it off by entering the program again.
Download HERE (sis, 56 kb, no signature required)
Do not forget that Python Runtime no lower than version 1.9.7 is required to work ( download ).
Put Python and the program on the same drive!