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 AddPresetSSS(AddPresetBase, bpy.types.Operator):
179 '''Add a Subsurface Scattering Preset'''
180 bl_idname = "material.sss_preset_add"
181 bl_label = "Add SSS Preset"
182 preset_menu = "MATERIAL_MT_sss_presets"
185 "material = (bpy.context.material.active_node_material if bpy.context.material.active_node_material else bpy.context.material)"
189 "material.subsurface_scattering.back",
190 "material.subsurface_scattering.color",
191 "material.subsurface_scattering.color_factor",
192 "material.subsurface_scattering.error_threshold",
193 "material.subsurface_scattering.front",
194 "material.subsurface_scattering.ior",
195 "material.subsurface_scattering.radius",
196 "material.subsurface_scattering.scale",
197 "material.subsurface_scattering.texture_factor",
200 preset_subdir = "sss"
203 class AddPresetCloth(AddPresetBase, bpy.types.Operator):
204 '''Add a Cloth Preset'''
205 bl_idname = "cloth.preset_add"
206 bl_label = "Add Cloth Preset"
207 preset_menu = "CLOTH_MT_presets"
210 "cloth = bpy.context.cloth"
214 "cloth.settings.air_damping",
215 "cloth.settings.bending_stiffness",
216 "cloth.settings.mass",
217 "cloth.settings.quality",
218 "cloth.settings.spring_damping",
219 "cloth.settings.structural_stiffness",
222 preset_subdir = "cloth"
225 class AddPresetSunSky(AddPresetBase, bpy.types.Operator):
226 '''Add a Sky & Atmosphere Preset'''
227 bl_idname = "lamp.sunsky_preset_add"
228 bl_label = "Add Sunsky Preset"
229 preset_menu = "LAMP_MT_sunsky_presets"
232 "sky = bpy.context.object.data.sky"
236 "sky.atmosphere_extinction",
237 "sky.atmosphere_inscattering",
238 "sky.atmosphere_turbidity",
239 "sky.backscattered_light",
240 "sky.horizon_brightness",
242 "sky.sun_brightness",
246 "sky.sky_blend_type",
247 "sky.sky_color_space",
251 preset_subdir = "sunsky"
254 class AddPresetInteraction(AddPresetBase, bpy.types.Operator):
255 '''Add an Application Interaction Preset'''
256 bl_idname = "wm.interaction_preset_add"
257 bl_label = "Add Interaction Preset"
258 preset_menu = "USERPREF_MT_interaction_presets"
261 "user_preferences = bpy.context.user_preferences"
265 "user_preferences.edit.use_drag_immediately",
266 "user_preferences.edit.use_insertkey_xyz_to_rgb",
267 "user_preferences.inputs.invert_mouse_zoom",
268 "user_preferences.inputs.select_mouse",
269 "user_preferences.inputs.use_emulate_numpad",
270 "user_preferences.inputs.use_mouse_continuous",
271 "user_preferences.inputs.use_mouse_emulate_3_button",
272 "user_preferences.inputs.view_rotate_method",
273 "user_preferences.inputs.view_zoom_axis",
274 "user_preferences.inputs.view_zoom_method",
277 preset_subdir = "interaction"
280 class AddPresetKeyconfig(AddPresetBase, bpy.types.Operator):
281 '''Add a Keyconfig Preset'''
282 bl_idname = "wm.keyconfig_preset_add"
283 bl_label = "Add Keyconfig Preset"
284 preset_menu = "USERPREF_MT_keyconfigs"
285 preset_subdir = "keyconfig"
287 def add(self, context, filepath):
288 bpy.ops.wm.keyconfig_export(filepath=filepath)
289 bpy.utils.keyconfig_set(filepath)
291 def pre_cb(self, context):
292 keyconfigs = bpy.context.window_manager.keyconfigs
293 if self.remove_active:
294 preset_menu_class = getattr(bpy.types, self.preset_menu)
295 preset_menu_class.bl_label = keyconfigs.active.name
297 def post_cb(self, context):
298 keyconfigs = bpy.context.window_manager.keyconfigs
299 if self.remove_active:
300 keyconfigs.remove(keyconfigs.active)
303 class AddPresetOperator(AddPresetBase, bpy.types.Operator):
304 '''Add an Application Interaction Preset'''
305 bl_idname = "wm.operator_preset_add"
306 bl_label = "Operator Preset"
307 preset_menu = "WM_MT_operator_presets"
309 operator = bpy.props.StringProperty(name="Operator", maxlen=64, options={'HIDDEN'})
313 "op = bpy.context.space_data.operator",
317 def preset_subdir(self):
318 return AddPresetOperator.operator_path(self.operator)
321 def preset_values(self):
322 properties_blacklist = bpy.types.Operator.bl_rna.properties.keys()
324 prefix, suffix = self.operator.split("_OT_", 1)
325 operator_rna = getattr(getattr(bpy.ops, prefix.lower()), suffix).get_rna().bl_rna
328 for prop_id, prop in operator_rna.properties.items():
329 if (not (prop.is_hidden or prop.is_skip_save)) and prop_id not in properties_blacklist:
330 ret.append("op.%s" % prop_id)
335 def operator_path(operator):
337 prefix, suffix = operator.split("_OT_", 1)
338 return os.path.join("operator", "%s.%s" % (prefix.lower(), suffix))
341 class WM_MT_operator_presets(bpy.types.Menu):
342 bl_label = "Operator Presets"
344 def draw(self, context):
345 self.operator = context.space_data.operator.bl_idname
346 bpy.types.Menu.draw_preset(self, context)
349 def preset_subdir(self):
350 return AddPresetOperator.operator_path(self.operator)
352 preset_operator = "script.execute_preset"