1 # SPDX-License-Identifier: GPL-2.0-or-later
3 # Authors: nfloyd, Francesco Siddi
11 from bpy
.types
import Operator
12 from bpy
.props
import (
16 from bpy_extras
.io_utils
import (
21 from .core
import get_preferences
22 from .operators
import DataStore
25 # TODO: reinstate filters?
29 def get_preset_path():
30 # locate stored_views preset folder
31 paths
= bpy
.utils
.preset_paths("stored_views")
33 # stored_views preset folder doesn't exist, so create it
34 paths
= [os
.path
.join(bpy
.utils
.user_resource('SCRIPTS'), "presets",
36 if not os
.path
.exists(paths
[0]):
42 def stored_views_apply_from_scene(scene_name
, replace
=True):
43 scene
= bpy
.context
.scene
44 scene_exists
= True if scene_name
in bpy
.data
.scenes
.keys() else False
47 sv
= bpy
.context
.scene
.stored_views
48 # io_filters = sv.settings.io_filters
50 structs
= [sv
.view_list
, sv
.pov_list
, sv
.layers_list
, sv
.display_list
]
52 for st
in structs
: # clear swap and list
56 f_sv
= bpy
.data
.scenes
[scene_name
].stored_views
57 # f_sv = bpy.data.scenes[scene_name].stored_views
58 f_structs
= [f_sv
.view_list
, f_sv
.pov_list
, f_sv
.layers_list
, f_sv
.display_list
]
60 is_filtered = [io_filters.views, io_filters.point_of_views,
61 io_filters.layers, io_filters.displays]
63 for i
in range(len(f_structs
)):
65 if is_filtered[i] is False:
68 for j
in f_structs
[i
]:
69 item
= structs
[i
].add()
70 # stored_views_copy_item(j, item)
71 for k
, v
in j
.items():
73 DataStore
.sanitize_data(scene
)
79 def stored_views_export_to_blsv(filepath
, name
='Custom Preset'):
80 # create dictionary with all information
81 dump
= {"info": {}, "data": {}}
82 dump
["info"]["script"] = bl_info
['name']
83 dump
["info"]["script_version"] = bl_info
['version']
84 dump
["info"]["version"] = bpy
.app
.version
85 dump
["info"]["preset_name"] = name
87 # get current stored views settings
88 scene
= bpy
.context
.scene
89 sv
= scene
.stored_views
91 def dump_view_list(dict, list):
92 if str(type(list)) == "<class 'bpy_prop_collection_idprop'>":
93 for i
, struct_dict
in enumerate(list):
94 dict[i
] = {"name": str,
98 dict[i
]["name"] = struct_dict
.name
99 dump_item(dict[i
]["pov"], struct_dict
.pov
)
100 dump_item(dict[i
]["layers"], struct_dict
.layers
)
101 dump_item(dict[i
]["display"], struct_dict
.display
)
103 def dump_list(dict, list):
104 if str(type(list)) == "<class 'bpy_prop_collection_idprop'>":
105 for i
, struct
in enumerate(list):
107 dump_item(dict[i
], struct
)
109 def dump_item(dict, struct
):
110 for prop
in struct
.bl_rna
.properties
:
111 if prop
.identifier
== "rna_type":
112 # not a setting, so skip
115 val
= getattr(struct
, prop
.identifier
)
116 if str(type(val
)) in ["<class 'bpy_prop_array'>"]:
118 dict[prop
.identifier
] = [v
for v
in val
]
119 # address the pickle limitations of dealing with the Vector class
120 elif str(type(val
)) in ["<class 'Vector'>",
121 "<class 'Quaternion'>"]:
122 dict[prop
.identifier
] = [v
for v
in val
]
125 dict[prop
.identifier
] = val
127 # io_filters = sv.settings.io_filters
128 dump
["data"] = {"point_of_views": {},
133 others_data
= [(dump
["data"]["point_of_views"], sv
.pov_list
), # , io_filters.point_of_views),
134 (dump
["data"]["layers"], sv
.layers_list
), # , io_filters.layers),
135 (dump
["data"]["displays"], sv
.display_list
)] # , io_filters.displays)]
136 for list_data
in others_data
:
137 # if list_data[2] is True:
138 dump_list(list_data
[0], list_data
[1])
140 views_data
= (dump
["data"]["views"], sv
.view_list
)
141 # if io_filters.views is True:
142 dump_view_list(views_data
[0], views_data
[1])
146 filepath
= bpy
.path
.ensure_ext(filepath
, '.blsv')
147 file = gzip
.open(filepath
, mode
='wb')
148 pickle
.dump(dump
, file, protocol
=pickle
.HIGHEST_PROTOCOL
)
152 def stored_views_apply_preset(filepath
, replace
=True):
156 file = gzip
.open(filepath
, mode
='rb')
157 dump
= pickle
.load(file)
160 scene
= bpy
.context
.scene
161 sv
= getattr(scene
, "stored_views", None)
166 # io_filters = sv.settings.io_filters
168 "point_of_views": sv
.pov_list
,
169 "views": sv
.view_list
,
170 "layers": sv
.layers_list
,
171 "displays": sv
.display_list
173 for sv_struct
, props
in dump
["data"].items():
175 is_filtered = getattr(io_filters, sv_struct)
176 if is_filtered is False:
179 sv_list
= sv_data
[sv_struct
] # .list
180 if replace
is True: # clear swap and list
181 while len(sv_list
) > 0:
183 for key
, prop_struct
in props
.items():
184 sv_item
= sv_list
.add()
186 for subprop
, subval
in prop_struct
.items():
187 if isinstance(subval
, dict): # views : pov, layers, displays
188 v_subprop
= getattr(sv_item
, subprop
)
189 for v_subkey
, v_subval
in subval
.items():
190 if isinstance(v_subval
, list): # array like of pov,...
191 v_array_like
= getattr(v_subprop
, v_subkey
)
192 for i
in range(len(v_array_like
)):
193 v_array_like
[i
] = v_subval
[i
]
195 setattr(v_subprop
, v_subkey
, v_subval
) # others
196 elif isinstance(subval
, list):
197 array_like
= getattr(sv_item
, subprop
)
198 for i
in range(len(array_like
)):
199 array_like
[i
] = subval
[i
]
201 setattr(sv_item
, subprop
, subval
)
203 DataStore
.sanitize_data(scene
)
208 class VIEW3D_stored_views_import(Operator
, ImportHelper
):
209 bl_idname
= "stored_views.import_blsv"
210 bl_label
= "Import Stored Views preset"
211 bl_description
= "Import a .blsv preset file to the current Stored Views"
213 filename_ext
= ".blsv"
214 filter_glob
: StringProperty(
218 replace
: BoolProperty(
221 description
="Replace current stored views, otherwise append"
225 def poll(cls
, context
):
226 return get_preferences()
228 def execute(self
, context
):
229 # the usual way is to not select the file in the file browser
230 exists
= os
.path
.isfile(self
.filepath
) if self
.filepath
else False
232 self
.report({'WARNING'},
233 "No filepath specified or file could not be found. Operation Cancelled")
236 # apply chosen preset
237 apply_preset
= IO_Utils
.stored_views_apply_preset(
238 filepath
=self
.filepath
, replace
=self
.replace
241 self
.report({'WARNING'},
242 "Please Initialize Stored Views first (in the 3D View Properties Area)")
245 # copy preset to presets folder
246 filename
= os
.path
.basename(self
.filepath
)
248 shutil
.copyfile(self
.filepath
,
249 os
.path
.join(IO_Utils
.get_preset_path()[0], filename
))
251 self
.report({'WARNING'},
252 "Stored Views: preset applied, but installing failed (preset already exists?)")
258 class VIEW3D_stored_views_import_from_scene(Operator
):
259 bl_idname
= "stored_views.import_from_scene"
260 bl_label
= "Import stored views from scene"
261 bl_description
= "Import currently stored views from an another scene"
263 scene_name
: StringProperty(
265 description
="A current blend scene",
268 replace
: BoolProperty(
271 description
="Replace current stored views, otherwise append"
275 def poll(cls
, context
):
276 return get_preferences()
278 def draw(self
, context
):
281 layout
.prop_search(self
, "scene_name", bpy
.data
, "scenes")
282 layout
.prop(self
, "replace")
284 def invoke(self
, context
, event
):
285 return context
.window_manager
.invoke_props_dialog(self
)
287 def execute(self
, context
):
288 # filepath should always be given
289 if not self
.scene_name
:
290 self
.report({"WARNING"},
291 "No scene name was given. Operation Cancelled")
294 is_finished
= IO_Utils
.stored_views_apply_from_scene(
295 self
.scene_name
, replace
=self
.replace
298 self
.report({"WARNING"},
299 "Could not find the specified scene. Operation Cancelled")
305 class VIEW3D_stored_views_export(Operator
, ExportHelper
):
306 bl_idname
= "stored_views.export_blsv"
307 bl_label
= "Export Stored Views preset"
308 bl_description
= "Export the current Stored Views to a .blsv preset file"
310 filename_ext
= ".blsv"
311 filepath
: StringProperty(
312 default
=os
.path
.join(IO_Utils
.get_preset_path()[0], "untitled")
314 filter_glob
: StringProperty(
318 preset_name
: StringProperty(
321 description
="Name of the stored views preset"
325 def poll(cls
, context
):
326 return get_preferences()
328 def execute(self
, context
):
329 IO_Utils
.stored_views_export_to_blsv(self
.filepath
, self
.preset_name
)
334 VIEW3D_stored_views_import
,
335 VIEW3D_stored_views_import_from_scene
,
336 VIEW3D_stored_views_export
341 bpy
.utils
.register_class(cls
)
345 bpy
.utils
.unregister_class(cls
)