Pull to refresh

Пишем апплет для GNOME на Python

Reading time9 min
Views4.8K
Публикую данный пост по просьбе уважаемого VladX, у которого возникло временное недопонимание с кармой.

Апплет — это маленькое приложение, встраиваемое непосредственно в панель GNOME. Обычно апплет выполняет какой-то опеределенный узкий функционал (изменение громкости, монтирование устройств), что выгодно отличает его от громоздкого оконного приложения. На самом деле знающему Python кодеру не составит труда написать собственный апплет, но и для незнающих есть выход: большое количество полезных (и не очень) апплетов лежит на сайте gnomefiles.org, помимо этого, на официальном сайте есть примеры написания апплетов на C.

Поехали!


Итак, мы пишем апплет. Апплет должен что-то делать. Наш апплет, например, будет показывать хабракарму и хабрасилу. Для получения всей этой информации мы будем использовать API хабра.

#!/usr/bin/env python
# coding=utf-8
import sysos, gtk, gtk.gdk, pygtk, gnomeapplet, gnome
 
pygtk.require('2.0')
 
__USER__ = 'VladX'
__URL__ = 'http://habrahabr.ru/api/profile/' + __USER__


Вот так начинается наш апплет. Тут все понятно — импортируем нужные библиотеки, заносим в переменные нужные параметры. Во избежание всяких проблем с русскими символами будем использовать кодировку UTF-8 (и Вам, кстати, советую всегда поступать так же).

