Import_3ds: Improved distance cue chunk import
[blender-addons.git] / space_view3d_stored_views / io.py
blob7dbe49265195f8ebbb51623c31738e59b0a53982
1 # SPDX-FileCopyrightText: 2017-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 # Authors: nfloyd, Francesco Siddi
7 import gzip
8 import os
9 import pickle
10 import shutil
12 import bpy
13 from bpy.types import Operator
14 from bpy.props import (
15 BoolProperty,
16 StringProperty,
18 from bpy_extras.io_utils import (
19 ExportHelper,
20 ImportHelper,
22 from .import bl_info
23 from .core import get_preferences
24 from .operators import DataStore
27 # TODO: reinstate filters?
28 class IO_Utils():
30 @staticmethod
31 def get_preset_path():
32 # locate stored_views preset folder
33 paths = bpy.utils.preset_paths("stored_views")
34 if not paths:
35 # stored_views preset folder doesn't exist, so create it
36 paths = [os.path.join(bpy.utils.user_resource('SCRIPTS'), "presets",
37 "stored_views")]
38 os.makedirs(paths[0], exist_ok=True)
40 return(paths)
42 @staticmethod
43 def stored_views_apply_from_scene(scene_name, replace=True):
44 scene = bpy.context.scene
45 scene_exists = True if scene_name in bpy.data.scenes.keys() else False
47 if scene_exists:
48 sv = bpy.context.scene.stored_views
49 # io_filters = sv.settings.io_filters
51 structs = [sv.view_list, sv.pov_list, sv.layers_list, sv.display_list]
52 if replace is True:
53 for st in structs: # clear swap and list
54 while len(st) > 0:
55 st.remove(0)
57 f_sv = bpy.data.scenes[scene_name].stored_views
58 # f_sv = bpy.data.scenes[scene_name].stored_views
59 f_structs = [f_sv.view_list, f_sv.pov_list, f_sv.layers_list, f_sv.display_list]
60 """
61 is_filtered = [io_filters.views, io_filters.point_of_views,
62 io_filters.layers, io_filters.displays]
63 """
64 for i in range(len(f_structs)):
65 """
66 if is_filtered[i] is False:
67 continue
68 """
69 for j in f_structs[i]:
70 item = structs[i].add()
71 # stored_views_copy_item(j, item)
72 for k, v in j.items():
73 item[k] = v
74 DataStore.sanitize_data(scene)
75 return True
76 else:
77 return False
79 @staticmethod
80 def stored_views_export_to_blsv(filepath, name='Custom Preset'):
81 # create dictionary with all information
82 dump = {"info": {}, "data": {}}
83 dump["info"]["script"] = bl_info['name']
84 dump["info"]["script_version"] = bl_info['version']
85 dump["info"]["version"] = bpy.app.version
86 dump["info"]["preset_name"] = name
88 # get current stored views settings
89 scene = bpy.context.scene
90 sv = scene.stored_views
92 def dump_view_list(dict, list):
93 if str(type(list)) == "<class 'bpy_prop_collection_idprop'>":
94 for i, struct_dict in enumerate(list):
95 dict[i] = {"name": str,
96 "pov": {},
97 "layers": {},
98 "display": {}}
99 dict[i]["name"] = struct_dict.name
100 dump_item(dict[i]["pov"], struct_dict.pov)
101 dump_item(dict[i]["layers"], struct_dict.layers)
102 dump_item(dict[i]["display"], struct_dict.display)
104 def dump_list(dict, list):
105 if str(type(list)) == "<class 'bpy_prop_collection_idprop'>":
106 for i, struct in enumerate(list):
107 dict[i] = {}
108 dump_item(dict[i], struct)
110 def dump_item(dict, struct):
111 for prop in struct.bl_rna.properties:
112 if prop.identifier == "rna_type":
113 # not a setting, so skip
114 continue
116 val = getattr(struct, prop.identifier)
117 if str(type(val)) in ["<class 'bpy_prop_array'>"]:
118 # array
119 dict[prop.identifier] = [v for v in val]
120 # address the pickle limitations of dealing with the Vector class
121 elif str(type(val)) in ["<class 'Vector'>",
122 "<class 'Quaternion'>"]:
123 dict[prop.identifier] = [v for v in val]
124 else:
125 # single value
126 dict[prop.identifier] = val
128 # io_filters = sv.settings.io_filters
129 dump["data"] = {"point_of_views": {},
130 "layers": {},
131 "displays": {},
132 "views": {}}
134 others_data = [(dump["data"]["point_of_views"], sv.pov_list), # , io_filters.point_of_views),
135 (dump["data"]["layers"], sv.layers_list), # , io_filters.layers),
136 (dump["data"]["displays"], sv.display_list)] # , io_filters.displays)]
137 for list_data in others_data:
138 # if list_data[2] is True:
139 dump_list(list_data[0], list_data[1])
141 views_data = (dump["data"]["views"], sv.view_list)
142 # if io_filters.views is True:
143 dump_view_list(views_data[0], views_data[1])
145 # save to file
146 filepath = filepath
147 filepath = bpy.path.ensure_ext(filepath, '.blsv')
148 file = gzip.open(filepath, mode='wb')
149 pickle.dump(dump, file, protocol=pickle.HIGHEST_PROTOCOL)
150 file.close()
152 @staticmethod
153 def stored_views_apply_preset(filepath, replace=True):
154 if not filepath:
155 return False
157 file = gzip.open(filepath, mode='rb')
158 dump = pickle.load(file)
159 file.close()
160 # apply preset
161 scene = bpy.context.scene
162 sv = getattr(scene, "stored_views", None)
164 if not sv:
165 return False
167 # io_filters = sv.settings.io_filters
168 sv_data = {
169 "point_of_views": sv.pov_list,
170 "views": sv.view_list,
171 "layers": sv.layers_list,
172 "displays": sv.display_list
174 for sv_struct, props in dump["data"].items():
176 is_filtered = getattr(io_filters, sv_struct)
177 if is_filtered is False:
178 continue
180 sv_list = sv_data[sv_struct] # .list
181 if replace is True: # clear swap and list
182 while len(sv_list) > 0:
183 sv_list.remove(0)
184 for key, prop_struct in props.items():
185 sv_item = sv_list.add()
187 for subprop, subval in prop_struct.items():
188 if isinstance(subval, dict): # views : pov, layers, displays
189 v_subprop = getattr(sv_item, subprop)
190 for v_subkey, v_subval in subval.items():
191 if isinstance(v_subval, list): # array like of pov,...
192 v_array_like = getattr(v_subprop, v_subkey)
193 for i in range(len(v_array_like)):
194 v_array_like[i] = v_subval[i]
195 else:
196 setattr(v_subprop, v_subkey, v_subval) # others
197 elif isinstance(subval, list):
198 array_like = getattr(sv_item, subprop)
199 for i in range(len(array_like)):
200 array_like[i] = subval[i]
201 else:
202 setattr(sv_item, subprop, subval)
204 DataStore.sanitize_data(scene)
206 return True
209 class VIEW3D_stored_views_import(Operator, ImportHelper):
210 bl_idname = "stored_views.import_blsv"
211 bl_label = "Import Stored Views preset"
212 bl_description = "Import a .blsv preset file to the current Stored Views"
214 filename_ext = ".blsv"
215 filter_glob: StringProperty(
216 default="*.blsv",
217 options={'HIDDEN'}
219 replace: BoolProperty(
220 name="Replace",
221 default=True,
222 description="Replace current stored views, otherwise append"
225 @classmethod
226 def poll(cls, context):
227 return get_preferences()
229 def execute(self, context):
230 # the usual way is to not select the file in the file browser
231 exists = os.path.isfile(self.filepath) if self.filepath else False
232 if not exists:
233 self.report({'WARNING'},
234 "No filepath specified or file could not be found. Operation Cancelled")
235 return {'CANCELLED'}
237 # apply chosen preset
238 apply_preset = IO_Utils.stored_views_apply_preset(
239 filepath=self.filepath, replace=self.replace
241 if not apply_preset:
242 self.report({'WARNING'},
243 "Please Initialize Stored Views first (in the 3D View Properties Area)")
244 return {'CANCELLED'}
246 # copy preset to presets folder
247 filename = os.path.basename(self.filepath)
248 try:
249 shutil.copyfile(self.filepath,
250 os.path.join(IO_Utils.get_preset_path()[0], filename))
251 except:
252 self.report({'WARNING'},
253 "Stored Views: preset applied, but installing failed (preset already exists?)")
254 return{'CANCELLED'}
256 return{'FINISHED'}
259 class VIEW3D_stored_views_import_from_scene(Operator):
260 bl_idname = "stored_views.import_from_scene"
261 bl_label = "Import stored views from scene"
262 bl_description = "Import currently stored views from an another scene"
264 scene_name: StringProperty(
265 name="Scene Name",
266 description="A current blend scene",
267 default=""
269 replace: BoolProperty(
270 name="Replace",
271 default=True,
272 description="Replace current stored views, otherwise append"
275 @classmethod
276 def poll(cls, context):
277 return get_preferences()
279 def draw(self, context):
280 layout = self.layout
282 layout.prop_search(self, "scene_name", bpy.data, "scenes")
283 layout.prop(self, "replace")
285 def invoke(self, context, event):
286 return context.window_manager.invoke_props_dialog(self)
288 def execute(self, context):
289 # filepath should always be given
290 if not self.scene_name:
291 self.report({"WARNING"},
292 "No scene name was given. Operation Cancelled")
293 return{'CANCELLED'}
295 is_finished = IO_Utils.stored_views_apply_from_scene(
296 self.scene_name, replace=self.replace
298 if not is_finished:
299 self.report({"WARNING"},
300 "Could not find the specified scene. Operation Cancelled")
301 return {"CANCELLED"}
303 return{'FINISHED'}
306 class VIEW3D_stored_views_export(Operator, ExportHelper):
307 bl_idname = "stored_views.export_blsv"
308 bl_label = "Export Stored Views preset"
309 bl_description = "Export the current Stored Views to a .blsv preset file"
311 filename_ext = ".blsv"
312 filepath: StringProperty(
313 default=os.path.join(IO_Utils.get_preset_path()[0], "untitled")
315 filter_glob: StringProperty(
316 default="*.blsv",
317 options={'HIDDEN'}
319 preset_name: StringProperty(
320 name="Preset name",
321 default="",
322 description="Name of the stored views preset"
325 @classmethod
326 def poll(cls, context):
327 return get_preferences()
329 def execute(self, context):
330 IO_Utils.stored_views_export_to_blsv(self.filepath, self.preset_name)
332 return{'FINISHED'}
334 classes = (
335 VIEW3D_stored_views_import,
336 VIEW3D_stored_views_import_from_scene,
337 VIEW3D_stored_views_export
340 def register():
341 for cls in classes:
342 bpy.utils.register_class(cls)
344 def unregister():
345 for cls in classes:
346 bpy.utils.unregister_class(cls)