Скриптовый интерфейс на Python

Скриптовый интерфейс на Python позволяет автоматизировать задачи обработки данных СЗМ, как внутри Gwyddion, так и при использовании его функциональности в отдельных программах на Python, которые запускаются отдельно от него. Эти программы могут варьироваться от последовательного выполнения нескольких команд как в макросах, до достаточно сложных программ, собирающих данные из нескольких файлов, обрабатывающих их и использующих сторонние библиотеки и программы.

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

Термин «Скриптовый интерфейс на Python» подразумевает, что скрипты в Gwyddion написаны на Python. Python является мощным, широко используемым высокоуровневым интерпретируемым языком программирования общего назначения с ясным, легким для понимания синтаксисом, широким набором библиотек и поддержкой большого числа операционных систем и сред. Все доступные возможности и потенциал языка Python можно использовать в скриптах в Gwyddion. Единственной новой вещью будет то, что вы также получите доступ к структурам данных, функциям и состоянию программы Gwyddion.

Расширение и встраивание, модули и модули.

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

Gwyddion предоставляет модуль расширения Python под названием gwy. Таким образом, можно запустить Python, загрузить модуль gwy так же, как любой другой модуль языка с помощью команды

import gwy

и начать использовать его функции и классы в командной оболочке Python или скрипте. Этот режим называется автономный скрипт pygwy. Естественно, что при этом вы запускаете Python, а не Gwyddion. таким образом, нельзя использовать функциональность, относящуюся к взаимодействию с Gwyddion (программой). Даже если экземпляр Gwyddion запущен параллельно, он полностью независим от скрипта и не может напрямую управляться им.

С одной стороны, модуль pygwy (модуль Gwyddion) позволяет комбинировать эти вещи в обратном порядке и запускать интерпретатор языка Python внутри программы Gwyddion, тем самым встраивая Python в Gwyddion. если запустить пункт меню Обработка данныхКонсоль Pygwy, появится оболочка для Python, в которой можно будет выполнять команды языка. В этом случае код на Python может взаимодействовать с текущим запущенным экземпляром Gwyddion различными способами. Скрипт, запущенный таким путём обычно называется просто скрипт pygwy (безо всяких дополнительных уточнений).

Пункт Команда в нижней части окна консоли позволяет выполнять отдельные простые команды. Они запускаются сразу после нажатия Enter и их вывод печатается в область журнала выше.

Более длинные скрипты могут быть набраны, скопированы или загружены в основную область в верхней части. Кнопка Выполнить (Ctrl-E) при этом запускает скрипт. Вывод скрипта снова будет записан в область журнала вместе с любыми сообщениями об ошибках. Другие кнопки управления позволяют сохранять (Ctrl-S) и загружать (Ctrl-O) скрипты. Кнопка Очистить журнал очищает область журнала.

Консоль Pygwy, демонстрирующая простой скрипт (печатающий диапазон значений для текущего изображения) и его вывод.

также существуют некоторые другие небольшие отличия от автономно запускаемых скриптов. Модуль gwy всегда автоматически импортирован для консоли. В дополнение, он будет доступен здесь даже если вы найдёте модуль расширения Python на диске и удалите его, поскольку здесь он генерируется на лету из Gwyddion.

И, наконец, можно писать модули расширения Gwyddion на Python. Такие модули будут появляться в меню Обработка данных подобно другим функциям обработки данных. также можно написать модули, которые реализуют загрузку и сохранение данных в новых форматах. Это слегка более запутанный процесс, чем предыдущие два варианта и будет более подробно объяснён ниже. Этот тип модулей обычно называется модуль обработки данных на Python (в контексте Gwyddion) или модуль Gwyddion для обработки данных на Python, если контекст неясен.

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

import gwyutils

В зависимости от того, как Gwyddion был установлен, может потребоваться добавить путь до gwyutils.py в sys.path перед этим:

import sys
sys.path.append('/the/path/to/the/gwyutils/module')

Поля данных — как представлены изображения

Наверное наиболее важной из структур данных Gwyddion, с которой вам придётся работать, будет DataField, которая представляет изображение.

По существу она представляет собой массив N×M значений данных с плавающей точкой, который также содержит некоторую дополнительную информацию, такую как размеры и единицы измерения. Значения данных всегда хранятся в основных единицах СИ (т.е. метрах или Вольтах, не в нанометрах или милливольтах). В большинстве случаев вы будете работать со структурами DataField, полученными из какого-то файла. Однако, создание нового заполненного нулями поля данных размером 128 на 128 точек (с физическими размерами один на один) не является сложным:

data_field = gwy.DataField(128, 128, 1.0, 1.0)

Размеры в пикселях называются в Gwyddion разрешениями и могут быть получены используя функции get_xres() и get_yres():

# Ширина в пикселях
xres = data_field.get_xres()

# Высота в пикселях
yres = data_field.get_yres()

