Pull to refresh

ХабраКарма — пишем приложение на Python for s60

Reading time 12 min
Views 3.4K
imageПосле того, как мы разогрели аппетит к Python for s60 и начали учиться писать приложения, хочу предложить продолжить постигать программирование для symbain на замечательном языке программирования python.

На этот раз сделаем что нибудь полезное, а именно напишем приложение, которое будет показывать карму. Не просто показывать, а выводить поверх всех «окон» табличку, на которой будет значение кармы и разница с предыдущем значением. Помимо этого, программу будет издавать победный гонг при повышении кармы и крик отчаяния при ее понижении. Ко всему прочему, мы запустим ее, как сервер, то есть она не будет отображаться в списке запущенных программ.

Тем, кому больше важен результат, нежели процесс, милости просим в конец статьи, где вас ожидает готовое приложения HabraKarma 1.0 !!!

На этот раз, я не буду комментировать каждую строчку в программе. Я подразумеваю, что вы хоть немного знакомы с python, умеете читать документацию, и хоть что нибудь поняли из предыдущей моей статьи.

Общие принципы работы


Сама программа будет состоять из 2-х частей: сам сервер, который выполняет всю работу, и GUI, в котором мы будем настраивать работу сервера, запускать его и останавливать.

Сам сервер, запускается функцией start_server из модуля e32. Параметр у функции всего один — полный путь к питоновскому скрипту, который следует запустить в роли сервера. Да да, все так просто, но с некоторыми ограничениями.

Так как сервер работает в фоне, он не может использовать модуль appuifw, отвечающий за UI. Но нам это и не нужно, так как демонстрировать результат работы мы будет, используя модуль topwindow, который позволяет выводить изображения поверх всех «окон». Кроме этого, нам доступен еще один модуль, под названием globalui, который позволяет так же поверх всех «окон», выводить такие вещи, как разные запросы, предупреждения, уведомления и пр. Этим мы тоже воспользуемся, демонстрируя факт запуска и остановки сервера.

Еще одно ограничение, это невозможность доступа к файлам, размещенных в папке с приложением (x:/private/UID). Поэтому, если мы хотим использовать различные мультимедийные файлы, файлы настроек, мы должны положить это в какое нибудь нейтральное место, например в папку x:/system/data/AppName. Эта папка имеет атрибут «скрытый», поэтому помещенные туда картинки не будут видны в галереи, а аудио файлы в медиатеке.

Еще одна проблема, это «оторванность» сервера от основной программы. То есть запустить мы ее может, а вот узнать, запущена ли она, или остановить, при использовании стандартных модулей нельзя. В нашей программе мы поступим следующим образом:

Сервер, при запуске создает файл c названием habra_flag в корне диска D. При завершении работы сервера, этот файл удалим. Теперь, при запуске оболочки, проверим, существует ли искомый файл. Если да, значит сервер считаем запущенным. Кроме этого, нужна возможность остановки сервера. Для этого, в этот самый файл запишем, что нибудь, а на сервере, в цикле, будем проверять. Если длинна файла больше нуля, останавливаем работу.

У некоторых возможно появится вопрос, что за диск D, на который м собираемся писать файл habra_flag. Это ram диск, небольшого объема, который очищается при перезагрузки. Используя его, мы убиваем двух зайцев: при перезагрузки телефона, ручного завершения нашего сервера, файл с флагом исчезнет, и файловая система на ram диске имеет большую производительность. Для более сложного взаимодействия с сервером, можно использовать сокеты.

Пишем UI


Мордой приложения, которое будет запускаться по «клику» на иконку программы, будет класс Form, который практически от и до описан в этой вики статье. Выглядит это вот так:

image

А вот и сам код программы с моими комментариями:

  1. #coding:utf-8
  2. from appuifw import *
  3. import e32
  4. import os
  5. # папка, в которой лежат файлы настроек, медиа файлы и пр.
  6. data_path=os.getcwd()[0]+u':\\System\\data\\HabraKarma\\'
  7.  
  8. class Main:    
  9.     def __init__(self):
  10.         app.screen='normal'
  11.         # читаем файл настроек, который является словарем,
  12.         # тупо сохраненным через repr :)
  13.         self.settings=eval(open(data_path+'set.dat').read())
  14.         # Список полей для нашей формы, вставляем сохраненные значения в ее поля.
  15.         self.fields=[
  16.                 (u'Хабраюзер','text',self.settings['user']),
  17.                 (u"Автообновление (мин.)",'number'self.settings['time']),
  18.                 (u"Включить звук?"'combo'([u'Да',u'Нет']int(self.settings['sound']))),
  19.                 (u"Считать разницу"'combo'([u'С начала работы',u'С последнего обновления']int(self.settings['diff']))),
  20.                 (u"Положение X",'number'self.settings['X']),
  21.                 (u"Положение Y",'number'self.settings['Y']),
  22.  
  23.                 ]
  24.         # создаем форму с флагами
  25.         self.form=Form(self.fields,flags=FFormDoubleSpaced|FFormEditModeOnly)
  26.         # задаем функцию, которая будет сохранять значения из формы
  27.         self.form.save_hook=self.save
  28.         # если сервер уже запущен, то в меню должно быть "остановить" и наоборот
  29.         if os.path.exists(u'd:\\habra_flag'):
  30.             self.form.menu=[(u'Остановить',self.stop)]
  31.         else:
  32.             self.form.menu=[(u'Запустить',self.start_server)]
  33.         # "выполняем" форму, то есть она будет показана на экране, до тех пор пока
  34.         # из нее не выйдем.
  35.         self.form.execute()
  36.         # после выхода из формы, проверяем запущен ли сервер и предлагаем его запустить
  37.         if not os.path.exists(u'd:\\habra_flag'):
  38.             if query(u'Запустить?','query'):
  39.                 self.start_server()
  40.  
  41.     def start_server(self):
  42.         # собственно функция запуска сервера
  43.         self.form.menu=[(u'Остановить',self.stop)]
  44.         e32.start_server(data_path+'server.py')
  45.  
  46.     def stop(self):
  47.         # функция остановки сервера.
  48.         self.form.menu=[(u'Запустить',self.start_server)]
  49.         open(u'd:\\habra_flag','w').write('stop')
  50.  
  51.     def save(self,arg=None):
  52.         # берем значения из формы и сохраняем их в файл настроек.
  53.         self.settings['user']=arg[0][2]
  54.         self.settings['time']=arg[1][2]
  55.         self.settings['sound']=arg[2][2][1]
  56.         self.settings['diff']=arg[3][2][1]
  57.         self.settings['X']=arg[4][2]
  58.         self.settings['Y']=arg[5][2]
  59.         open(data_path+'set.dat','w').write(repr(self.settings))
  60.         return True        
  61.  
  62. a=Main()


