Refactor: Node Wrangler: PreviewNode operator
[blender-addons.git] / space_view3d_stored_views / io.py
blob08b0675a578bcf15b5d0b55874bea4809e39cae5
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 if not os.path.exists(paths[0]):
39 os.makedirs(paths[0])
41 return(paths)
43 @staticmethod
44 def stored_views_apply_from_scene(scene_name, replace=True):
45 scene = bpy.context.scene
46 scene_exists = True if scene_name in bpy.data.scenes.keys() else False
48 if scene_exists:
49 sv = bpy.context.scene.stored_views
50 # io_filters = sv.settings.io_filters
52 structs = [sv.view_list, sv.pov_list, sv.layers_list, sv.display_list]
53 if replace is True:
54 for st in structs: # clear swap and list
55 while len(st) > 0:
56 st.remove(0)
58 f_sv = bpy.data.scenes[scene_name].stored_views
59 # f_sv = bpy.data.scenes[scene_name].stored_views
60 f_structs = [f_sv.view_list, f_sv.pov_list, f_sv.layers_list, f_sv.display_list]
61 """
62 is_filtered = [io_filters.views, io_filters.point_of_views,
63 io_filters.layers, io_filters.displays]
64 """
65 for i in range(len(f_structs)):
66 """
67 if is_filtered[i] is False:
68 continue
69 """
70 for j in f_structs[i]:
71 item = structs[i].add()
72 # stored_views_copy_item(j, item)
73 for k, v in j.items():
74 item[k] = v
75 DataStore.sanitize_data(scene)
76 return True
77 else:
78 return False
80 @staticmethod
81 def stored_views_export_to_blsv(filepath, name='Custom Preset'):
82 # create dictionary with all information
83 dump = {"info": {}, "data": {}}
84 dump["info"]["script"] = bl_info['name']
85 dump["info"]["script_version"] = bl_info['version']
86 dump["info"]["version"] = bpy.app.version
87 dump["info"]["preset_name"] = name
89 # get current stored views settings
90 scene = bpy.context.scene
91 sv = scene.stored_views
93 def dump_view_list(dict, list):
94 if str(type(list)) == "<class 'bpy_prop_collection_idprop'>":
95 for i, struct_dict in enumerate(list):
96 dict[i] = {"name": str,
97 "pov": {},
98 "layers": {},
99 "display": {}}
100 dict[i]["name"] = struct_dict.name
101 dump_item(dict[i]["pov"], struct_dict.pov)
102 dump_item(dict[i]["layers"], struct_dict.layers)
103 dump_item(dict[i]["display"], struct_dict.display)
105 def dump_list(dict, list):
106 if str(type(list)) == "<class 'bpy_prop_collection_idprop'>":
107 for i, struct in enumerate(list):
108 dict[i] = {}
109 dump_item(dict[i], struct)
111 def dump_item(dict, struct):
112 for prop in struct.bl_rna.properties:
113 if prop.identifier == "rna_type":
114 # not a setting, so skip
115 continue
117 val = getattr(struct, prop.identifier)
118 if str(type(val)) in ["<class 'bpy_prop_array'>"]:
119 # array
120 dict[prop.identifier] = [v for v in val]
121 # address the pickle limitations of dealing with the Vector class
122 elif str(type(val)) in ["<class 'Vector'>",
123 "<class 'Quaternion'>"]:
124 dict[prop.identifier] = [v for v in val]
125 else:
126 # single value
127 dict[prop.identifier] = val
129 # io_filters = sv.settings.io_filters
130 dump["data"] = {"point_of_views": {},
131 "layers": {},
132 "displays": {},
133 "views": {}}
135 others_data = [(dump["data"]["point_of_views"], sv.pov_list), # , io_filters.point_of_views),
136 (dump["data"]["layers"], sv.layers_list), # , io_filters.layers),
137 (dump["data"]["displays"], sv.display_list)] # , io_filters.displays)]
138 for list_data in others_data:
139 # if list_data[2] is True:
140 dump_list(list_data[0], list_data[1])
142 views_data = (dump["data"]["views"], sv.view_list)
143 # if io_filters.views is True:
144 dump_view_list(views_data[0], views_data[1])
146 # save to file
147 filepath = filepath
148 filepath = bpy.path.ensure_ext(filepath, '.blsv')
149 file = gzip.open(filepath, mode='wb')
150 pickle.dump(dump, file, protocol=pickle.HIGHEST_PROTOCOL)
151 file.close()
153 @staticmethod
154 def stored_views_apply_preset(filepath, replace=True):
155 if not filepath:
156 return False
158 file = gzip.open(filepath, mode='rb')
159 dump = pickle.load(file)
160 file.close()
161 # apply preset
162 scene = bpy.context.scene
163 sv = getattr(scene, "stored_views", None)
165 if not sv:
166 return False
168 # io_filters = sv.settings.io_filters
169 sv_data = {
170 "point_of_views": sv.pov_list,
171 "views": sv.view_list,
172 "layers": sv.layers_list,
173 "displays": sv.display_list
175 for sv_struct, props in dump["data"].items():
177 is_filtered = getattr(io_filters, sv_struct)
178 if is_filtered is False:
179 continue
181 sv_list = sv_data[sv_struct] # .list
182 if replace is True: # clear swap and list
183 while len(sv_list) > 0:
184 sv_list.remove(0)
185 for key, prop_struct in props.items():
186 sv_item = sv_list.add()
188 for subprop, subval in prop_struct.items():
189 if isinstance(subval, dict): # views : pov, layers, displays
190 v_subprop = getattr(sv_item, subprop)
191 for v_subkey, v_subval in subval.items():
192 if isinstance(v_subval, list): # array like of pov,...
193 v_array_like = getattr(v_subprop, v_subkey)
194 for i in range(len(v_array_like)):
195 v_array_like[i] = v_subval[i]
196 else:
197 setattr(v_subprop, v_subkey, v_subval) # others
198 elif isinstance(subval, list):
199 array_like = getattr(sv_item, subprop)
200 for i in range(len(array_like)):
201 array_like[i] = subval[i]
202 else:
203 setattr(sv_item, subprop, subval)
205 DataStore.sanitize_data(scene)
207 return True
210 class VIEW3D_stored_views_import(Operator, ImportHelper):
211 bl_idname = "stored_views.import_blsv"
212 bl_label = "Import Stored Views preset"
213 bl_description = "Import a .blsv preset file to the current Stored Views"
215 filename_ext = ".blsv"
216 filter_glob: StringProperty(
217 default="*.blsv",
218 options={'HIDDEN'}
220 replace: BoolProperty(
221 name="Replace",
222 default=True,
223 description="Replace current stored views, otherwise append"
226 @classmethod
227 def poll(cls, context):
228 return get_preferences()
230 def execute(self, context):
231 # the usual way is to not select the file in the file browser
232 exists = os.path.isfile(self.filepath) if self.filepath else False
233 if not exists:
234 self.report({'WARNING'},
235 "No filepath specified or file could not be found. Operation Cancelled")
236 return {'CANCELLED'}
238 # apply chosen preset
239 apply_preset = IO_Utils.stored_views_apply_preset(
240 filepath=self.filepath, replace=self.replace
242 if not apply_preset:
243 self.report({'WARNING'},
244 "Please Initialize Stored Views first (in the 3D View Properties Area)")
245 return {'CANCELLED'}
247 # copy preset to presets folder
248 filename = os.path.basename(self.filepath)
249 try:
250 shutil.copyfile(self.filepath,
251 os.path.join(IO_Utils.get_preset_path()[0], filename))
252 except:
253 self.report({'WARNING'},
254 "Stored Views: preset applied, but installing failed (preset already exists?)")
255 return{'CANCELLED'}
257 return{'FINISHED'}
260 class VIEW3D_stored_views_import_from_scene(Operator):
261 bl_idname = "stored_views.import_from_scene"
262 bl_label = "Import stored views from scene"
263 bl_description = "Import currently stored views from an another scene"
265 scene_name: StringProperty(
266 name="Scene Name",
267 description="A current blend scene",
268 default=""
270 replace: BoolProperty(
271 name="Replace",
272 default=True,
273 description="Replace current stored views, otherwise append"
276 @classmethod
277 def poll(cls, context):
278 return get_preferences()
280 def draw(self, context):
281 layout = self.layout
283 layout.prop_search(self, "scene_name", bpy.data, "scenes")
284 layout.prop(self, "replace")
286 def invoke(self, context, event):
287 return context.window_manager.invoke_props_dialog(self)
289 def execute(self, context):
290 # filepath should always be given
291 if not self.scene_name:
292 self.report({"WARNING"},
293 "No scene name was given. Operation Cancelled")
294 return{'CANCELLED'}
296 is_finished = IO_Utils.stored_views_apply_from_scene(
297 self.scene_name, replace=self.replace
299 if not is_finished:
300 self.report({"WARNING"},
301 "Could not find the specified scene. Operation Cancelled")
302 return {"CANCELLED"}
304 return{'FINISHED'}
307 class VIEW3D_stored_views_export(Operator, ExportHelper):
308 bl_idname = "stored_views.export_blsv"
309 bl_label = "Export Stored Views preset"
310 bl_description = "Export the current Stored Views to a .blsv preset file"
312 filename_ext = ".blsv"
313 filepath: StringProperty(
314 default=os.path.join(IO_Utils.get_preset_path()[0], "untitled")
316 filter_glob: StringProperty(
317 default="*.blsv",
318 options={'HIDDEN'}
320 preset_name: StringProperty(
321 name="Preset name",
322 default="",
323 description="Name of the stored views preset"
326 @classmethod
327 def poll(cls, context):
328 return get_preferences()
330 def execute(self, context):
331 IO_Utils.stored_views_export_to_blsv(self.filepath, self.preset_name)
333 return{'FINISHED'}
335 classes = (
336 VIEW3D_stored_views_import,
337 VIEW3D_stored_views_import_from_scene,
338 VIEW3D_stored_views_export
341 def register():
342 for cls in classes:
343 bpy.utils.register_class(cls)
345 def unregister():
346 for cls in classes:
347 bpy.utils.unregister_class(cls)