add templates menu for OSL, use preprocessor directive color for decorators in python.

This commit is contained in:
2012-12-30 01:39:55 +00:00
parent e12354c4c5
commit 33955940e4
28 changed files with 24 additions and 5 deletions

View File

@@ -0,0 +1,92 @@
bl_info = {
"name": "New Object",
"author": "Your Name Here",
"version": (1, 0),
"blender": (2, 65, 0),
"location": "View3D > Add > Mesh > New Object",
"description": "Adds a new Mesh Object",
"warning": "",
"wiki_url": "",
"tracker_url": "",
"category": "Add Mesh"}
import bpy
from bpy.types import Operator
from bpy.props import FloatVectorProperty
from bpy_extras.object_utils import AddObjectHelper, object_data_add
from mathutils import Vector
def add_object(self, context):
scale_x = self.scale.x
scale_y = self.scale.y
verts = [Vector((-1 * scale_x, 1 * scale_y, 0)),
Vector((1 * scale_x, 1 * scale_y, 0)),
Vector((1 * scale_x, -1 * scale_y, 0)),
Vector((-1 * scale_x, -1 * scale_y, 0)),
]
edges = []
faces = [[0, 1, 2, 3]]
mesh = bpy.data.meshes.new(name="New Object Mesh")
mesh.from_pydata(verts, edges, faces)
# useful for development when the mesh may be invalid.
# mesh.validate(verbose=True)
object_data_add(context, mesh, operator=self)
class OBJECT_OT_add_object(Operator, AddObjectHelper):
"""Create a new Mesh Object"""
bl_idname = "mesh.add_object"
bl_label = "Add Mesh Object"
bl_options = {'REGISTER', 'UNDO'}
scale = FloatVectorProperty(
name="scale",
default=(1.0, 1.0, 1.0),
subtype='TRANSLATION',
description="scaling",
)
def execute(self, context):
add_object(self, context)
return {'FINISHED'}
# Registration
def add_object_button(self, context):
self.layout.operator(
OBJECT_OT_add_object.bl_idname,
text="Add Object",
icon='PLUGIN')
# This allows you to right click on a button and link to the manual
def add_object_manual_map():
url_manual_prefix = "http://wiki.blender.org/index.php/Doc:2.6/Manual/"
url_manual_mapping = (
("bpy.ops.mesh.add_object", "Modeling/Objects"),
)
return url_manual_prefix, url_manual_mapping
def register():
bpy.utils.register_class(OBJECT_OT_add_object)
bpy.utils.register_manual_map(add_object_manual_map)
bpy.types.INFO_MT_mesh_add.append(add_object_button)
def unregister():
bpy.utils.unregister_class(OBJECT_OT_add_object)
bpy.utils.unregister_manual_map(add_object_manual_map)
bpy.types.INFO_MT_mesh_add.remove(add_object_button)
if __name__ == "__main__":
register()

View File

