LXF128:Clutter

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

Перейти к: навигация, поиск
Python Реальные проекты для развития ваших навыков

Содержание

Clutter: Пишем датчик скорости

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

Внашей серии уроков Python мы уже написали множество web-диковинок, но лишь изредка касались создания графического интерфейса (GUI). Одна из причин этого в том, что в большинстве случаев код GUI очень быстро разрастается, и обычная отрисовка на экране панели с несколькими кнопками способна съесть всю площадь статьи.

Сделаем небольшой перерыв в недружелюбии к пользователю, поскольку в следующие месяцы мы будем создавать приложения с использованием библиотеки PyClutter. Если вы не знакомы с Clutter [англ. «захламлять»], то обратитесь к врезке в конце.

На первом уроке мы создадим небольшую, но полезную утилиту, чтобы разобраться в работе Clutter и PyClutter. Поскольку Clutter используется пока не очень широко, то документации и примеров весьма не хватает; будем надеяться, что приведенный здесь код даст вам представление, как можно реально применять Clutter в приложениях Python.

Задача данного урока – создать программку, отображающую скорость вашего текущего интернет-соединения. Ну да, таких мониторов хватает и без нас, но этот будет наш собственный, и всего из 70 строк простого кода.

Чтобы разобраться в Clutter, начнем с терминологии. В отличие от других GUI-инструментариев, обычно определяющих объекты типа панелей или окон, Clutter рассматривает визуальную область как «сцену» [stage]. Продолжая аналогию – появляющиеся на ней (точнее, в ней, но это как-то нелепо звучит) объекты называют ся актерами [actor]. В процессе кодирования все станет понятнее, и названия уже не будут казаться странными. Особенность актеров в том, что они обладают большим количеством свойств, чем обычные виджеты, потому что на самом деле существуют в 3D-окружении, а не в 2D.

Зачем нужен Clutter?

Clutter – это лицензированная по GPL графическая и GUI-библиотека, изначально разработанная командой OpenedHand. Позднее ее продали Intel, которая продолжила дальнейшую разработку и развитие.

Самое замечательное в Clutter – простота, скорость и мощные методы создания 3D-и 2D-графики на разных платформах. За кулисами в основном используется OpenGL, но, применяя библиотеку Clutter, разработчики приобретают преимущества быстрого, эффективного и дружелюбного способа создания графических приложений без углубления в технические аспекты OpenGL-библиотек.

Clutter также формирует неотъемлемую часть Moblin, последнее слово в создании легковесной, но мощной графической версии Linux для мобильных устройств. Moblin в основном предназначена для устройств на базе Intel Atom, но работает и на других машинах.

Весь мир — сцена

Темновато… Но может служить началом приключенческой 3D-игры. Или первым шагом в создании вашего кода Clutter!

Прямо по Шекспиру. Но довольно гиперболтовни – все прояснится, когда мы начнем создавать код. Откройте свое обычное окружение Python (для меня это Bash, но вы можете использовать нечто более симпатичное), и создайте первый скрипт Clutter...

>>> import clutter
 >>> stage = clutter.Stage()
 >>> stage.set_size(500,300)
 >>> red=clutter.Color(255,0,0,255)
 >>> black=clutter.Color(0,0,0,255)
 >>> stage.set_color(black)
 >>> stage.show_all()

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

Настройка атрибутов сводится к простому вызову методов класса stage, в нашем случае это размер и цвет. Параметры для set_szie() – длины вдоль x и y, а цвет берется из объекта clutter.Color (требующего значения RGB и альфа-канала). Как и в других инструментариях, прежде чем объект появится на экране, его необходимо сделать видимым – этим и занимается по следняя строка.

Но что же с актерами – объектами, которые мы хотим показать на экране? Давайте добавим несколько текстовых надписей:

Первое приложение — традиционное, но мы дерзнули опустить запятую.

>>> a=clutter.Text()
 >>> a.set_font_name(“Sans 30)
 >>> a.set_colour(red)
 >>> a.set_text (“Hello World!”)
 >>> a.set_position(130,100)
 >>> stage.add(a)

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

Как я отметил ранее, документация к PyClutter скудна, но мы можем утешиться тем фактом, что Python – сам себе анализ. Попробуйте сейчас ввести dir(a), чтобы увидеть доступные методы и атрибуты этого объекта.