Также можно создать поле данных как копию другого:

another_field = data_field.duplicate()

или сделать его таким же, как другое, но заполнить при этом нулями:

similar_field = data_field.new_alike()

Объект DataField имеет множество методов от простых, таких как получение физических размеров или простых статистических свойств:

# Физические размеры
xreal, yreal = data_field.get_xreal(), data_field.get_yreal()

# Диапазон значений
zmin, zmax = data_field.get_min_max()

# Среднее значение
mean = data_field.get_avg()

до сложных, которые проводят нетривиальные операции со множеством настраиваемых параметров, такие как удаление штрихов (царапин)

data_field.mark_scars(result_field, high, low, min_len, max_width, False)

Их все можно найти в справочной документации. Некоторые распространённые операции могут включать в себя сочетание нескольких функций вместе. Например, эквивалентом функции Исправить нуль можно получить комбинацией get_min() и add():

data_field.add(-data_field.get_min())

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

value = data_field[0]

и аналогично

data_field[0] = 1.5

установит значение в верхнем левом углу в 1.5. Следует отметить, что данные изображения представлены в «плоском» виде, т.е. представлены в виде одного большого массива, упорядоченного строка за строкой, и даже несмотря на то, что DataField представляет собой изображение, используется только один индекс (в противоположность раздельным индексам для строк и столбцов). Таким образом, последнее значение в третьей строке поля данных размером 128 на 128 может быть изменено на -1.0 с помощью команды

data_field[2*128 + 127] = -1.0

Другим возможным вариантом будет использование методов get_data() и set_data() которые извлекают полный набор данных в список Python и заполняют полный набор данных DataField из списка Python (или другой последовательности), соответственно. Следует заметить, что эти методы всегда копируют данные. Если изменить список после извлечения, при этом не поменяются значения поля данных и наоборот. Например, если вы не боитесь запачкать руки, Исправить нуль может быть реализован заново следующим образом:

data = data_field.get_data()
m = min(data)
data = [z-m for z in data]
data_field.set_data(data)

наконец, если использовать NumPy, вам возможно потребуется напрямую проводить операции над полем данных с использованием функций NumPy. Вспомогательный модуль gwyutils предоставляет функции, которые позволяют это делать. Вызов

array = gwyutils.data_field_data_as_array(data_field)

создаёт массив NumPy, который будет содержать данные поля данных. Этот подход необходимо использовать осторожно, поскольку некоторые операции с типом DataField меняют представление данных (например, меняют размер) и после этого массив NumPy ломается. В общем случае не рекомендуется смешивать случайным образом операции с массивом и методы DataField.

Когда вы закончили менять поле данных соответствующее изображению в Gwyddion, необходимо вызвать

data_field.data_changed()

Это посылает сигнал объекту DataField, уведомляющий что все окна данных и миниатюры, показывающие изображение, должны обновить сами себя. В общем случае это должно быть сделано по одному разу для каждого изменённого поля данных в конце скрипта (или после того, как вы закончили работать с конкретным изображением). Если забыть сделать это, отображаемая информация может оказаться рассинхронизированной с реальным содержимым изображений.

Контейнеры — как представлены файлы

Файл СЗМ представлен в виде класса Container, который является похожим на словарь объектом, содержащим всё, что может быть в файле GWY. Каждый файл при импорте преобразуется в Container с аналогичной структурой. Таким образом файлы GWY и любые другие типы файлов не отличаются после того, как они будут загружены в программу.

Элементы в типе Container идентифицируются строковыми ключами (или эквивалентыми целочисленными ключами, хотя в Python их использование будет более редким), например, "/0/data" идентифицирует изображение номер 0 и "/5/select/rectangle" идентифицирует прямоугольное выделение на изображении номер 5. Положение отдельных фрагментов данных описано в спецификации формата файла GWY.

Можно получить доступ к элементам объекта Container путём простого индексирования:

# Сделать изображением 0 наше поле данных
container['/0/data'] = data_field

# Получить прямоугольное выделение для изображения 5
selection = container['/5/select/rectangle']