@@ -0,0 +1,122 @@
# This script is an example of how you can run blender from the command line
# (in background mode with no interface) to automate tasks, in this example it
# creates a text object, camera and light, then renders and/or saves it.
# This example also shows how you can parse command line options to scripts.
#
# Example usage for this test.
# blender --background --factory-startup --python $HOME/background_job.py -- \
# --text="Hello World" \
# --render="/tmp/hello" \
# --save="/tmp/hello.blend"
#
# Notice:
# '--factory-startup' is used to avoid the user default settings from
# interfearing with automated scene generation.
#
# '--' causes blender to ignore all following arguments so python can use them.
#
# See blender --help for details.
import bpy
def example_function(text, save_path, render_path):
scene = bpy.context.scene
# Clear existing objects.
scene.camera = None
for obj in scene.objects:
scene.objects.unlink(obj)
txt_data = bpy.data.curves.new(name="MyText", type='FONT')
# Text Object
txt_ob = bpy.data.objects.new(name="MyText", object_data=txt_data)
scene.objects.link(txt_ob) # add the data to the scene as an object
txt_data.body = text # the body text to the command line arg given
txt_data.align = 'CENTER' # center text
# Camera
cam_data = bpy.data.cameras.new("MyCam")
cam_ob = bpy.data.objects.new(name="MyCam", object_data=cam_data)
scene.objects.link(cam_ob) # instance the camera object in the scene
scene.camera = cam_ob # set the active camera
cam_ob.location = 0.0, 0.0, 10.0
# Lamp
lamp_data = bpy.data.lamps.new("MyLamp", 'POINT')
lamp_ob = bpy.data.objects.new(name="MyCam", object_data=lamp_data)
scene.objects.link(lamp_ob)
lamp_ob.location = 2.0, 2.0, 5.0
if save_path:
try:
f = open(save_path, 'w')
f.close()
ok = True
except:
print("Cannot save to path %r" % save_path)
import traceback
traceback.print_exc()
if ok:
bpy.ops.wm.save_as_mainfile(filepath=save_path)
if render_path:
render = scene.render
render.use_file_extension = True
render.filepath = render_path
bpy.ops.render.render(write_still=True)
def main():
import sys # to get command line args
import argparse # to parse options for us and print a nice help message
# get the args passed to blender after "--", all of which are ignored by
# blender so scripts may receive their own arguments
argv = sys.argv
if "--" not in argv:
argv = [] # as if no args are passed
else:
argv = argv[argv.index("--") + 1:] # get all args after "--"
# When --help or no args are given, print this help
usage_text = \
"Run blender in background mode with this script:"
" blender --background --python " + __file__ + " -- [options]"
parser = argparse.ArgumentParser(description=usage_text)
# Example utility, add some text and renders or saves it (with options)
# Possible types are: string, int, long, choice, float and complex.
parser.add_argument("-t", "--text", dest="text", type=str, required=True,
help="This text will be used to render an image")
parser.add_argument("-s", "--save", dest="save_path", metavar='FILE',
help="Save the generated file to the specified path")
parser.add_argument("-r", "--render", dest="render_path", metavar='FILE',
help="Render an image to the specified path")
args = parser.parse_args(argv) # In this example we wont use the args
if not argv:
parser.print_help()
return
if not args.text:
print("Error: --text=\"some string\" argument not given, aborting.")
parser.print_help()
return
# Run the example function
example_function(args.text, args.save_path, args.render_path)
print("batch job finished, exiting")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,33 @@
# exports each selected object into its own file
import bpy
import os
# export to blend file location
basedir = os.path.dirname(bpy.data.filepath)
if not basedir:
raise Exception("Blend file is not saved")
selection = bpy.context.selected_objects
bpy.ops.object.select_all(action='DESELECT')
for obj in selection:
obj.select = True
name = bpy.path.clean_name(obj.name)
fn = os.path.join(basedir, name)
bpy.ops.export_scene.fbx(filepath=fn + ".fbx", use_selection=True)
## Can be used for multiple formats
# bpy.ops.export_scene.x3d(filepath=fn + ".x3d", use_selection=True)
obj.select = False
print("written:", fn)
for obj in selection:
obj.select = True

View File

@@ -0,0 +1,22 @@
# This example assumes we have a mesh object selected
import bpy
import bmesh
# Get the active mesh
me = bpy.context.object.data
# Get a BMesh representation
bm = bmesh.new() # create an empty BMesh
bm.from_mesh(me) # fill it in from a Mesh
# Modify the BMesh, can do anything here...
for v in bm.verts:
v.co.x += 1.0
# Finish up, write the bmesh back to the mesh
bm.to_mesh(me)
bm.free() # free and prevent further access

View File

@@ -0,0 +1,23 @@
# This example assumes we have a mesh object in edit-mode
import bpy
import bmesh
# Get the active mesh
obj = bpy.context.edit_object
me = obj.data
# Get a BMesh representation
bm = bmesh.from_edit_mesh(me)
bm.faces.active = None
# Modify the BMesh, can do anything here...
for v in bm.verts:
v.co.x += 1.0
# Show the updates in the viewport
# and recalculate n-gon tessellation.
bmesh.update_edit_mesh(me, True)

View File

@@ -0,0 +1,37 @@
import bpy
class BUILTIN_KSI_hello(bpy.types.KeyingSetInfo):
bl_label = "Hello World KeyingSet"
# poll - test for whether Keying Set can be used at all
def poll(ksi, context):
return context.active_object or context.selected_objects
# iterator - go over all relevant data, calling generate()
def iterator(ksi, context, ks):
for ob in context.selected_objects:
ksi.generate(context, ks, ob)
# generator - populate Keying Set with property paths to use
def generate(ksi, context, ks, data):
id_block = data.id_data
ks.paths.add(id_block, "location")
for i in range(5):
ks.paths.add(id_block, "layers", i, group_method='NAMED', group_name="5x Hello Layers")
ks.paths.add(id_block, "show_x_ray", group_method='NONE')
def register():
bpy.utils.register_class(BUILTIN_KSI_hello)
def unregister():
bpy.utils.unregister_class(BUILTIN_KSI_hello)
if __name__ == '__main__':
register()

View File

