Как написать дополнение для GIMP на языке Python


Или Script-Fu — это так называемый «фильтр массовости»? Далеко не каждый может с ним разобраться и большинство даже не пытаются делать какие-то плагины к GIMP.

RPG


Введение


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

В качестве языков, на которых можно писать дополнения, годятся Scheme и Python. Существует возможность писать дополения и на других языках (Perl, Tcl/Tk и т.д.), но модули, которые реализуют эту возможность, плохо поддерживаются либо вовсе не работают со свежими версиями GIMP.

Избрав языком для написания дополнения Scheme, вы автоматически оказываетесь в выигрыше, так как не существует в мире такой сборки GIMP-а, в которую бы интерпретатор Scheme не входил, и написаное вами дополнение гарантировано будет работать на всех платформах «из коробки», однако, писать на Scheme — то ещё удовольствие… Scheme является диалектом LISP. LISP — это аббревиатура, расшифровывается как LISt Processing, то есть, язык для обработки списков. Существует и другая расшифровка: Language of Idiotic Silly Parentheses (язык идиотских глупых скобок), спорное, но не лишенное смысла утверждение, — несоблюдение баланса скобок — один из главных источников ошибок программы, написанной на LISP и ему подобных. Далеко не каждый может разобраться с непростым синтаксисом этого языка, и большинство даже не пытаются писать какие-то дополнения для GIMP. Но сложный синтаксис — это мелочь по сравнению с отсутствием ряда возможностей. Например, нельзя использовать свой графический интерфейс, сохранить настройки дополнения в конфигурационный файл, подключить некий внешний модуль с дополнительными функциями и т.д. и т.п. Но есть язык, лишённый большинства недостатков Scheme и обладающий рядом достоинств. Этот язык — Python. О нём и пойдёт речь.


PDB и «Браузер процедур»


GIMP предоставляет нам API, в котором отражены все аспекты управления им. На любое действие, которое можно выполнить мышкой и клавиатурой, имеется соответствующая API функция. Совокупность API функций, которую предоставляет GIMP, образует так называемую процедурную базу данных PDB (The Procedural Database). Для просмотра PDB необходимо воспользоваться специальным инструментом, который называется «Браузер процедур» и вызывается кнопкой «Просмотр...» из окна ФильтрыPython-FuКонсоль.



Окно «Браузера процедур» разделено на две части: в левой части представлен список доступных функций, в правой — информация о выделенной в данный момент функции (имя, краткое описание выполняемых действий, перечень входных и выходных параметров, данные об авторе, времени создания и копирайте). Список достаточно велик (как я упоминал выше, GIMP — мощный графический редактор), но для облегчения жизни предусмотрена возможность фильтрации по имени (части имени) функции, по её краткому описанию и по ряду иных параметров, которые можно выбрать из выпадающего списка, расположенного справа от строки поиска.

Основные модули


Для того, чтобы наш Python-скрипт научился управлять GIMP-ом, дергать за ниточки его API, необходимо подключить к нему соответствующие модули:
  • gimpfu — это основной модуль, который содержит в себе функции register() (регистрирует дополнение в PDB) и main() (запускает дополнение), необходимые константы и переменные окружения, а также подключает ряд модулей, необходимых для работы
  • gimp — здесь собраны основные процедуры, функции и структуры данных, включая объекты Image, Layer и переменную pdb для доступа к The Procedural Database. Модуль автоматически подключается при подключении модуля gimpfu
  • gimpenums — полезные констранты, модуль автоматически подключается при подключении модуля gimpfu
  • gimpui — этот модуль включает в себя элементы UI, содержащиеся в библиотеке libgimpui, и помогает работать с элементами графического интерфейса
  • gimpshelf — позволяет дополнению запоминать какие-либо данные на время работы с изображением и хранить их до закрытия окна GIMP (этакие сеансовые cookie только для GIMP)
  • gimpplugin — альтернативный модуль, предоставляющий больше гибкости, но меньше возможностей, чем стандартный gimpfu. Если представить, что gimpfu — это конструктор лего, набор заранее подготовленых инструментов, из которых следует строить дополнение, то gimpplugin — это пластичная глина, из которой можно вылепить все, что угодно, но и всю грязную работу, соответственно, придётся делать самому

