Difference between revisions of "HowTo:Use POV-Ray with Blender/Developers page"

From POV-Wiki
Jump to navigation Jump to search
m
m (Python tips for new developers)
Line 4: Line 4:
 
*define some code to perform actions, mostly through 'operators' or 'functions'
 
*define some code to perform actions, mostly through 'operators' or 'functions'
 
*and make sure these are registered so that they can be used.
 
*and make sure these are registered so that they can be used.
 +
 +
===bpy===
 +
Blender has an embedded Python interpreter which is loaded when Blender is started and stays active while Blender is running. This interpreter runs scripts to draw the user interface and is used for some of Blender’s internal tools as well. Blender’s embedded interpreter provides a typical Python environment, so code from tutorials on how to write Python scripts can also be run with Blender’s interpreter. Blender provides its Python [https://docs.python.org/3/tutorial/modules.html modules], such as ''bpy'' and ''mathutils'', to the embedded interpreter so they can be imported into a script and give access to Blender’s data, classes, and functions. Scripts that deal with Blender data will need to '''import''' the modules to work. So of all modules, if no other, bpy is generally always imported in blender scripts. (except in interactive console were this import is automatically done for convenience)
  
 
===bl_info===
 
===bl_info===
Line 51: Line 54:
 
<source lang="py">tracker_url</source>
 
<source lang="py">tracker_url</source>
 
The POV add-on being a bundled part of Blender it does have its own bug tracker entry associated with it and this key will provide a pointer to it.
 
The POV add-on being a bundled part of Blender it does have its own bug tracker entry associated with it and this key will provide a pointer to it.
 +
===Comments===
 +
Single line comments begin with a hash (#) character. While multiline comments can begin with triple quotes. It is the convention that three double quotes are used for ''docstrings'', a specific kind of comment not doing anything in the program but still appearing when console is used to get help about a function.
 +
<source lang="py">
 +
"""This is an explanation of general relevance"""
 +
</source>
 +
It is good practice to put a doc string at the start of every class to describe it and also at the start of every file to explain its relative use.
 +
 +
 +
===Operators===
 +
Most add-ons define new operators, they are classes that implement specific functionality.
 +
The actual definition of the operator takes the form a class that is derived from ''bpy.types.Operator''
 +
 +
<source lang="py">
 +
import bpy
 +
class MyTool(bpy.types.Operator):
 +
    """Do things to an object"""   
 +
    bl_idname = "object.do_things_to_object"
 +
    bl_label = "Do things to an object"
 +
    bl_options = {'REGISTER', 'UNDO'}
 +
</source>
 +
The docstring at the start of the class definition will be used as a tooltip anywhere this operator will be available, for example in a menu, while the bl_label defines the actual label used in the menu entry itself. Here we kept both the same. Operators will be part of Blender's data, and operators are stored in the module  bpy.ops. This bl_idname will make sure this operator's entry will be called bpy.ops.object.move_object. Operators are normally registered in order to make them usable and that is indeed the default of bl_options. However, if we also want theadd-on to show up in the history so it can be undone or repeated, we should add UNDO to the set of flags that is assigned to ''bl_options'', as is done here.
 +
 +
===Functions===
 +
Functions are defined with the ''def'' keyword, followed by the name of the function and a pair of parenthesis. When the function is referenced to move it around and pass it along between files, it no longer needs the parenthesis, but pretty much every other time, they do follow function's name and in between them, some attributes can eventually be specified.
 +
 +
===The execute() function===
 +
An operator class can have any number of member functions but to be useful it normally
 +
overrides the execute() function:   
 +
<source lang="py">
 +
def execute(self, context):
 +
    context.active_object.rotation.z += 33
 +
    return {'FINISHED'}
 +
</source>
 +
 +
The execute() function is passed a reference to a ''context'' object. This context object contains among other things an active_object attribute which points to, Blenders active object. Each object in Blender has a rotation attribute which is a vector with x, y and z components. Changing the rotation of an object is as simple as changing one of these components, which is exactly what we do in line 2.
 +
 +
The execute() function signals successful completion by returning a set of flags, in this case a set consisting solely of a string FINISHED.
 +
 +
===Registering and adding a menu entry===
 +
Defining an operator is not in itself enough to make this operator usable. In order for the user to find and use an operator, for example by pressing SPACE in the 3D view window and typing the label of the operator, we must register the operator. Adding a registered operator to a menu requires a separate action.
 +
<source lang="py">
 +
def register():   
 +
    bpy.utils.register_module(__name__)
 +
    bpy.types.VIEW3D_MT_object.append(menu_func)
 +
def unregister():
 +
    bpy.utils.unregister_module(__name__)
 +
    bpy.types.VIEW3D_MT_object.remove(menu_func)
 +
def menu_func(self, context):
 +
    self.layout.operator(MyTool.bl_idname, icon='MESH_CUBE')
 +
</source>
 +
 +
When we check the ''Enable an add-on'' check-box in the ''user preferences'', Blender will look for a '''register()''' function and execute it. Likewise, when disabling an add-on the '''unregister()''' function is called. Here we use this to both register our operator with Blender and to insert a menu entry that refers to our operator.
 +
The '''bpy.utils.register_module()''' function will register any class in a module that has ''REGISTER'' defined in its ''bl_options'' set. In order to create a menu entry we have to do two things:
 +
*Create a function that will produce a menu entry
 +
*And append this function to a suitable menu.
 +
Now almost everything in Blender is available as a Python object and menus are no exception. We want to add our entry to the Object menu in the 3D view so we call <source lang="py">bpy.types.VIEW3D_MT_object.append()</source> and pass it a reference to the function we define a little below. How do we know how this menu object is called? If you have checked ''File⇒ User preferences ⇒ Interface ⇒ Python Tooltips'' the name of the menu will be shown in a tooltip when you hover over a menu.From the image above we can see that we can use ''bpy.types.VIEW3D_MT_object.append()'' to add something to the Object menu because ''VIEW3D_MT_object'' is shown in the balloon text. Note that the ''menu_func()'' function does not implement an action itself but will, when called, append a user interface element to the object that is passed to it in the '''self''' parameter. This user interface element in turn will interact with the user. Here we will simply add an operator entry (that is, an item that will execute our operator when clicked). The ''self'' argument that is passed to ''menu_func()'' refers to the menu. This menu has a ''layout'' attribute with an ''operator()'' function to which we pass the name of our operator. This will ensure that every time a user hovers over the '''Object''' menu, our operator will be shown in the list of options. The name of our new ''MyTool'' operator can be found in its ''bl_idname'' attribute so that is why we pass ''MyTool.bl_idname''.
 +
The name of the entry and its tooltip is determined by looking at the ''bl_label'' and docstring defined in our ''MyTool'' class and the icon used in the menu is determined by passing an optional '''icon''' parameter to the ''operator()'' function. This may sound overly complicated but it makes it possible for example to show different things than just click-able entries in a menu for example to group several operators in a box.

Revision as of 22:16, 18 July 2020

The Blender to POV-Ray add-on and vice versa (aka "POV Converter") started out as a single Python file. To be compliant with blender add-on packaging system this add-on has to

  • provide some general information about the add-on: its name and version,
  • define some code to perform actions, mostly through 'operators' or 'functions'
  • and make sure these are registered so that they can be used.

bpy

Blender has an embedded Python interpreter which is loaded when Blender is started and stays active while Blender is running. This interpreter runs scripts to draw the user interface and is used for some of Blender’s internal tools as well. Blender’s embedded interpreter provides a typical Python environment, so code from tutorials on how to write Python scripts can also be run with Blender’s interpreter. Blender provides its Python modules, such as bpy and mathutils, to the embedded interpreter so they can be imported into a script and give access to Blender’s data, classes, and functions. Scripts that deal with Blender data will need to import the modules to work. So of all modules, if no other, bpy is generally always imported in blender scripts. (except in interactive console were this import is automatically done for convenience)

bl_info

bl_info = {
    "name": "Persistence of Vision",
    "author": "Campbell Barton, "
    "Maurice Raybaud, "
    "Leonid Desyatkov, "
    "Bastien Montagne, "
    "Constantin Rahn, "
    "Silvio Falcinelli",
    "version": (0, 1, 1),
    "blender": (2, 81, 0),
    "location": "Render Properties > Render Engine > Persistence of Vision",
    "description": "Persistence of Vision integration for blender",
    "doc_url": "{BLENDER_MANUAL_URL}/addons/render/povray.html",
    "category": "Render",
    "warning": "Under active development, seeking co-maintainer(s)",
}


The general information about the add-on is defined in a dictionary with the name

bl_info

Each key of this dictionary provides Blender with specific information about the add-on although not all are equally important. Most of the information is used in the user preferences dialog and helps the user to find and select an add-on.

name

Kind of a code name for the addon, by which it is referenced in the Blender source... (UI name can show something else)

author

If you contribute you will be credited here !

version

The version of the add-on. Any numbering scheme is valid, as long as it is a tuple of three integers. Preferably only move the last position but we might choose for a more structured scheme later on.

blender

The minimal Blender version needed by this add-on. Again a tuple of three integers. Even if you expect things to work with older and nnewer versions it might be a good idea to list the earliest version that you actually tested your add-on with! Developing with something stable enough is important, and then test with latest version just before commit but don't generally update compatible version number then, only when something breaks at that stage.

category

The category in the user preferences our add-on is grouped under. It operates POV compatible engines which are renderers so it made sense to add it to the Render category historically. Unfortunately it doesnt reflect the multipurpose nature it ended up growing (such as shipped in importer, text editor syntax highlighting etc...) If several categories are possible maybe we should add them.

location

Where to find the add-on once it is enabled. This might a reference to a specific panel or inout case, a description of its location in a menu.

description

A concise description of what the add-on does.

warning

If this is not an empty string, the add-on will show up with a warning sign in the user preferences. You might use this to mark an add-on as experimental for example.

doc_url

the public on-line documentation, it is a derivative of the work happening here. It will be a click-able item in the user preferences.

tracker_url

The POV add-on being a bundled part of Blender it does have its own bug tracker entry associated with it and this key will provide a pointer to it.

Comments

Single line comments begin with a hash (#) character. While multiline comments can begin with triple quotes. It is the convention that three double quotes are used for docstrings, a specific kind of comment not doing anything in the program but still appearing when console is used to get help about a function.

"""This is an explanation of general relevance"""

It is good practice to put a doc string at the start of every class to describe it and also at the start of every file to explain its relative use.


Operators

Most add-ons define new operators, they are classes that implement specific functionality. The actual definition of the operator takes the form a class that is derived from bpy.types.Operator

import bpy
class MyTool(bpy.types.Operator):
    """Do things to an object"""    
    bl_idname = "object.do_things_to_object"
    bl_label = "Do things to an object"
    bl_options = {'REGISTER', 'UNDO'}

The docstring at the start of the class definition will be used as a tooltip anywhere this operator will be available, for example in a menu, while the bl_label defines the actual label used in the menu entry itself. Here we kept both the same. Operators will be part of Blender's data, and operators are stored in the module bpy.ops. This bl_idname will make sure this operator's entry will be called bpy.ops.object.move_object. Operators are normally registered in order to make them usable and that is indeed the default of bl_options. However, if we also want theadd-on to show up in the history so it can be undone or repeated, we should add UNDO to the set of flags that is assigned to bl_options, as is done here.

Functions

Functions are defined with the def keyword, followed by the name of the function and a pair of parenthesis. When the function is referenced to move it around and pass it along between files, it no longer needs the parenthesis, but pretty much every other time, they do follow function's name and in between them, some attributes can eventually be specified.

The execute() function

An operator class can have any number of member functions but to be useful it normally overrides the execute() function:

def execute(self, context):
    context.active_object.rotation.z += 33
    return {'FINISHED'}

The execute() function is passed a reference to a context object. This context object contains among other things an active_object attribute which points to, Blenders active object. Each object in Blender has a rotation attribute which is a vector with x, y and z components. Changing the rotation of an object is as simple as changing one of these components, which is exactly what we do in line 2.

The execute() function signals successful completion by returning a set of flags, in this case a set consisting solely of a string FINISHED.

Registering and adding a menu entry

Defining an operator is not in itself enough to make this operator usable. In order for the user to find and use an operator, for example by pressing SPACE in the 3D view window and typing the label of the operator, we must register the operator. Adding a registered operator to a menu requires a separate action.

def register():    
    bpy.utils.register_module(__name__)
    bpy.types.VIEW3D_MT_object.append(menu_func)
def unregister():
    bpy.utils.unregister_module(__name__)
    bpy.types.VIEW3D_MT_object.remove(menu_func)
def menu_func(self, context):
    self.layout.operator(MyTool.bl_idname, icon='MESH_CUBE')

When we check the Enable an add-on check-box in the user preferences, Blender will look for a register() function and execute it. Likewise, when disabling an add-on the unregister() function is called. Here we use this to both register our operator with Blender and to insert a menu entry that refers to our operator. The bpy.utils.register_module() function will register any class in a module that has REGISTER defined in its bl_options set. In order to create a menu entry we have to do two things:

  • Create a function that will produce a menu entry
  • And append this function to a suitable menu.

Now almost everything in Blender is available as a Python object and menus are no exception. We want to add our entry to the Object menu in the 3D view so we call

bpy.types.VIEW3D_MT_object.append()

and pass it a reference to the function we define a little below. How do we know how this menu object is called? If you have checked File⇒ User preferences ⇒ Interface ⇒ Python Tooltips the name of the menu will be shown in a tooltip when you hover over a menu.From the image above we can see that we can use bpy.types.VIEW3D_MT_object.append() to add something to the Object menu because VIEW3D_MT_object is shown in the balloon text. Note that the menu_func() function does not implement an action itself but will, when called, append a user interface element to the object that is passed to it in the self parameter. This user interface element in turn will interact with the user. Here we will simply add an operator entry (that is, an item that will execute our operator when clicked). The self argument that is passed to menu_func() refers to the menu. This menu has a layout attribute with an operator() function to which we pass the name of our operator. This will ensure that every time a user hovers over the Object menu, our operator will be shown in the list of options. The name of our new MyTool operator can be found in its bl_idname attribute so that is why we pass MyTool.bl_idname.

The name of the entry and its tooltip is determined by looking at the bl_label and docstring defined in our MyTool class and the icon used in the menu is determined by passing an optional icon parameter to the operator() function. This may sound overly complicated but it makes it possible for example to show different things than just click-able entries in a menu for example to group several operators in a box.