class MyApplet (gnomeapplet.Applet):
 
    def __init__ (self, applet, iid):
 
        self.applet = applet
        self.applet.set_name('MyApplet')
        self.hbox = gtk.HBox()
        self.applet.add(self.hbox)
        self.event = gtk.EventBox()
        self.hbox.add(self.event)
        self.info = gtk.Label()
        self.event.add(self.info) # Чтобы объект мог реагировать на различные события, его нужно поместить в Event Box
        self.event.set_tooltip_text('Хабраюзер ' + __USER__)
        self.event.connect('button-press-event'self.callback_button)
        self.__init_popupmenu()
        self.applet.connect('destroy'self.callback_destroy)
        self.applet.show_all() # Показываем все это на панели
        self.info.set_text(self.get_info())
 
    def __init_popupmenu (self):
 
        self.applet.setup_menu('''
            <popup name='
button3'>
                <menuitem name='
Open Item' verb='Open' stockid='gtk-open'/>
                <menuitem name='
About Item' verb='About' stockid='gtk-about'/>
            </popup>'
''[
                          ('Open'self.callback_open),
                          ('About'self.callback_about)
                         ]None)
 
    def get_info (self):
        '''Делаем запрос, например, при помощи Pycurl'''
        import pycurl, StringIO
        content = StringIO.StringIO()
        c = pycurl.Curl()
        c.setopt(pycurl.URL, __URL__)
        c.setopt(pycurl.FOLLOWLOCATION1)
        c.setopt(pycurl.WRITEFUNCTION, content.write) # Задаем метод, записывающий ответ в переменную
        c.perform()
        c.close()
        content = content.getvalue()
        '''Парсим код'''
        try:
            karma = content[content.index('<karma>')+7:content.index('</karma>')]
 
        except:
            karma = '*'
 
        try:
            rating = content[content.index('<rating>')+8:content.index('</rating>')]
 
        except:
            rating = '*'
 
 
        return karma + '/' + rating
 
    def callback_button (self, widget, event):
 
        self.callback_open(self)
 
    def callback_open (self, event, data=None):
        '''Открываем в браузере по клику'''
        gnome.url_show (
                        'http://' +
                        __USER__.replace('_''-') + # Заменяем в поддомене символы подчеркивания на дефисы
                        '.habrahabr.ru/'
                        )
 
    def callback_about (self, event, data=None):
        '''Показываем стандартное окошко среды GNOME'''
        os.system('gnome-about')
 
    def callback_destroy (self, applet):
        '''Уничтожаем объект'''
        del self.applet
 


Это, собственно, самая интересная часть апплета. Метод-конструктор __init__ () инициализирует объект, метод __init_popupmenu () добавляет в контекстное меню комманды «Открыть» и «О программе» и задает соответствующий им callback-метод. Метод get_info () получает ответ от сервера и обрабатывает его на предмет заветных чисел кармы и силы. Было бы глупо использовать специальный xml-парсер в столь тривиальной задаче, поэтому обойдемся простым быдлокодерским способом.

def applet_factory (applet, iid):
 
    MyApplet(applet, iid)
    return True
 
def main (args):
 
    if len(sys.argv) == 2 and sys.argv[1] == 'run-in-window':
 
        window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        window.set_title('It works!')
        window.connect('destroy', gtk.main_quit)
        applet = gnomeapplet.Applet()
        applet_factory(applet, None)
        applet.reparent(window)
        window.show_all()
        gtk.main()
        sys.exit()
 
    elif len(sys.argv) == 2 and sys.argv[1] == 'help':
        '''Выводим хелп по параметрам'''
        print '''
        --run-in-window - run applet independent of gnome-panel
        --help - show this message'
''
 
    else:
 
        gnomeapplet.bonobo_factory('OAFIID:GNOME_MyApplet_Factory',
                                   MyApplet.__gtype__,
                                   'My Applet',
                                   '1.0',
                                   applet_factory)
 
 
 
if __name__ == '__main__':
    main(sys.argv)
 


Эти функции одинаковы в большинстве апплетов на Python, необходимо только изменить аргументы, передаваемые методу bonobo_factory на свои.
Также есть один полезный момент: при передаче скрипту параметра --run-in-window апплет запускается в отдельном окне, что очень помогает при отладке.

Ну вот, казалось бы, и все. Но нет — апплет написан, но в списке апплетов гнома его нет. Чтобы это исправить, нужно положить файлик (в данном случае можно использовать имя gnomeMyAppletFactory.server) в директорию /usr/lib64/bonobo/servers с примерно таким содержанием:

<oaf_info>
<oaf_server iid='OAFIID:GNOME_MyApplet_Factory' type='exe' location='/home/vlad/applets/src/habrapplet.py'>
    <oaf_attribute name='repo_ids' type='stringv'>
        <item value='IDL:Bonobo/GenericFactory:1.0' />
        <item value='IDL:Bonobo/Unknown:1.0' />
    </oaf_attribute>
    <oaf_attribute name='name' type='string' value='My Applet' />
    <oaf_attribute name='description' type='string' value='Show your karma and ratio' /> 
</oaf_server>
 
<oaf_server iid='OAFIID:GNOME_MyApplet' type='factory' location='OAFIID:GNOME_MyApplet_Factory'>
    <oaf_attribute name='repo_ids' type='stringv'>
        <item value='IDL:GNOME/Vertigo/PanelAppletShell:1.0' />
        <item value='IDL:Bonobo/Control:1.0' />
        <item value='IDL:Bonobo/Unknown:1.0' />
    </oaf_attribute>
    <oaf_attribute name='name' type='string' value='My First Applet' />
    <oaf_attribute name='name-ru' type='string' value='Хабрапплет' />
    <oaf_attribute name='description' type='string' value='Show your karma and ratio' />
    <oaf_attribute name='description-ru' type='string' value='Отображает вашу карму и хабрасилу' />
    <oaf_attribute name='panel:category' type='string' value='Utility' />
    <oaf_attribute name='panel:icon' type='string' value='computer.png' />
</oaf_server>
</oaf_info>


Не забываем поменять путь к скрипту на свой и разрешить его исполнение с помощью chmod +x.

Удачного коддинга, %username%!

p.s. При подготовке материала использовался ресурс highlight.hohli.com.
Tags:
Hubs:
Total votes 37: ↑34 and ↓3+31
Comments24

Articles