Однако, Container отличается от словаря Python в некоторых аспектах. Он может содержать только определённые типы данных: булевы, целочисленные, значения с плавающей точкой, строки и объекты Gwyddion, которые можно перевести в последовательную форму (что фактически означает любой объект данных в Gwyddion, который может вам встретиться. Он содержит такие методы, как set_object_by_name() или get_boolean_by_name(), которые уточняют тип данных, который будет сохранён или извлечён и их можно использовать вместо индексации. Более того, даже с этими ограничениями, если начать сохранять случайные вещи под случайными именами здесь, в результате не получится то, что Gwyddion понимает как файл СЗМ, и скорее всего это нарушит его нормальную работу вплоть до падения. Следовательно, всегда нужно использовать имена элементов, заданные в спецификации и соответствующие им типы элементов.

Функции, которые помогают с конструированием ключей, идентифицирующих различные элементы в файле, называются gwy_app_get_something_key_for_id(), где «something» заменяется на тип элемента. Так, ключ для изображения 3 может быть сконструирован используя

key = gwy.gwy_app_get_data_key_for_id(3)

Следует отметить, что эти функции конструируют целочисленные ключи (которые работают столь же хорошо, но непонятны для пользователя). Можно использовать функции gwy_name_from_key() и gwy_key_from_name() чтобы делать преобразования между целочисленными и строковыми ключами. Вызов

gwy.gwy_name_from_key(gwy.gwy_app_get_data_key_for_id(3))

вернёт "/3/data", как и ожидалось.

Список всего, содержащегося внутри объекта Container может быть получен с помощью методов keys(), который выдаёт список целочисленных ключей, или keys_by_name() который выдаёт список строковых идентификаторов. Это может иногда пригодиться если вы хотите просканировать содержимое контейнера. В большинстве случаев, однако, функции для работы с отдельными типами данных могут оказаться более удобными.

Другие типы данных

Файл Gwyddion содержит не только изображения. Он также может содержать маски, выбранные области, объёмные данные, графики, данные XYZ, ... Таким образом здесь мы вкратце опишем остальные объекты, которые вы имеете шанс встретить на своём пути.

DataField также используется для представления масок и презентаций. Они должны иметь те же пространственные размеры, что и основное поле данных. В полях данных маски значение 1 (или больше) означает маску и 0 (или меньше) отсутствие маски. Значений между 0 и 1 следует избегать. Для презентаций диапазон значений не важен. Они сохраняются под ключами в виде "/0/mask" или "/0/show".

Container также используется для представления метаданных. Элементы типа Container метаданных используют другие правила: все значения метаданных должны быть строками. В остальном и ключи, и значения записываются в свободной форме.

Brick представляет объёмные данные. Он очень похож на DataField за исключением того, что он имеет три оси размеров вместо двух.

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

Spectra представляет набор кривых спектров, т.е. данных спектроскопии в отдельных точках.

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

GraphModel представляет график, содержащий набор кривых.

GraphCurveModel представляет отдельную кривую на графике.

Selection представляет выделенные элементы на изображении или графики. Они представляют собой последовательности координат. Selection фактически является абстрактным базовым классом, каждый тип выделенных элементов (точка, линия, прямоугольник, ...) это отдельный подкласс.

Поиск и перебор данных

Зная всё про элементы Container, DataField and SIUnit возможно вы также захотите узнать как добраться до данных, которые нужно обрабатывать.

В контексте скриптов или модулей, которые должны работать с данными, активными в данный момент, как делают обычные модули обработки данных Gwyddion, вопрос ставится как каким образом можно получить активные в данный момент данные. Ответом будет функция gwy_app_data_browser_get_current(). Она принимает аргумент AppWhat, задающий какой тип текущего элемента вам нужен и возвращает соответствующий элемент. Она может использоваться чтобы получить сами объекты данных

# Получить текущее активное изображение (DataField)
data_field = gwy.gwy_app_data_browser_get_current(gwy.APP_DATA_FIELD)

# Получить текущий файл (Container)
container = gwy.gwy_app_data_browser_get_current(gwy.APP_CONTAINER)

или их номер в файле

# Получить номер активного изображения
i = gwy.gwy_app_data_browser_get_current(gwy.APP_DATA_FIELD_ID)

# Получить номер активного графика
i = gwy.gwy_app_data_browser_get_current(gwy.APP_GRAPH_MODEL_ID)

или их целочисленные ключи

# Получить ключ активного изображения
key = gwy.gwy_app_data_browser_get_current(gwy.APP_DATA_FIELD_KEY)

# Получить ключ активного поля данных маски
key = gwy.gwy_app_data_browser_get_current(gwy.APP_MASK_FIELD_KEY)

и некоторых менее распространённых вещей, таких как текущая активная страница в браузере данных. Если активных элементов требуемого типа нет, функция возвращает None.

С другой стороны, нередко необходимо найти все данные определённого типа в файле. Список всех номеров изображений (каналов) может быть получен с помощью

ids = gwy.gwy_app_data_browser_get_data_ids(container)

Когда функция возвращает список [0, 1, 4] это означает, что контейнер содержит изображения с номерами 0, 1 и 4. Другими словами, там будут изображения с ключами "/0/data", "/1/data" и "/4/data". Подобные функции будут для перечисления графиков, спектров, объёмных данных и данных XYZ. Также есть функция gwy_app_data_browser_get_containers() которая возвращает список всех файлов (т.е. элементов Container соответствующих файлам), которые в данное время открыты.

Наконец, нередко полезно найти объекты данных по заголовку. Для этого представлена простая функция поиска gwy_app_data_browser_find_data_by_title() Она сравнивает заголовки используя символы подстановки так же, как командная оболочка Unix:

# Найти изображения с заголовком точно соответствующим "Height"
ids = gwy.gwy_app_data_browser_find_data_by_title(container, 'Height')

# Найти изображения с заголовком, начинающимся с "Current"
ids = gwy.gwy_app_data_browser_find_data_by_title(container, 'Current*')

# Найти изображения с заголовком, содержащим "03"
ids = gwy.gwy_app_data_browser_find_data_by_title(container, '*03*')

Когда вы добавляете новое изображение или другой тип данных в файл используйте функцию браузера данных:

i = gwy.gwy_app_data_browser_add_data_field(container, data_field, True)

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

Выполнение функций модуля

Использование DataField (и других) методов, возможно в комбинации с небольшим использованием прямого доступа к данным, является наиболее простым и часто предпочтительным подходом к обработке данных в скрипте pygwy. Тем не менее, иногда вам необходимо запустить функцию Gwyddion точно таким же образом, как если бы она была выбрана из меню. Либо потому, что это функция не доступна напрямую как метод DataField и не может быть легко скопирована, или вы предпочитаете писать скрипт как макрос, который вызывает различные существующие функции Gwyddion.

Функция обработки данных (соответствующая элементу меню или панели инструментов) запускается используя gwy_process_func_run():

gwy.gwy_process_func_run('level', container, gwy.RUN_IMMEDIATE)

Первый аргумент 'level' это имя функции, второй это контейнер (который всегда должен быть текущим контейнером чтобы не происходили разные странные вещи) и последний аргумент это режим, в котором запускать функцию. Функции обработки данных имеют два возможных режима: RUN_INTERACTIVE, который означает, что если у функции есть какие-либо диалоговые окна, она должна их показать, и RUN_IMMEDIATE, который означает, что функция не должна показывать диалоговых окон и должна немедленно выполнить нужную операцию. Следует отметить, что не для всех функций доступны оба режима. У некоторых отсутствует интерфейс пользователя, в то время, как другие могут быть запущены исключительно интерактивно.

Как функция узнаёт, какие из данных нужно обрабатывать? Она обрабатывает текущие данные. Для макросоподобных скриптов и функций обработки данных, написанных на Python это в точности то, что вы хотите. Можно также выбрать (сделать текущими) другие данные в скрипте, как описано в следующем разделе.

Где окажется результата работы? Некоторые из функций меняют существующие данные, некоторые создают новые изображения (и иные виды выходных данных). Некоторые даже позволяют выбирать между этими двумя вариантами. То, что функция делает при запуске из графического интерфейса, она также делает при запуске из скрипта pygwy. Если создаются новые изображения, вам необходимо их найти. Это можно сделать следующим образом:

ids_before = set(gwy.gwy_app_data_browser_get_data_ids(container))
gwy.gwy_process_func_run('some_func', container, gwy.RUN_IMMEDIATE)
ids_after = set(gwy.gwy_app_data_browser_get_data_ids(container))
new_ids = ids_after - ids_before

Обычно множество new_ids будет содержать единственный элемент, численный идентификатор только что созданного изображения, но некоторые модули могут создавать несколько элементов выходных данных.

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

gwy.gwy_process_func_run('weird_func', container, gwy.RUN_IMMEDIATE)
data_field = container['/5/data']

не будет эквивалентом

data_field = container['/5/data']
gwy.gwy_process_func_run('weird_func', container, gwy.RUN_IMMEDIATE)

поскольку функция 'weird_func' могла заменить (или удалить) поле данных в '/5/data'. Таким образом, хотя data_field будет оставаться правильным элементом DataField, она может больше не соответствовать изображению номер пять в файле.

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

Список функций может быть получен различными путями (кроме изучения исходного кода):

  • Используя онлайновый просмотр модулей. Если навести мышь поверх имени модуля, браузер покажет список предоставляемых функций. Те, у которых тип указан как «proc», являются функциями обработки данных. Если знать путь в меню, функция может быть быстро найдена поиском в расширенном онлайновом браузере модулей. Следует отметить, что онлайновый браузер показывает модули (и функции) доступные в последнем стабильном релизе.
  • Отыскать функцию в Gwyddion в меню ИнформацияПросмотр модулей. Здесь представлена та же информация, что и в онлайновом браузере, за исключением того, что здесь будут перечислены модули и функции присутствующие в текущей инсталляции Gwyddion.
  • Используя журнал. Когда вы используете функцию обработки данных, она появляется в журнале, включая имена её параметров, называемые настройки. Это пожалуй наиболее удобный способ поскольку можно интерактивно увидеть функции, появляющиеся в журнале, по мере того, как вы их выполняете.

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

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

settings = gwy.gwy_app_settings_get()

Результатом будет снова Container и снова существуют некоторые правила что и где хранится в нём. Настройки функций модуля обычно хранятся под именем "/module/modulename". Для настроек модулей отсутствует формальная документация и фактически есть два способа найти какие у модуля есть настройки: наблюдать за журналом, в котором будут перечислены все параметры и посмотреть файл настроек settings. Например, модуль выравнивания плоскости может быть запущен с использованием маски установленным в исключить маску следующим образом (следует отметить, что такого же результата можно достичь напрямую используя методы DataField):

settings['/module/level/mode'] = int(gwy.MASKING_EXCLUDE)
gwy.gwy_process_func_run('level', container, gwy.RUN_IMMEDIATE)

Работа с файлами

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

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

container = gwy.gwy_app_data_browser_get_current(gwy.APP_CONTAINER)
for i in gwy.gwy_app_data_browser_get_data_ids(container):
    data_field = container[gwy.gwy_app_get_data_key_for_id(i)]
    coeffs = data_field.fit_plane()
    data_field.plane_level(*coeffs)
    data_field.data_changed()

Для выполнения функции обработки данных с помощью gwy_process_func_run(), последним недостающим фрагментом является выбор изображения в браузере данных, так что они могли работать с корректными. Это делается вызовом gwy_app_data_browser_select_data_field():

gwy.gwy_app_data_browser_select_data_field(container, i)

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

container = gwy.gwy_app_data_browser_get_current(gwy.APP_CONTAINER)
orig_i = gwy.gwy_app_data_browser_get_current(gwy.APP_DATA_FIELD_ID)
for i in gwy.gwy_app_data_browser_get_data_ids(container):
    gwy.gwy_app_data_browser_select_data_field(container, i)
    gwy_process_func_run('align_rows', container, gwy.RUN_IMMEDIATE)
gwy.gwy_app_data_browser_select_data_field(container, orig_i)

Также просто перебираются все открытые файлы с использованием функции gwy_app_data_browser_get_containers():

for container in gwy.gwy_app_data_browser_get_containers():
    for i in gwy.gwy_app_data_browser_get_data_ids(container):
        # Ничего нового здесь

Чтобы обработать несколько файлов с диска необходимо найти способ найти или идентифицировать их на диске, но это выходит за рамки данного руководства (Модули Python glob и os могут помочь в данном случае). Чтобы загрузить файл данных СЗМ можно использовать просто gwy_file_load():

container = gwy.gwy_file_load(filename, gwy.RUN_NONINTERACTIVE)

Эта функция определяет тип файла и вызывает правильный модуль работы с файлами чтобы загрузить его. Полученный в результате Container не будет добавлен автоматически в браузер данных. Другими словами, Gwyddion не знает о нём и не может непосредственно использовать функции модулей обработки данных на его содержимом. Это ваш личный Container. Можно добавить его в браузер данных используя

gwy.gwy_app_data_browser_add(container)

В качестве альтернативы можно использовать функцию загрузки файлов из приложения

container = gwy.gwy_app_file_load(filename)

которая не только загружает файл, но и добавляет его в браузер данных. Когда нужно обработать много файлов требуется также убедиться, что они не скапливаются в оперативной памяти. Функция gwy_app_data_browser_remove() удаляет Container из браузер данных. Чтобы действительно освободить ресурсу нужно также убедиться, что объекты данных не удерживаются в каких-то других структурах данных в вашем скрипте.

Следует отметить, что мы использовали RUN_NONINTERACTIVE вместо RUN_IMMEDIATE в примере функции gwy_file_load(). Функции работы с файлами имеют два возможных режима, RUN_INTERACTIVE, который означает то же самое, что и для функций обработки данных, и RUN_NONINTERACTIVE, который означает что функция не должна показывать никаких диалоговых окон. Большинство функций работы с файлами не имеют никакого интерфейса пользователя, заметными исключениями являются экспорт изображений и импорт сырых данных и других типов данных, которые не могут быть загружены автоматически.

Сохранение файла происходит подобным образом:

gwy.gwy_file_save(container, filename, gwy.RUN_NONINTERACTIVE)

Формат файла определяется автоматически из расширения. Эта же функция может быть использована для экспорта изображений. Существует два основных метода создания файла с изображением, отрисованным псевдоцветом. Простая функция gwy_app_get_channel_thumbnail() создаёт GdkPixbuf который затем можно сохранить в файл используя его метод save(). Этот подход наиболее полезен для значков и простых изображений предпросмотра. Если вы хотите использовать все возможности модуля экспорта изображений, то можно использовать функцию gwy_file_save() в формат изображений (PNG, PDF, SVG, ...)

gwy.gwy_file_save(container, "image.png", gwy.RUN_NONINTERACTIVE)

Естественно, подобно функциям обработки данных, модуль экспорта изображений будет отрисовывать его в соответствии с текущими настройками. Можно изменить настройки (они находятся в поддереве "/module/pixmap") чтобы рисовать изображение заданным образом. Учитывайте, что экспорт изображений имеет очень много настроек.

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

import gwy, glob, sys

# Set up parameters for the 'align_rows' function.
settings = gwy.gwy_app_settings_get()
settings['/module/linematch/direction'] = int(gwy.ORIENTATION_HORIZONTAL)
settings['/module/linematch/do_extract'] = False
settings['/module/linematch/do_plot'] = False
settings['/module/linematch/method'] = 2

print 'Filename\tRa\tRms\tSkewness\tKurtosis'

# Go through files matching a given pattern:
for filename in glob.glob('MARENA-0??C-Maximum.0??'):
    container = gwy.gwy_file_load(filename, gwy.RUN_NONINTERACTIVE)
    gwy.gwy_app_data_browser_add(container)

    # Find channel(s) called 'Height', expecting to find one.
    ids = gwy.gwy_app_data_browser_find_data_by_title(container, 'Height')
    if len(ids) == 1:
        i = ids[0]
        # Select the channel and run some functions.
        gwy.gwy_app_data_browser_select_data_field(container, i)
        gwy.gwy_process_func_run('align_rows', container, gwy.RUN_IMMEDIATE)
        gwy.gwy_process_func_run('flatten_base', container, gwy.RUN_IMMEDIATE)
        # Extract simple statistics and print them.
        data_field = container[gwy.gwy_app_get_data_key_for_id(i)]
        avg, ra, rms, skew, kurtosis = data_field.get_stats()
        print '%s\t%g\t%g\t%g\t%g' % (filename, ra, rms, skew, kurtosis)
    else:
        sys.stderr.write('Expected one Height channel in %s but found %u.\n'
                         % (filename, len(ids)))

    gwy.gwy_app_data_browser_remove(container)

Написание модулей в Python

Модули Python не слишком отличаются от скриптов. Однако, они должны определять определённые функции и переменные чтобы gwyddion знал какой тип функциональности предоставляет модуль, куда поместить его в меню или как его запускать. Gwyddion распознаёт несколько типов модулей: файловые, обработки данных (точнее, обработки данных изображений), графиков, обработки объёмных данных, обработки данных XYZ, слоёв и инструментов. Последние два типа являются сложными и не могут быть написаны в настоящее время на Python. Все остальные типы могут.

Все модули расширения написанные на Python должны помещаться в каталог пользователя в подкаталоге pygwy. Это будет ~/.gwyddion/pygwy (Unix) или Documents and Settings\gwyddion\pygwy (старые версии MS Windows) чтобы Gwyddion мог найти их.

Переменные, которые модуль должен определить, являются более или менее общими для всех типов. По историческим причинам они все имеют префикс plugin_, хотя module_ в настоящее время было бы более подходящим:

plugin_type

Тип модуля, как строка в верхнем регистре. Может быть одной из "FILE", "PROCESS", "GRAPH", "VOLUME" и "XYZ", которые соответствуют доступным типам модуля очевидным образом.

plugin_desc

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

plugin_menu

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

plugin_icon

Опциональное имя иконки по умолчанию в виде строки (чтобы функцию можно было поместить в панель инструментов). Можно выбирать из наборов стандартных иконок Gwyddion и GTK+. В первом имен иконок содержат символы подчёркивания, например, "gwy_level", последний с символами дефисов, например, "gtk-execute". См. документацию по API для получения списка доступных иконок. Иконки, устанавливаемой по умолчанию, в Gwyddion нет.

plugin_sens

Опциональные флаги чувствительности в виде комбинации значений GwyMenuSensFlags, таких как MENU_FLAG_DATA или MENU_FLAG_DATA_MASK. Флаги задают будет ли элемент меню (или кнопка панели инструментов) доступна. Если не задано, по умолчанию требовать чтобы присутствовал соответствующий тип данных, например, модули работы с графиками требуют чтобы в файле существовал хотя бы один график. Обычно значения по умолчанию работают хорошо, но могут быть неправильными, например, для модулей синтеза данных (которые не требуют чтобы данные нужного типа уже были загружены), иногда модуль требует для своей работы обязательного наличия маски, и т.д.

plugin_run

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

Все типы модулей обработки данных (изображений, объёмных и XYZ) должны определить функцию run(), которая принимает два аргумента, текущий Container и режим (RUN_INTERACTIVE или RUN_IMMEDIATE). Если вам всё равно насчёт режима, можно также определить функцию с одним аргументом. Простой полный пример модуля обработки данных приведён ниже:

plugin_menu = "/Pygwy Examples/Invert"
plugin_desc = "Invert values"
plugin_type = "PROCESS"

def run(data, mode):
   key = gwy.gwy_app_data_browser_get_current(gwy.APP_DATA_FIELD_KEY)
   gwy.gwy_app_undo_qcheckpoint(data, [key])
   data_field = gwy.gwy_app_data_browser_get_current(gwy.APP_DATA_FIELD)
   data_field.invert(False, False, True)
   data_field.data_changed()

Он делает простую инверсию значений используя функцию invert() объекта DataField. Вызов gwy_app_undo_qcheckpoint() реализует правильную поддержку отмены операции. Если вы хотите поддерживать отмену, вызовите его со списком целочисленных ключей всех элементов данных, которые функция будет менять перед тем, как начнёте их менять.

Модули работы с графиками отличаются в мелочах, их функция run() принимает только один аргумент, типа Graph.

Модули работы с файлами должны предоставить несколько функций. Если они реализуют импорт файла, они определяют функции load() и detect_by_content(). Функция загрузки берёт имя файла (и опционально режим выполнения) и возвращает либо Container представляющий загруженный файл, либо None. Функция детекции должна определить, является ли файл файлом того формата, который реализован модуле. Она получает имя файла, байтовые строки содержащие начало и конец содержимого файла (поскольку они обычно содержат информацию полезную для определения типа файла) и размер файла. Она должна вернуть целочисленную оценку между 0 и 100, где 0 означает «определённо не этот тип файла» и 100 означает «точно этот тип файла». Ни один из файловых модулей Gwyddion не возвращает оценку больше 100. Если вам действительно нужно подменить встроенный модуль, то это можно сделать вернув оценку больше 100, но в общем случае это не рекомендуется.

Если модуль реализует сохранение файла, он наоборот должен определить функции save() и detect_by_name(). Функция сохранения принимает аргументы Container, имя файла и опционально режим выполнения и должна записать файл. Нередко вы не хотите или не можете сохранить всё, что есть в файле, открытом сейчас в Gwyddion. В этом случае можно получить текущее изображение (или другой объект данных) используя функцию gwy_app_data_browser_get_current() в точности как в модуле обработки данных и сохранить только это изображение. Функция детекции намного проще поскольку файл ещё только нужно записать и она может использовать только имя файла. Простой законченный пример файлового модуля, реализующего как загрузку, так и сохранение, приведён ниже. Настоящий модуль должен заботиться об обработке ошибок, которая не включена в этот пример для простоты и ясности:

plugin_type = "FILE"
plugin_desc = "High definition stable format store (.hdsf)"

def detect_by_name(filename):
    if filename.endswith(".hdsf"):
        return 100
    return 0

def detect_by_content(filename, head, tail, filesize):
    if head.startswith("HDSF:"):
        return 100
    return 0

def load(filename, mode=None):
    f = file(filename)
    header = f.readline()
    magic, xres, yres, xreal, yreal = header.split()
    xres, yres = int(xres), int(yres)
    xreal, yreal = float(xreal), float(yreal)
    container = gwy.Container()
    data_field = gwy.DataField(xres, yres, xreal, yreal)
    data_field.set_data([float(z) for z in f])
    container['/0/data'] = data_field
    return container

def save(container, filename, mode=None):
    data_field = gwy.gwy_app_data_browser_get_current(gwy.APP_DATA_FIELD)
    if not data_field:
        return False
    f = file(filename, 'w')
    f.write('HDSF: %d %d %g %g\n'
            % (data_field.get_xres(), data_field.get_yres(),
               data_field.get_xreal(), data_field.get_yreal()))
    f.write('\n'.join('%g' % z for z in data_field))
    f.write('\n')
    f.close()
    return True

Модули Pygwy перезагружаются если они были изменены со времени последнего использования. Таким образом, их можно менять при запущенном Gwyddion, при этом всегда будет использоваться последняя версия модуля. Однако, при этом они не регистрируются заново. Это означает, что записанный в файл код заново исполняется при перезапуске модуля, но значения переменных plugin_ имеют значение только при первой загрузке модуля. Последующие их изменения не будут влиять на работу пока Gwyddion не будет закрыт и перезапущен заново. Вследствие этого модуль работы с файлами не может внезапно стать модулем работы с графиками, возможно, что к лучшему.

Графический интерфейс пользователя

Если вы хотите добавить к модулю графический интерфейс, то можно использовать PyGTK совместно с виджетами Gwyddion для отображения данных. Следует отметить, что необходимо явно импортировать PyGTK командой

import gtk

поскольку, в отличие от gwy, оно не импортируется автоматически. Модуль с опциональным графическим интерфейсом пользователя должен в общем случае определить вариант run() с двумя аргументами и придерживаться заданного режима, действуя или сразу, или показывая сначала графический интерфейс. Типичной структурой модуля будет:

import gtk

# Define module info...

def run(data, mode):
    if mode == gwy.RUN_INTERACTIVE:
        dialogue = gtk.Dialog(title='Title...', flags=gtk.DIALOG_MODAL,
                              buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                       gtk.STOCK_OK, gtk.RESPONSE_OK))

        # Construct and show the user interface...

        response = dialogue.run()
        dialogue.destroy()
        if response != gtk.RESPONSE_OK:
            return

    # Process the data...