@@ -0,0 +1,35 @@
# This script defines functions to be used directly in drivers expressions to
# extend the builtin set of python functions.
#
# This can be executed on manually or set to 'Register' to
# initialize thefunctions on file load.
# two sample functions
def invert(f):
""" Simple function call:
invert(val)
"""
return 1.0 - f
uuid_store = {}
def slow_value(value, fac, uuid):
""" Delay the value by a factor, use a unique string to allow
use in multiple drivers without conflict:
slow_value(val, 0.5, "my_value")
"""
value_prev = uuid_store.get(uuid, value)
uuid_store[uuid] = value_new = (value_prev * fac) + (value * (1.0 - fac))
return value_new
import bpy
# Add variable defined in this script into the drivers namespace.
bpy.app.driver_namespace["invert"] = invert
bpy.app.driver_namespace["slow_value"] = slow_value

View File

@@ -0,0 +1,73 @@
# This script must be assigned to a python controller
# where it can access the object that owns it and the sensors/actuators that it connects to.
import bge
# support for Vector(), Matrix() types and advanced functions like Matrix.Scale(...) and Matrix.Rotation(...)
# import mathutils
# for functions like getWindowWidth(), getWindowHeight()
# import Rasterizer
def main():
cont = bge.logic.getCurrentController()
# The KX_GameObject that owns this controller.
own = cont.owner
# for scripts that deal with spacial logic
own_pos = own.worldPosition
# Some example functions, remove to write your own script.
# check for a positive sensor, will run on any object without errors.
print("Logic info for KX_GameObject", own.name)
input = False
for sens in cont.sensors:
# The sensor can be on another object, we may want to use it
own_sens = sens.owner
print(" sensor:", sens.name, end=" ")
if sens.positive:
print("(true)")
input = True
else:
print("(false)")
for actu in cont.actuators:
# The actuator can be on another object, we may want to use it
own_actu = actu.owner
print(" actuator:", actu.name)
# This runs the actuator or turns it off
# note that actuators will continue to run unless explicitly turned off.
if input:
cont.activate(actu)
else:
cont.deactivate(actu)
# Its also good practice to get sensors and actuators by name
# rather then index so any changes to their order wont break the script.
# sens_key = cont.sensors["key_sensor"]
# actu_motion = cont.actuators["motion"]
# Loop through all other objects in the scene
sce = bge.logic.getCurrentScene()
print("Scene Objects:", sce.name)
for ob in sce.objects:
print(" ", ob.name, ob.worldPosition)
# Example where collision objects are checked for their properties
# adding to our objects "life" property
"""
actu_collide = cont.sensors["collision_sens"]
for ob in actu_collide.objectHitList:
# Check to see the object has this property
if "life" in ob:
own["life"] += ob["life"]
ob["life"] = 0
print(own["life"])
"""
main()

View File

@@ -0,0 +1,27 @@
# This module can be accessed by a python controller with
# its execution method set to 'Module'
# * Set the module string to "gamelogic_module.main" (without quotes)
# * When renaming the script it MUST have a .py extension
# * External text modules are supported as long as they are at
# the same location as the blendfile or one of its libraries.
import bge
# variables defined here will only be set once when the
# module is first imported. Set object specific vars
# inside the function if you intend to use the module
# with multiple objects.
def main(cont):
own = cont.owner
sens = cont.sensors['mySensor']
actu = cont.actuators['myActuator']
if sens.positive:
cont.activate(actu)
else:
cont.deactivate(actu)
# dont call main(bge.logic.getCurrentController()), the py controller will

View File

@@ -0,0 +1,17 @@
import bge
def main():
cont = bge.logic.getCurrentController()
own = cont.owner
sens = cont.sensors['mySensor']
actu = cont.actuators['myActuator']
if sens.positive:
cont.activate(actu)
else:
cont.deactivate(actu)
main()

View File