Исходный код этих модулей можно найти в файлах, расположенных по адресу: /usr/lib/gimp/2.0/python — если вы работаете в среде GNU/Linux, или C:\Program Files\Gimp-2.7.5\lib\gimp\2.0\python — если вы работаете в среде Windows.

Попробуем?


Итак, о «Браузере процедур» и основных модулях я рассказал, давайте теперь попробуем поуправлять GIMP-ом посредством Python-a. Для начала запустите консоль Python-Fu (напомню: ФильтрыPython-FuКонсоль).
Перед вами откроется вот такое окно:



Модуль gimpfu подключается автоматически при запуске консоли, поэтому можно ни о чём не беспокоиться и немедленно начинать вводить команды.
Для начала создадим новое изображение, содержащее в себе один слой цвета фона.



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

Функция, предназначенная для создания нового изображения, называется gimp_image_new. Браузер процедур говорит нам о том, что она имеет три входных параметра:
  • width — ширина изображения в пикселях (целое число)
  • height — высота изображения в пикселях (целое число)
  • type — тип изображения (целое число)

Для типа изображения приведен перечень возможных значений и имена предопределенных констант, которые можно использовать вместо чисел для лучшей читаемости программ:
  • RGB — соответствует 0 (будет создано цветное изображение в режиме RGB)
  • GRAY — соответствует 1 (будет создано изображение в градациях серого)
  • INDEXED — соответствует 2 (будет создано индексированное изображение)

Возвращает эта функция один параметр — идентификационный номер (ID) вновь созданного изображения (указатель на изображение, если вам угодно). Попробуем создать новое RGB изображение размером 640х480 пикселей. Для этого введите в командную строку следующее:

>>> image = pdb.gimp_image_new(640, 480, RGB)
>>>


(На самом деле, вводить не обязательно, достаточно дважды кликнуть по имени необходимой функции в окне браузера процедур либо выделить её и нажать кнопку «Применить», после чего шаблон функции будет вставлен в консоль, и вам останется лишь вписать необходимые параметры).

Если после выполнения команды ничего не было выведено, значит, GIMP успешно создал требуемое изображение, и теперь мы можем обращаться к нему через переменную image.

В данный момент изображение состоит только из пустого «холста» и не содержит в себе ни одного слоя. Браузер процедур говорит нам о том, что создать новый слой можно при помощи функции gimp_layer_new, которая имеет следующие входные параметры:
  • image — ID изображения, в котором создается слой
  • width — ширина слоя в пикселях
  • heidht — высота слоя в пикселях (да, в GIMP размеры слоев могут отличаться от размеров изображения)
  • type — тип слоя
  • name — имя слоя
  • opacity — прозрачность слоя (число в пределах от 0 до 100, причем, 100 означает полную непрозрачность)
  • mode — режим наложения нового слоя на существующие слои. Обычно этот параметр имеет значение NORMAL

Параметр «тип слоя» (type) может принимать следующие значения:
  • RGB_IMAGE — цветной слой в режиме RGB
  • RGBA_IMAGE — цветной слой, содержащий альфа-канал
  • GRAY_IMAGE — слой в градациях серого
  • GRAYA_IMAGE — слой в градациях серого, содержащий альфа-канал
  • INDEXED_IMAGE — слой, содержащий индексированные цвета
  • INDEXEDA_IMAGE — слой, содержащий индексированные цвета и альфа-канал

Возвращает эта функция ID созданного слоя. Создадим новый слой:

>>> layer = pdb.gimp_layer_new(image, 640, 480, RGB_IMAGE, "Фон", 100, NORMAL_MODE)
>>>


Если ничего не было выведено, значит, новый слой с именем «Фон» успешно создан, и мы можем обращаться к нему через переменную layer.

