Разработка GPS-приложения для Nokia N810: Часть 2. Рассмотрим варианты

Хорошие программы нуждаются в хорошем планировании

В статье подробно рассказывается о проектировании программы, выборе библиотек, модульном тестировании и наиболее подходящих вариантах выбора пользовательского интерфейса.

Пол Ферилл (Paul Ferrill), технический директор, ATAC

Пол Ферилл (Paul Ferrill) пишет для компьютерной прессы на протяжении более 20 лет. Он начинал с обзоров сетевых продуктов для PC Magazine, в том числе таких, как LANtastic и ранние версии Novell Netware. У Пола есть степени бакалавра и магистра технических наук в области электроники. Он писал программы для такого количества компьютерных платформ и архитектур, что уже сам не может вспомнить их все.



11.08.2009

В части 1 этой серии из трех статей была заложена основа для создания приложения, предназначенного для Интернет-планшета Nokia N810, включая выбор языка (Python), выбор и конфигурирование среды разработки (Eclipse с Pluthon) и сборку небольшого приложения-примера для проверки того, что все работает так, как обещано. В этой части рассматриваются различные варианты выбора вспомогательных библиотек, программирование с расчетом на модульное тестирование и выбор пользовательского интерфейса.

Создание приложения с использованием Python несколько похоже на сборку велосипеда в комнате, полной запасных частей. Одни детали можно выбирать совершенно независимо, другие нужно выбирать, отталкиваясь от конкретной конфигурации. Скажем, никто не станет устанавливать 29-дюймовые покрышки, на 26-дюймовые колеса. Выбор библиотек Python, используемых для построения приложения для N810, требует аналогичного подхода.

Процесс разработки любого приложения следует начинать с набора основных требований. Для этого проекта я решил построить инструмент для отслеживания последовательности точек маршрута, полученных от глобальной системы позиционирования (global positioning system, GPS), и для различных вычислений, например, расстояния до предыдущей или следующей точки маршрута. Как и многие "пользовательские" приложения, это приложение "выросло" из семейного отдыха на природе и из мысли, что было бы здорово загрузить с карты ряд пунктов маршрута и увидеть, сколько уже пройдено и сколько еще осталось. Такой инструмент можно использовать как простой GPS-регистратор или походный инструмент.

Проектирование программы

Python — изначально объектно-ориентированный язык, потому что все в Python является объектом. Синтаксис и структура этого языка позволяют создавать небольшие, понятные функции, из которых можно строить приложение. Проектирование приложения начинается с составления списка требований и их разложения на функциональные компоненты, чтобы понять, какую программу нужно написать, чтобы эти требования выполнить.

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

Написание функции для создания пустой базы данных является хорошим примером небольшой программы, состоящей из одной функции. Для нашего проекта этот код будет выглядеть так, как показано в листинге 1.

Листинг 1. Подпрограмма на Python для создания пустой базы данных
def create_db():
	"""Создает пустую базу данных."""
    
	query('''CREATE TABLE 'places' (
	id INTEGER PRIMARY KEY AUTOINCREMENT,
	lat INTEGER,
	lon INTEGER,
	comment TEXT
);''')

В листинге можно видеть встроенное SQL-предложение CREATE TABLE, которое создает таблицу с именем places и четыре поля id, lat, lon и comment. Подобный код легко читать и тестировать.

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

Еще одно проектное решение, которое необходимо принять сразу, — выбор того, как будет создаваться интерфейс пользователя. Распространенным подходом в PyGTK-приложениях является построение интерфейса пользователя из программы: фактически вся функциональность интерфейса пользователя создается в программе на Python. Например, программа в листинге 2 создает список пунктов меню, имеющихся в типичном приложении, затем полностью из программы создаются оставшиеся элементы интерфейса.

