Dev:2.4/Py/Scripts/Guidelines/previous work

提供: wiki
移動先: 案内検索

General Script Functionality

Blender 2.41++

This section sets out functionality and standards for scripts to be included in Blender.

Python Version

Scripts distributed with Blender should be compatible with Python 2.3.

Script Context

Before a user executes a script they need to know what it is operating on.

This may be set in the name of the script (in the menu), its tooltip or the scripts user interface.

Contexts may include...

  • Active Object (only ever 1 object)
obj = Blender.Scene.GetCurrent().getActiveObject()
  • Selection (Assumed to mean visible Selection of current scene)
scn = Blender.Scene.GetCurrent()<br>objects = [ob for ob in scn.getChildren() if ob.sel if ob.Layers & scn.Layers]
or...
objects = Blender.Object.GetSelected()
  • Scene All objects in the current scene
objects = Blender.Scene.GetCurrent().getChildren()
  • All Scenes (In unusual situations you may need to operate on all scenes)
objects = Blender.Object.Get()

The context also needs to be set as to the type of data that is used. So if a script just operates on meshes, this needs to be stated. If no type is defined then the user will assume that its operating on objects/data of all types.

Rationale

The user needs to know what the script is modifying, especialy when the data that is affected may not be visible at the time the script runs.

The worst case is a script modifys data in hidden layers or another scene, the user saves over the older file and then discovers later that the hidden data has been modified and needs to be made again.


New Objects

Scripts that create new objects by importing, generation or working from the current selection must set all new objects selected and other objects unselected.

New objects must never conflict with or overwrite existing data. scripts that use object or data names need to work properly when objects with the same names already exist. (be wary of

NMesh.PutRaw

)


New objects must be...

  • selected, unselecting all previously selected objects in the current scene.
  • unique and not interfere with the existing data
  • created in a visible layer

Rationale

This is ideal because the user knows that all resulting data from the script is selected and can deal with the new data straight away.

e.g. Move to another layer, remove an unwanted import, link to an empty scene or switch to local view to better see the newly created data.

It is also consistant with current duplicate functionality.

Scenes Context

Scripts, exporters specifically, need to only deal with the current scene unless otherwise specified.

The usual mistake is to use

Blender.Object.Get()

as a list of objects to export from.

This will export all scenes on-top of each other and may take a long time as well as giving the user a bad result.

Use

Blender.Scene.GetCurrent().getChildren()

instead, so that all objects are taken from the scene that the user is currently working on.

Progress

For scripts that could take more then 1 second, the user needs to know that the script is processing data and when the script is finished. Use

Blender.Window.WaitCursor(1/0)

to let the user know when the script is finished. You can also use

Blender.Window.DrawProgressBar(0.0, 'progressText')

Errors

Scripts should not cause python to raise errors.

Problems with the users data, should be detected and a useful error appear.

Error messages also need to say if they stop the script from completeing or just a warning. e.g.:

Blender.Draw.PupMenu('ERROR%t|Select a mesh as your active object. aborting.')

or

Blender.Draw.PupMenu('WARNING%t|No materials applied to some objects. continuing.')

Exception Handling

Using try/except blocks to handle errors is a good Thing and should be encouraged.

Good exception handling can be used to make sure a script does not leave the Blender data in a half-finished or damaged state or to simply do general cleanup.

Documenting Scripts

Willian- Comment?

Security

Don't use

exec

on unknown data, ask before overwriting? - expand

Import/Export API

Blender has a growing list of importers and exporters. Standardized function names allows us to use these scripts in a generic way.

The most obvious use for this is as a file format converter, Blender supports these formats:

  • 3DS
  • OpenFlight FLT
  • dxf
  • obj
  • Softaimage XSI

With standards, we can have importers/exporters act in a predictable way for the user as well as making them automated from other python scripts.

Here is an initial draft for a importer exporter API as well as some examples.

File Naming

Files should start with either

import

or

export

then prefixed with the format they work with e.g.:

import_xsi.py
import_obj.py

,

export_3ds.py

loading and saving function names

To be used by external scripts, the script's own user interface, or the file selector. These scripts cannot contain any user interaction.

True

or

False

are returned based on weather the operation was successfull. These functions take 1 absolute path to a file and any number of optional keywords:

read(file, *keywords*)

or

write(file, *keywords*)

e.g.

write(file, EXPORT_EDGES=False, EXPORT_NORMALS=False, EXPORT_MTL=True, EXPORT_COPY_IMAGES=False, EXPORT_APPLY_MODIFIERS=True):

User interface (read_ui/write_ui)

This function handles all the user interation, and gathers the optional arguments to pass to read/write. UI Drawing, Calls to the FileSelector are set here.

This function should never be called from anywhere other then the script itself.

In most cases,

Blender.Window.Fileselector

will call this function and all it will take is the filename.

Optional keywords

