LXF122:OOo2

Материал из Linuxformat.

Перейти к: навигация, поиск
EOEC Простой способ создавать расширения OpenOffice.org на Python

Содержание

OOo: Создадим конвертор валют

Полагаете, что разработка расширений для OpenOffice.org – непростая задача? Так оно и есть, но Сергей Бачурин знает, как сделать ее чуточку легче.

Возможности офисного пакета OpenOffice.org легко расширить, написав макрос, реализующий недостающий функционал. Это можно сделать, вооружившись API OpenOffice.org и выбрав язык программирования: OOBasic (встроенный в OOo), Java, JavaScript или Python. В случае, если написанный код будет использоваться на одной машине, можно добавить пункт меню или панель инструментов вручную. Если же необходимо установить макрос на нескольких компьютерах, лучшим способом его распространения будет упаковка в расширение. В этом случае нужны инструменты для удобного создания расширений. Для OOBasic это, например, BasicAddonBuilder (подробности ищите по адресу http://myooo.ru/content/view/137/32/). Для Java можно использовать Eclipse или NetBeans – дополнения к ним для работы с OOo уже давно и успешно используются. Расширения на Python ранее нужно было собирать вручную, выискивая документацию по разным сайтам. Сегодня нам на помощь приходит молодой продукт – EuroOffice Extension Creator (EOEC). Он прост в использовании, хорошо документирован, содержит несколько интересных примеров в виде готовых расширений и распространяется под свободной лицензией.

Для ознакомления с EOEC создадим расширение «Конвертор валют», загружающее с сайта Центробанка РФ курсы валют на текущую дату и переводящее суммы из одной валюты в другую. Также расширение будет пытаться брать сумму из открытого документа и вставлять результат обратно в документ.

EuroOffice Extension Creator

Это конструктор расширений на Python, распространяемый под лицензией GNU GPl. Продукт был представлен в ноябре 2008 года на OpenOffice.org Conference (OOoCon 2008).

Так как расширения, создаваемые при помощи EOEC, включают его исходный код, они также должны быть лицензированы по GPL v3. Для создания коммерческих продуктов на сайте азработчика предлагается коммерческая версия.

Разработчик EuroOffice Extension Creator– венгерская компания MultiRacio Ltd (http://www.multiracio.com). Фирма выпускает собственную сборку OpenOffice.orgEuroOffice и множество расширений: клипарты, словари, пакет Solver для Calc и много других интересных решений, которые можно найти на сайте http://extensions.services.openoffice.org. Продукты доступны под GNU/GPL или коммерческой лицензией.

Подготовка к работе

Чтобы разработать расширение с помощью EuroOffice Extension Creator, необходимы: хорошая ОС, свежий OpenOffice.org, интерпретатор языка Python и архив с EOEC. Также крайне желательна удобная IDE для Python.

Первому требованию удовлетворяет практически любой современный дистрибутив Linux (в моем случае – Debian Lenny). Последний официальный русскоязычный выпуск OpenOffice.org доступен на сайте http://ru.openoffice.org; при написании статьи использовалась «ванильная» версия 3.1. Интерпретатор Python можно взять системный или же тот, что идет в комплекте с OOo. В OOo 3.1 это Python 2.6.1, в более ранних версиях – Python 2.3. В случае, если Python отсутствует в вашей системе, для более удобного использования сборки из состава OpenOffice.org можно создать символьную ссылку в каталоге /usr/bin (целевой файл – путь_к_ooo/program/python). Если у вас установлен OOo без Python, можно попробовать настроить его на использование внешнего интерпретатора (см. http://udk.openoffice.org/python/pythonbridge.html#replacing). В качестве IDE я буду использовать Geany.

Идем на страницу EOEC: https://launchpad.net/eoec и скачиваем последнюю версию (на момент написания статьи – 0.3, ее же можно найти на LXFDVD). Распаковав полученный архив, вы увидите каталог с тремя папками, тремя скриптами и PDF-файлом с документацией. Версия 0.3 заметно увеличилась по сравнению с предыдущими – в архиве теперь содержится шесть расширений, созданных с помощью EOEC. Находятся они в каталоге examples. Настоятельно рекомендую ознакомиться с их исходным кодом после изучения документации. В расширениях Lookup и Sharpen использованы функции встраивания в контекстное меню OOo и назначения комбинаций клавиш.

Новое расширение

Рис. 1. CurConverter в списке установленных расширений

Для создания собственного расширения необходимо открыть терминал и запустить скрипт create.py, находящийся в каталоге EOEC, с аргументами:

python create.py --vendor=VENDOR project-name

Здесь project-name – название нашего нового расширения, VENDOR – имя его разработчика. Справку по использованию скриптов create.py, pack.py и update.py можно получить, запустив их из терминала с ключом --help.

Выполним команду:

python create.py --vendor=OOoCoder CurConverter

В результате скрипт create.py создаст каталог нового расширения CurConverter на основе директории template с использованием введенных вами данных.

Теперь нужно создать версию расширения для разработки. Для этого нам потребуется скрипт pack.py с ключом -D. Синтаксис таков:

python pack.py -D project-name

В нашем случае это значит:

python pack.py -D CurConverter

В результате получим версию расширения CurConverter_Debug.oxt «для разработчика». Она не предназначена для распространения конечным пользователям! Скопированное на другой компьютер, такое расширение не будет работать.

Давайте добавим CurConverter к OOo через меню Сервис > Управление расширениями. Он появится в списке установленных расширений (рис. 1).


Рис. 2. Окно отладчика CurConverter.

Иконку расширения можно изменить на свою, переписав соответствующие графические файлы в каталоге расширения. Текст описания изменяется в файле description.txt.

Перезапустите OpenOffice.org: в третьей версии OOo это необходимо делать после установки любого расширения. Войдите в меню Справка > About CurConverter > Debug и посмотрите на поле под меткой Debug output. Если вы увидите такой же текст, как на рис. 2, значит, в вашей системе сообщения отладчика идут в стандартный вывод (в противном случае их следует искать в окне Debug, которое вы сейчас видите). Оно и к лучшему – терминал (в отличие от диалога Debug) всегда у вас перед глазами. Закройте OOo и запустите его из терминала – теперь вы будете видеть в нем все отладочные сообщения.

Третий скрипт, update.py, предназначен для обновления ранее созданного расширения при выходе новой версии EOEC. Предварительно прочтите документацию – разработчики предупреждают о возможной потере данных. Старая, как мир, истина: не забывайте регулярно делать резервные копии каталога расширения!

Пункт в меню

Прежде чем приступать к написанию собственного расширения, необходимо ознакомиться с главами 3 и 5 документации EOEC для понимания файловой структуры расширения и методов EOEC. Желательно также просмотреть примеры расширений, идущих в составе EOEC, и документацию по API OpenOffice.org (http://api.openoffice.org). Скачать SDK OpenOffice.org можно со страницы http://api.openoffice.org/SDK/index.html.

Для начала обеспечим интернационализацию расширения. Откройте Сервис > Макросы > Управление диалогами, перейдите на вкладку Библиотеки, выберите OOoCoderCurConverterDialogs и нажмите на кнопку Изменить (рис. 3).

Рис. 3. Диалог управления библиотеками OOo.

В открытом окне на панели нажмите кнопку Управление языками > Добавить > Русский > Закрыть (при необходимости сделайте языковую панель видимой через меню Вид). Сохраните изменения.

Для более удобного перевода элементов управления и пунктов меню возьмем расширение Extension Translator, доступное по адресу http://extensions.services.openoffice.org/project/extension-translator. Установите его, опять перезапустите OOo и вызовите Сервис > Extension Translator. Выберите в списке CurConverter, и в Calc откроются имеющиеся локализации – en и ru. Здесь будут перечислены все элементы форм и названия пунктов меню, используемые нашим расширением. Переведите все, что нужно, на родной язык. Придумайте название для пункта меню, который будет запускать наше расширение. Пусть поле с названием пункта меню (первый столбец) имеет имя title. По-английски им будет «CurConverter», а по-русски – «Конвертор валют». Нажмите Сервис > Save Localizations и закройте файл с локализациями, не сохраняя его. Перейдите в меню Справка > About CurConverter > Debug > Save Dialogs. Изменения в диалогах будут скопированы из профиля OOo (каталога с пользовательскими настройками, шаблонами и скриптами – ~/.openoffice.org) в рабочую директорию расширения. Эту кнопку необходимо использовать каждый раз после изменений диалогов. Чтобы в русскоязычном диалоге, вызываемом при выборе пункта меню Справка > About CurConverter, отображался логотип CurConverter, создадим файл logo_ru.gif в каталоге расширения. Для каждого языка можно создать отдельный логотип, сохранив его в файле с именем logo_<двухбуквенный_код_языка>.gif.

Здесь и далее мы будем работать с модулем CurConverter/curconverter/curconverter.py. Добавим поддержку русского языка –

#-*- coding:utf-8 -*-

и в строке

SUPPORTED_LANGUAGES = (‘en’,’ru’)

определим расширению место для запуска: поместим его в меню Сервис за пунктом Рассылка писем. Для этого нам нужно узнать идентификатор последнего пункта. Вызовите диалог Справка > About CurConverter и нажмите кнопку Debug. В открывшемся окне введите следующий текст:

self.dumpMenus('com.sun.star.text.TextDocument')

и посмотрите вывод в терминале. Необходимые нам имена пунктов меню находятся после «(u'- CommandURL:». При минимальном знании английского языка найти необходимую позицию не составит большого труда: в нашем случае это '.uno:MailMergeWizard'. Введите в окне отладки

self.addMenuItem( 'com.sun.star.text.TextDocument’, ‘.uno:MailMergeWizard’, self.localize( ‘title’ ), ‘curconverter’ )

и нажмите кнопку Execute code. Эту же строку надо добавить в метод firstrun класса CurConverter.

Несмотря на то, что при добавлении расширений в OpenOffice.org все файлы устанавливаются в профиль OOo в домашнем каталоге пользователя (для версии 3.x это ~/.openoffice.org/3/user/uno_packages/cache/uno_packages), мы будем вносить изменения в файл curconverter.py, расположенный в директории EOEC. Иными словами, при запуске расширения из OpenOffice.org будет выполняться код, расположенный в каталоге EOEC.

Для удаления пункта меню в метод uninstall после try: добавляем строку

self.removeMenuItem( 'com.sun.star.text.TextDocument', 'curconverter' )

С установкой/удалением пунктов меню мы определились; самое время «оживить» их. В коде метода firstrun мы указали, что при выборе пункта меню будет вызван метод curconverter. Создадим его:

def curconverter( self ):
   self.box(u'Работает!')

Сохраните код и попробуйте выбрать пункт меню Сервис > Конвертор валют. Как можно видеть, все действительно работает.

Добавим интерфейс

Рис. 4. Конструктор диалогов OpenOffice.org в действии.

При написании расширений для OOo с помощью EOEC не нужно подключать сторонние графические библиотеки – сгодятся «родные» интерфейсные элементы OpenOffice.org. EOEC по умолчанию создает два диалога – окна About и Debug.

Выберите в меню Сервис > Макросы > Управление диалогами, выделите OOoCoderCurConverterDialogs и нажмите кнопку Новый диалог. Придумайте имя – скажем, Main, и нажмите Правка. Откроется новый пустой диалог. Добавим в него следующие виджеты:

  • Два элемента «Числовое поле» ValuteNum1 и ValuteNum2 – для отображения сумм.
  • Два элемента «Список» ValuteName1 и ValuteName2 – для выбора валют.
  • Свойство «Раскрываемый» установим в «Да».
  • Флажок chkInsert с надписью «Вставить результат в текст документа».
  • Кнопку btnCalculate с надписью «Рассчитать».
  • Кнопку btnClear с надписью «Очистить».
  • Кнопку btnClose с надписью «Закрыть», тип кнопки – «Отмена»

Примерный вид диалога, который должен получиться, показан на рис. 4.

Через свойства элементов можно выставить их размеры, положение и т. д.; затем нажмите кнопку Сохранить. Вид и текст диалога About CurConverter изменяется здесь же, во вкладке About. Теперь необходимо перейти из оболочки OOBasic в другой элемент ООo, например, Writer (по умолчанию расширение встраивается в меню Справка всех компонентов, кроме среды программирования). Выберите Справка > About CurConverter > Debug > Save Dialogs.

Время кодировать

Создайте конфигурационный файл settings, в котором будет храниться URL сервиса, предоставляющего курсы валют. Пусть его содержимое будет таким:

[Global]
URL = http://www.cbr.ru/scripts/XML_daily.asp

Скопируйте файл настроек в каталог расширения. Внимание! В случае, если в расширение нужно добавить какие-либо файлы, например, изображения, настройки и т. д., необходимо делать это одновременно в два места: в рабочий каталог расширения и его папку в профиле. Все критические изменения в них вносятся синхронно, так как в профиле OOo лежат файлы, которые запускаются при отладке нашего dev-расширения, а в рабочем каталоге – файлы, которые будут упакованы для отправки пользователям.

Откройте curconverter.py (в рабочем каталоге) и добавьте импорт модулей:

from xml.dom import minidom
 from urlparse import urlparse
 import urllib
 import os, ConfigParser

Видоизмените метод curconverter':

def curconverter( self ):
       self.dlgMain = dlgMain = self.createdialog( ‘Main’ )
       dlgMain.execute()

Сохраните файл и запустите расширение через меню. Должен появиться созданный нами диалог. Кнопки еще не работают, но вскоре мы это исправим.

В каталоге util EOEC содержатся несколько модулей с некоторыми полезными функциями. В writer.py это функция getWord(). Воспользуемся ею для считывания суммы из документа.

import util.writer
 ...
 self.summa=util.writer.getWord(self.getcontroller()).String
 self.cursor=self.getcontroller().getViewCursor()
     Заполним поле ValuteNum1:
 try:
     eval(self.summa)
     self.dlgMain.ValuteNum1.Value=self.summa
 except:
     self.dlgMain.ValuteNum1.Value=100.00

Как видно, обращение к элементам управления диалога происходит как к объектам, являющимся атрибутами self.dlgMain, по именам.

Далее обработаем конфигурационный файл, считав из него URL:

self.config = ConfigParser.ConfigParser()
 self.config.read(os.path.join(self.path, 'settings'))
 if not(self.config.has_option('Global','URL')):
        self.config.set('Global','URL', 'http://www.cbr.ru/scripts/XML_daily.asp')
        self.BANK_URL = self.config.get('Global','URL')

self.path – свойство, содержащее путь к установленному расширению. Вызываем метод получения курса валют

self.getKurs()

который определятся следующим образом:

def getKurs(self):
     dom=minidom.parse(urllib.urlopen(self.BANK_URL))
     self.kurses=[]
     for node in dom.getElementsByTagName('Valute'):
         self.kurses.append({'CharCode':node.getElementsByTagName('CharCode')[0].childNodes[0].nodeValue,
                     'Nominal': node.getElementsByTagName('Nominal')[0].childNodes[0].nodeValue, 
                     'Name': node.getElementsByTagName('Name')[0].childNodes[0].nodeValue,
                     'Value': node.getElementsByTagName('Value')[0].childNodes[0].nodeValue})
         self.kurses.append({'CharCode': 'RUR',
                     'Nominal': '1',
                     'Name': u'Российских рублей',
                     'Value': '1'})

В итоге получаем курсы валют в виде списка словарей и список наименований валют для подстановки в выпадающие меню ValuteName1 и ValuteName2.

Добавляем «слушателей» нажатий на кнопки:

self.dlgMain.btnCalculate.addActionListener( self )
 self.dlgMain.btnClear.addActionListener( self )

Осталось создать обработчики нажатий – методы, вызываемые при активации соответствующих элементов управления. Именуются они следующим образом: on_action_<имя_элемента>.

Кнопка btnClear сбрасывает значения элементов управления на начальные:

def on_action_btnClear(self):
    self.dlgMain.ValuteNum1.Value=100.00
    self.dlgMain.ValuteNum2.Value=0
    self.dlgMain.ValuteName1.selectItemPos(4,True)
    self.dlgMain.ValuteName2.selectItemPos(self.dlgMain.ValuteName2.ItemCount-1,True)

Кнопка btnCalculate вычисляет курс первой валюты относительно другой, подсчитывает сумму в другой валюте и, в зависимости от состояния флажка, вставляет результат в документ.

def on_action_btnCalculate(self):
    if (self.dlgMain.ValuteName1.getSelectedItemPos()>-1) and 
    (self.dlgMain.ValuteName2.getSelectedItemPos()>-1):
                  valuteID1=self.dlgMain.ValuteName1.getSelectedItemPos()
                  valuteID2=self.dlgMain.ValuteName2.getSelectedItemPos()
                  self.kursnode1=self.kurses[valuteID1]
                  self.kursnode2=self.kurses[valuteID2]
                  v1=eval(self.kursnode1['Value'].replace(',','.'))/eval(self.kursnode1['Nominal'])
                  v2=eval(self.kursnode2['Value'].replace(',','.'))/eval(self.kursnode2['Nominal'])
                  sum1=self.dlgMain.ValuteNum1.Value
                  val1to2=v1/v2
                  self.dlgMain.ValuteNum2.Value=sum1*val1to2
                  if (self.dlgMain.chkInsert.State):
                       if self.cursor.isCollapsed():
                                self.cursor.String=round(self.dlgMain.ValuteNum2.Value,2)
                       else:
                                self.cursor.collapseToEnd()
                                self.cursor.String=round(self.dlgMain.ValuteNum2.Value,2)

Для кнопки Закрыть обработчик не нужен.

Наконец, добавим метод заполнения списков наименований валют и добьемся, чтобы пункты «Доллар США» и «Российских рублей» были выбраны по умолчанию:

self.fillList()
 self.dlgMain.ValuteName1.selectItemPos(4,True)
 self.dlgMain.ValuteName2.selectItemPos(self.dlgMain.ValuteName2.ItemCount-1,True)

Метод fillList выглядит так:

Скорая помощь

В случае, если вам нужно просмотреть содержимое какого-либо объекта во время выполнения программы, можно воспользоваться встроенной функцией debug (<Expression>). Для вывода сообщений во всплывающих окнах служит метод self.box(“Сообщение”).

def fillList(self):
     for i in range(0, len(self.kurses)):
         self.dlgMain.ValuteName1.addItem(self.kurses[i][‘Name’],i)
         self.dlgMain.ValuteName2.addItem(self.kurses[i][‘Name’],i)

В итоге у нас должен получиться такой метод curconverter:

def curconverter( self ):
     self.summa=util.writer.getWord(self.getcontroller()).String
     self.cursor=self.getcontroller().getViewCursor()
     self.dlgMain = dlgMain = self.createdialog( 'Main' )
     self.dlgMain.btnCalculate.addActionListener( self )
     self.dlgMain.btnClear.addActionListener( self )
     try:
         eval(self.summa)
         self.dlgMain.ValuteNum1.Value=self.summa
     except:
         self.dlgMain.ValuteNum1.Value=100.00
         self.config = ConfigParser.ConfigParser()
         self.config.read(os.path.join(self.path, 'settings'))
         if not(self.config.has_option('Global','URL')):
                self.config.set('Global','URL', 'http://www.cbr.ru/scripts/XML_daily.asp')
                self.BANK_URL = self.config.get('Global','URL')
                self.getKurs()
                self.fillList()
                self.dlgMain.ValuteName1.selectItemPos(4,True)
                self.dlgMain.ValuteName2.selectItemPos(self.dlgMain.ValuteName2.ItemCount-1,True)
                dlgMain.execute()

Результаты наших трудов можно видеть на рис. 5.

Рис. 5. Расширение CurConverter готово к работе.

Финальный штрих

Наше расширение готово. На всякий случай еще раз сохраним диалоги через окно О расширении. Затем войдите в каталог EOEC и соберите готовое расширение.

python pack.py CurConverter

Чтобы изменить номер версии расширения, необходимо отредактировать файл description.xml. В строке

<version value=”1.0”></version>

вместо 1.0 нужно установить требуемый номер. Формат файла description.xml подробно описан в руководстве разработчика: http://api.openoffice.org/docs/DevelopersGuide/Extensions/Extensions.xhtml#1_5_description.xml

Появившееся в каталоге расширение CurConverter.oxt версии 1.0 готово для распространения. Разместить его для общего доступа можно на сайте расширений для OpenOffice.org: http://extensions.services.openoffice.org. Инструкцию о том, как правильно это сделать, можно найти по следующему адресу: http://wiki.services.openoffice.org/wiki/Extensions/website/submission LXF

Личные инструменты
  • Купить электронную версию
  • Подписаться на бумажную версию