Теперь слой нужно встроить в изображение. Это делается при помощи функции gimp_image_insert_layer. По данным браузера процедур, эта функция имеет четыре входных параметра:
  • image — ID изображения
  • layer — ID слоя
  • parent — группа слоев, в которую необходимо добавить слой. Если parent равен None, слой будет добавлен в основной стек, вне какой-либо группы
  • position — определяет местоположение слоя в основном стеке слоёв или группе (если идентификатор группы, переданный в параметре parent, действителен). Чем больше цифра, тем выше располагается слой в стеке. Счет начинается с 0. Если position равен -1, то новый слой будет вставлен над активным в данный момент слоем.

Важно помнить о том, что тип слоя должен соответствовать типу изображения, иными словами, не надо пытаться в RGB изображение вставлять GRAY слой.

В нашем случае, нужно встроить слой layer в изображение image, поместив его на нижний (нулевой) уровень:

>>> pdb.gimp_image_insert_layer(image, layer, None, 0)
>>>


Теперь для того, чтобы новый слой приобрел цвет фона, его необходимо очистить (если этого не сделать, то слой будет цвета переднего плана) при помощи функции gimp-edit-clear, имеющей только один входной параметр:
drawable — доступная для рисования область.
drawable — это довольно интересный параметр. В отличии от image, который всегда изображение, или width, который всегда ширина, drawable может быть слоем, выделенной областью или каналом, в зависимости от того, с каким именно объектом работает та или иная функция, в данном случае, gimp-edit-clear в качестве параметра следует передать указатель на слой).

Эффект, производимый функцией gimp_edit_clear, зависит от типа слоя: слой, содержащий альфа-канал, в результате очистки станет прозрачным, а RGB слой будет залит цветом фона.

Очистим слой layer:

>>> pdb.gimp_edit_clear(layer)
>>>


Новое изображение готово, теперь можно его вывести на дисплей:

>>> display = pdb.gimp_display_new(image)
>>>


Функция gimp_display_new создает на экране новое окно и выводит в нем изображение, ID которого передан ей в качестве параметра. Возвращает эта функция ID окна.
В результате проделанных манипуляций, на экране должна появиться вкладка, содержащая RGB изображение, размером 640х480 пикселей, состоящее из одного слоя, который заполнен цветом фона.



Итак. Управлять GIMP-ом посредством API функций мы теперь умеем. Каким же образом эти манипуляции оформить в виде Python-скрипта? Об этом ниже.

«Скелет» дополнения


Типичное дополнение, написанное с использованием модуля gimpfu, выглядит следующим образом:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Импортируем необходимые модули
from gimpfu import *

# Объявляем функцию
def plugin_func(image, drawable, args):
  #
  # Здесь код вашего дополнения:
  # всякие функции из браузера процедур
  #

# Регистрируем функцию в PDB
register(
          "python-fu-plugin-func", # Имя регистрируемой функции
          "Такое-то очень функциональное дополнение", # Информация о дополнении
          "Делает то-то и то-то, так-то и так-то", # Короткое описание выполняемых скриптом действий
          "Автор авторов", # Информация об авторе
          "Автор авторов (author@server.ru)", # Информация о копирайте (копилефте?)
          "8.01.2012", # Дата изготовления
          "Крутой фильтр", # Название пункта меню, с помощью которого дополнение будет запускаться
          "*", # Типы изображений, с которыми может работать дополнение
          [
              (PF_IMAGE, "image", "Исходное изображение", None), # Параметры, которые будут переданы дополнению
              (PF_DRAWABLE, "drawable", "Исходный слой", None),# Всякие указатели на изображение, слои и т.д.
              (PF_STRING, "arg", "The argument", "default-value")
          ],
          [], # Список переменных, которые вернет дополнение
          plugin_func, menu="<Image>/Фильтры/") # Имя исходной функции и меню, в которое будет помещён пункт, запускающий дополнение

# Запускаем скрипт
main()