These optional keyword arguments can be passed to read and write functions. Keywords are specific to that function and are used to set options as with the example above. The keywords all need to have a default set so that the functions are optional, and scripts act as expected without having to pass specific keywords. - In most cases scripts that call the importers and exporters will not set non-default parameters. However, this may be desirable under some conditions.

Options keywords may be...

  • IMPORT_AT_CURSOR
    
    - Imports objects using the 3d cursor loaction for the 0/0/0 center.
  • IMPORT_INTO_NEW_SCENE
    
    - Creates a scene for the imported data.
  • IMPORT_MATERIAL
    
    - Creates materials for the imported file.
  • IMPORT_MESH_FGONS
    
    - Imports faces with more then 4 verts as fgons.

...and for exporters...

  • EXPORT_SCENE
    
    - Exports all objects in the scene, not just the selected, visible ones.
  • EXPORT_COPY_IMAGES
    
    - Copys all images to the output dir of the exported file.
  • EXPORT_SCALE
    
    - Scales the exported data by this factor.

File compatibility testing function, importer only (iscompat)

This function takes a pathname and returns True/False depending on whether the file at the given path can be imported, this can be used to see if an importer can import a given file. For basic testing the file suffix is enough.

e.g.:

return file.lower.endswith('.obj')

Data Compatibility Constant

Every i/o scipt must have a variable called

__FORMAT_COMPATIBILITY__'''

, this is a list of standard names that define what kinds of data the script can read/write. e.g.

__FORMAT_COMPATIBILITY__ = 'MESH', 'MESH_NAME', 'MESH_VERT', 'MESH_EDGE', 'MESH_FACE', 'MESH_MATERIAL', 'MATERIAL', 'MESH_TEXFACE'

The rationale for having this variable is that an external script can load an importer & exporter, telling the user what data will be lost if a conversion is made.

As scripts are converted the list of names for supported data will be added to. since there is a lot of data that blender does not import/export to, once defined here, scripts will be expected to use these names.

Blender Context

At the time of writing, importers and exporters have no defined context. A context standard is needed so read and write can be called one after the other, converting the data, without losing objects in a hidden layer or through it being unselected.

Currently some scripts export only the active object

(Blender.Object.GetSelection()[0])

Other scripts export objects from all scenes at once -

Blender.Object.Get()

- Some scripts silently import over other meshes with existing names.

Blender.NMesh.PutRaw()

The new standard will be to import and export the user selection. - That is...

  • Importing replaces the selection in the current scene with a new set of selected objects in the visible layers.
  • Exporting uses the current scenes visible selection as the data to export.

...In situations where a different context is required, an optional argument can be used to change the default context.

Code Layout

All importers and exporters must follow this structure, foobar is used inplace of a real format name.

#!BPY
"""
Name: 'FooBar Model (.fbr)...'
Blender: 232
Group: 'Import'
Tooltip: 'Import a foobar file.'
"""
__bpydoc__ = """\
Breif format description and any special notes.
"""

__FORMAT_COMPATIBILITY__ = 'MESH', 'MESH_VERT', 'MESH_FACE', 'MESH_MATERIAL'

import Blender # Imports are optional, import whatever needs importing here.

def iscompat(file): # Only for importers
  return file.lower().endswith('.fbr')

def read(file, IMPORT_AT_CURSOR=False, IMPORT_SCALE=1.0, IMPORT_INTO_NEW_SCENE=False):
   # open and import the file
   return True # Import was a success
# This function can work however you like
# and should only be used by the script its self.
def read_ui(file): 
  # Gather user option via PupMenu's, PupInput's, PupBlocks
  # or a Registered python user interface.
  
  # User interface aborts and tells
  # the user if the file isnt the right format.
  if not iscompat(file): ...

  # Importing can take some time,
  #best give some visual feedback.
  Blender.Window.WaitCursor(1) 
  time1 = Blender.sys.time()
  
  # We know the file exists and is compatible, import it.
  read(file, IMPORT_SOME_OPTION=True...) 

  Blender.Window.WaitCursor(0)
  # Usefull for the user to know the time an operation takes.
  print 'Foobar exported in %.4f sec.' % ( Blender.sys.time()-time1 ) 


# This "if" is used so that importing the
# module dosent run the import user interface.
if __name__ == '__main__': 
  Blender.Window.FileSelector(read_ui, 'Import FooBar', Blender.sys.makename(ext=".fbr"))

Accessing read/write from other scripts

One of the main goals is to make the importers and exporters accessable as modules. Here is an example if a script that imports and exports a file.

import import_xsi
import export_3ds
file= 'C:\\temp\\testfile.xsi'
outfile= 'C:\\temp\\testfile.3ds'
if import_xsi.iscompat(file):
  retval= import_xsi.read(file)
  if retval:
    export_3ds.write(outfile)
  else:
    print 'File not written, error occured'