У скриптов Pygwy нет режима, они просто запускаются, таким образом, хотя они тоже могут показывать диалоговое окно похожим образом, если они так делают, то оно будет показываться всегда. Создание и использование флажков, кнопок, ползунков, элементов ввода и других элементов интерфейса пользователя выходит за рамки данного руководства. В этом случае вам нужно обратиться к подробной документации PyGTK.

Модуль может использовать настройки таким же образом, как и другие модули Gwyddion. В начале, прочесть параметры из файла настроек, обработать случай когда в файле настроек ещё нет значений параметров:

settings = gwy.gwy_app_settings_get()

tuning_parameter = 1.0
if '/module/mymodule/tuning_parameter' in settings:
    tuning_parameter = settings['/module/mymodule/tuning_parameter']

и в конце работы сохранить параметры в файл настроек чтобы их можно было восстановить при следующем запуске:

settings['/module/mymodule/tuning_parameter'] = tuning_parameter

Если нужно показать данные изображений, можно использовать виджет Gwyddion DataView. Он работает не совсем напрямую. Вместо того, чтобы передать DataField, который необходимо показать, вы всегда передаёте Container и затем указываете, где там найти что нужно показывать. Показ изображений реализуется путём создания так называемого слоя просмотра данных и добавления его к отображению данных. Простой модуль, который просто показывает текущее изображение может выглядеть как:

