1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
24 class AddPresetBase():
25 '''Base preset class, only for subclassing
26 subclasses must define
29 # bl_idname = "script.preset_base_add"
30 # bl_label = "Add a Python Preset"
31 bl_options = {'REGISTER'} # only because invoke_props_popup requires.
33 name = bpy.props.StringProperty(name="Name", description="Name of the preset, used to make the path name", maxlen=64, default="")
34 remove_active = bpy.props.BoolProperty(default=False, options={'HIDDEN'})
37 def as_filename(name): # could reuse for other presets
38 for char in " !@#$%^&*(){}:\";'[]<>,.\\/?":
39 name = name.replace(char, '_')
40 return name.lower().strip()
42 def execute(self, context):
45 if hasattr(self, "pre_cb"):
48 preset_menu_class = getattr(bpy.types, self.preset_menu)
50 if not self.remove_active:
51 name = self.name.strip()
55 filename = self.as_filename(name)
57 target_path = bpy.utils.user_resource('SCRIPTS', os.path.join("presets", self.preset_subdir), create=True)
60 self.report({'WARNING'}, "Failed to create presets path")
63 filepath = os.path.join(target_path, filename) + ".py"
65 if hasattr(self, "add"):
66 self.add(context, filepath)
68 file_preset = open(filepath, 'w')
69 file_preset.write("import bpy\n")
71 if hasattr(self, "preset_defines"):
72 for rna_path in self.preset_defines:
74 file_preset.write("%s\n" % rna_path)
75 file_preset.write("\n")
77 for rna_path in self.preset_values:
78 value = eval(rna_path)
79 # convert thin wrapped sequences to simple lists to repr()
85 file_preset.write("%s = %r\n" % (rna_path, value))
89 preset_menu_class.bl_label = bpy.path.display_name(filename)
92 preset_active = preset_menu_class.bl_label
94 # fairly sloppy but convenient.
95 filepath = bpy.utils.preset_find(preset_active, self.preset_subdir)
98 filepath = bpy.utils.preset_find(preset_active, self.preset_subdir, display_name=True)
103 if hasattr(self, "remove"):
104 self.remove(context, filepath)
110 traceback.print_exc()
113 preset_menu_class.bl_label = "Presets"
115 if hasattr(self, "post_cb"):
116 self.post_cb(context)
120 def check(self, context):
121 self.name = self.as_filename(self.name.strip())
123 def invoke(self, context, event):
124 if not self.remove_active:
125 wm = context.window_manager
126 return wm.invoke_props_dialog(self)
128 return self.execute(context)
131 class ExecutePreset(bpy.types.Operator):
132 ''' Executes a preset '''
133 bl_idname = "script.execute_preset"
134 bl_label = "Execute a Python Preset"
136 filepath = bpy.props.StringProperty(name="Path", description="Path of the Python file to execute", maxlen=512, default="")
137 menu_idname = bpy.props.StringProperty(name="Menu ID Name", description="ID name of the menu this was called from", default="")
139 def execute(self, context):
140 from os.path import basename
141 filepath = self.filepath
143 # change the menu title to the most recently chosen option
144 preset_class = getattr(bpy.types, self.menu_idname)
145 preset_class.bl_label = bpy.path.display_name(basename(filepath))
147 # execute the preset using script.python_file_run
148 bpy.ops.script.python_file_run(filepath=filepath)
152 class AddPresetRender(AddPresetBase, bpy.types.Operator):
153 '''Add a Render Preset'''
154 bl_idname = "render.preset_add"
155 bl_label = "Add Render Preset"
156 preset_menu = "RENDER_MT_presets"
159 "scene = bpy.context.scene"
163 "scene.render.field_order",
165 "scene.render.fps_base",
166 "scene.render.pixel_aspect_x",
167 "scene.render.pixel_aspect_y",
168 "scene.render.resolution_percentage",
169 "scene.render.resolution_x",
170 "scene.render.resolution_y",
171 "scene.render.use_fields",
172 "scene.render.use_fields_still",
175 preset_subdir = "render"
178 class AddPresetCamera(AddPresetBase, bpy.types.Operator):
179 '''Add a Camera Preset'''
180 bl_idname = "camera.preset_add"
181 bl_label = "Add Camera Preset"
182 preset_menu = "CAMERA_MT_presets"
185 "cam = bpy.context.object.data"
193 preset_subdir = "camera"
196 class AddPresetSSS(AddPresetBase, bpy.types.Operator):
197 '''Add a Subsurface Scattering Preset'''
198 bl_idname = "material.sss_preset_add"
199 bl_label = "Add SSS Preset"
200 preset_menu = "MATERIAL_MT_sss_presets"
203 "material = (bpy.context.material.active_node_material if bpy.context.material.active_node_material else bpy.context.material)"
207 "material.subsurface_scattering.back",
208 "material.subsurface_scattering.color",
209 "material.subsurface_scattering.color_factor",
210 "material.subsurface_scattering.error_threshold",
211 "material.subsurface_scattering.front",
212 "material.subsurface_scattering.ior",
213 "material.subsurface_scattering.radius",
214 "material.subsurface_scattering.scale",
215 "material.subsurface_scattering.texture_factor",
218 preset_subdir = "sss"
221 class AddPresetCloth(AddPresetBase, bpy.types.Operator):
222 '''Add a Cloth Preset'''
223 bl_idname = "cloth.preset_add"
224 bl_label = "Add Cloth Preset"
225 preset_menu = "CLOTH_MT_presets"
228 "cloth = bpy.context.cloth"
232 "cloth.settings.air_damping",
233 "cloth.settings.bending_stiffness",
234 "cloth.settings.mass",
235 "cloth.settings.quality",
236 "cloth.settings.spring_damping",
237 "cloth.settings.structural_stiffness",
240 preset_subdir = "cloth"
243 class AddPresetSunSky(AddPresetBase, bpy.types.Operator):
244 '''Add a Sky & Atmosphere Preset'''
245 bl_idname = "lamp.sunsky_preset_add"
246 bl_label = "Add Sunsky Preset"
247 preset_menu = "LAMP_MT_sunsky_presets"
250 "sky = bpy.context.object.data.sky"
254 "sky.atmosphere_extinction",
255 "sky.atmosphere_inscattering",
256 "sky.atmosphere_turbidity",
257 "sky.backscattered_light",
258 "sky.horizon_brightness",
260 "sky.sun_brightness",
264 "sky.sky_blend_type",
265 "sky.sky_color_space",
269 preset_subdir = "sunsky"
272 class AddPresetInteraction(AddPresetBase, bpy.types.Operator):
273 '''Add an Application Interaction Preset'''
274 bl_idname = "wm.interaction_preset_add"
275 bl_label = "Add Interaction Preset"
276 preset_menu = "USERPREF_MT_interaction_presets"
279 "user_preferences = bpy.context.user_preferences"
283 "user_preferences.edit.use_drag_immediately",
284 "user_preferences.edit.use_insertkey_xyz_to_rgb",
285 "user_preferences.inputs.invert_mouse_zoom",
286 "user_preferences.inputs.select_mouse",
287 "user_preferences.inputs.use_emulate_numpad",
288 "user_preferences.inputs.use_mouse_continuous",
289 "user_preferences.inputs.use_mouse_emulate_3_button",
290 "user_preferences.inputs.view_rotate_method",
291 "user_preferences.inputs.view_zoom_axis",
292 "user_preferences.inputs.view_zoom_method",
295 preset_subdir = "interaction"
298 class AddPresetTrackingCamera(AddPresetBase, bpy.types.Operator):
299 '''Add a Tracking Camera Intrinsics Preset'''
300 bl_idname = "clip.camera_preset_add"
301 bl_label = "Add Camera Preset"
302 preset_menu = "CLIP_MT_camera_presets"
305 "camera = bpy.context.edit_movieclip.tracking.camera"
309 "camera.sensor_width",
310 "camera.sensor_height",
312 "camera.focal_length",
318 preset_subdir = "tracking_camera"
321 class AddPresetKeyconfig(AddPresetBase, bpy.types.Operator):
322 '''Add a Keyconfig Preset'''
323 bl_idname = "wm.keyconfig_preset_add"
324 bl_label = "Add Keyconfig Preset"
325 preset_menu = "USERPREF_MT_keyconfigs"
326 preset_subdir = "keyconfig"
328 def add(self, context, filepath):
329 bpy.ops.wm.keyconfig_export(filepath=filepath)
330 bpy.utils.keyconfig_set(filepath)
332 def pre_cb(self, context):
333 keyconfigs = bpy.context.window_manager.keyconfigs
334 if self.remove_active:
335 preset_menu_class = getattr(bpy.types, self.preset_menu)
336 preset_menu_class.bl_label = keyconfigs.active.name
338 def post_cb(self, context):
339 keyconfigs = bpy.context.window_manager.keyconfigs
340 if self.remove_active:
341 keyconfigs.remove(keyconfigs.active)
344 class AddPresetOperator(AddPresetBase, bpy.types.Operator):
345 '''Add an Application Interaction Preset'''
346 bl_idname = "wm.operator_preset_add"
347 bl_label = "Operator Preset"
348 preset_menu = "WM_MT_operator_presets"
350 operator = bpy.props.StringProperty(name="Operator", maxlen=64, options={'HIDDEN'})
354 "op = bpy.context.space_data.operator",
358 def preset_subdir(self):
359 return AddPresetOperator.operator_path(self.operator)
362 def preset_values(self):
363 properties_blacklist = bpy.types.Operator.bl_rna.properties.keys()
365 prefix, suffix = self.operator.split("_OT_", 1)
366 operator_rna = getattr(getattr(bpy.ops, prefix.lower()), suffix).get_rna().bl_rna
369 for prop_id, prop in operator_rna.properties.items():
370 if (not (prop.is_hidden or prop.is_skip_save)) and prop_id not in properties_blacklist:
371 ret.append("op.%s" % prop_id)
376 def operator_path(operator):
378 prefix, suffix = operator.split("_OT_", 1)
379 return os.path.join("operator", "%s.%s" % (prefix.lower(), suffix))
382 class WM_MT_operator_presets(bpy.types.Menu):
383 bl_label = "Operator Presets"
385 def draw(self, context):
386 self.operator = context.space_data.operator.bl_idname
387 bpy.types.Menu.draw_preset(self, context)
390 def preset_subdir(self):
391 return AddPresetOperator.operator_path(self.operator)
393 preset_operator = "script.execute_preset"