Осталось написать сервер :)



  1. #-*-coding:utf-8-*-
  2. import os
  3. import e32
  4. import sys
  5. # перенаправляем ошибки в файл.
  6. sys.stderr=open('d:\\err.txt','w')
  7. import globalui as ui
  8. import topwindow
  9. from graphics import *
  10. import urllib
  11. import audio
  12. open(u'd:\\habra_flag','w').write('')
  13.  
  14.  
  15. data_path=os.getcwd()[0]+u':\\System\\data\\HabraKarma\\'
  16. # показывам, что мы запустились.
  17. ui.global_note(u'Хабрастарт!')
  18.  
  19. class Habra:
  20.  
  21.     def __init__(self):
  22.         # открываем изображения
  23.         self.habr_img=Image.open(data_path+'habr.png')
  24.         self.update_img=Image.open(data_path+'update.png')
  25.         # читаем настройки
  26.         self.settings=eval(open(data_path+'set.dat').read())
  27.         self.last_karma=eval(open(data_path+'karma.dat').read())
  28.         # открываем звука
  29.         self.bad_sound=audio.Sound.open(data_path+'bad.wav')
  30.         self.good_sound=audio.Sound.open(data_path+'good.wav')
  31.         self.loop=True
  32.         # создаем окно TopWindow
  33.         self.top=topwindow.TopWindow()
  34.         # указываем закругление
  35.         self.top.corner_type='corner5'
  36.         self.top.size=(170,35)
  37.         # ставим позицию из файла настроек
  38.         self.top.position=(self.settings['X'],self.settings['Y'])
  39.         self.top.show()
  40.         self.karma_screen()
  41.         self.mainloop()
  42.  
  43.     def stop(self):
  44.         self.loop=False
  45.         self.top.hide()
  46.  
  47.     def karma_screen(self):
  48.         # функция, вызываемая для обновления
  49.         self.update_screen()
  50.         e32.ao_sleep(0.2)
  51.         # тянем инфу с харба api
  52.         data=urllib.urlopen('http://habrahabr.ru/api/profile/'+self.settings['user']).read()
  53.         # меня наверное запинают, но я не хочу тянуть в этом
  54.         # случае библиотеку для парсинга xml
  55.         karma=float(data.split('<karma>')[1].split('</karma>')[0])    
  56.         # сравниваем новую карму со старой и 
  57.         # в зависимости от ситуации собираем текст, указываем цвет
  58.         # и играем звук, если это задано в настройках
  59.         if self.last_karma<karma:
  60.             karma_diff=u'(+'+str(abs(self.last_karma-karma))+u')'
  61.             karma_color=0x009f31
  62.             if not self.settings['sound']:
  63.                 self.good_sound.play()
  64.         elif self.last_karma>karma:
  65.             karma_diff=u'(-'+str(abs(self.last_karma-karma))+u')'
  66.             karma_color=0xc30202
  67.             if not self.settings['sound']:
  68.                 self.bad_sound.play()
  69.         else:
  70.             karma_diff=u''
  71.             karma_color=0xffffff
  72.  
  73.         if self.settings['diff']:
  74.             self.last_karma=karma
  75.         open(data_path+'karma.dat','w').write(repr(karma))
  76.  
  77.         # здесь мы узнаем, сколько пикселей по ширине бужет занимать текст
  78.         karma_width=self.img.measure_text(unicode(karma))[0][2]
  79.         diff_width=self.img.measure_text(karma_diff)[0][2]
  80.         # а дальше дело техники :)
  81.         self.img=Image.new((45+karma_width+diff_width,35))
  82.         self.top.size=(self.img.size[0],35)
  83.         self.img.text((35,25),unicode(karma))
  84.  
  85.         self.img.text((35+karma_width+5,25),karma_diff,fill=karma_color)
  86.         self.img.blit(self.habr_img)
  87.         self.top.add_image(self.img,(0,0))
  88.  
  89.     def update_screen(self):
  90.         # это будет выводится во время обновления
  91.         self.img=Image.new((165,35))
  92.         self.top.size=(self.img.size[0],35)
  93.         self.img.blit(self.update_img)
  94.         self.img.text((35,25),u'Обновляется')
  95.         self.top.add_image(self.img,(0,0))
  96.  
  97.     def mainloop(self):
  98.         # и пускаем главный цикл сервера
  99.         interval=self.settings['time']*60
  100.         count=0
  101.         while self.loop:
  102.             # если нам так приказали, останавливаем сервер
  103.             if open(u'd:\\habra_flag').read():
  104.                 os.remove(u'd:\\habra_flag')
  105.                 ui.global_note(u'Хабрастоп!')
  106.                 self.loop=False
  107.             count+=1
  108.             # если пришло время, обнавляем карму.
  109.             if count>interval:
  110.                 count=0
  111.                 self.karma_screen()
  112.             e32.ao_sleep(1)
  113.  
  114.  
  115. habr=Habra()


Осталось собрать все это дело в sis и наблюдать за кармой :)

Исходники, вместе с картинками, звуками, иконкой можно взять ЗДЕСЬ. В ensymble в опциях сборки прописывать:
--extrasdir=data --lang=RU --icon=путь_к_иконке

И забираем результат...


Итак, представляю вашему вниманию приложение HabraKarma для symbain 9.x
Установите приложение, введите ваш username, настройте время обновления, положение окна. Теперь можно запустить сервер, который будет отображать поверх всех окон вашу карму. После этого, программу можно закрыть, окно останется до тех пор, пока вы его не выключите, зайдя опять в программу.
Скачиваем ОТСЮДА (sis, 56 кб, подписи не требует)
Не забывайте, что для работы необходим Python Runtime не ниже версии 1.9.7 (скачать).
Python и программу ставить на один и тот же диск!
Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
+51
Comments 48
Comments Comments 48

Articles