import gtk, gobject

plugin_menu = "/Pygwy Examples/Display Data"
plugin_desc = "Just display the current image"
plugin_type = "PROCESS"

def run(data, mode):
    dialogue = gtk.Dialog(title='Display Data', flags=gtk.DIALOG_MODAL,
                          buttons=(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE,))

    data_field = gwy.gwy_app_data_browser_get_current(gwy.APP_DATA_FIELD)

    container = gwy.Container()
    container['/0/data'] = data_field

    data_view = gwy.DataView(container)
    data_view.set_zoom(400.0/data_field.get_xres())
    data_view.set_data_prefix('/0/data')

    layer = gwy.LayerBasic()
    layer.set_data_key('/0/data')
    data_view.set_base_layer(layer)

    dialogue.vbox.pack_start(data_view, False, 4)

    dialogue.show_all()
    dialogue.run()
    dialogue.destroy()

Следует отметить использование функции set_zoom(), которая ограничивает размер области просмотра до 400 пикселей.

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

import gtk, gobject

plugin_menu = "/Pygwy Examples/Analyse"
plugin_desc = "Display the current image and selected coordinates"
plugin_type = "PROCESS"

def update_info_label(selection, i, label, vformat):
    coords = selection.get_data()
    if not coords:
        label.set_markup('')
        return

    label.set_markup('(%.*f, %.*f) %s'
                     % (vformat.precision, coords[0]/vformat.magnitude,
                        vformat.precision, coords[1]/vformat.magnitude,
                        vformat.units))