@@ -0,0 +1,72 @@
import bpy
def write_some_data(context, filepath, use_some_setting):
print("running write_some_data...")
f = open(filepath, 'w', encoding='utf-8')
f.write("Hello World %s" % use_some_setting)
f.close()
return {'FINISHED'}
# ExportHelper is a helper class, defines filename and
# invoke() function which calls the file selector.
from bpy_extras.io_utils import ExportHelper
from bpy.props import StringProperty, BoolProperty, EnumProperty
from bpy.types import Operator
class ExportSomeData(Operator, ExportHelper):
"""This appears in the tooltip of the operator and in the generated docs"""
bl_idname = "export_test.some_data" # important since its how bpy.ops.import_test.some_data is constructed
bl_label = "Export Some Data"
# ExportHelper mixin class uses this
filename_ext = ".txt"
filter_glob = StringProperty(
default="*.txt",
options={'HIDDEN'},
)
# List of operator properties, the attributes will be assigned
# to the class instance from the operator settings before calling.
use_setting = BoolProperty(
name="Example Boolean",
description="Example Tooltip",
default=True,
)
type = EnumProperty(
name="Example Enum",
description="Choose between two items",
items=(('OPT_A', "First Option", "Description one"),
('OPT_B', "Second Option", "Description two")),
default='OPT_A',
)
def execute(self, context):
return write_some_data(context, self.filepath, self.use_setting)
# Only needed if you want to add into a dynamic menu
def menu_func_export(self, context):
self.layout.operator(ExportSomeData.bl_idname, text="Text Export Operator")
def register():
bpy.utils.register_class(ExportSomeData)
bpy.types.INFO_MT_file_export.append(menu_func_export)
def unregister():
bpy.utils.unregister_class(ExportSomeData)
bpy.types.INFO_MT_file_export.remove(menu_func_export)
if __name__ == "__main__":
register()
# test call
bpy.ops.export_test.some_data('INVOKE_DEFAULT')

View File

@@ -0,0 +1,75 @@
import bpy
def read_some_data(context, filepath, use_some_setting):
print("running read_some_data...")
f = open(filepath, 'r', encoding='utf-8')
data = f.read()
f.close()
# would normally load the data here
print(data)
return {'FINISHED'}
# ImportHelper is a helper class, defines filename and
# invoke() function which calls the file selector.
from bpy_extras.io_utils import ImportHelper
from bpy.props import StringProperty, BoolProperty, EnumProperty
from bpy.types import Operator
class ImportSomeData(Operator, ImportHelper):
"""This appears in the tooltip of the operator and in the generated docs"""
bl_idname = "import_test.some_data" # important since its how bpy.ops.import_test.some_data is constructed
bl_label = "Import Some Data"
# ImportHelper mixin class uses this
filename_ext = ".txt"
filter_glob = StringProperty(
default="*.txt",
options={'HIDDEN'},
)
# List of operator properties, the attributes will be assigned
# to the class instance from the operator settings before calling.
use_setting = BoolProperty(
name="Example Boolean",
description="Example Tooltip",
default=True,
)
type = EnumProperty(
name="Example Enum",
description="Choose between two items",
items=(('OPT_A', "First Option", "Description one"),
('OPT_B', "Second Option", "Description two")),
default='OPT_A',
)
def execute(self, context):
return read_some_data(context, self.filepath, self.use_setting)
# Only needed if you want to add into a dynamic menu
def menu_func_import(self, context):
self.layout.operator(ImportSomeData.bl_idname, text="Text Import Operator")
def register():
bpy.utils.register_class(ImportSomeData)
bpy.types.INFO_MT_file_import.append(menu_func_import)
def unregister():
bpy.utils.unregister_class(ImportSomeData)
bpy.types.INFO_MT_file_import.remove(menu_func_import)
if __name__ == "__main__":
register()
# test call
bpy.ops.import_test.some_data('INVOKE_DEFAULT')

View File

@@ -0,0 +1,122 @@
import bpy
import bmesh
def add_box(width, height, depth):
"""
This function takes inputs and returns vertex and face arrays.
no actual mesh data creation is done here.
"""
verts = [(+1.0, +1.0, -1.0),
(+1.0, -1.0, -1.0),
(-1.0, -1.0, -1.0),
(-1.0, +1.0, -1.0),
(+1.0, +1.0, +1.0),
(+1.0, -1.0, +1.0),
(-1.0, -1.0, +1.0),
(-1.0, +1.0, +1.0),
]
faces = [(0, 1, 2, 3),
(4, 7, 6, 5),
(0, 4, 5, 1),
(1, 5, 6, 2),
(2, 6, 7, 3),
(4, 0, 3, 7),
]
# apply size
for i, v in enumerate(verts):
verts[i] = v[0] * width, v[1] * depth, v[2] * height
return verts, faces
from bpy.props import FloatProperty, BoolProperty, FloatVectorProperty
class AddBox(bpy.types.Operator):
"""Add a simple box mesh"""
bl_idname = "mesh.primitive_box_add"
bl_label = "Add Box"
bl_options = {'REGISTER', 'UNDO'}
width = FloatProperty(
name="Width",
description="Box Width",
min=0.01, max=100.0,
default=1.0,
)
height = FloatProperty(
name="Height",
description="Box Height",
min=0.01, max=100.0,
default=1.0,
)
depth = FloatProperty(
name="Depth",
description="Box Depth",
min=0.01, max=100.0,
default=1.0,
)
# generic transform props
view_align = BoolProperty(
name="Align to View",
default=False,
)
location = FloatVectorProperty(
name="Location",
subtype='TRANSLATION',
)
rotation = FloatVectorProperty(
name="Rotation",
subtype='EULER',
)
def execute(self, context):
verts_loc, faces = add_box(self.width,
self.height,
self.depth,
)
mesh = bpy.data.meshes.new("Box")
bm = bmesh.new()
for v_co in verts_loc:
bm.verts.new(v_co)
for f_idx in faces:
bm.faces.new([bm.verts[i] for i in f_idx])
bm.to_mesh(mesh)
mesh.update()
# add the mesh as an object into the scene with this utility module
from bpy_extras import object_utils
object_utils.object_data_add(context, mesh, operator=self)
return {'FINISHED'}
def menu_func(self, context):
self.layout.operator(AddBox.bl_idname, icon='MESH_CUBE')
def register():
bpy.utils.register_class(AddBox)
bpy.types.INFO_MT_mesh_add.append(menu_func)
def unregister():
bpy.utils.unregister_class(AddBox)
bpy.types.INFO_MT_mesh_add.remove(menu_func)
if __name__ == "__main__":
register()
# test call
bpy.ops.mesh.primitive_box_add()

View File

@@ -0,0 +1,51 @@
import bpy
from bpy.props import IntProperty, FloatProperty
class ModalOperator(bpy.types.Operator):
"""Move an object with the mouse, example"""
bl_idname = "object.modal_operator"
bl_label = "Simple Modal Operator"
first_mouse_x = IntProperty()
first_value = FloatProperty()
def modal(self, context, event):
if event.type == 'MOUSEMOVE':
delta = self.first_mouse_x - event.mouse_x
context.object.location.x = self.first_value + delta * 0.01
elif event.type == 'LEFTMOUSE':
return {'FINISHED'}
elif event.type in {'RIGHTMOUSE', 'ESC'}:
context.object.location.x = self.first_value
return {'CANCELLED'}
return {'RUNNING_MODAL'}
def invoke(self, context, event):
if context.object:
self.first_mouse_x = event.mouse_x
self.first_value = context.object.location.x
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
else:
self.report({'WARNING'}, "No active object, could not finish")
return {'CANCELLED'}
def register():
bpy.utils.register_class(ModalOperator)
def unregister():
bpy.utils.unregister_class(ModalOperator)
if __name__ == "__main__":
register()
# test call
bpy.ops.object.modal_operator('INVOKE_DEFAULT')

View File

@@ -0,0 +1,79 @@
import bpy
import bgl
import blf
def draw_callback_px(self, context):
print("mouse points", len(self.mouse_path))
font_id = 0 # XXX, need to find out how best to get this.
# draw some text
blf.position(font_id, 15, 30, 0)
blf.size(font_id, 20, 72)
blf.draw(font_id, "Hello Word " + str(len(self.mouse_path)))
# 50% alpha, 2 pixel width line
bgl.glEnable(bgl.GL_BLEND)
bgl.glColor4f(0.0, 0.0, 0.0, 0.5)
bgl.glLineWidth(2)
bgl.glBegin(bgl.GL_LINE_STRIP)
for x, y in self.mouse_path:
bgl.glVertex2i(x, y)
bgl.glEnd()
# restore opengl defaults
bgl.glLineWidth(1)
bgl.glDisable(bgl.GL_BLEND)
bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
class ModalDrawOperator(bpy.types.Operator):
"""Draw a line with the mouse"""
bl_idname = "view3d.modal_operator"
bl_label = "Simple Modal View3D Operator"
def modal(self, context, event):
context.area.tag_redraw()
if event.type == 'MOUSEMOVE':
self.mouse_path.append((event.mouse_region_x, event.mouse_region_y))
elif event.type == 'LEFTMOUSE':
bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
return {'FINISHED'}
elif event.type in {'RIGHTMOUSE', 'ESC'}:
bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
return {'CANCELLED'}
return {'RUNNING_MODAL'}
def invoke(self, context, event):
if context.area.type == 'VIEW_3D':
# the arguments we pass the the callback
args = (self, context)
# Add the region OpenGL drawing callback
# draw in view space with 'POST_VIEW' and 'PRE_VIEW'
self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL')
self.mouse_path = []
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
else:
self.report({'WARNING'}, "View3D not found, cannot run operator")
return {'CANCELLED'}
def register():
bpy.utils.register_class(ModalDrawOperator)
def unregister():
bpy.utils.unregister_class(ModalDrawOperator)
if __name__ == "__main__":
register()

