io_export_selected.py
Go to the documentation of this file.
1 # ***** BEGIN GPL LICENSE BLOCK *****
2 #
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation, either version 3 of the License, or
6 # (at your option) any later version.
7 #
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.
12 #
13 # You should have received a copy of the GNU General Public License
14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 #
16 # ***** END GPL LICENSE BLOCK *****
17 
18 # <pep8 compliant>
19 
20 bl_info = {
21  "name": "Export Selected",
22  "author": "dairin0d, rking",
23  "version": (1, 4),
24  "blender": (2, 6, 9),
25  "location": "File > Export > Selected",
26  "description": "Export selected objects to a chosen format",
27  "warning": "",
28  "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"\
29  "Scripts/Import-Export/Export_Selected",
30  "tracker_url": "http://projects.blender.org/tracker/"\
31  "?func=detail&aid=30942",
32  "category": "Import-Export"}
33 #============================================================================#
34 
35 import bpy
36 
37 from bpy_extras.io_utils import ExportHelper
38 
39 join_before_export = {
40  "export_mesh.ply",
41 }
42 
43 bpy_props = {
44  bpy.props.BoolProperty,
45  bpy.props.BoolVectorProperty,
46  bpy.props.IntProperty,
47  bpy.props.IntVectorProperty,
48  bpy.props.FloatProperty,
49  bpy.props.FloatVectorProperty,
50  bpy.props.StringProperty,
51  bpy.props.EnumProperty,
52  bpy.props.PointerProperty,
53  bpy.props.CollectionProperty,
54 }
55 
56 def is_bpy_prop(value):
57  if isinstance(value, tuple) and (len(value) == 2):
58  if (value[0] in bpy_props) and isinstance(value[1], dict):
59  return True
60  return False
61 
62 def iter_public_bpy_props(cls, exclude_hidden=False):
63  for key in dir(cls):
64  if key.startswith("_"):
65  continue
66  value = getattr(cls, key)
67  if is_bpy_prop(value):
68  if exclude_hidden:
69  options = value[1].get("options", "")
70  if 'HIDDEN' in options:
71  continue
72  yield (key, value)
73 
74 def get_op(idname):
75  category_name, op_name = idname.split(".")
76  category = getattr(bpy.ops, category_name)
77  return getattr(category, op_name)
78 
80  def __init__(self, mode='OBJECT', undo=False):
81  if not isinstance(mode, str):
82  mode = ('OBJECT' if mode else None)
83 
84  obj = bpy.context.object
85  if obj and (obj.mode != mode):
86  self.mode = mode
87  else:
88  self.mode = None
89  self.undo = undo
90 
91  def __enter__(self):
92  if self.mode:
93  edit_preferences = bpy.context.user_preferences.edit
94 
95  self.global_undo = edit_preferences.use_global_undo
96  self.prev_mode = bpy.context.object.mode
97 
98  if self.prev_mode != self.mode:
99  if self.undo is not None:
100  edit_preferences.use_global_undo = self.undo
101  bpy.ops.object.mode_set(mode=self.mode)
102 
103  return self
104 
105  def __exit__(self, type, value, traceback):
106  if self.mode:
107  edit_preferences = bpy.context.user_preferences.edit
108 
109  if self.prev_mode != self.mode:
110  bpy.ops.object.mode_set(mode=self.prev_mode)
111  edit_preferences.use_global_undo = self.global_undo
112 
114  #categories = dir(bpy.ops)
115  categories = ["export_anim", "export_mesh", "export_scene"]
116  for category_name in categories:
117  op_category = getattr(bpy.ops, category_name)
118 
119  for name in dir(op_category):
120  total_name = category_name + "." + name
121 
122  if total_name == ExportSelected.bl_idname:
123  continue
124 
125  if "export" in total_name:
126  op = getattr(op_category, name)
127 
128  yield total_name, op
129 
130 class CurrentFormatProperties(bpy.types.PropertyGroup):
131  @classmethod
132  def _clear_props(cls):
133  keys_to_remove = list(cls._keys())
134 
135  for key in keys_to_remove:
136  delattr(cls, key)
137 
138  CurrentFormatProperties.__dict = None
139 
140  @classmethod
141  def _add_props(cls, template):
142  for key, value in iter_public_bpy_props(template):
143  setattr(cls, key, value)
144 
145  CurrentFormatProperties.__dict = {}
146  for key in dir(template):
147  value = getattr(template, key)
148  if is_bpy_prop(value): continue
149  CurrentFormatProperties.__dict[key] = value
150 
151  @classmethod
152  def _keys(cls, exclude_hidden=False):
153  for kv in iter_public_bpy_props(cls, exclude_hidden):
154  yield kv[0]
155 
156  def __getattr__(self, name):
157  return CurrentFormatProperties.__dict[name]
158 
159  def __setattr__(self, name, value):
160  if hasattr(self.__class__, name) and (not name.startswith("_")):
161  supercls = super(CurrentFormatProperties, self.__class__)
162  supercls.__setattr__(self, name, value)
163  else:
164  CurrentFormatProperties.__dict[name] = value
165 
167  # Special case: Collada (built-in) -- has no explicitly defined Python properties
168  apply_modifiers = bpy.props.BoolProperty(name="Apply Modifiers", description="Apply modifiers to exported mesh (non destructive)", default=False)
169  #export_mesh_type=0 # couldn't find correspondence in the UI
170  export_mesh_type_selection = bpy.props.EnumProperty(name="Type of modifiers", description="Modifier resolution for export", default='view', items=[('render', "Render", "Apply modifier's render settings"), ('view', "View", "Apply modifier's view settings")])
171  selected = bpy.props.BoolProperty(name="Selection Only", description="Export only selected elements", default=False)
172  include_children = bpy.props.BoolProperty(name="Include Children", description="Export all children of selected objects (even if not selected)", default=False)
173  include_armatures = bpy.props.BoolProperty(name="Include Armatures", description="Export related armatures (even if not selected)", default=False)
174  include_shapekeys = bpy.props.BoolProperty(name="Include Shape Keys", description="Export all Shape Keys from Mesh Objects", default=True)
175  deform_bones_only = bpy.props.BoolProperty(name="Deform Bones only", description="Only export deforming bones with armatures", default=False)
176  active_uv_only = bpy.props.BoolProperty(name="Only Active UV layer", description="Export textures assigned to the object UV maps", default=False)
177  include_uv_textures = bpy.props.BoolProperty(name="Include UV Textures", description="Export textures assigned to the object UV maps", default=False)
178  include_material_textures = bpy.props.BoolProperty(name="Include Material Textures", description="Export textures assigned to the object Materials", default=False)
179  use_texture_copies = bpy.props.BoolProperty(name="Copy Textures", description="Copy textures to the same folder where .dae file is exported", default=True)
180  triangulate = bpy.props.BoolProperty(name="Triangulate", description="Export Polygons (Quads & NGons) as Triangles", default=True)
181  use_object_instantiation = bpy.props.BoolProperty(name="Use Object Instances", description="Instantiate multiple Objects from same Data", default=True)
182  sort_by_name = bpy.props.BoolProperty(name="Sort by Object name", description="Sort exported data by Object name", default=False)
183  #export_transformation_type=0 # couldn't find correspondence in the UI
184  export_transformation_type_selection = bpy.props.EnumProperty(name="Transformation Type", description="Transformation type for translation, scale and rotation", default='matrix', items=[('both', "Both", "Use <matrix> AND <translate>, <rotate>, <scale> to specify transformations"), ('transrotloc', "TransLocRot", "Use <translate>, <rotate>, <scale> to specify transformations"), ('matrix', "Matrix", "Use <matrix> to specify transformations")])
185  open_sim = bpy.props.BoolProperty(name="Export for OpenSim", description="Compatibility mode for OpenSim and compatible online worlds", default=False)
186 
187  def draw(self, context):
188  layout = self.layout
189 
190  box = layout.box()
191  box.label(text="Export Data Options", icon='MESH_DATA')
192  row = box.split(0.6)
193  row.prop(self, "apply_modifiers")
194  row.prop(self, "export_mesh_type_selection", text="")
195  box.prop(self, "selected")
196  box.prop(self, "include_children")
197  box.prop(self, "include_armatures")
198  box.prop(self, "include_shapekeys")
199 
200  box = layout.box()
201  box.label(text="Texture Options", icon='TEXTURE')
202  box.prop(self, "active_uv_only")
203  box.prop(self, "include_uv_textures")
204  box.prop(self, "include_material_textures")
205  box.prop(self, "use_texture_copies", text="Copy")
206 
207  box = layout.box()
208  box.label(text="Armature Options", icon='ARMATURE_DATA')
209  box.prop(self, "deform_bones_only")
210  box.prop(self, "open_sim")
211 
212  box = layout.box()
213  box.label(text="Collada Options", icon='MODIFIER')
214  box.prop(self, "triangulate")
215  box.prop(self, "use_object_instantiation")
216  row = box.split(0.6)
217  row.label(text="Transformation Type")
218  row.prop(self, "export_transformation_type_selection", text="")
219  box.prop(self, "sort_by_name")
220 
221 class ExportSelected(bpy.types.Operator, ExportHelper):
222  '''Export selected objects to a chosen format'''
223  bl_idname = "export_scene.selected"
224  bl_label = "Export Selected"
225 
226  filename_ext = bpy.props.StringProperty(
227  default="",
228  options={'HIDDEN'},
229  )
230 
231  filter_glob = bpy.props.StringProperty(
232  default="*.*",
233  options={'HIDDEN'},
234  )
235 
236  selection_mode = bpy.props.EnumProperty(
237  name="Selection Mode",
238  description="Limit/expand the selection",
239  default='SELECTED',
240  items=[
241  ('SELECTED', "Selected", ""),
242  ('VISIBLE', "Visible", ""),
243  ('ALL', "All", ""),
244  ],
245  )
246 
247  include_children = bpy.props.BoolProperty(
248  name="Include Children",
249  description="Keep children even if they're not selected",
250  default=True,
251  )
252 
253  remove_orphans = bpy.props.BoolProperty(
254  name="Remove Orphans",
255  description="Remove datablocks that have no users",
256  default=True,
257  )
258 
259  keep_materials = bpy.props.BoolProperty(
260  name="Keep Materials",
261  description="Keep Materials",
262  default=True,
263  )
264 
265  keep_textures = bpy.props.BoolProperty(
266  name="Keep Textures",
267  description="Keep Textures",
268  default=True,
269  )
270 
271  keep_world_textures = bpy.props.BoolProperty(
272  name="Keep World Textures",
273  description="Keep World Textures",
274  default=False,
275  )
276 
277  object_types = bpy.props.EnumProperty(
278  name="Object types",
279  description="Object type(s) to export",
280  default={'ALL'},
281  items=[
282  ('ALL', "All", ""),
283  ('MESH', "Mesh", ""),
284  ('CURVE', "Curve", ""),
285  ('SURFACE', "Surface", ""),
286  ('META', "Meta", ""),
287  ('FONT', "Font", ""),
288  ('ARMATURE', "Armature", ""),
289  ('LATTICE', "Lattice", ""),
290  ('EMPTY', "Empty", ""),
291  ('CAMERA', "Camera", ""),
292  ('LAMP', "Lamp", ""),
293  ('SPEAKER', "Speaker", ""),
294  ],
295  options={'ENUM_FLAG'},
296  )
297 
298  visible_name = bpy.props.StringProperty(
299  name="Visible name",
300  description="Visible name",
301  options={'HIDDEN'},
302  )
303 
304  format = bpy.props.StringProperty(
305  name="Format",
306  description="Export format",
307  options={'HIDDEN'},
308  )
309 
310  format_props = bpy.props.PointerProperty(
311  type=CurrentFormatProperties,
312  options={'HIDDEN'},
313  )
314 
315  props_initialized = bpy.props.BoolProperty(
316  options={'HIDDEN'},
317  default=False,
318  )
319 
320  @classmethod
321  def poll(cls, context):
322  return len(context.scene.objects) != 0
323 
324  def fill_props(self):
325  if self.props_initialized: return
326 
327  CurrentFormatProperties._clear_props()
328 
329  if self.format:
330  op = get_op(self.format)
331  op_class = type(op.get_instance())
332 
333  if self.format == "wm.collada_export":
334  op_class = ColladaEmulator
335 
336  CurrentFormatProperties._add_props(op_class)
337  else:
338  self.visible_name = "Blend"
339  self.filename_ext = ".blend"
340  self.filter_glob = "*.blend"
341 
342  self.props_initialized = True
343 
344  def invoke(self, context, event):
345  self.fill_props()
346  self.filepath = context.object.name + self.filename_ext
347  return ExportHelper.invoke(self, context, event)
348 
349  def clear_world(self, context):
350  bpy.ops.ed.undo_push(message="Delete unselected")
351 
352  for scene in bpy.data.scenes:
353  if scene != context.scene:
354  bpy.data.scenes.remove(scene)
355 
356  scene = context.scene
357 
358  objs = set()
359 
360  def add_obj(obj):
361  if self.object_types.intersection({'ALL', obj.type}):
362  objs.add(obj)
363 
364  if self.include_children:
365  for child in obj.children:
366  add_obj(child)
367 
368  for obj in scene.objects:
369  if (self.selection_mode == 'SELECTED') and obj.select:
370  add_obj(obj)
371  elif (self.selection_mode == 'VISIBLE') and obj.is_visible(scene):
372  obj.hide_select = False
373  add_obj(obj)
374  elif (self.selection_mode == 'ALL'):
375  obj.hide_select = False
376  add_obj(obj)
377 
378  for obj in scene.objects:
379  if obj in objs:
380  obj.select = True
381  else:
382  scene.objects.unlink(obj)
383  bpy.data.objects.remove(obj)
384  scene.update()
385 
386  if not self.format:
387  if not self.keep_materials:
388  for material in bpy.data.materials:
389  material.user_clear()
390  bpy.data.materials.remove(material)
391 
392  if not self.keep_textures:
393  for world in bpy.data.worlds:
394  for i in range(len(world.texture_slots)):
395  world.texture_slots.clear(i)
396  for material in bpy.data.materials:
397  for i in range(len(material.texture_slots)):
398  material.texture_slots.clear(i)
399  for brush in bpy.data.brushes:
400  brush.texture = None
401  for texture in bpy.data.textures:
402  texture.user_clear()
403  bpy.data.textures.remove(texture)
404  elif not self.keep_world_textures:
405  for world in bpy.data.worlds:
406  for i in range(len(world.texture_slots)):
407  world.texture_slots.clear(i)
408 
409  if self.remove_orphans:
410  datablocks_cleanup_order = [
411  #"window_managers",
412  #"screens",
413  "scenes",
414  "worlds",
415 
416  "grease_pencil",
417  "fonts",
418  "scripts",
419  "texts",
420  "movieclips",
421  "actions",
422  "speakers",
423  "sounds",
424  "brushes",
425 
426  "node_groups",
427  "groups",
428  "objects",
429 
430  "armatures",
431  "cameras",
432  "lamps",
433  "lattices",
434  "shape_keys",
435  "meshes",
436  "metaballs",
437  "particles",
438  "curves",
439 
440  "materials",
441  "textures",
442  "images",
443 
444  "libraries",
445  ]
446  for datablocks_name in datablocks_cleanup_order:
447  datablocks = getattr(bpy.data, datablocks_name)
448  if type(datablocks).__name__ == "bpy_prop_collection":
449  for datablock in datablocks:
450  if datablock.users == 0:
451  datablocks.remove(datablock)
452 
453  if self.format in join_before_export:
454  bpy.ops.object.convert()
455  bpy.ops.object.join()
456 
457  def execute(self, context):
458  with ToggleObjectMode(undo=None):
459  self.clear_world(context)
460 
461  if self.format:
462  props = {}
463  for key in CurrentFormatProperties._keys():
464  props[key] = getattr(self.format_props, key)
465  props["filepath"] = self.filepath
466 
467  op = get_op(self.format)
468 
469  op(**props)
470  else:
471  bpy.ops.wm.save_as_mainfile(
472  filepath=self.filepath,
473  copy=True,
474  )
475 
476  bpy.ops.ed.undo()
477  bpy.ops.ed.undo_push(message="Export Selected")
478 
479  return {'FINISHED'}
480 
481  def draw(self, context):
482  layout = self.layout
483 
484  layout.label("Export " + self.visible_name)
485 
486  layout.prop(self, "selection_mode", text="")
487  layout.prop(self, "include_children")
488  layout.prop_menu_enum(self, "object_types")
489 
490  layout.box()
491 
492  if not self.format:
493  layout.prop(self, "remove_orphans")
494  layout.prop(self, "keep_materials")
495  layout.prop(self, "keep_textures")
496  sublayout = layout.row()
497  sublayout.enabled = self.keep_textures
498  sublayout.prop(self, "keep_world_textures")
499  return
500 
501  op = get_op(self.format)
502  op_class = type(op.get_instance())
503 
504  if self.format == "wm.collada_export":
505  op_class = ColladaEmulator
506 
507  if hasattr(op_class, "draw"):
508  self.format_props.layout = layout
509  op_class.draw(self.format_props, context)
510  else:
511  for key in CurrentFormatProperties._keys(True):
512  if key == 'filepath': continue
513  layout.prop(self.format_props, key)
514 
515 class OBJECT_MT_selected_export(bpy.types.Menu):
516  bl_idname = "OBJECT_MT_selected_export"
517  bl_label = "Selected"
518 
519  def draw(self, context):
520  layout = self.layout
521 
522  def def_op(visible_name, total_name="", layout=layout):
523  if visible_name.lower().startswith("export "):
524  visible_name = visible_name[len("export "):]
525 
526  if total_name:
527  op = get_op(total_name)
528  if not op.poll():
529  layout = layout.row()
530  layout.enabled = False
531 
532  op_info = layout.operator(
533  ExportSelected.bl_idname,
534  text=visible_name,
535  )
536  op_info.format = total_name
537  op_info.visible_name = visible_name
538 
539  return op_info
540 
541  # Special case: export to .blend (the default)
542  def_op("Blend")
543 
544  # Special case: Collada is built-in, resides
545  # in an unconventional category, and has no
546  # explicit ext/glob properties defined
547  op_info = def_op("Collada", "wm.collada_export")
548  op_info.filename_ext = ".dae"
549  op_info.filter_glob = "*.dae"
550 
551  for total_name, op in iter_exporters():
552  op_class = type(op.get_instance())
553  rna = op.get_rna()
554 
555  op_info = def_op(rna.rna_type.name, total_name)
556 
557  if hasattr(op_class, "filename_ext"):
558  op_info.filename_ext = op_class.filename_ext
559 
560  if hasattr(rna, "filter_glob"):
561  op_info.filter_glob = rna.filter_glob
562 
563 def menu_func_export(self, context):
564  self.layout.menu("OBJECT_MT_selected_export", text="Selected")
565 
566 def register():
567  bpy.utils.register_class(CurrentFormatProperties)
568  bpy.utils.register_class(ExportSelected)
569  bpy.utils.register_class(OBJECT_MT_selected_export)
570  bpy.types.INFO_MT_file_export.prepend(menu_func_export)
571 
573  bpy.types.INFO_MT_file_export.remove(menu_func_export)
574  bpy.utils.unregister_class(OBJECT_MT_selected_export)
575  bpy.utils.unregister_class(ExportSelected)
576  bpy.utils.unregister_class(CurrentFormatProperties)
577 
578 if __name__ == "__main__":
579  register()
580 
def __exit__(self, type, value, traceback)
def iter_public_bpy_props(cls, exclude_hidden=False)
def menu_func_export(self, context)
def invoke(self, context, event)
def __init__(self, mode='OBJECT', undo=False)


naoqi_tools
Author(s): Mikael Arguedas
autogenerated on Thu Jul 16 2020 03:18:37