Листинг 2. Подпрограмма на Python для построения простого интерфейса пользователя
def init_ui(id='main'):
    """Построение пользовательского интерфейса."""
 
    global APP
    APP = ui.App('GPS Logger', id=id,
                 
                 menu=(
                       ui.Menu.Item(label='New', callback=on_new),
                       ui.Menu.Item(label='Open', callback=on_open),
                       ui.Menu.Item(label='Save', callback=on_save),
                       ui.Menu.Item(label='Save As', callback=on_save_as),
                       ),
                 
                 top=(
                      ui.Group('dev_stat', horizontal=True, border=None, 
                               children=(ui.Label(label='Status:'),
                                         ui.Label('status', 'Ready'),
                                         )
                               ),
                      ui.Group('controls', horizontal=True, border=None,
                               children=(ui.Button('start_btn',
                                                   label='Start',
                                                   callback=on_start),
                                         ui.Button('stop_btn',
                                                   label='Stop',
                                                   callback=on_stop))
                               ),
                      ),
                 
                 center=ui.Table('log', 'GPS Log',
                                 headers=('Time', 'Latitude',
                                          'Longitude',
                                          'Altitude (meters)'),
                                 types=(str, str, str, str)
                                 )
                 )

Выбор библиотек

Первым же поиском в Google можно найти множество библиотек для Python. На самом деле надо определять критерии поиска более конкретно, чтобы не получить слишком много результатов. Для этого проекта мне потребовалось несколько очень специфических библиотек, чтобы немного облегчить написание программы. В первую очередь нужна была библиотека, которая могла бы взаимодействовать с аппаратными средствами GPS в N810. Далее понадобилась библиотека, которая позволяла бы делать расчеты расстояния по координатам GPS, и, наконец, библиотека для интерфейса пользователя, которая давала бы пользователям возможность вводить и отображать информацию.

Многие библиотеки для Python являются просто обертками программ, первоначально написанных на другом языке, например, на C. Одна такая библиотека, которую я нашел для этого проекта, является оберткой для API liblocation из Maemo версии 4.0 (дополнительную информацию см. в Ресурсах). Первоначально я нашел упоминание об этой библиотеке на форуме по интернет-планшетам, где автор выложил пример программы под открытой лицензией GNU Lesser General Public License (LGPL) версии 3. Ссылки на эту программу и дополнительная информация есть в Ресурсах.

Базовый дистрибутив Python содержит колоссальную библиотеку программ и функций, которые только и ждут, чтобы их использовали. Кроме того, некоторые библиотеки, которые раньше распространялись отдельно, теперь включены в основной проект Python. Одной из них является SQLite-библиотека для доступа к базам данных SQLite. Эта библиотека по сути снимает сложности в использовании любых функций для работы с базой данных, которые только могут потребоваться в программе. Последний выпуск Python (версия 2.6.1) комплектуется SQLite3, и именно ее я использовал для этого проекта.

Для расчета расстояний между двумя GPS-координатами я нашел geopy — библиотеку, которая разрабатывается в течение нескольких лет и к которой имеется огромное число примеров (смотрите ссылку в Ресурсах). У нее также есть интерфейсы для ряда средств геокодирования, в том числе Google, Yahoo! и GeoNames, что может быть полезным для преобразования адреса в пару широта-долгота, но для получения данных требуется работающее подключение к Интернету. Для нашего приложения эта библиотека предоставляет эффективный инструмент для расчета расстояний различными методами.


Тестируем, тестируем

Процесс создания готовых приложений требует довольно объёмного тестирования. Один из наиболее популярных подходов к этой проблеме среди разработчиков программ использует концепцию модульного тестирования. При написании программы предполагается, что каждая функция будет контролируемо проверяться как отдельный модуль. Функциям, ожидающим входные данные, предоставляются значения, которые дадут известные результаты.

Библиотека PyUnit, входящая в состав последних дистрибутивов Python, предоставляет структурированный метод построения модульных тестов. PyUnit основывается на технологии Java™ JUnit и использует примерно такой же подход (см. ссылки на информацию по JUnit в Ресурсах). Для тестирования фрагмента кода программы, который осуществляет доступ к аппаратным средствам, необходимо написать программу, которая реагирует аналогично аппаратным средствам. На языке модульного тестирования это называется фиктивным объектом (дополнительно смотрите в Ресурсах). Для этого проекта я создаю два фиктивных объекта для имитации соответственно GPS-устройства и интерфейса пользователя. В листинге 3 приводится текст программы фиктивного устройства.

Листинг 3. Программа для модульного тестирования фиктивного устройства
class MockDevice:
    """Частично имитирует аргумент gps, передаваемый в вызове on_changed."""
 
    @staticmethod
    def struct():
        class value:
            class fix:
                latitude = 0.0
                longitude = 0.0
                altitude = 0
        
        return value

