2015-06-17 21:52:52 +00:00
|
|
|
|
|
2011-09-08 23:59:47 +00:00
|
|
|
|
*************
|
|
|
|
|
Best Practice
|
|
|
|
|
*************
|
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
When writing your own scripts python is great for new developers to pick up and become productive,
|
|
|
|
|
but you can also pick up odd habits or at least write scripts that are not easy for others to understand.
|
2011-09-08 23:59:47 +00:00
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
For your own work this is of course fine,
|
|
|
|
|
but if you want to collaborate with others or have your work included with blender there are practices we encourage.
|
2011-09-08 23:59:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Style Conventions
|
|
|
|
|
=================
|
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
For Blender/Python development we have chosen to follow python suggested style guide to avoid mixing styles
|
|
|
|
|
amongst our own scripts and make it easier to use python scripts from other projects.
|
2011-09-08 23:59:47 +00:00
|
|
|
|
|
|
|
|
|
Using our style guide for your own scripts makes it easier if you eventually want to contribute them to blender.
|
|
|
|
|
|
|
|
|
|
This style guide is known as pep8 and can be found `here <http://www.python.org/dev/peps/pep-0008>`_
|
|
|
|
|
|
|
|
|
|
A brief listing of pep8 criteria.
|
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
- camel caps for class names: MyClass
|
|
|
|
|
- all lower case underscore separated module names: my_module
|
|
|
|
|
- indentation of 4 spaces (no tabs)
|
|
|
|
|
- spaces around operators. ``1 + 1``, not ``1+1``
|
|
|
|
|
- only use explicit imports, (no importing ``*``)
|
|
|
|
|
- don't use single line: ``if val: body``, separate onto 2 lines instead.
|
2011-09-08 23:59:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
As well as pep8 we have other conventions used for blender python scripts.
|
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
- Use single quotes for enums, and double quotes for strings.
|
2011-09-08 23:59:47 +00:00
|
|
|
|
|
|
|
|
|
Both are of course strings but in our internal API enums are unique items from a limited set. eg.
|
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
2011-11-28 17:19:25 +00:00
|
|
|
|
bpy.context.scene.render.image_settings.file_format = 'PNG'
|
2011-09-08 23:59:47 +00:00
|
|
|
|
bpy.context.scene.render.filepath = "//render_out"
|
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
- pep8 also defines that lines should not exceed 79 characters,
|
|
|
|
|
we felt this is too restrictive so this is optional per script.
|
2011-09-08 23:59:47 +00:00
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
Periodically we run checks for pep8 compliance on blender scripts,
|
|
|
|
|
for scripts to be included in this check add this line as a comment at the top of the script.
|
2011-09-08 23:59:47 +00:00
|
|
|
|
|
|
|
|
|
``# <pep8 compliant>``
|
|
|
|
|
|
|
|
|
|
To enable line length checks use this instead.
|
|
|
|
|
|
|
|
|
|
``# <pep8-80 compliant>``
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
User Interface Layout
|
|
|
|
|
=====================
|
|
|
|
|
|
2012-07-29 01:38:31 +00:00
|
|
|
|
Some notes to keep in mind when writing UI layouts:
|
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
- UI code is quite simple. Layout declarations are there to easily create a decent layout.
|
|
|
|
|
|
|
|
|
|
General rule here: If you need more code for the layout declaration,
|
|
|
|
|
then for the actual properties, you do it wrong.
|
2012-07-29 01:38:31 +00:00
|
|
|
|
|
|
|
|
|
Example layouts:
|
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
- layout()
|
|
|
|
|
|
|
|
|
|
The basic layout is a simple Top -> Bottom layout.
|
2012-07-29 01:38:31 +00:00
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
layout.prop()
|
|
|
|
|
layout.prop()
|
|
|
|
|
|
|
|
|
|
- layout.row()
|
2012-07-29 01:38:31 +00:00
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
Use row(), when you want more than 1 property in one line.
|
2012-07-29 01:38:31 +00:00
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
row = layout.row()
|
|
|
|
|
row.prop()
|
|
|
|
|
row.prop()
|
|
|
|
|
|
|
|
|
|
- layout.column()
|
2012-07-29 01:38:31 +00:00
|
|
|
|
|
|
|
|
|
Use column(), when you want your properties in a column.
|
2015-06-17 21:52:52 +00:00
|
|
|
|
|
2012-07-29 01:38:31 +00:00
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
col = layout.column()
|
|
|
|
|
col.prop()
|
|
|
|
|
col.prop()
|
2012-07-29 01:38:31 +00:00
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
- layout.split()
|
|
|
|
|
|
|
|
|
|
This can be used to create more complex layouts.
|
|
|
|
|
For example you can split the layout and create two column() layouts next to each other.
|
2012-07-29 01:38:31 +00:00
|
|
|
|
Don't use split, when you simply want two properties in a row. Use row() for that.
|
2015-06-17 21:52:52 +00:00
|
|
|
|
|
2012-07-29 01:38:31 +00:00
|
|
|
|
.. code-block:: python
|
2015-06-17 21:52:52 +00:00
|
|
|
|
|
|
|
|
|
split = layout.split()
|
|
|
|
|
|
|
|
|
|
col = split.column()
|
|
|
|
|
col.prop()
|
|
|
|
|
col.prop()
|
|
|
|
|
|
|
|
|
|
col = split.column()
|
|
|
|
|
col.prop()
|
|
|
|
|
col.prop()
|
|
|
|
|
|
2012-07-29 01:38:31 +00:00
|
|
|
|
Declaration names:
|
|
|
|
|
|
|
|
|
|
Try to only use these variable names for layout declarations:
|
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
- row for a row() layout
|
|
|
|
|
- col for a column() layout
|
|
|
|
|
- split for a split() layout
|
|
|
|
|
- flow for a column_flow() layout
|
|
|
|
|
- sub for a sub layout (a column inside a column for example)
|
2011-09-08 23:59:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Script Efficiency
|
|
|
|
|
=================
|
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
|
2011-10-17 10:43:55 +00:00
|
|
|
|
List Manipulation (General Python Tips)
|
|
|
|
|
---------------------------------------
|
2011-09-08 23:59:47 +00:00
|
|
|
|
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
|
|
|
|
Searching for list items
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
|
|
|
|
In Python there are some handy list functions that save you having to search through the list.
|
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
Even though you're not looping on the list data **python is**,
|
|
|
|
|
so you need to be aware of functions that will slow down your script by searching the whole list.
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
|
|
my_list.count(list_item)
|
|
|
|
|
my_list.index(list_item)
|
|
|
|
|
my_list.remove(list_item)
|
|
|
|
|
if list_item in my_list: ...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Modifying Lists
|
|
|
|
|
^^^^^^^^^^^^^^^
|
2015-06-17 21:52:52 +00:00
|
|
|
|
In python we can add and remove from a list, this is slower when the list length is modified,
|
|
|
|
|
especially at the start of the list, since all the data after the index of
|
|
|
|
|
modification needs to be moved up or down 1 place.
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
The most simple way to add onto the end of the list is to use
|
|
|
|
|
``my_list.append(list_item)`` or ``my_list.extend(some_list)`` and the fastest way to
|
|
|
|
|
remove an item is ``my_list.pop()`` or ``del my_list[-1]``.
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
To use an index you can use ``my_list.insert(index, list_item)`` or ``list.pop(index)``
|
|
|
|
|
for list removal, but these are slower.
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
|
|
|
|
Sometimes its faster (but more memory hungry) to just rebuild the list.
|
|
|
|
|
|
|
|
|
|
|
2012-08-19 15:28:24 +00:00
|
|
|
|
Say you want to remove all triangular faces in a list.
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
|
|
|
|
Rather than...
|
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
2012-08-19 15:28:24 +00:00
|
|
|
|
faces = mesh.tessfaces[:] # make a list copy of the meshes faces
|
2011-10-17 10:43:55 +00:00
|
|
|
|
f_idx = len(faces) # Loop backwards
|
|
|
|
|
while f_idx: # while the value is not 0
|
|
|
|
|
f_idx -= 1
|
|
|
|
|
|
|
|
|
|
if len(faces[f_idx].vertices) == 3:
|
|
|
|
|
faces.pop(f_idx) # remove the triangle
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
It's faster to build a new list with list comprehension.
|
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
2012-08-19 15:28:24 +00:00
|
|
|
|
faces = [f for f in mesh.tessfaces if len(f.vertices) != 3]
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Adding List Items
|
|
|
|
|
^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
2012-08-19 15:28:24 +00:00
|
|
|
|
If you have a list that you want to add onto another list, rather than...
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
2011-10-17 13:54:47 +00:00
|
|
|
|
for l in some_list:
|
|
|
|
|
my_list.append(l)
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
|
|
|
|
Use...
|
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
|
|
my_list.extend([a, b, c...])
|
|
|
|
|
|
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
Note that insert can be used when needed,
|
|
|
|
|
but it is slower than append especially when inserting at the start of a long list.
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
|
|
|
|
This example shows a very sub-optimal way of making a reversed list.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
|
|
reverse_list = []
|
|
|
|
|
for list_item in some_list:
|
|
|
|
|
reverse_list.insert(0, list_item)
|
|
|
|
|
|
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
Python provides more convenient ways to reverse a list using the slice method,
|
|
|
|
|
but you may want to time this before relying on it too much:
|
2012-08-19 15:28:24 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
|
|
some_reversed_list = some_list[::-1]
|
|
|
|
|
|
|
|
|
|
|
2011-10-17 10:43:55 +00:00
|
|
|
|
Removing List Items
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
|
|
|
|
Use ``my_list.pop(index)`` rather than ``my_list.remove(list_item)``
|
|
|
|
|
|
|
|
|
|
This requires you to have the index of the list item but is faster since ``remove()`` will search the list.
|
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
Here is an example of how to remove items in 1 loop,
|
|
|
|
|
removing the last items first, which is faster (as explained above).
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
|
|
list_index = len(my_list)
|
|
|
|
|
|
|
|
|
|
while list_index:
|
|
|
|
|
list_index -= 1
|
|
|
|
|
if my_list[list_index].some_test_attribute == 1:
|
|
|
|
|
my_list.pop(list_index)
|
|
|
|
|
|
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
This example shows a fast way of removing items,
|
|
|
|
|
for use in cases where you can alter the list order without breaking the scripts functionality.
|
|
|
|
|
This works by swapping 2 list items, so the item you remove is always last.
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
|
|
pop_index = 5
|
|
|
|
|
|
|
|
|
|
# swap so the pop_index is last.
|
|
|
|
|
my_list[-1], my_list[pop_index] = my_list[pop_index], my_list[-1]
|
|
|
|
|
|
|
|
|
|
# remove last item (pop_index)
|
|
|
|
|
my_list.pop()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
When removing many items in a large list this can provide a good speedup.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Avoid Copying Lists
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
When passing a list/dictionary to a function,
|
|
|
|
|
it is faster to have the function modify the list rather than returning
|
|
|
|
|
a new list so python doesn't have to duplicate the list in memory.
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
2012-08-19 15:28:24 +00:00
|
|
|
|
Functions that modify a list in-place are more efficient than functions that create new lists.
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
This is generally slower so only use for functions when it makes sense not to modify the list in place.
|
|
|
|
|
|
|
|
|
|
>>> my_list = some_list_func(my_list)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
This is generally faster since there is no re-assignment and no list duplication.
|
|
|
|
|
|
|
|
|
|
>>> some_list_func(vec)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Also note that passing a sliced list makes a copy of the list in python memory
|
|
|
|
|
|
|
|
|
|
>>> foobar(my_list[:])
|
|
|
|
|
|
|
|
|
|
If my_list was a large array containing 10000's of items, a copy could use a lot of extra memory.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Writing Strings to a File (Python General)
|
|
|
|
|
------------------------------------------
|
|
|
|
|
|
|
|
|
|
Here are 3 ways of joining multiple strings into 1 string for writing
|
|
|
|
|
|
|
|
|
|
This really applies to any area of your code that involves a lot of string joining.
|
|
|
|
|
|
|
|
|
|
|
2012-08-19 15:28:24 +00:00
|
|
|
|
Python’s string addition, *don't use if you can help it, especially when writing data in a loop.*
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
|
|
|
|
>>> file.write(str1 + " " + str2 + " " + str3 + "\n")
|
|
|
|
|
|
|
|
|
|
|
2012-08-19 15:28:24 +00:00
|
|
|
|
String formatting. Use this when you're writing string data from floats and ints
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
|
|
|
|
>>> file.write("%s %s %s\n" % (str1, str2, str3))
|
|
|
|
|
|
|
|
|
|
|
2012-08-19 15:28:24 +00:00
|
|
|
|
Python’s string joining function. To join a list of strings
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
|
|
|
|
>>> file.write(" ".join([str1, str2, str3, "\n"]))
|
|
|
|
|
|
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
join is fastest on many strings,
|
|
|
|
|
`string formatting <http://docs.python.org/py3k/library/string.html#string-formatting>`__
|
|
|
|
|
is quite fast too (better for converting data types). String arithmetic is slowest.
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Parsing Strings (Import/Exporting)
|
|
|
|
|
----------------------------------
|
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
Since many file formats are ASCII,
|
|
|
|
|
the way you parse/export strings can make a large difference in how fast your script runs.
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
2012-08-19 15:28:24 +00:00
|
|
|
|
There are a few ways to parse strings when importing them into Blender.
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
|
2011-10-17 10:43:55 +00:00
|
|
|
|
Parsing Numbers
|
|
|
|
|
^^^^^^^^^^^^^^^
|
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
Use ``float(string)`` rather than ``eval(string)``, if you know the value will be an int then ``int(string)``,
|
|
|
|
|
float() will work for an int too but it's faster to read ints with int().
|
|
|
|
|
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
|
|
|
|
Checking String Start/End
|
|
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
2012-01-04 03:22:37 +00:00
|
|
|
|
If you're checking the start of a string for a keyword, rather than...
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
|
|
|
|
>>> if line[0:5] == "vert ": ...
|
|
|
|
|
|
|
|
|
|
Use...
|
|
|
|
|
|
|
|
|
|
>>> if line.startswith("vert "):
|
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
Using ``startswith()`` is slightly faster (approx 5%) and also avoids a possible
|
|
|
|
|
error with the slice length not matching the string length.
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
|
|
|
|
my_string.endswith("foo_bar") can be used for line endings too.
|
|
|
|
|
|
2012-08-19 15:28:24 +00:00
|
|
|
|
If you are unsure whether the text is upper or lower case use ``lower()`` or ``upper()`` string function.
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
|
|
|
|
>>> if line.lower().startswith("vert ")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Use try/except Sparingly
|
|
|
|
|
------------------------
|
|
|
|
|
|
2012-03-07 17:36:38 +00:00
|
|
|
|
The **try** statement is useful to save time writing error checking code.
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
However **try** is significantly slower than an **if** since an exception has to be set each time,
|
|
|
|
|
so avoid using **try** in areas of your code that execute in a loop and runs many times.
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
There are cases where using **try** is faster than checking whether the condition will raise an error,
|
|
|
|
|
so it is worth experimenting.
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Value Comparison
|
|
|
|
|
----------------
|
|
|
|
|
|
2015-06-17 21:52:52 +00:00
|
|
|
|
Python has two ways to compare values ``a == b`` and ``a is b``,
|
|
|
|
|
the difference is that ``==`` may run the objects comparison function ``__cmp__()`` whereas ``is`` compares identity,
|
|
|
|
|
that both variables reference the same item in memory.
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
|
|
|
|
In cases where you know you are checking for the same value which is referenced from multiple places, ``is`` is faster.
|
|
|
|
|
|
|
|
|
|
|
2012-01-04 03:22:37 +00:00
|
|
|
|
Time Your Code
|
|
|
|
|
--------------
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
2012-08-19 15:28:24 +00:00
|
|
|
|
While developing a script it's good to time it to be aware of any changes in performance, this can be done simply.
|
2011-10-17 10:43:55 +00:00
|
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
|
|
import time
|
|
|
|
|
time_start = time.time()
|
|
|
|
|
|
|
|
|
|
# do something...
|
|
|
|
|
|
|
|
|
|
print("My Script Finished: %.4f sec" % time.time() - time_start)
|
2015-06-17 21:52:52 +00:00
|
|
|
|
|