Остановимся подробнее на функции, регистрирующей наше дополнеие в PDB. Обратите внимание на префикс python-fu. Вообще, это правило хорошего тона — давать имена функциям по принадлежности и по выполняемому ими действию, чтобы по имени функции было понятно, чего от нее ждать. Если имя функции начинается со слова «gimp» — это «родная» функция GIMP, «plug-in» — это обычно компилируемые дополнения, написанные на С (скорее всего, фильтры), «script-fu» — расширения, написанные на Scheme, «python-fu» — как вы, наверное, уже догадались — расширения, написанные на Python-e.

Имена также содержат указание на объект, с которым функция работает, например, если вам необходимо что-то сделать со слоями, искать необходимую функцию в PDB следует, указав в строке поиска «layer», чтобы найти функции, работающие с выделенными областями — «select» и т.д. К тому же, если не указать префикс «python-fu» в начале функции, то она не будет зарегистрирована (по-моему, это только для Python-скриптов такое ограничение, и оно к лучшему, я считаю).

Также обратите внимание на то, что все нижние подчеркивания «_» в имени регистрируемой функции необходимо заменить на знак «-» — эта неоднозначная особенность попортила мне много крови, когда я начал осваивать написание дополнений на Pythone. Не до конца понимаю, зачем это сделано, но иначе дополнение просто не заработает…

Параметр функции register(), отвечающий за указание типа изображения, с которым будет работать дополнение, может принимать следующие значения: RGB, RGBA, GRAY, GRAYA, INDEXED. Для указания всех типов RGB и оттенков серого можно использовать «*» вот так: RGB*, GRAY*. Если дополнение для всех типов изображений, то можно просто указать «*». Этот параметр также влияет на активность пункта меню, запускающего дополнение. Например, если ваше дополнение работает только с RGB изображениями, а у вас открыто изображение, представляющее из себя лишь оттенки серого, то меню будет неактивным. В свою очередь, если указать в качестве типа изображения «*», то меню будет активным, даже если никакого изображения в данный момент не открыто (полезный хак, когда возникает потребность в генерации изображения с нуля, а не в редактировании готового).

Меньше слов, больше кода


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

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Импортируем необходимые модули
from gimpfu import *

def add_colored_border(image, drawable, border_width, border_color):
  pdb.gimp_context_push()
  # Запрещаем запись информации для отмены действий,
  # что бы все выполненные скриптом операции можно было отменить одим махом
  # нажав Ctrl + Z или выбрав из меню "Правка" пункт "Отменить действие"
  pdb.gimp_image_undo_group_start(image)

  # Увеличиваем размер холста и смещаем на необходимое расстояние
  pdb.gimp_image_resize(image,
                        pdb.gimp_image_width(image)  + border_width,
                        pdb.gimp_image_height(image)  + border_width,
                        border_width / 2,
                        border_width / 2)
  # Запоминаем цвет фона.
  # Это тоже правило хорошего тона:
  # оставь после себя все в исходном состоянии
  old_background = pdb.gimp_context_get_background()
  # Меняем цвет фона
  pdb.gimp_context_set_background(border_color)
  # Сводим изображение
  pdb.gimp_image_flatten(image)
  # Возвращаем в исходное состояние цвет фона
  pdb.gimp_context_set_background(old_background)
  # Обновляем изоборажение на дисплее
  pdb.gimp_displays_flush()

  # Разрешаем запись информации для отмены действий
  pdb.gimp_image_undo_group_end(image)
  pdb.gimp_context_pop()