Следующим шагом мы создадим работающий скрипт, но остался еще один момент: чтобы магия Clutter действовала, нужно передать ему управление приложением посредством функции clutter.main(), предварительно предусмотрев выход из программы. В ситуациях, подобных нашей, Python перехватывает прерывание Ctrl+C, и пока у нас нет шанса выйти: надо создать некоторые события клавиатуры.

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

>>> def parseKeyPress(self, event):
 ... if event.keyval == clutter.keysyms.q:
 ... clutter.main_quit()
 ... elif event.keyval == clutter.keysyms.r:
 ... self.set_color(red)
 ...
 >>> stage.connect(‘key-press-event’, parseKeyPress)
 >>> clutter.main()
К вопросу о версиях

Библиотека Clutter, а следовательно, и модули Python, использующие ее, недавно обновились до версии 1.0. Для обновлений обычна некоторая несовместимость между старой и новой версиями, но в данном случае в коде версий до и после 0.9 отличия фундаментальные. Модуль PyClutter и библиотека Clutter должны быть доступны в репозитории вашего дистрибутива, но после ее установки убедитесь, что вы получили версию 0.9 (лучше 1.0) или выше; в противном случае гарантирую, что код данного учебника работать не будет. Кто подумал «фи», пусть напишет учебник, а затем поинтересуется обновлениями библиотеки…


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

Время что-то отследить

Числа. Цветные. Они изменяются. И что-то отслеживают. По-моему, начало хорошее…

Итак, интерфейс у нас готов, но как создать великолепный монитор трафика? Сперва определим, где взять текущую скорость. В таких случаях я всегда обращаюсь к моему старому другу proc. Да, псевдо-файловая система /proc – это кладезь всевозможных сведений о работающей Linux-машине; из всей беспорядочной мешанины файлов здесь нам нужен лишь /proc/net/dev. Это список всех сетевых устройств, и его просмотр даст нам статистику входящих и исходящих байтов, пакетов, пропущенных пакетов, ошибок и т. д. Нам сейчас интересны только полученные и отправленные байты. Я знаю, что указывается их количество, а нам необходи ма скорость, но узрите мощь proc – просто откройте файл вновь, и числа изменятся, как по волшебству. Надеюсь, вы поспеваете за мной и простая арифметика для нас не препятствие. Если открывать файл каждую секунду и вычитать старое число из нового, то вот вам и ответ.

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

devfile=open(‘/proc/net/dev’,’r’)
 for line in devfile.readlines():
  line=line.strip()
  if (line[:4] == ‘eth0’):
   line=line[5:].split()
   print line[0], line[8]

Авось, вы разберетесь в ее коде и без блок-схемы. Мы читаем файл и проходим по строкам в поиске той, что начинается с eth0; перед сравнением вырезаются пробелы, которыми вывод дополняется для выравнивания таблицы. Обнаружив нужную строку, мы удаляем имя интерфейса и разбиваем ее, чтобы каждое значение стало элементом списка. Количества входящих и исходящих байтов будут находиться в 0‑й и 8‑й его позициях. Пока мы просто печатаем их – можете набрать наш код и посмотреть, что выведется. Необходимо добавить лишь преобразование строки в целое и его сохранение, и можно начинать отслеживать происходящее.

Поможет математика

Любители вникать в практические детали могут спросить: а как мы учтем время, занятое выполнением кода? Хотите засечь время выполнения этого кода – вперед: в моей системе на него требуется 0.0001 секунд. Если интересно, то полное приложение командной строки будет выглядеть так:

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

Отслеживание версий может превратиться в кошмар, но большинство модулей хранит свои версии в <имя_модуля>.__version__ . Это может быть полезно не только для вас, но и для вашего приложения, которое может проверять совместимость версий перед тем, как сделать нечто хитроумное.

import time
lasttime=1
lastin=0
lastout=0
def getspeed():
x=open(‘/proc/net/dev’,’r’)
for line in x.readlines():
  line=line.strip()
  if (line[:4] == ‘eth0’):
    line=line[5:].split()
    bin=int(line[0])
    bout=int(line[8])
return (bin, bout)
while True :
  z= getspeed()
  timedelta=time.time()-lasttime
  lasttime=time.time()
  sin=(float(z[0]-lastin))/(1024*timedelta)
  sout=(float(z[1]-lastout))/(1024*timedelta)
  print sin, sout
  lastin=z[0]
  lastout=z[1]
  time.sleep(5)

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

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

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

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