В листинге 4 показан простой пример теста для программы GPS.

Листинг 4. Испытание GPS
class TestGPS(unittest.TestCase):
    def setUp(self):
        unittest.TestCase.setUp(self)
        logger.init_gps()
        logger.ui.get_app_by_id = lambda id: MockUI()
        
    def test_changed(self):
        logger.on_changed(MockDevice)

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


Выбор интерфейса пользователя

Наиболее очевидным выбором интерфейса пользователя для N810 был бы Hildon, поскольку он изначально был разработан компанией Nokia для операционной системы Maemo. В дальнейшем он вошел в состав проекта GNOME, а также был выбран для Ubuntu Mobile and Embedded Edition. Hildon устанавливается по умолчанию на N810 и позволяет создавать интерфейс пользователя, аналогичный интерфейсу уже существующих приложений.

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

Одна из наиболее визуально привлекательных библиотек для пользовательского интерфейса происходит из проекта Enlightenment (см. Ресурсы). Сам по себе Enlightenment, или просто e, является оконным менеджером и средой рабочего стола для операционной системы Linux®. Проект предоставляет еще и ряд базовых строительных блоков для создания красивых приложений в специальной версии для Maemo (смотрите ссылки в Ресурсах). Ряд существующих приложений для Maemo создан на базе библиотек Enlightenment Foundation Libraries (EFL), включая Carman—приложение для сопряжения N810 с автомобилем, использующим совместимый с OBD2 протокол, для отображения диагностических кодов и характеристик работы двигателя — и мультимедийное приложение Canola. Оба эти приложения демонстрируют многие новейшие функции пользовательского интерфейса, специально ориентированные на устройства с небольшим экраном.

Отрицательной стороной использования EFL является объем усилий, необходимых для создания даже самого простого приложения. Даже при наличии обертки Python-EFL все равно приходится создавать довольно много кода и картинок для того, чтобы программа заработала. Основные концепции напоминают другие среды разработки, в которых уровень представления отделен от программы. Желаемая компоновка пользовательского интерфейса делается фактически с помощью графического редактора, например, GNU Image Manipulation Program (GIMP). Затем необходимо создать файл в формате Edje Data Collections (EDC) с описанием интерфейса. Для целей нашей программы эта работа была бы излишней, но в разделе Ресурсы есть ссылки на дополнительную информацию.

На сайте с проектами для Maemo (см. Ресурсы) я нашел еще один вариант — библиотеку easy. В этой библиотеке имеется ряд вспомогательных функций, облегчающих доступ к низкоуровневым API. Одной из особенностей этой библиотеки является среда для быстрой разработки графического интерфейса пользователя на основе PyGTK. Часть, относящаяся к пользовательскому интерфейсу, основана на Eagle, слое абстракции поверх GTK+. Он предоставляет все необходимое для построения базового приложения.

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


Заключительные замечания

Создание приложения с использованием правильных методов разработки программного обеспечения — это целый процесс. Он включает в себя много шагов, которые обычно следует выполнять строго по порядку. Иногда мы бываем вынуждены отступить назад на несколько шагов, если какое-то конкретное проектное решение оказывается неудачным. Самое лучшее время для отыскания проблем такого типа — начальная стадия процесса проектирования и разработки программного обеспечения. Гораздо легче исправить ошибку проектирования в начале работы, чем в конце.

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

Ресурсы

Научиться

Получить продукты и технологии

  • На странице проектов Роба Брюера (Rob Brewer) можно найти текст написанной им программы-обертки для liblocation и другие приложения для Maemo. (EN)
  • Ознакомьтесь с geopy, библиотекой геокодирования для языка Python. (EN)
  • Фонд Enlightenment Foundation выпускает специальный вариант библиотек для операционной системы Maemo, называемый EFL Maemo Edition. (EN)
  • Используйте в вашем следующем проекте разработки для Linux ознакомительные версии ПО IBM, которые можно скачать непосредственно с developerWorks.(EN)

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Мобильные приложения
ArticleID=420043
ArticleTitle=Разработка GPS-приложения для Nokia N810: Часть 2. Рассмотрим варианты
publish-date=08112009