# Регистрируем функцию в PDB
register(
          "python-fu-add-colored-border", # Имя регистрируемой функции
          "Добавление цветной рамки к изображению", # Информация о дополнении
          "Рисует вокруг изображения рамку заданного цвета и заданной ширины", # Короткое описание выполняемых скриптом действий
          "Александр Карабанов", # Информация об авторе
          "Александр Карабанов (zend.karabanov@gmail.com)", # Информация о копирайте (копилефте?)
          "8.01.2012", # Дата изготовления
          "Добавить рамку", # Название пункта меню, с помощью которого дополнение будет запускаться
          "*", # Типы изображений с которыми может работать дополнение
          [
              (PF_IMAGE, "image", "Исходное изображение", None), # Указатель на изображение
              (PF_DRAWABLE, "drawable", "Исходный слой", None), # Указатель на слой
              (PF_INT, "border_width", "Ширана рамки", "10"), # Ширана рамки
              (PF_COLOR, "border_color",  "Цвет рамки", (39,221,65)) # Цвет рамки
              
          ],
          [], # Список переменных которые вернет дополнение
          add_colored_border, menu="<Image>/ТЕСТ/") # Имя исходной функции и меню в которое будет помещён пункт запускающий дополнение

# Запускаем скрипт
main()


Теперь сохраните этот код в файл add_colored_border.py, например, и скопируйте его в папку с дополнениями.
Если вы работаете в среде GNU/Linux, то этой папкой будет ~/.gimp-2.x.x/plug-ins/ (не забудьте дать вашему скрипту права на выполнение).

Пользователям Windows необходимо поместить дополнение в папку %USERPROFILE%\.gimp-2.x.x\plug-ins\ (Подробнее об установке дополнений вы можете узнать на официальном русскоязычном сайте свободного графического редактора GIMP из статьи «Установка дополнений в GIMP»).

Запустите GIMP и посмотрите на строку меню. Если вы все сделали правильно, то рядом с меню «Фильтры» появилось меню «ТЕСТ», из которого можно будет запускать наше дополнение.



А в PDB появилась новая процедура python-fu-add-colored-border.



Попробуйте дополнение в действии. Для этого создайте новое изображение, вызовите наше дополнение из меню ТЕСТДобавить рамку. Появится примерно такой диалог:



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

А пока выберите ширину рамки, её цвет и нажмите «OK». Наше дополнение отработает, и вы увидете приблизительно такой результат:



Полезные ссылки


