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