import clutter
 import time
 lasttime=1
 lastbin=0
 lastbout=0
 black =clutter.Color(0,0,0,255)
 red = clutter.Color(255, 0, 0, 255)
 green =clutter.Color(0,255,0,255)
 blue =clutter.Color(0,0,255,255)
 def updatespeed(t, a, b):
   global lasttime, lastbin, lastbout
   f=open(‘/proc/net/dev’,’r’)
   for line in f.readlines():
      line=line.strip()
            if (line[:4] == ‘eth0’):
         line=line[5:].split()
         bin=int(line[0])
         bout=int(line[8])
         timedelta=time.time()-lasttime
         lasttime=time.time()
         speedin=round((bin-lastbin)/(1024*timedelta), 2)
         speedout=round((bout-lastbout)/(1024*timedelta), 2)
         lastbin, lastbout = bin, bout
         a.set_text(str(speedin)+’KB/s’)
         xx, yy=a.get_size()
         a.set_position(int((300-xx)/2),int((100-yy)/2) )
         b.set_text(str(speedout)+’KB/s’)
         xx, yy=b.get_size()
         b.set_position(int((300-xx)/2),int((100-yy)/2)+100 )
 def parseKeyPress(self, event):
   # Опрашиваем клавиату ру
   # Вызывается объек том сцены
   if event.keyval == clutter.keysyms.q:
      #Вы ходим из тес та, ес ли поль зователь на жал “q”
      clutter.main_quit()
   elif event.keyval == clutter.keysyms.r:
      #де лаем объект красным при на жатии “r”
      self.set_color(red)
   elif event.keyval == clutter.keysyms.g:
      # де лаем объект зе леным при на жатии “g”
      self.set_color(green)
   elif event.keyval == clutter.keysyms.b:
      # де лаем объект синим при на жатии “b”
      self.set_color(blue)
   elif event.keyval == clutter.keysyms.Up:
      #стрелка вверх = де лаем объект черным
      self.set_color(black)
   print ‘event processed’, event.keyval
 stage = clutter.Stage()
 stage.set_size(300,200)
 stage.set_color(blue)
 stage.connect(‘key-press-event’, parseKeyPress)
 intext=clutter.Text()
 intext.set_font_name(“Sans 30)
 intext.set_color(green)
 stage.add(intext)
 outtext=clutter.Text()
 outtext.set_font_name(“Sans 30)
 outtext.set_color(red)
 stage.add(outtext)
 stage.show_all()
 t=clutter.Timeline()
 t.set_duration(5000)
 t.set_loop(True)
 t.connect(‘completed’, updatespeed, intext, outtext)
 t.start()
 clutter.main()

Здесь мы собрали воедино все элементы, изученные на этом уроке. Мы создали сцену, заселили ее актерами, а за тем воспользовались объектами Clutter типа «шкала времени», чтобы все обновлялось самостоятельно и согласно нашим капризам. Но мы лишь слегка коснулись графических возможностей Clutter. Мы даже еще не затронули поведение и анимацию, не говоря уж об эффектах альфа-канала. Но верьте: они появятся вгрядущих проектах.

Все о таймерах

Библиотека Clutter использует объекты, называемые шкалой времени [timeline] для выполнения практически всего, что необходимо делать при работе приложения. Шкала времени – это пульс вашего скрипта, который гарантирует, что все по крайней мере пытается функционировать совместно.

Шкала времени широко используется в Clutter для управления анимацией и эффектами, но ее также можно применять как самостоятельное прерывание для периодического вызова подпрограмм. Это достигается реакцией на сигналы для таких событий, как started, next-frame, completed и т. д. Каждый из этих сигналов можно связать с функцией обратного вызова для управления чем-то еще.

Вот короткий пример, который можно ввести в оболочке Python:

>>> import clutter
 >>> t=clutter.Timeline()
 >>> t.set_duration(2000)
 >>> t.set_loop(True)
 >>> def ping(caller):
 ... print caller
 ...
 >>> t.connect(‘completed’,ping)
 9L
 >>> t.start()
 >>> <clutter.Timeline object at 0xb779639c
 (ClutterTimeline at 0x95b9860)>

Надеюсь, методы объекта временной шкалы достаточно понятны. Длительность задается в миллисекундах. Затем шкала зацикливается. Здесь мы создали простую функцию с именем ping, которая просто печатает передаваемый ей параметр. Затем мы связали подаваемый сигнал completed с функцией ping и запустили шкалу времени. Теперь без всякого дополнительного воздействия функция ping будет вызываться каждые две секунды позавершении отрезка временной шкалы, пока вы не закроете окружение Python.

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