Чтение онлайн

ЖАНРЫ

Написание скриптов для Blender 2.49

Anders Michel

Шрифт:

Если имя уже заканчивается на суффикс, состоящий из точки и некоторого числа (например, .42), мы хотели бы увеличить это число. Это именно то, что выполняет довольно пугающая выделенная строка. Функция sub модуля Питона re принимает регулярное выражение как первый аргумент (мы используем здесь сырую строку, так что нам не надо экранировать обратную косую черту), и проверяет, соответствует ли это регулярное выражение своему третьему аргументу (name, в данном случае). Регулярное выражение, используемое здесь, (\.(\d+)$) совпадает с точкой, за которой следуют одна или более десятичных цифр, но только, если эти цифры являются последними символами. Если есть соответствие образцу, он заменяется вторым аргументом функции sub. В нашем случае, замена - это не простая строка, а лямбда-функция (то есть, безымянная), в которую мы передаём объект сопоставления, и ожидаем, что она вернёт строку.

Мы окружили часть цифр нашего регулярного выражения круглыми скобками, теперь мы можем просто извлечь эти цифры (без первоначальной точки), вызвав метод group объекта сопоставления. Мы передаем ему 1 в качестве аргумента, так как первые открывающие скобки обозначают первую группу (группа 0 является всем образцом целиком). Мы преобразуем эту строку цифр в целое, используя встроенную функцию int, добавляем к ней 1, и преобразуем её обратно в строку с функцией str. До того, как этот результат автоматически будет возвращён из лямбда-функции, мы снова добавляем точку, чтобы соответствовать нашему желаемому образцу.

Мы завершаем проверкой, отличается ли результирующее имя от оригинального. Если они совпадают, значит оригинальное имя не соответствовало нашему образцу, и мы просто добавляем .1 к имени. Наконец, мы восстанавливаем полное имя файла, добавляя расширение, и вызывая функцию join из модуля os.path, чтобы добавить путь платформо-независимым способом:

def nextfile(filename):

(path,base) = os.path.split(filename)

(name,ext) = os.path.splitext(base)

new = re.sub(r'\.(\d+)$',

lambda m:'.'+ str(1+int(m.group(1))),

name)

if new == name :

new = name + '.1'

return os.path.join(path,new+ext)

Теперь мы полностью готовы заняться реальной работой загрузки файла на FTP-сервер. Сначала мы удостоверимся, что наше окружение имеет переменную HOME, вызывая функцию sethome. Затем, мы извлекаем имя хоста FTP-сервера, на который мы хотим загрузить (вполне законно, между прочим, ввести IP-адрес вместо имени хоста):

if __name__ == "__main__":

sethome

host = getftphost

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

try:

(user,acct,password) = \

netrc.netrc.authenticators(host)

except:

acct=None

user = Draw.PupStrInput(

'No .netrc file found, enter username:',

"",75)

password = Draw.PupStrInput('Enter password:',"",75)

Отрендеренное изображение было сохранено как объект Блендера Image с именем Render Result. Следующая вещь, которую мы делаем - извлекаем ссылку на это изображение и убеждаемся, что оно сохранено на диск. Функция imagefilename, которую мы определили раньше, возвращает имя файла загруженного изображения.

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

im = Image.Get('Render Result')

filename = imagefilename(im)

ftp = FTP(host,user,password,acct)

files = ftp.nlst

Хм, автор так аккуратно обрабатывает ситуации отсутствия файла .netrc, имени с паролем в нём, сохранённости рендеренного изображения, а о работоспособности FTP-сервера вообще не упоминает. По-моему, ситуацию отсутствия связи, а также неверности логина или пароля тоже необходимо обрабатывать через try/except.
– прим. пер.

Поскольку мы хотим убедиться, что мы не перезаписываем никаких файлов на FTP-сервере, мы удаляем путь из имени файла нашего загруженного изображения с помощью функции basename и сравниваем результат со списком имён файлов, извлеченным с сервера (выделено). Если имя файла уже присутствует, мы генерируем новое имя функцией nextfile и снова проверяем, и продолжаем проверять, пока у нас, наконец, не появится имя файла, которое в данный момент отсутствует на FTP-сервере.

dstfilename = os.path.basename(filename)

while dstfilename in files:

dstfilename = nextfile(dstfilename)

Затем, мы выгружаем наш файл изображения, вызывая метод storbinary. Этот метод принимает имя целевого файла с префиксом STOR, как первый аргумент, и открытый файловый дескриптор как второй аргумент. Мы предоставляем последний, вызывая встроенную функцию Питона open с именем нашего файла изображения в качестве единственного аргумента. (Если нужна дополнительная информация о довольно диковинном поведении модуля ftplib, ссылка на его документацию: Мы грациозно заканчиваем связь в FTP-сервером, вызывая метод quit, и сообщаем пользователю о завершении задачи, показывая сообщение с упоминанием имени целевого файла, так как оно может отличаться от ожидаемого, если существует файл с аналогичным именем:

ftp.storbinary('STOR '+dstfilename,open(filename))

ftp.quit

Draw.PupMenu('Render result stored as "%s"%s|Ok'

%(dstfilename,'%t'))

Полный код доступен как ftp.py в файле ftp.blend. Его можно запустить из текстового редактора, но в общем случае, несомненно, значительно удобнее поместить ftp.py в каталог скриптов Блендера. Скрипт сконфигурирован так, чтобы он был доступен в меню Файл | Экспорт (File | Export).

Весенняя уборка - архивация неиспользуемых изображений

Через некоторое время у любого долгоживущего проекта набирается много хлама. Например, изображения текстур, которые Вы пытались применить, но они были отвергнуты в пользу более подходящих. Этот скрипт поможет нам найти все файлы в выбранном каталоге, на которые нет ссылок в нашем .blend файле, и упаковать их в ZIP-архив.

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

Поделиться с друзьями: