diff options
author | Campbell Barton <ideasman42@gmail.com> | 2012-12-30 05:39:55 +0400 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2012-12-30 05:39:55 +0400 |
commit | 33955940e4931e2184434393e0f8c15c36a5c3c6 (patch) | |
tree | 7b3c10343162a256e85ff4346f57122032715e82 /release/scripts/templates_py | |
parent | e12354c4c5850864f925d22f53ec31578384bc63 (diff) |
add templates menu for OSL, use preprocessor directive color for decorators in python.
Diffstat (limited to 'release/scripts/templates_py')
26 files changed, 1455 insertions, 0 deletions
diff --git a/release/scripts/templates_py/addon_add_object.py b/release/scripts/templates_py/addon_add_object.py new file mode 100644 index 00000000000..66da6a969c7 --- /dev/null +++ b/release/scripts/templates_py/addon_add_object.py @@ -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() diff --git a/release/scripts/templates_py/background_job.py b/release/scripts/templates_py/background_job.py new file mode 100644 index 00000000000..11b51e5a9b5 --- /dev/null +++ b/release/scripts/templates_py/background_job.py @@ -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() diff --git a/release/scripts/templates_py/batch_export.py b/release/scripts/templates_py/batch_export.py new file mode 100644 index 00000000000..45d26f4b525 --- /dev/null +++ b/release/scripts/templates_py/batch_export.py @@ -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 diff --git a/release/scripts/templates_py/bmesh_simple.py b/release/scripts/templates_py/bmesh_simple.py new file mode 100644 index 00000000000..45e6b52d578 --- /dev/null +++ b/release/scripts/templates_py/bmesh_simple.py @@ -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 diff --git a/release/scripts/templates_py/bmesh_simple_editmode.py b/release/scripts/templates_py/bmesh_simple_editmode.py new file mode 100644 index 00000000000..d79ba02c2cb --- /dev/null +++ b/release/scripts/templates_py/bmesh_simple_editmode.py @@ -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) diff --git a/release/scripts/templates_py/builtin_keyingset.py b/release/scripts/templates_py/builtin_keyingset.py new file mode 100644 index 00000000000..19f92dc75e7 --- /dev/null +++ b/release/scripts/templates_py/builtin_keyingset.py @@ -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() diff --git a/release/scripts/templates_py/driver_functions.py b/release/scripts/templates_py/driver_functions.py new file mode 100644 index 00000000000..1c6af0e574f --- /dev/null +++ b/release/scripts/templates_py/driver_functions.py @@ -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 diff --git a/release/scripts/templates_py/gamelogic.py b/release/scripts/templates_py/gamelogic.py new file mode 100644 index 00000000000..01ac27c56cd --- /dev/null +++ b/release/scripts/templates_py/gamelogic.py @@ -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() diff --git a/release/scripts/templates_py/gamelogic_module.py b/release/scripts/templates_py/gamelogic_module.py new file mode 100644 index 00000000000..88c8cf0d75b --- /dev/null +++ b/release/scripts/templates_py/gamelogic_module.py @@ -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 diff --git a/release/scripts/templates_py/gamelogic_simple.py b/release/scripts/templates_py/gamelogic_simple.py new file mode 100644 index 00000000000..dbfcf948b18 --- /dev/null +++ b/release/scripts/templates_py/gamelogic_simple.py @@ -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() diff --git a/release/scripts/templates_py/operator_file_export.py b/release/scripts/templates_py/operator_file_export.py new file mode 100644 index 00000000000..9511cb163bc --- /dev/null +++ b/release/scripts/templates_py/operator_file_export.py @@ -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') diff --git a/release/scripts/templates_py/operator_file_import.py b/release/scripts/templates_py/operator_file_import.py new file mode 100644 index 00000000000..9940a1b98eb --- /dev/null +++ b/release/scripts/templates_py/operator_file_import.py @@ -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') diff --git a/release/scripts/templates_py/operator_mesh_add.py b/release/scripts/templates_py/operator_mesh_add.py new file mode 100644 index 00000000000..fa248cb9005 --- /dev/null +++ b/release/scripts/templates_py/operator_mesh_add.py @@ -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() diff --git a/release/scripts/templates_py/operator_modal.py b/release/scripts/templates_py/operator_modal.py new file mode 100644 index 00000000000..88e5ee80590 --- /dev/null +++ b/release/scripts/templates_py/operator_modal.py @@ -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') diff --git a/release/scripts/templates_py/operator_modal_draw.py b/release/scripts/templates_py/operator_modal_draw.py new file mode 100644 index 00000000000..d11ddf0b467 --- /dev/null +++ b/release/scripts/templates_py/operator_modal_draw.py @@ -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() diff --git a/release/scripts/templates_py/operator_modal_timer.py b/release/scripts/templates_py/operator_modal_timer.py new file mode 100644 index 00000000000..72c153df9d2 --- /dev/null +++ b/release/scripts/templates_py/operator_modal_timer.py @@ -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() diff --git a/release/scripts/templates_py/operator_modal_view3d.py b/release/scripts/templates_py/operator_modal_view3d.py new file mode 100644 index 00000000000..c870bbffdcf --- /dev/null +++ b/release/scripts/templates_py/operator_modal_view3d.py @@ -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() diff --git a/release/scripts/templates_py/operator_modal_view3d_raycast.py b/release/scripts/templates_py/operator_modal_view3d_raycast.py new file mode 100644 index 00000000000..eac76922187 --- /dev/null +++ b/release/scripts/templates_py/operator_modal_view3d_raycast.py @@ -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() + diff --git a/release/scripts/templates_py/operator_node.py b/release/scripts/templates_py/operator_node.py new file mode 100644 index 00000000000..b689ce7634e --- /dev/null +++ b/release/scripts/templates_py/operator_node.py @@ -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() diff --git a/release/scripts/templates_py/operator_simple.py b/release/scripts/templates_py/operator_simple.py new file mode 100644 index 00000000000..715daa3a8b4 --- /dev/null +++ b/release/scripts/templates_py/operator_simple.py @@ -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() diff --git a/release/scripts/templates_py/operator_uv.py b/release/scripts/templates_py/operator_uv.py new file mode 100644 index 00000000000..fdd0b993f8b --- /dev/null +++ b/release/scripts/templates_py/operator_uv.py @@ -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() diff --git a/release/scripts/templates_py/script_stub.py b/release/scripts/templates_py/script_stub.py new file mode 100644 index 00000000000..44c7b802e2c --- /dev/null +++ b/release/scripts/templates_py/script_stub.py @@ -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() diff --git a/release/scripts/templates_py/ui_menu.py b/release/scripts/templates_py/ui_menu.py new file mode 100644 index 00000000000..a21e5ed86c8 --- /dev/null +++ b/release/scripts/templates_py/ui_menu.py @@ -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) diff --git a/release/scripts/templates_py/ui_menu_simple.py b/release/scripts/templates_py/ui_menu_simple.py new file mode 100644 index 00000000000..2129dfd81a4 --- /dev/null +++ b/release/scripts/templates_py/ui_menu_simple.py @@ -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) diff --git a/release/scripts/templates_py/ui_panel.py b/release/scripts/templates_py/ui_panel.py new file mode 100644 index 00000000000..cacdb83e815 --- /dev/null +++ b/release/scripts/templates_py/ui_panel.py @@ -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() diff --git a/release/scripts/templates_py/ui_panel_simple.py b/release/scripts/templates_py/ui_panel_simple.py new file mode 100644 index 00000000000..9bcc750560f --- /dev/null +++ b/release/scripts/templates_py/ui_panel_simple.py @@ -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() |