View File

@@ -0,0 +1,45 @@
import bpy
class ModalTimerOperator(bpy.types.Operator):
"""Operator which runs its self from a timer"""
bl_idname = "wm.modal_timer_operator"
bl_label = "Modal Timer Operator"
_timer = None
def modal(self, context, event):
if event.type == 'ESC':
return self.cancel(context)
if event.type == 'TIMER':
# change theme color, silly!
color = context.user_preferences.themes[0].view_3d.space.back
color.s = 1.0
color.h += 0.01
return {'PASS_THROUGH'}
def execute(self, context):
self._timer = context.window_manager.event_timer_add(0.1, context.window)
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
def cancel(self, context):
context.window_manager.event_timer_remove(self._timer)
return {'CANCELLED'}
def register():
bpy.utils.register_class(ModalTimerOperator)
def unregister():
bpy.utils.unregister_class(ModalTimerOperator)
if __name__ == "__main__":
register()
# test call
bpy.ops.wm.modal_timer_operator()

View File

@@ -0,0 +1,70 @@
import bpy
from mathutils import Vector
from bpy.props import FloatVectorProperty
class ViewOperator(bpy.types.Operator):
"""Translate the view using mouse events"""
bl_idname = "view3d.modal_operator"
bl_label = "Simple View Operator"
offset = FloatVectorProperty(
name="Offset",
size=3,
)
def execute(self, context):
v3d = context.space_data
rv3d = v3d.region_3d
rv3d.view_location = self._initial_location + Vector(self.offset)
def modal(self, context, event):
v3d = context.space_data
rv3d = v3d.region_3d
if event.type == 'MOUSEMOVE':
self.offset = (self._initial_mouse - Vector((event.mouse_x, event.mouse_y, 0.0))) * 0.02
self.execute(context)
context.area.header_text_set("Offset %.4f %.4f %.4f" % tuple(self.offset))
elif event.type == 'LEFTMOUSE':
context.area.header_text_set()
return {'FINISHED'}
elif event.type in {'RIGHTMOUSE', 'ESC'}:
rv3d.view_location = self._initial_location
context.area.header_text_set()
return {'CANCELLED'}
return {'RUNNING_MODAL'}
def invoke(self, context, event):
if context.space_data.type == 'VIEW_3D':
v3d = context.space_data
rv3d = v3d.region_3d
if rv3d.view_perspective == 'CAMERA':
rv3d.view_perspective = 'PERSP'
self._initial_mouse = Vector((event.mouse_x, event.mouse_y, 0.0))
self._initial_location = rv3d.view_location.copy()
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
else:
self.report({'WARNING'}, "Active space must be a View3d")
return {'CANCELLED'}
def register():
bpy.utils.register_class(ViewOperator)
def unregister():
bpy.utils.unregister_class(ViewOperator)
if __name__ == "__main__":
register()

View File

