Читать интересную книгу Написание скриптов для Blender 2.49 - Michel Anders

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 49 50 51 52 53 54 55 56 57 58

def sethome():

   from os import environ

   if not 'HOME' in environ:

      if 'USERPROFILE'in environ:

         environ['HOME'] = environ['USERPROFILE']

Наша следующая задача в том, чтобы выяснить, на какой FTP-сервер пользователь хочет загрузить результат рендера. Мы запоминаем это в ключе реестра Блендера, чтобы не надоедать пользователю с приглашением всякий раз, когда он хочет отправить свой рендер. Функция getftphost() принимает аргумент reuse (повторное использование), который может быть использован для очистки этого ключа, если он установлен в False (для обеспечения возможности выбора другого FTP-сервера), но переписать интерфейс пользователя, чтобы предлагать ему такую возможность, мы оставляем в качестве упражнения читателю.

Фактический код начинает с поиска ключа реестра (с диска, если необходимо, следовательно, по умолчанию аргумент True, выделено). Если там нет ключа, или он не содержит имя сервера, мы запрашиваем у пользователя имя FTP-сервера посредством всплывающего окна. Если пользователь его не вводит, мы заканчиваем, возбуждая исключение. В противном случае, мы сохраняем имя хоста - сначала создаём словарь, если он ещё не существует, и сохраняем этот словарь в реестр Блендера. Наконец, мы возвращаем сохранённое имя хоста.

def getftphost(reuse=True):

   dictname = 'ftp'

   if reuse == False:

      Registry.RemoveKey(dictname)

   d = Registry.GetKey(dictname,True)

   if d == None or not 'host' in d:

      host = Draw.PupStrInput("Ftp hostname:", "", 45)

      if host == None or len(host) == 0 :

         raise Exception("no hostname specified")

      if d == None :

         d ={}

      d['host'] = host

      Registry.SetKey(dictname,d,True)

   return d['host']

Нам нужна другая вспомогательная функция, чтобы убедиться, что на диск в качестве изображения Блендера сохранено последнее отрендеренное изображение, которое присутствует как изображение с именем  Render Result, но это изображение не пишется на диск автоматически. Функция imagefilename() принимает изображение Блендера как аргумент, и, во-первых, проверяет, существует ли корректное имя файла, связанное с ним (выделено). Если нет, она создает имя файла из имени изображения, добавляя расширение .tga (изображения можно сохранять только как файлы TARGA). Затем создаётся полный путь из этого имени файла и пути к временному каталогу. Теперь, когда у нас есть корректное имя файла, мы его сохраняем, вызывая метод save(), и возвращая имя файла:

def imagefilename(im):

   filename = im.getFilename()

   if filename == None or len(filename) == 0:

      filename = im.getName()+'.tga'

      filename = os.path.join(tempfile.gettempdir(),

                              filename)

      im.setFilename(filename)

   im.save()

   return filename

Когда мы загружаем файл на FTP-сервер, мы хотим убедиться, что мы не перезапишем существующий файл. Если мы обнаружим, что файл с данным именем уже существует, мы хотели бы иметь функцию, которая создаёт новое имя файла предсказуемым способом, похожим на то, как ведёт себя Блендер при создании имён для объектов Блендера. Мы хотели бы сохранять расширение файла, так что мы не можем просто прилепить к имени цифровой суффикс. Функция nextfile(), следовательно, сначала разделяет имя пути и часть с расширением. Она использует функции split() и splitext() из модуля os.path, чтобы оставить нам чистое имя.

Если имя уже заканчивается на суффикс, состоящий из точки и некоторого числа (например, .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, ссылка на его документацию: http://docs.python.org/library/ftplib.html.) Мы грациозно заканчиваем связь в FTP-сервером, вызывая метод quit(), и сообщаем пользователю о завершении задачи, показывая сообщение с упоминанием имени целевого файла, так как оно может отличаться от ожидаемого, если существует файл с аналогичным именем:

1 ... 49 50 51 52 53 54 55 56 57 58
На этом сайте Вы можете читать книги онлайн бесплатно русская версия Написание скриптов для Blender 2.49 - Michel Anders.
Книги, аналогичгные Написание скриптов для Blender 2.49 - Michel Anders

Оставить комментарий