Журнал LinuxFormat - перейти на главную

LXF129:Clutter

Материал из Linuxformat
Перейти к: навигация, поиск
Python Настоящие задачи, чтобы вы поупражнялись в кодировании

Содержание

Python: Clutter­-анимация

Python
Python для профессионалов
Python + PyGame
Python + Web
Python + Clutter
Злой гений от программирования Ник Вейч вскружил себе голову новостными лентами и хитроумной анимацией Clutter.

На прошлом уроке мы освоили основы Clutter, применив его для создания монитора скорости сетевого соединения. Сегодня мы рассмотрим ряд мощных технологий анимации, применяемых в Clutter, группировку объектов, и немного – текстовых актеров. Делать это мы будем под видом написания читалки лент новостей. Здесь не хватит места для реализации полноценной многопотоковой читалки и изучения анимации, но мы опишем достаточно, чтобы создать почву для сотворения подобных зверюг, включая извлечение данных из ленты и размещение их в объектах Clutter.

Для тех, кто еще не видел ни одного из наших грандиозных уроков по Python: обычно мы пытаемся поначалу выполнять как можно больше в интерактивном режиме. Это окружение добрее и мягче, чем обычный способ запуска программ, поскольку можно вводить команды и экспериментировать. В этом случае в листингах Python в начале строки появляется приглашение >>>, если что-то следует ввести, а без него – это вывод отклика системы на экран.

Как стать лентяем

LXF129 72 1.jpg Говорят, отсутствие новостей — тоже новость, причем хорошая; и мы знаем, из какого уважаемого в мире агентства она пришла

Первым делом подумаем, как получить из ленты данные. Раньше мы уже работали с прекрасной Python-библиотекой Feedparser; воспользуемся ею вновь, чтобы добыть для нашего приложения сенсационные сведения.

Однако предварительно надо еще найти URL ленты. Выбирайте любую. Лучший способ определить адрес конкретной ленты – это открыть подходящую страницу в web-браузере и найти иконку RSS. Чаще всего это ссылка на адрес ленты, так что просто скопируйте ее из браузера или прочитайте в строке состояния. Например, можно воспользоваться лентой TuxRadar на www.tuxradar.com/rss. В нашем примере мы воспользуемся лентой новостей BBC, по двум причинам. Во-первых, она предоставляет ссылки на изображения – это пригодится для наших экспериментов с текстурами Clutter, а во-вторых, весьма быстро обновляется, что удобно при тестировании.

Лента новостей BBC находится на сайте http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/world/rss.xml.

 >>> import feedparser
 >>>f= feedparser.parse(‘http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/world/rss.xml)
 >>>f
 {‘feed’: {‘lastbuilddate’: u’Wed, 30 Dec 2010 19:11:25 GMT’,
 ‘subtitle’: u’Get the latest BBC World News: international news,
 features and analysis from Africa, Americas, South Asia, Asia-
 Pacific, Europe and the Middle East.’, ‘language ...

По сути, заставив Python отобразить переменную, которую мы связали с лентой, мы получили на экране просто все содержимое контейнера (здесь приведен его фрагмент). Чтобы вычленить то, что нам интересно, понадобятся ключи. Все записи ленты хранятся в большом списке, известном как ‘entries’: здесь содержатся такие сведения, как заголовок, краткое описание, время, URL и так далее.

 
 >>> f.entries[0].title
 u’Nokia expands claim against Apple’
 >>> f.entries[0].link
 u’http://news.bbc.co.uk/1/hi/technology/8434132.stm>>> f.entries[0].updated_parsed
 time.struct_time(tm_year=2009, tm_mon=12, tm_mday=29, tm_
 hour=18, tm_min=0, tm_sec=46,
 tm_wday=2, tm_yday=364, tm_isdst=0)
 >>> f.entries[0].updated
 u’Wed, 30 Dec 2009 18:00:46 +0000’
 >>> f.entries[0].summary
 u’Nokia ramps up its legal fight against Apple, claiming that almost
 all of its products infringe its
 patents.’

Как видите, отсюда можно извлечь практически все, что нужно для создания обработчика подобных записей. Один из необязательных элементов, который также может нам пригодиться – это изображение. Спецификации позволяют задать картинку в качестве визуального «идентификатора» канала, передаваемого в виде URL изображения и некоторого текстового описания для него. Он находится в теле ленты, в элементе под именем image, и мы можем сослаться на него (в нашем случае) так: bbc.feed.image.href.

Способов применения данного изображения полно; простейший – взять и загрузить его, а там уж делать с ним все, что угодно. Подходящий путь для загрузки – воспользоваться отличным модулем Python urllib. Среди его изощренных инструментов есть метод urlretrieve, он скачивает содержимое URL и сохраняет его во временном каталоге системы (обычно /tmp), а потом возвращает имя файла и набор HTTP-информации. Сохраненный файл будет уничтожен при очистке временного хранилища; но для порядка лучше удалить его при выходе. Взгляните:

 >>> import urllib
 >>> img, data =urllib.urlretrieve(f.feed.image.href)
 >>> img
 ‘/tmp/tmpTsCyDc.gif

Буквально сейчас мы сделаем с этим файлом кое-что полезное.

RSS и другие ленты

В стандартной RSS-ленте множество элементов. Кроме изображения, мы будем брать только те, присутствие которых гарантировано в любой встреченной вами ленте. Если вы хотите разузнать о том, что бывает в лентах, просмотрите различные стандарты документов. Для вящей путаницы, существует несколько версий RSS, разработанных в разное время разными группами с весьма непохожими идеями о том, как все должно быть. Использование модуля Feedparser сглаживает многие углы.

На сайте Гарварда имеется очень толковый учебник по созданию RSS-ленты, который, на наше счастье, содержит неплохое руководство также и по извлечению информации. http://cyber.law.harvard.edu/rss/rss.html.

Захламление

После небольшого экскурса в мир новостных лент, вернемся к серьезным делам в Clutter. Итак, на прошлом уроке мы описали основные элементы Clutter: сцену, актеров и шкалу времени. Тем, кто пропустил урок, очевидно, придется найти предыдущий номер. (Кстати, вы ведь не захотите пропускать и другие номера – почему бы не подписаться? Подписчики получают доступ к электронной версии журнала в формате PDF раньше всех. Подумайте.) В любом случае, вот краткое резюме для тех, кто был «не в теме». Окно в Clutter называется сценой. В нем происходят все действия, и по умолчанию в PyClutter оно реализуется в виде стандартного окна GTK. Элементы, появляющиеся на (в) сцене, называются актерами и могут представлять собой все, от текста до простых фигур или растровых текстур.

На сей раз нас интересует именно последнее. Мы еще вернемся к текстовым актерам, а пока отметим, что Clutter позволяет импортировать изображения прямо из файлов. Те из вас, у кого память не девичья, несомненно, знают, что такой файл у нас имеется. Что бы просто доказать, что это работает, настроим сцену и весь прочий Clutter, а затем импортируем нашу новою текстуру.

 >>> import clutter
 >>> black=clutter.color(0,0,0,255)
 >>> white=clutter.color(255,255,255,255)
 >>> stage= clutter.Stage()
 >>> stage.set_size(400,60)
 >>> stage.set_color(white)
 >>> stage.show_all()
 >>> ident =clutter.texture_new_from_file(img)
 >>> stage.add(ident)

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

Интуиция – это прекрасно, вот почему изображение столь идеально вписалось в сцену. Поскольку мы не указали его позицию, оно просто вывелось с точки (0,0), то есть с верхнего левого угла сцены

Анимируем

Прежде чем завершить нашу суперскую читалку новостей, будет полезно немного прерваться и поработать с анимацией. В прошлый раз мы анимировали некоторые текстовые объекты при помощи шкалы времени – мощной составляющей магии Clutter, предоставляющей нам простые прерывания, применимые для анимации любых элементов. Но в версии Clutter 1.0 появился еще более мощный способ анимации объектов. Модуль Clutter теперь предоставляет новые методы для анимации актеров, и с важнейшим из них мы и поэкспериментируем.

Актер Clutter имеет метод animate. Принимаемые им аргументы – режим анимации и длительность (в миллисекундах), а также свойства и значения, подлежащие анимации. Рассмотрим их подробнее. Режим анимации можно определить самим, но в Clutter уже встроено несколько типов, на которые можно сослаться через модуль Clutter. Он действует как механизм построения промежуточных фаз для всех значений, указанных в вашем списке.

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

Хотите добыть полный список все встроенных кодов анимации Clutter? Обратитесь к более актуальной документации C: http://clutter-project.org/docs/clutter/stable/clutter-Implicit-Animations.html#ClutterAnimationMode.

Вот некоторые из них:

CLUTTER_LINEAR
CLUTTER_EASE_IN_QUAD
CLUTTER_EASE_OUT_QUAD
CLUTTER_EASE_IN_OUT_QUAD
CLUTTER_EASE_IN_CUBIC
CLUTTER_EASE_OUT_EXPO
CLUTTER_EASE_IN_OUT_EXPO
CLUTTER_EASE_IN_OUT_CIRC

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

Пусть у нас есть объект в позиции 0,0 и мы перемещаем его в течение 2000 мс в позицию 100,0 только вдоль оси Х. В режиме линейной анимации спустя секунду он окажется в позиции 50,0. Анимация принимает текущие значения за стартовые и берет указанные вами в качестве конечных. Все не оговоренные свойства не меняются. Оговоренные свойства должны сопровождаться желаемым конечным значением, и анимировать можно любое свойство. На примере это проще (надеюсь, ваша сцена еще открыта):

 >>> ident.set_anchor_point(60,30)
 >>> ident.set_position(60,30)
 >>> ident.animate(clutter.LINEAR,2000,’rotation-angle-y’,720)
 <clutter.Animation object at 0xa3f861c (ClutterAnimation at 0xa4bb050)>
 >>> ident.animate(clutter.LINEAR,2000,’rotation-angle-y’,720)
 <clutter.Animation object at 0xa3f85f4 (ClutterAnimation at 0xa4bb0c8)>

При втором вызове ничего не происходит, потому что значение свойства «поворот относительно оси Y» не меняется – вы указываете не на сколько повернуть изображение, а его конечное положение. При втором запуске мы производим анимацию, но объект уже находится в заданной позиции, поэтому он анимируется – но никуда не движется. Естественно, анимировать можно более чем одно свойство за раз. Опробуйте это:

 >>> ident.animate(clutter.LINEAR,2000,’x’,100, ‘rotationangle-y’, 360 )
 <clutter.Animation object at 0xa3f85f4 (ClutterAnimation at 0xa4bb2c8)>
 >>> ident.animate(clutter.EASE_IN_SINE,2000,’x’,0, ‘rotationangle-y’, 0 )
 <clutter.Animation object at 0xa3f85f4 (ClutterAnimation at 0xa4bb3ae)>

На сей раз небольшой логотип потанцует и возвратится в исходную точку.

Праздник документации

Clutter действительно великолепен. Единственной проблемой на данный момент является документация для модуля Python. Хотя классы и методы в основном (естественно) идентичны C-реализации Clutter, имеется несколько тонких отличий, а иногда и смысл действий в Python может привести вас в замешательство. Тут помогут инструменты самоанализа Python – в частности, функция dir(), которую можно вызвать для любого объекта, даже модуля. Испытайте ее на Clutter, что бы увидеть список доступных статических типов и методов: dir(clutter), или на методе: dir(clutter.Text).

Только факты

Что же, танцы логотипов – дело хорошее, но нам-то нужно видеть текст нашего заголовка, а рядом с ним – краткое содержание соответствующей новости. Так мы приходим к необходимости трех актеров на сцене.

Clutter управляет несколькими анимированными актерами параллельно, но держать по отдельной анимации для всех актеров из целой группы становится затруднительно. Ключевое слово здесь, если кто не понял, «группа». Clutter поддерживает контейнеры, включая контейнер группы (кое-кто предпочитает стековые контейнеры GTK, и при случае мы ими обязательно воспользуемся). Прежде всего создадим новую группу, затем определим ее элементы и добавим их. Во избежание путаницы мы импортируем идентифицирующее изображение опять, уже как новый объект.

 >>> group1= clutter.Group()
 >>> ident1=clutter.texture_new_from_file(img)
 >>> head1=clutter.Text()
 >>> head1.set_position(130, 5)
 >>> head1.set_color(blue)
 >>> head1.set_text(f.entries[0].title)
 >>> body1=clutter.Text()
 >>> body1.set_max_length(75)
 >>> body1.set_position(130, 22)
 >>> body1.set_size(250, 100)
 >>> body1.set_line_wrap(True)
 >>> body1.set_text(f.entries[0].summary)
 >>> group1.add(ident1, head1, body1)
 >>> group1.show_all()
 >>> stage.add(group1)

В этом коде мы создали новое изображение-идентификатор ленты и два текстовых объекта: один представляет заголовок новости (head1), а другой – сам текст (body1). Мы должны занести в них текст из первой записи, обнаруженной в ленте, и вывести в правильной позиции рядом с картинкой. Здесь мы задаем абсолютную позицию для текстовых объектов head1 и body1, но на самом деле она остается относительной для группы. При инициализации группы она, а не сцена, как было раньше, становится для актеров родительским объектом. Следовательно, позиция объектов определяется относительно позиции группы (которая по умолчанию расположена в 0,0).

Это следует помнить. Пример:

 >>> head1.get_position() (130,5)
 >>> group1.set_position(10,10)
 >>> head1.get_position() (130,5)
 >>> group1.set_position(0,0)

Вы увидите, что при изменении позиции группы все объекты движутся по экрану, но не заблуждайтесь: дочерние объекты все еще неподвижны. Сами для себя они позиции не меняли – перемещается их мир... Замечательно здесь то, что мы можем так же анимировать группу – достаточно одного преобразования, так как свойства отдельных актеров определяются относительно группы.

 >>> group.animate(clutter.LINEAR,2000,”x”,200,”y,”30”)
 <clutter.Animation object at 0xa3f85f4 (ClutterAnimation at 0xa4bb2c8)>
 >>> group.animate(clutter.LINEAR,2000,”x”,0,”y,”0”)
 <clutter.Animation object at 0xa3f85f4 (ClutterAnimation at 0xa4bb4a8)>

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

Итак, у нас есть группа; мы можем добавить еще одну и реализовать два режима анимации для выполнения перехода между различными элементами ленты новостей:

 >>> group2= clutter.Group()
 >>> ident2=clutter.texture_new_from_file(img)
 >>> head2=clutter.Text()
 >>> head2.set_position(130, 5)
 >>> head2.set_color(blue)
 >>> head2.set_text(f.entries[1].title)
 >>> body2=clutter.Text()
 >>> body2.set_max_length(75)
 >>> body2.set_position(130, 22)
 >>> body2.set_size(250, 100)
 >>> body2.set_line_wrap(True)
 >>> body2.set_text(f.entries[1].summary)
 >>> group2.add(ident2, head2, body2)
 >>> group2.hide()
 >>> stage.add(group2)
 >>> group1.animate(clutter.EASE_OUT_EXPO,4000,’x’,800,’y’,0,’ rotation-angle-y’,180)
 <clutter.Animation object at 0x92ca61c (ClutterAnimation at 0x935a2a0)>
 >>> group2.animate(clutter.EASE_OUT_EXPO,1,’x’,400,’y’,100,’rotation-angle-y’,720)
 <clutter.Animation object at 0x92ca66c (ClutterAnimation at 0x935a118)>
 >>> group2.show()
 >>> group2.animate(clutter.EASE_OUT_EXPO,3000,’x’,0,’y’,0,’rotation-angle-y’,0)
 <clutter.Animation object at 0x92ca16c (ClutterAnimation at 0x8f46028)>

Здесь мы создали новую группу и перед добавлением на сцену спрятали ее. Затем, удобства ради, мы анимируем ее за кадром и делаем видимой. Поскольку она находится вне сцены, то не видна и не перекрывает первую группу. Когда мы делаем ее видимой, она все еще находится за сценой, но новая анимация переносит ее в должную позицию, и кажется, что она вплывает на свое место. Ура! Теперь мы эксперты в анимации, прямо как Дисней. Ну, почти.

Двигаемся дальше

На DVD-приложении находится менее лаконичная версия читалки новостей, но и ее можно расширить: например, вставить обработку ошибок (вдруг лента пуста или оборвано соединение с сетью?), а кроме того, она работает только с одной лентой, не объединяя данные из нескольких источников. Комментарии в исходном коде на диске подскажут вам идеи по расширению приложения.

В следующий раз мы рассмотрим более продвинутые методы анимации и объединение эффектов. Кроме того, мы расширим базовые строительные блоки Clutter, показав, как внедрить элементы графической библиотеки Cairo.

Персональные инструменты
купить
подписаться
Яндекс.Метрика