1 # SPDX-FileCopyrightText: 2017-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 # Authors: nfloyd, Francesco Siddi
13 from bpy
.types
import Operator
14 from bpy
.props
import (
18 from bpy_extras
.io_utils
import (
23 from .core
import get_preferences
24 from .operators
import DataStore
27 # TODO: reinstate filters?
31 def get_preset_path():
32 # locate stored_views preset folder
33 paths
= bpy
.utils
.preset_paths("stored_views")
35 # stored_views preset folder doesn't exist, so create it
36 paths
= [os
.path
.join(bpy
.utils
.user_resource('SCRIPTS'), "presets",
38 os
.makedirs(paths
[0], exist_ok
=True)
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
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
]
53 for st
in structs
: # clear swap and list
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
]
61 is_filtered = [io_filters.views, io_filters.point_of_views,
62 io_filters.layers, io_filters.displays]
64 for i
in range(len(f_structs
)):
66 if is_filtered[i] is False:
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():
74 DataStore
.sanitize_data(scene
)
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,
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):
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
116 val
= getattr(struct
, prop
.identifier
)
117 if str(type(val
)) in ["<class 'bpy_prop_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
]
126 dict[prop
.identifier
] = val
128 # io_filters = sv.settings.io_filters
129 dump
["data"] = {"point_of_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])
147 filepath
= bpy
.path
.ensure_ext(filepath
, '.blsv')
148 file = gzip
.open(filepath
, mode
='wb')
149 pickle
.dump(dump
, file, protocol
=pickle
.HIGHEST_PROTOCOL
)
153 def stored_views_apply_preset(filepath
, replace
=True):
157 file = gzip
.open(filepath
, mode
='rb')
158 dump
= pickle
.load(file)
161 scene
= bpy
.context
.scene
162 sv
= getattr(scene
, "stored_views", None)
167 # io_filters = sv.settings.io_filters
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:
180 sv_list
= sv_data
[sv_struct
] # .list
181 if replace
is True: # clear swap and list
182 while len(sv_list
) > 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
]
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
]
202 setattr(sv_item
, subprop
, subval
)
204 DataStore
.sanitize_data(scene
)
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(
219 replace
: BoolProperty(
222 description
="Replace current stored views, otherwise append"
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
233 self
.report({'WARNING'},
234 "No filepath specified or file could not be found. Operation Cancelled")
237 # apply chosen preset
238 apply_preset
= IO_Utils
.stored_views_apply_preset(
239 filepath
=self
.filepath
, replace
=self
.replace
242 self
.report({'WARNING'},
243 "Please Initialize Stored Views first (in the 3D View Properties Area)")
246 # copy preset to presets folder
247 filename
= os
.path
.basename(self
.filepath
)
249 shutil
.copyfile(self
.filepath
,
250 os
.path
.join(IO_Utils
.get_preset_path()[0], filename
))
252 self
.report({'WARNING'},
253 "Stored Views: preset applied, but installing failed (preset already exists?)")
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(
266 description
="A current blend scene",
269 replace
: BoolProperty(
272 description
="Replace current stored views, otherwise append"
276 def poll(cls
, context
):
277 return get_preferences()
279 def draw(self
, context
):
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")
295 is_finished
= IO_Utils
.stored_views_apply_from_scene(
296 self
.scene_name
, replace
=self
.replace
299 self
.report({"WARNING"},
300 "Could not find the specified scene. Operation Cancelled")
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(
319 preset_name
: StringProperty(
322 description
="Name of the stored views preset"
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
)
335 VIEW3D_stored_views_import
,
336 VIEW3D_stored_views_import_from_scene
,
337 VIEW3D_stored_views_export
342 bpy
.utils
.register_class(cls
)
346 bpy
.utils
.unregister_class(cls
)