@@ -0,0 +1,110 @@
import bpy
from mathutils import Vector
from bpy_extras import view3d_utils
def main(context, event, ray_max=10000.0):
"""Run this function on left mouse, execute the ray cast"""
# get the context arguments
scene = context.scene
region = context.region
rv3d = context.region_data
coord = event.mouse_region_x, event.mouse_region_y
# get the ray from the viewport and mouse
view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord)
ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord)
ray_target = ray_origin + (view_vector * ray_max)
def visible_objects_and_duplis():
"""Loop over (object, matrix) pairs (mesh only)"""
for obj in context.visible_objects:
if obj.type == 'MESH':
yield (obj, obj.matrix_world.copy())
if obj.dupli_type != 'NONE':
obj.dupli_list_create(scene)
for dob in obj.dupli_list:
obj_dupli = dob.object
if obj_dupli.type == 'MESH':
yield (obj_dupli, dob.matrix.copy())
obj.dupli_list_clear()
def obj_ray_cast(obj, matrix):
"""Wrapper for ray casting that moves the ray into object space"""
# get the ray relative to the object
matrix_inv = matrix.inverted()
ray_origin_obj = matrix_inv * ray_origin
ray_target_obj = matrix_inv * ray_target
# cast the ray
hit, normal, face_index = obj.ray_cast(ray_origin_obj, ray_target_obj)
if face_index != -1:
return hit, normal, face_index
else:
return None, None, None
# cast rays and find the closest object
best_length_squared = ray_max * ray_max
best_obj = None
for obj, matrix in visible_objects_and_duplis():
if obj.type == 'MESH':
hit, normal, face_index = obj_ray_cast(obj, matrix)
if hit is not None:
hit_world = matrix * hit
scene.cursor_location = hit_world
length_squared = (hit_world - ray_origin).length_squared
if length_squared < best_length_squared:
best_length_squared = length_squared
best_obj = obj
# now we have the object under the mouse cursor,
# we could do lots of stuff but for the example just select.
if best_obj is not None:
best_obj.select = True
context.scene.objects.active = best_obj
class ViewOperatorRayCast(bpy.types.Operator):
"""Modal object selection with a ray cast"""
bl_idname = "view3d.modal_operator_raycast"
bl_label = "RayCast View Operator"
def modal(self, context, event):
if event.type in {'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}:
# allow navigation
return {'PASS_THROUGH'}
elif event.type == 'LEFTMOUSE':
main(context, event)
return {'RUNNING_MODAL'}
elif event.type in {'RIGHTMOUSE', 'ESC'}:
return {'CANCELLED'}
return {'RUNNING_MODAL'}
def invoke(self, context, event):
if context.space_data.type == 'VIEW_3D':
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
else:
self.report({'WARNING'}, "Active space must be a View3d")
return {'CANCELLED'}
def register():
bpy.utils.register_class(ViewOperatorRayCast)
def unregister():
bpy.utils.unregister_class(ViewOperatorRayCast)
if __name__ == "__main__":
register()

View File

@@ -0,0 +1,59 @@
import bpy
def main(context):
space = context.space_data
node_tree = space.node_tree
node_active = context.active_node
node_selected = context.selected_nodes
# now we have the context, perform a simple operation
if node_active in node_selected:
node_selected.remove(node_active)
if len(node_selected) != 1:
operator.report({'ERROR'}, "2 nodes must be selected")
return
node_other, = node_selected
# now we have 2 nodes to operate on
if not node_active.inputs:
operator.report({'ERROR'}, "Active node has no inputs")
return
if not node_other.outputs:
operator.report({'ERROR'}, "Selected node has no outputs")
return
socket_in = node_active.inputs[0]
socket_out = node_other.outputs[0]
# add a link between the two nodes
node_link = node_tree.links.new(socket_in, socket_out)
class NodeOperator(bpy.types.Operator):
"""Tooltip"""
bl_idname = "node.simple_operator"
bl_label = "Simple Node Operator"
@classmethod
def poll(cls, context):
space = context.space_data
return space.type == 'NODE_EDITOR'
def execute(self, context):
main(context)
return {'FINISHED'}
def register():
bpy.utils.register_class(NodeOperator)
def unregister():
bpy.utils.unregister_class(NodeOperator)
if __name__ == "__main__":
register()

View File

@@ -0,0 +1,35 @@
import bpy
def main(context):
for ob in context.scene.objects:
print(ob)
class SimpleOperator(bpy.types.Operator):
"""Tooltip"""
bl_idname = "object.simple_operator"
bl_label = "Simple Object Operator"
@classmethod
def poll(cls, context):
return context.active_object is not None
def execute(self, context):
main(context)
return {'FINISHED'}
def register():
bpy.utils.register_class(SimpleOperator)
def unregister():
bpy.utils.unregister_class(SimpleOperator)
if __name__ == "__main__":
register()
# test call
bpy.ops.object.simple_operator()

View File

@@ -0,0 +1,56 @@
import bpy
def main(context):
obj = context.active_object
mesh = obj.data
is_editmode = (obj.mode == 'EDIT')
if is_editmode:
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
if not mesh.uv_textures:
uvtex = bpy.ops.mesh.uv_texture_add()
else:
uvtex = mesh.uv_textures.active
# adjust UVs
for i, uv in enumerate(uvtex.data):
uvs = uv.uv1, uv.uv2, uv.uv3, uv.uv4
for j, v_idx in enumerate(mesh.faces[i].vertices):
if uv.select_uv[j]:
# apply the location of the vertex as a UV
uvs[j][:] = mesh.vertices[v_idx].co.xy
if is_editmode:
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
class UvOperator(bpy.types.Operator):
"""UV Operator description"""
bl_idname = "uv.simple_operator"
bl_label = "Simple UV Operator"
@classmethod
def poll(cls, context):
obj = context.active_object
return (obj and obj.type == 'MESH')
def execute(self, context):
main(context)
return {'FINISHED'}
def register():
bpy.utils.register_class(UvOperator)
def unregister():
bpy.utils.unregister_class(UvOperator)
if __name__ == "__main__":
register()
# test call
bpy.ops.uv.simple_operator()

View File

@@ -0,0 +1,14 @@
# This stub runs a python script relative to the currently open
# blend file, useful when editing scripts externally.
import bpy
import os
# Use your own script name here:
filename = "my_script.py"
filepath = os.path.join(os.path.dirname(bpy.data.filepath), filename)
global_namespace = {"__file__": filepath, "__name__": "__main__"}
file = open(filepath, 'rb')
exec(compile(file.read(), filepath, 'exec'), global_namespace)
file.close()

View File

@@ -0,0 +1,49 @@
import bpy
class CustomMenu(bpy.types.Menu):
bl_label = "Custom Menu"
bl_idname = "OBJECT_MT_custom_menu"
def draw(self, context):
layout = self.layout
layout.operator("wm.open_mainfile")
layout.operator("wm.save_as_mainfile").copy = True
layout.operator("object.shade_smooth")
layout.label(text="Hello world!", icon='WORLD_DATA')
# use an operator enum property to populate a sub-menu
layout.operator_menu_enum("object.select_by_type",
property="type",
text="Select All by Type...",
)
# call another menu
layout.operator("wm.call_menu", text="Unwrap").name = "VIEW3D_MT_uv_map"
def draw_item(self, context):
layout = self.layout
layout.menu(CustomMenu.bl_idname)
def register():
bpy.utils.register_class(CustomMenu)
# lets add ourselves to the main header
bpy.types.INFO_HT_header.append(draw_item)
def unregister():
bpy.utils.unregister_class(CustomMenu)
bpy.types.INFO_HT_header.remove(draw_item)
if __name__ == "__main__":
register()
# The menu can also be called from scripts
bpy.ops.wm.call_menu(name=CustomMenu.bl_idname)

View File

@@ -0,0 +1,26 @@
import bpy
class SimpleCustomMenu(bpy.types.Menu):
bl_label = "Simple Custom Menu"
bl_idname = "OBJECT_MT_simple_custom_menu"
def draw(self, context):
layout = self.layout
layout.operator("wm.open_mainfile")
layout.operator("wm.save_as_mainfile")
def register():
bpy.utils.register_class(SimpleCustomMenu)
def unregister():
bpy.utils.unregister_class(SimpleCustomMenu)
if __name__ == "__main__":
register()
# The menu can also be called from scripts
bpy.ops.wm.call_menu(name=SimpleCustomMenu.bl_idname)

View File

@@ -0,0 +1,73 @@
import bpy
class LayoutDemoPanel(bpy.types.Panel):
"""Creates a Panel in the scene context of the properties editor"""
bl_label = "Layout Demo"
bl_idname = "SCENE_PT_layout"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "scene"
def draw(self, context):
layout = self.layout
scene = context.scene
# Create a simple row.
layout.label(text=" Simple Row:")
row = layout.row()
row.prop(scene, "frame_start")
row.prop(scene, "frame_end")
# Create an row where the buttons are aligned to each other.
layout.label(text=" Aligned Row:")
row = layout.row(align=True)
row.prop(scene, "frame_start")
row.prop(scene, "frame_end")
# Create two columns, by using a split layout.
split = layout.split()
# First column
col = split.column()
col.label(text="Column One:")
col.prop(scene, "frame_end")
col.prop(scene, "frame_start")
# Second column, aligned
col = split.column(align=True)
col.label(text="Column Two:")
col.prop(scene, "frame_start")
col.prop(scene, "frame_end")
# Big render button
layout.label(text="Big Button:")
row = layout.row()
row.scale_y = 3.0
row.operator("render.render")
# Different sizes in a row
layout.label(text="Different button sizes:")
row = layout.row(align=True)
row.operator("render.render")
sub = row.row()
sub.scale_x = 2.0
sub.operator("render.render")
row.operator("render.render")
def register():
bpy.utils.register_class(LayoutDemoPanel)
def unregister():
bpy.utils.unregister_class(LayoutDemoPanel)
if __name__ == "__main__":
register()

View File

@@ -0,0 +1,38 @@
import bpy
class HelloWorldPanel(bpy.types.Panel):
"""Creates a Panel in the Object properties window"""
bl_label = "Hello World Panel"
bl_idname = "OBJECT_PT_hello"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "object"
def draw(self, context):
layout = self.layout
obj = context.object
row = layout.row()
row.label(text="Hello world!", icon='WORLD_DATA')
row = layout.row()
row.label(text="Active object is: " + obj.name)
row = layout.row()
row.prop(obj, "name")
row = layout.row()
row.operator("mesh.primitive_cube_add")
def register():
bpy.utils.register_class(HelloWorldPanel)
def unregister():
bpy.utils.unregister_class(HelloWorldPanel)
if __name__ == "__main__":
register()