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 if not os
.path
.exists(paths
[0]):
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
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
]
54 for st
in structs
: # clear swap and list
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
]
62 is_filtered = [io_filters.views, io_filters.point_of_views,
63 io_filters.layers, io_filters.displays]
65 for i
in range(len(f_structs
)):
67 if is_filtered[i] is False:
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():
75 DataStore
.sanitize_data(scene
)
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,
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):
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
117 val
= getattr(struct
, prop
.identifier
)
118 if str(type(val
)) in ["<class 'bpy_prop_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
]
127 dict[prop
.identifier
] = val
129 # io_filters = sv.settings.io_filters
130 dump
["data"] = {"point_of_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])
148 filepath
= bpy
.path
.ensure_ext(filepath
, '.blsv')
149 file = gzip
.open(filepath
, mode
='wb')
150 pickle
.dump(dump
, file, protocol
=pickle
.HIGHEST_PROTOCOL
)
154 def stored_views_apply_preset(filepath
, replace
=True):
158 file = gzip
.open(filepath
, mode
='rb')
159 dump
= pickle
.load(file)
162 scene
= bpy
.context
.scene
163 sv
= getattr(scene
, "stored_views", None)
168 # io_filters = sv.settings.io_filters
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:
181 sv_list
= sv_data
[sv_struct
] # .list
182 if replace
is True: # clear swap and list
183 while len(sv_list
) > 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
]
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
]
203 setattr(sv_item
, subprop
, subval
)
205 DataStore
.sanitize_data(scene
)
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(
220 replace
: BoolProperty(
223 description
="Replace current stored views, otherwise append"
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
234 self
.report({'WARNING'},
235 "No filepath specified or file could not be found. Operation Cancelled")
238 # apply chosen preset
239 apply_preset
= IO_Utils
.stored_views_apply_preset(
240 filepath
=self
.filepath
, replace
=self
.replace
243 self
.report({'WARNING'},
244 "Please Initialize Stored Views first (in the 3D View Properties Area)")
247 # copy preset to presets folder
248 filename
= os
.path
.basename(self
.filepath
)
250 shutil
.copyfile(self
.filepath
,
251 os
.path
.join(IO_Utils
.get_preset_path()[0], filename
))
253 self
.report({'WARNING'},
254 "Stored Views: preset applied, but installing failed (preset already exists?)")
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(
267 description
="A current blend scene",
270 replace
: BoolProperty(
273 description
="Replace current stored views, otherwise append"
277 def poll(cls
, context
):
278 return get_preferences()
280 def draw(self
, context
):
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")
296 is_finished
= IO_Utils
.stored_views_apply_from_scene(
297 self
.scene_name
, replace
=self
.replace
300 self
.report({"WARNING"},
301 "Could not find the specified scene. Operation Cancelled")
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(
320 preset_name
: StringProperty(
323 description
="Name of the stored views preset"
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
)
336 VIEW3D_stored_views_import
,
337 VIEW3D_stored_views_import_from_scene
,
338 VIEW3D_stored_views_export
343 bpy
.utils
.register_class(cls
)
347 bpy
.utils
.unregister_class(cls
)