Метки:
Поделиться публикацией
Похожие публикации
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама
Комментарии 22
  • +8
    > Но есть язык, лишённый большинства недостатков Scheme и обладающий рядом достоинств.

    Scheme, лучше Python ??? Многие, я думаю готовы поспорить на эту тему.
    • +4
      Экспресс-сравнение scheme vs. Python в топике явно лишнее.

      Потому что вроде оффтопик, а вопросы возникают. Откуда например взялось следующее?
      «Например, нельзя использовать свой графический интерфейс, сохранить настройки дополнения в конфигурационный файл, подключить некий внешний модуль с дополнительными функциями и т.д. и т.п.»
      • +2
        >> Откуда например взялось следующее?
        >> «Например, нельзя использовать свой графический интерфейс, сохранить настройки дополнения в
        >> конфигурационный файл, подключить некий внешний модуль с дополнительными функциями и т.д. и т.п.»


        К сожалению интерпретатор Scheme который поставляется вместе с GIMP-ом действительно не позволяет этого делать.
        • 0
          Там tiny scheme используется. Экстеншены судя по всему поддерживаются.
          • 0
            Это все не важно на самом деле.

            Ясно что питон из коробки умеет намного больше, а его синтаксис для большинства комфортнее схемы. Зато tiny scheme в разы меньше и осознанно позиционируется как «встраиваемый». Поэтому в GIMP встроен именно tiny scheme, а не python. Могли бы встроить lua, чтобы порадовать тех, кому не нравятся скобочки. Но увы.
            • +1
              Полный переход на новое ядро, скорее всего, принесет возможность писать дополнения на lua.
              • +1
                А лучше бы встроили guile. www.gnu.org/software/guile/
                Он умеет и scheme и lua и даже ECMAScript3
        • 0
          Спасибо!
          Когда-то давным-давно писал своё дополнение на LISP'e, было интересно, но сложно.
          С питоном попроще будет! :)

          Кажется, вводите пользователя в заблуждение по поводу border_width:

          pdb.gimp_image_resize(image,
                                  pdb.gimp_image_width(image)  + border_width,
                                  pdb.gimp_image_height(image)  + border_width,
                                  border_width / 2,
                                  border_width / 2)


          Рамка добавляется ширины border_width / 2. Я бы писал так (и ожидал бы такого поведения от чужого модуля):

            pdb.gimp_image_resize(image,
                                  pdb.gimp_image_width(image)  + border_width*2,
                                  pdb.gimp_image_height(image)  + border_width*2,
                                  border_width,
                                  border_width)


          Но, вобщем, не суть. Статья хорошая. Спасибо!

          P.S. Тег
              <code>
          в предпросмотре не работает…
          • 0
            >> С питоном попроще будет! :)

            Да проще. Можно даже ещё проще:

            image.resize(image.width + border_width * 2,
                           image.height +  border_width * 2,
                           border_width / 2,
                           border_width / 2)

            Об этом расскажу в следующей статье.



            >> Рамка добавляется ширины

            Вы правы. Просмотрел. Приношу извинения.
            • 0
              Невнимательность меня погубит… Вот так правильно:

              image.resize(image.width + border_width * 2,
                                image.height +  border_width * 2,
                                border_width,
                                border_width)
              
          • +5
            (язык идиотских глупых скобок, в которых заключено это выражение).
            Нормальный язык, мое оценочное суждение.
            Везде есть правила и ограничения.
            • +1
              > Также обратите внимание на то, что все нижние подчеркивания «_» в имени регистрируемой функции необходимо заменить на знак «-» — эта неоднозначная особенность попортила мне много крови, когда я начал осваивать написание дополнений на Pythone. Не до конца понимаю, зачем это сделано, но иначе дополнение просто не заработает…

              Я так понимаю, это сделано для совместимости со Scheme. Имена функций / параметров в браузере функций тоже выдаются с дефисами в стиле Scheme.
              • 0
                Python — наше все!
                • +10
                  > несоблюдение баланса скобок — один из главных источников ошибок программы, написанной на LISP и ему подобных

                  Скажите уже, в каком языке несоблюдение баланса скобок не будет ошибкой?

                  > Далеко не каждый может разобраться с непростым синтаксисом этого языка

                  Одна единственная синтаксическая конструкция (список) — это «непростой синтаксис»?!
                  • +2
                    А почему script-fu, но python-fy? Потому что Python?
                    • 0
                      Нет. Это потому что я невнимателен.
                      Таким образом получаем, что произнесённую мной с такой уверенностью фразу: «К тому же, если не указать префикс «python-fy» в начале функции, то она не будет зарегистрирована (по-моему, это только для Python-скриптов такое ограничение, и оно к лучшему, я считаю).» можно считать бредом.

                      Сейчас внесу необходимые изменения в пост.
                      Спасибо за внимательность. Вы правы, должно быть python-fu.
                    • 0
                      Молодой человек, два предложения вашей статьи — «Но есть язык, лишённый большинства недостатков Scheme и обладающий рядом достоинств. Этот язык — Python.» — сделали вас знаменитым.

                      Постарайтесь распорядиться своей славой разумно.
                      • 0
                        Не вырывайте фразы из контекста пожалуйста.
                      • +1
                        > Далеко не каждый может разобраться с непростым синтаксисом этого языка

                        Все слова которые пришли мне в голову после прочтения этого предложеия будут восприняты автором как оскорбления, поэтому я лучше промолчу.

                        Третий абзац введения лучше просто уберите. Статья станет лучше.
                        • –2
                          >> Третий абзац введения лучше просто уберите. Статья станет лучше.

                          «Слово не воробей...», как говориться, раз уж опубликовал, то убирать не буду.
                          • 0
                            Слова в статье — не птицы, кнопку edit никто не отнимал. Пусть все эти «воробьи» для истории останутся в подобных доброжелательных комментах, а статья станет целостнее и целеустремлённее, без посторонних для её темы холиварных зацепок.
                        • 0
                          Спасибо, статья не теряет актуальность

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