def run(data, mode):
    dialogue = gtk.Dialog(title='Analyse Data', flags=gtk.DIALOG_MODAL,
                          buttons=(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE,))

    data_field = gwy.gwy_app_data_browser_get_current(gwy.APP_DATA_FIELD)

    container = gwy.Container()
    container['/0/data'] = data_field

    data_view = gwy.DataView(container)
    data_view.set_zoom(400.0/data_field.get_xres())
    data_view.set_data_prefix('/0/data')

    layer = gwy.LayerBasic()
    layer.set_data_key('/0/data')
    data_view.set_base_layer(layer)

    layer = gobject.new(gobject.type_from_name('GwyLayerPoint'))
    data_view.set_top_layer(layer)
    layer.set_selection_key('/0/select/point')
    selection = layer.ensure_selection()

    dialogue.vbox.pack_start(data_view, False, 4)

    info_label = gtk.Label()
    info_label.set_alignment(0.0, 0.5)
    dialogue.vbox.pack_start(info_label, False, 4)

    value_format = data_field.get_value_format_xy(gwy.SI_UNIT_FORMAT_VFMARKUP)
    selection.connect('changed', update_info_label, info_label, value_format)

    dialogue.show_all()
    response = dialogue.run()
    dialogue.destroy()

Примечания

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

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

Функции на Python в Gwyddion, которые принимают массив в качестве аргумента, принимают любую последовательность из Python (список, кортеж (tuple), массив, ...) с элементами требуемого типа (обычно либо вещественного, либо целочисленного). И если они возвращают данные в формате типа массива, они всегда возвращают созданные заново списки, которые можно менять не влияя ни на что.

Обычно функции на Python не принимают отдельные аргументы длины последовательности, о которых может быть написано в документации, сгенерированной из C. Если существует какое-либо ограничение на число элементов, оно естественно распространяется и на Python. Например, метод set_data() для типа Selection просто принимает координаты последовательности списком. Однако, длина последовательности должна быть кратна «размеру объекта», т.е. числу координат, представляющих один выбранный объект для данного типа выбранного элемента.