diff options
author | lijenstina <lijenstina@gmail.com> | 2017-05-12 02:59:08 +0300 |
---|---|---|
committer | lijenstina <lijenstina@gmail.com> | 2017-05-12 02:59:08 +0300 |
commit | 32b977d1b2f4abb9f09308636ba10c224c6a76f6 (patch) | |
tree | 993bc1cd3da72efac105bfc1ddfbfa9127ea3760 /stored_views | |
parent | d9e259cd94194c74387ff445a1623119a48505e8 (diff) |
Stored Views: cleanup, refactor, various fixes
Bumped version to 0.3.6
Pep 8 cleanup
Remove the logger setup, uncomment the log calls in the sripts
Replace deprecated imp call with importlib
Add AddonPreferences settings importers, draw update
Fix crash with New Camera To View
if there is no object present
Fix import, export BLSV crashes:
No file was selected during import
Pickle crashes during export with Vectors, Quaternions
Crash with import if the scene wasn't initialized
Finish the IO user interface switchable from the preferences
Finish Import from an another scene - scene selection
was missing making the feature not working
Replace type comparisons with isinstance
Fix the 3D view text alignment so it doesn't show bellow the
properties area
Fix the 3d view draw crash when opening the File Browser
Fix crash with Material enum missing in DisplayData
Diffstat (limited to 'stored_views')
-rw-r--r-- | stored_views/__init__.py | 117 | ||||
-rw-r--r-- | stored_views/core.py | 65 | ||||
-rw-r--r-- | stored_views/io.py | 239 | ||||
-rw-r--r-- | stored_views/operators.py | 86 | ||||
-rw-r--r-- | stored_views/properties.py | 114 | ||||
-rw-r--r-- | stored_views/ui.py | 110 |
6 files changed, 478 insertions, 253 deletions
diff --git a/stored_views/__init__.py b/stored_views/__init__.py index cfc0beb0..8deca8f0 100644 --- a/stored_views/__init__.py +++ b/stored_views/__init__.py @@ -18,73 +18,113 @@ bl_info = { "name": "Stored Views", - "description": "Save and restore User defined views, pov, layers and display configs.", + "description": "Save and restore User defined views, pov, layers and display configs", "author": "nfloyd, Francesco Siddi", - "version": (0, 3, 4,), + "version": (0, 3, 6), "blender": (2, 7, 8), "location": "View3D > Properties > Stored Views", - "warning": 'beta release, single view only', - "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.5/Py/Scripts/3D_interaction/stored_views", - "tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/", + "warning": "", + "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.5/" + "Py/Scripts/3D_interaction/stored_views", "category": "3D View"} -# ACKNOWLEDGMENT -# ============== -# import/export functionality is mostly based -# on Bart Crouch's Theme Manager Addon +""" +ACKNOWLEDGMENT +============== +import/export functionality is mostly based +on Bart Crouch's Theme Manager Addon -# TODO: check against 2.63 -# TODO: quadview complete support : investigate. Where's the data? -# TODO: lock_camera_and_layers. investigate usage -# TODO: list reordering +TODO: quadview complete support : investigate. Where's the data? +TODO: lock_camera_and_layers. investigate usage +TODO: list reordering -# logging setup -''' -import logging - -logger = logging.getLogger(__name__) -logger.setLevel(logging.DEBUG) -hdlr = logging.StreamHandler() -fmtr = logging.Formatter('%(asctime)s %(levelname)s %(name)s : %(funcName)s - %(message)s') -hdlr.setFormatter(fmtr) -logger.addHandler(hdlr) -''' +NOTE: logging setup has to be provided by the user in a separate config file + as Blender will not try to configure logging by default in an add-on + The Config File should be in the Blender Config folder > /scripts/startup/config_logging.py + For setting up /location of the config folder see: + https://docs.blender.org/manual/en/dev/getting_started/ + installing/configuration/directories.html + For configuring logging itself in the file, general Python documentation should work + As the logging calls are not configured, they can be kept in the other modules of this add-on + and will not have output until the logging configuration is set up +""" if "bpy" in locals(): - import imp - imp.reload(ui) - imp.reload(properties) - imp.reload(core) - imp.reload(operators) - imp.reload(io) + import importlib + importlib.reload(core) + importlib.reload(ui) + importlib.reload(properties) + importlib.reload(operators) + importlib.reload(io) else: - #from . import properties, core - from . import ui, properties, core, operators, io + from . import core + from . import ui + from . import properties + from . import operators + from . import io import bpy -from bpy.props import PointerProperty +from bpy.props import ( + BoolProperty, + IntProperty, + PointerProperty, + ) +from bpy.types import ( + AddonPreferences, + Operator, + ) -class VIEW3D_stored_views_initialize(bpy.types.Operator): +class VIEW3D_stored_views_initialize(Operator): bl_idname = "view3d.stored_views_initialize" - bl_label = "Initilize" + bl_label = "Initialize" @classmethod def poll(cls, context): return not hasattr(bpy.types.Scene, 'stored_views') def execute(self, context): - bpy.types.Scene.stored_views = PointerProperty(type=properties.StoredViewsData) + bpy.types.Scene.stored_views = PointerProperty( + type=properties.StoredViewsData + ) scenes = bpy.data.scenes for scene in scenes: core.DataStore.sanitize_data(scene) return {'FINISHED'} +# Addon Preferences + +class VIEW3D_stored_views_preferences(AddonPreferences): + bl_idname = __name__ + + show_exporters = BoolProperty( + name="Enable I/O Operators", + default=False, + description="Enable Import/Export Operations in the UI:\n" + "Import Stored Views preset,\n" + "Export Stored Views preset and \n" + "Import stored views from scene", + ) + view_3d_update_rate = IntProperty( + name="3D view update", + description="Update rate of the 3D view redraw\n" + "Increse the value if the UI feels sluggish", + min=1, max=10, + default=1 + ) + + def draw(self, context): + layout = self.layout + + row = layout.row(align=True) + row.prop(self, "view_3d_update_rate", toggle=True) + row.prop(self, "show_exporters", toggle=True) + + def register(): bpy.utils.register_module(__name__) - # Context restricted, need to initialize different (button to be clicked by user) - #initialize() + def unregister(): ui.VIEW3D_stored_views_draw.handle_remove(bpy.context) @@ -92,5 +132,6 @@ def unregister(): if hasattr(bpy.types.Scene, "stored_views"): del bpy.types.Scene.stored_views + if __name__ == "__main__": register() diff --git a/stored_views/core.py b/stored_views/core.py index 21e49b31..34f57dfe 100644 --- a/stored_views/core.py +++ b/stored_views/core.py @@ -1,15 +1,25 @@ -''' +# gpl authors: nfloyd, Francesco Siddi + + import logging module_logger = logging.getLogger(__name__) -''' -import hashlib +import hashlib import bpy +# Utility function get preferences setting for exporters +def get_preferences(): + # replace the key if the add-on name changes + addon = bpy.context.user_preferences.addons["stored_views"] + show_warn = (addon.preferences.show_exporters if addon else False) + + return show_warn + + class StoredView(): def __init__(self, mode, index=None): -# self.logger = logging.getLogger('%s.StoredView' % __name__) + self.logger = logging.getLogger('%s.StoredView' % __name__) self.scene = bpy.context.scene self.view3d = bpy.context.space_data self.index = index @@ -21,12 +31,12 @@ class StoredView(): else: stored_view = self.data_store.get(self.index) self.from_v3d(stored_view) -# self.logger.debug('index: %s name: %s' % (self.data_store.current_index, stored_view.name)) + self.logger.debug('index: %s name: %s' % (self.data_store.current_index, stored_view.name)) def set(self): stored_view = self.data_store.get(self.index) self.update_v3d(stored_view) -# self.logger.debug('index: %s name: %s' % (self.data_store.current_index, stored_view.name)) + self.logger.debug('index: %s name: %s' % (self.data_store.current_index, stored_view.name)) def from_v3d(self, stored_view): raise NotImplementedError("Subclass must implement abstract method") @@ -42,7 +52,7 @@ class StoredView(): class POV(StoredView): def __init__(self, index=None): super().__init__(mode='POV', index=index) -# self.logger = logging.getLogger('%s.POV' % __name__) + self.logger = logging.getLogger('%s.POV' % __name__) def from_v3d(self, stored_view): view3d = self.view3d @@ -61,7 +71,7 @@ class POV(StoredView): stored_view.camera_type = view3d.camera.type # type : 'CAMERA' or 'MESH' stored_view.camera_name = view3d.camera.name # store string instead of object stored_view.camera_pointer = view3d.camera.as_pointer() - if view3d.lock_object != None: + if view3d.lock_object is not None: stored_view.lock_object_name = view3d.lock_object.name # idem stored_view.lock_object_pointer = view3d.lock_object.as_pointer() # idem @@ -79,7 +89,7 @@ class POV(StoredView): view3d.clip_start = stored_view.clip_start view3d.clip_end = stored_view.clip_end view3d.lock_cursor = stored_view.lock_cursor - if stored_view.lock_cursor == True: + if stored_view.lock_cursor is True: # update cursor only if view is locked to cursor view3d.cursor_location = stored_view.cursor_location @@ -120,18 +130,19 @@ class POV(StoredView): @staticmethod def is_modified(context, stored_view): - # TODO: check for others param, currently only perspectiveand perspective_matrix are checked -# logger = logging.getLogger('%s.POV' % __name__) + # TODO: check for others param, currently only perspective + # and perspective_matrix are checked + POV.logger = logging.getLogger('%s.POV' % __name__) view3d = context.space_data region3d = view3d.region_3d if region3d.view_perspective != stored_view.perspective: -# logger.debug('view_perspective') + POV.logger.debug('view_perspective') return True md5 = POV._get_perspective_matrix_md5(region3d) if (md5 != stored_view.perspective_matrix_md5 and - region3d.view_perspective != "CAMERA"): -# logger.debug('perspective_matrix') + region3d.view_perspective != "CAMERA"): + POV.logger.debug('perspective_matrix') return True return False @@ -145,7 +156,7 @@ class POV(StoredView): class Layers(StoredView): def __init__(self, index=None): super().__init__(mode='LAYERS', index=index) -# self.logger = logging.getLogger('%s.Layers' % __name__) + self.logger = logging.getLogger('%s.Layers' % __name__) def from_v3d(self, stored_view): view3d = self.view3d @@ -156,26 +167,25 @@ class Layers(StoredView): def update_v3d(self, stored_view): view3d = self.view3d view3d.lock_camera_and_layers = stored_view.lock_camera_and_layers - if stored_view.lock_camera_and_layers == True: + if stored_view.lock_camera_and_layers is True: self.scene.layers = stored_view.scene_layers else: view3d.layers = stored_view.view_layers @staticmethod def is_modified(context, stored_view): -# logger = logging.getLogger('%s.Layers' % __name__) + Layers.logger = logging.getLogger('%s.Layers' % __name__) if stored_view.lock_camera_and_layers != context.space_data.lock_camera_and_layers: -# logger.debug('lock_camera_and_layers') + Layers.logger.debug('lock_camera_and_layers') return True - if stored_view.lock_camera_and_layers == True: + if stored_view.lock_camera_and_layers is True: for i in range(20): if stored_view.scene_layers[i] != context.scene.layers[i]: -# logger.debug('scene_layers[%s]' % (i, )) + Layers.logger.debug('scene_layers[%s]' % (i, )) return True else: for i in range(20): if stored_view.view_layers[i] != context.space_data.view3d.layers[i]: -# logger.debug('view_layers[%s]' % (i ,)) return True return False @@ -183,7 +193,7 @@ class Layers(StoredView): class Display(StoredView): def __init__(self, index=None): super().__init__(mode='DISPLAY', index=index) -# self.logger = logging.getLogger('%s.Display' % __name__) + self.logger = logging.getLogger('%s.Display' % __name__) def from_v3d(self, stored_view): view3d = self.view3d @@ -221,24 +231,23 @@ class Display(StoredView): @staticmethod def is_modified(context, stored_view): - # logger = logging.getLogger('%s.Display' % __name__) + Display.logger = logging.getLogger('%s.Display' % __name__) view3d = context.space_data excludes = ["material_mode", "quad_view", "lock_rotation", "show_sync_view", "use_box_clip", "name"] for k, v in stored_view.items(): if k not in excludes: if getattr(view3d, k) != getattr(stored_view, k): -# logger.debug('%s' % (k ,)) return True if stored_view.material_mode != context.scene.game_settings.material_mode: -# logger.debug('material_mode') + Display.logger.debug('material_mode') return True class View(StoredView): def __init__(self, index=None): super().__init__(mode='VIEW', index=index) -# self.logger = logging.getLogger('%s.View' % __name__) + self.logger = logging.getLogger('%s.View' % __name__) self.pov = POV() self.layers = Layers() self.display = Display() @@ -264,12 +273,12 @@ class View(StoredView): class DataStore(): def __init__(self, scene=None, mode=None): - if scene == None: + if scene is None: scene = bpy.context.scene stored_views = scene.stored_views self.mode = mode - if mode == None: + if mode is None: self.mode = stored_views.mode if self.mode == 'VIEW': diff --git a/stored_views/io.py b/stored_views/io.py index 862b2f92..b6aec4dd 100644 --- a/stored_views/io.py +++ b/stored_views/io.py @@ -1,19 +1,28 @@ +# gpl authors: nfloyd, Francesco Siddi + import gzip import os import pickle import shutil import bpy -from bpy.props import (BoolProperty, - StringProperty) - -from bpy_extras.io_utils import ExportHelper, ImportHelper - +from bpy.types import Operator +from bpy.props import ( + BoolProperty, + StringProperty + ) +from bpy_extras.io_utils import ( + ExportHelper, + ImportHelper, + ) from . import bl_info -from . operators import DataStore +from .core import get_preferences +from .operators import DataStore + # TODO: reinstate filters? class IO_Utils(): + @staticmethod def get_preset_path(): # locate stored_views preset folder @@ -21,7 +30,7 @@ class IO_Utils(): if not paths: # stored_views preset folder doesn't exist, so create it paths = [os.path.join(bpy.utils.user_resource('SCRIPTS'), "presets", - "stored_views")] + "stored_views")] if not os.path.exists(paths[0]): os.makedirs(paths[0]) @@ -30,28 +39,39 @@ class IO_Utils(): @staticmethod def stored_views_apply_from_scene(scene_name, replace=True): scene = bpy.context.scene - sv = bpy.context.scene.stored_views -# io_filters = sv.settings.io_filters - - structs = [sv.view_list, sv.pov_list, sv.layers_list, sv.display_list] - if replace == True: - for st in structs: # clear swap and list - while len(st) > 0: - st.remove(0) - - f_sv = bpy.data.scenes[scene_name].stored_views - f_structs = [f_sv.view_list, f_sv.pov_list, f_sv.layers_list, f_sv.display_list] -# is_filtered = [io_filters.views, io_filters.point_of_views, io_filters.layers, io_filters.displays] - - for i in range(len(f_structs)): -# if is_filtered[i] == False: -# continue - for j in f_structs[i]: - item = structs[i].add() - #stored_views_copy_item(j, item) - for k, v in j.items(): - item[k] = v - DataStore.sanitize_data(scene) + scene_exists = True if scene_name in bpy.data.scenes.keys() else False + + if scene_exists: + sv = bpy.context.scene.stored_views + # io_filters = sv.settings.io_filters + + structs = [sv.view_list, sv.pov_list, sv.layers_list, sv.display_list] + if replace is True: + for st in structs: # clear swap and list + while len(st) > 0: + st.remove(0) + + f_sv = bpy.data.scenes[scene_name].stored_views + # f_sv = bpy.data.scenes[scene_name].stored_views + f_structs = [f_sv.view_list, f_sv.pov_list, f_sv.layers_list, f_sv.display_list] + """ + is_filtered = [io_filters.views, io_filters.point_of_views, + io_filters.layers, io_filters.displays] + """ + for i in range(len(f_structs)): + """ + if is_filtered[i] is False: + continue + """ + for j in f_structs[i]: + item = structs[i].add() + # stored_views_copy_item(j, item) + for k, v in j.items(): + item[k] = v + DataStore.sanitize_data(scene) + return True + else: + return False @staticmethod def stored_views_export_to_blsv(filepath, name='Custom Preset'): @@ -60,7 +80,6 @@ class IO_Utils(): dump["info"]["script"] = bl_info['name'] dump["info"]["script_version"] = bl_info['version'] dump["info"]["version"] = bpy.app.version -# dump["info"]["build_revision"] = bpy.app.build_revision dump["info"]["preset_name"] = name # get current stored views settings @@ -90,32 +109,34 @@ class IO_Utils(): if prop.identifier == "rna_type": # not a setting, so skip continue + val = getattr(struct, prop.identifier) - if str(type(val)) in ["<class 'bpy_prop_array'>", - "<class 'mathutils.Quaternion'>", - "<class 'mathutils.Vector'>"]: + if str(type(val)) in ["<class 'bpy_prop_array'>"]: # array - dict[prop.identifier] = [v \ - for v in val] + dict[prop.identifier] = [v for v in val] + # adress the pickle limitations of dealing with the Vector class + elif str(type(val)) in ["<class 'Vector'>", + "<class 'Quaternion'>"]: + dict[prop.identifier] = [v for v in val] else: # single value dict[prop.identifier] = val -# io_filters = sv.settings.io_filters + # io_filters = sv.settings.io_filters dump["data"] = {"point_of_views": {}, "layers": {}, "displays": {}, "views": {}} - others_data = [(dump["data"]["point_of_views"], sv.pov_list), # , io_filters.point_of_views), - (dump["data"]["layers"], sv.layers_list), # , io_filters.layers), - (dump["data"]["displays"], sv.display_list)] # , io_filters.displays)] + others_data = [(dump["data"]["point_of_views"], sv.pov_list), # , io_filters.point_of_views), + (dump["data"]["layers"], sv.layers_list), # , io_filters.layers), + (dump["data"]["displays"], sv.display_list)] # , io_filters.displays)] for list_data in others_data: -# if list_data[2] == True: + # if list_data[2] is True: dump_list(list_data[0], list_data[1]) views_data = (dump["data"]["views"], sv.view_list) -# if io_filters.views == True: + # if io_filters.views is True: dump_view_list(views_data[0], views_data[1]) # save to file @@ -127,40 +148,49 @@ class IO_Utils(): @staticmethod def stored_views_apply_preset(filepath, replace=True): + if not filepath: + return False + file = gzip.open(filepath, mode='rb') dump = pickle.load(file) file.close() - # apply preset scene = bpy.context.scene - sv = scene.stored_views -# io_filters = sv.settings.io_filters + sv = getattr(scene, "stored_views", None) + + if not sv: + return False + + # io_filters = sv.settings.io_filters sv_data = {"point_of_views": sv.pov_list, "views": sv.view_list, "layers": sv.layers_list, "displays": sv.display_list} for sv_struct, props in dump["data"].items(): -# is_filtered = getattr(io_filters, sv_struct) -# if is_filtered == False: -# continue - sv_list = sv_data[sv_struct]#.list - if replace == True: # clear swap and list + """ + is_filtered = getattr(io_filters, sv_struct) + if is_filtered is False: + continue + """ + sv_list = sv_data[sv_struct] # .list + if replace is True: # clear swap and list while len(sv_list) > 0: sv_list.remove(0) for key, prop_struct in props.items(): sv_item = sv_list.add() + for subprop, subval in prop_struct.items(): - if type(subval) == type({}): # views : pov, layers, displays + if isinstance(subval, dict): # views : pov, layers, displays v_subprop = getattr(sv_item, subprop) for v_subkey, v_subval in subval.items(): - if type(v_subval) == type([]): # array like of pov,... + if isinstance(v_subval, list): # array like of pov,... v_array_like = getattr(v_subprop, v_subkey) for i in range(len(v_array_like)): v_array_like[i] = v_subval[i] else: setattr(v_subprop, v_subkey, v_subval) # others - elif type(subval) == type([]): + elif isinstance(subval, list): array_like = getattr(sv_item, subprop) for i in range(len(array_like)): array_like[i] = subval[i] @@ -169,21 +199,45 @@ class IO_Utils(): DataStore.sanitize_data(scene) + return True + -class VIEW3D_stored_views_import(bpy.types.Operator, ImportHelper): +class VIEW3D_stored_views_import(Operator, ImportHelper): bl_idname = "stored_views.import" bl_label = "Import Stored Views preset" bl_description = "Import a .blsv preset file to the current Stored Views" filename_ext = ".blsv" - filter_glob = StringProperty(default="*.blsv", options={'HIDDEN'}) - replace = BoolProperty(name="Replace", - default=True, - description="Replace current stored views, otherwise append") + filter_glob = StringProperty( + default="*.blsv", + options={'HIDDEN'} + ) + replace = BoolProperty( + name="Replace", + default=True, + description="Replace current stored views, otherwise append" + ) + + @classmethod + def poll(cls, context): + return get_preferences() def execute(self, context): + # the usual way is to not select the file in the file browser + exists = os.path.isfile(self.filepath) if self.filepath else False + if not exists: + self.report({'WARNING'}, + "No filepath specified, or file could not be found. Operation Cancelled") + return {'CANCELLED'} + # apply chosen preset - IO_Utils.stored_views_apply_preset(filepath=self.filepath, replace=self.replace) + apply_preset = IO_Utils.stored_views_apply_preset( + filepath=self.filepath, replace=self.replace + ) + if not apply_preset: + self.report({'WARNING'}, + "Please Initialize Stored Views first (in the 3D View Properties Area)") + return {'CANCELLED'} # copy preset to presets folder filename = os.path.basename(self.filepath) @@ -191,47 +245,82 @@ class VIEW3D_stored_views_import(bpy.types.Operator, ImportHelper): shutil.copyfile(self.filepath, os.path.join(IO_Utils.get_preset_path()[0], filename)) except: - self.report({'WARNING'}, "Stored Views: preset applied, but installing failed (preset already exists?)") + self.report({'WARNING'}, + "Stored Views: preset applied, but installing failed (preset already exists?)") return{'CANCELLED'} return{'FINISHED'} -class VIEW3D_stored_views_import_from_scene(bpy.types.Operator): +class VIEW3D_stored_views_import_from_scene(Operator): bl_idname = "stored_views.import_from_scene" bl_label = "Import stored views from scene" - bl_description = "Import current stored views by those from another scene" + bl_description = "Import currently stored views from an another scene" + + scene_name = StringProperty( + name="Scene Name", + description="A current blend scene", + default="" + ) + replace = BoolProperty( + name="Replace", + default=True, + description="Replace current stored views, otherwise append" + ) + + @classmethod + def poll(cls, context): + return get_preferences() + + def draw(self, context): + layout = self.layout - scene_name = bpy.props.StringProperty(name="Scene Name", - description="A current blend scene", - default="") + layout.prop_search(self, "scene_name", bpy.data, "scenes") + layout.prop(self, "replace") - replace = BoolProperty(name="Replace", - default=True, - description="Replace current stored views, otherwise append") + def invoke(self, context, event): + return context.window_manager.invoke_props_dialog(self) def execute(self, context): # filepath should always be given if not self.scene_name: - self.report("ERROR", "Could not find scene") + self.report({"WARNING"}, + "No scene name was given. Operation Cancelled") return{'CANCELLED'} - IO_Utils.stored_views_apply_from_scene(self.scene_name, replace=self.replace) + is_finished = IO_Utils.stored_views_apply_from_scene( + self.scene_name, replace=self.replace + ) + if not is_finished: + self.report({"WARNING"}, + "Could not find the specified scene. Operation Cancelled") + return {"CANCELLED"} return{'FINISHED'} -class VIEW3D_stored_views_export(bpy.types.Operator, ExportHelper): +class VIEW3D_stored_views_export(Operator, ExportHelper): bl_idname = "stored_views.export" bl_label = "Export Stored Views preset" bl_description = "Export the current Stored Views to a .blsv preset file" filename_ext = ".blsv" - filepath = StringProperty(default=os.path.join(IO_Utils.get_preset_path()[0], "untitled")) - filter_glob = StringProperty(default="*.blsv", options={'HIDDEN'}) - preset_name = StringProperty(name="Preset name", - default="", - description="Name of the stored views preset") + filepath = StringProperty( + default=os.path.join(IO_Utils.get_preset_path()[0], "untitled") + ) + filter_glob = StringProperty( + default="*.blsv", + options={'HIDDEN'} + ) + preset_name = StringProperty( + name="Preset name", + default="", + description="Name of the stored views preset" + ) + + @classmethod + def poll(cls, context): + return get_preferences() def execute(self, context): IO_Utils.stored_views_export_to_blsv(self.filepath, self.preset_name) diff --git a/stored_views/operators.py b/stored_views/operators.py index d4500502..1ec3026b 100644 --- a/stored_views/operators.py +++ b/stored_views/operators.py @@ -1,12 +1,19 @@ -import bpy - -from bpy.props import (FloatProperty, BoolProperty, IntProperty, - FloatVectorProperty, StringProperty, EnumProperty) -from . core import stored_view_factory, DataStore -from . ui import init_draw +# gpl authors: nfloyd, Francesco Siddi - -class VIEW3D_stored_views_save(bpy.types.Operator): +import bpy +from bpy.props import ( + IntProperty, + StringProperty, + ) +from bpy.types import Operator +from .core import ( + stored_view_factory, + DataStore, + ) +from .ui import init_draw + + +class VIEW3D_stored_views_save(Operator): bl_idname = "stored_views.save" bl_label = "Save Current" bl_description = "Save the view 3d current state" @@ -23,7 +30,7 @@ class VIEW3D_stored_views_save(bpy.types.Operator): return {'FINISHED'} -class VIEW3D_stored_views_set(bpy.types.Operator): +class VIEW3D_stored_views_set(Operator): bl_idname = "stored_views.set" bl_label = "Set" bl_description = "Update the view 3D according to this view" @@ -40,12 +47,12 @@ class VIEW3D_stored_views_set(bpy.types.Operator): return {'FINISHED'} -class VIEW3D_stored_views_delete(bpy.types.Operator): +class VIEW3D_stored_views_delete(Operator): bl_idname = "stored_views.delete" bl_label = "Delete" bl_description = "Delete this view" - index = bpy.props.IntProperty() + index = IntProperty() def execute(self, context): data = DataStore() @@ -53,36 +60,43 @@ class VIEW3D_stored_views_delete(bpy.types.Operator): return {'FINISHED'} -class VIEW3D_New_Camera_to_View(bpy.types.Operator): + +class VIEW3D_New_Camera_to_View(Operator): bl_idname = "stored_views.newcamera" bl_label = "New Camera To View" - bl_description = "Add a new Casmera Active & Aligned to this view" + bl_description = "Add a new Active Camera and align it to this view" def execute(self, context): - bpy.ops.object.mode_set(mode='OBJECT') - - bpy.ops.object.camera_add( - view_align=True) - cam = bpy.context.active_object - bpy.ops.view3d.object_as_camera() - bpy.ops.view3d.camera_to_view() - - - # this will name the Camera Object - if 'View_Camera' not in context.scene.objects: - cam.name = "View_Camera" - else: - cam.name = "View_Camera.000" + try: + # check for operator's poll (there is no active object, for instance) + if bpy.ops.object.mode_set.poll(): + bpy.ops.object.mode_set(mode='OBJECT') + + bpy.ops.object.camera_add(view_align=True) + cam = bpy.context.active_object + bpy.ops.view3d.object_as_camera() + bpy.ops.view3d.camera_to_view() + + # this will name the Camera Object + if 'View_Camera' not in context.scene.objects: + cam.name = "View_Camera" + else: + cam.name = "View_Camera.000" + + return {'FINISHED'} + except: + self.report({'WARNING'}, + "Operation Cancelled. New Camera to View failed to add a new camera") + return {'CANCELLED'} - return {'FINISHED'} -### Camera marker & switcher by Fsiddi -class SetSceneCamera(bpy.types.Operator): +# Camera marker & switcher by Fsiddi +class SetSceneCamera(Operator): bl_idname = "cameraselector.set_scene_camera" bl_label = "Set Scene Camera" - bl_description = "Set chosen camera as the scene's active camera." + bl_description = "Set chosen camera as the scene's active camera" - chosen_camera = bpy.props.StringProperty() + chosen_camera = StringProperty() select_chosen = False def execute(self, context): @@ -113,18 +127,18 @@ class SetSceneCamera(bpy.types.Operator): return self.execute(context) -class AddCameraMarker(bpy.types.Operator): +class AddCameraMarker(Operator): bl_idname = "cameraselector.add_camera_marker" bl_label = "Add Camera Marker" - bl_description = "Add a timeline marker bound to chosen camera." + bl_description = "Add a timeline marker bound to chosen camera" - chosen_camera = bpy.props.StringProperty() + chosen_camera = StringProperty() def execute(self, context): chosen_camera = bpy.data.objects.get(self.chosen_camera, None) scene = context.scene if not chosen_camera: - self.report({'ERROR'}, "Camera %s not found.") + self.report({'WARNING'}, "Camera %s not found. Operation Cancelled") return {'CANCELLED'} current_frame = scene.frame_current diff --git a/stored_views/properties.py b/stored_views/properties.py index f011ce68..6dffc3d0 100644 --- a/stored_views/properties.py +++ b/stored_views/properties.py @@ -1,25 +1,35 @@ +# gpl authors: nfloyd, Francesco Siddi + from bpy.types import PropertyGroup -from bpy.props import (BoolProperty, - BoolVectorProperty, - CollectionProperty, - FloatProperty, - FloatVectorProperty, - EnumProperty, - IntProperty, - IntVectorProperty, - PointerProperty, - StringProperty) +from bpy.props import ( + BoolProperty, + BoolVectorProperty, + CollectionProperty, + FloatProperty, + FloatVectorProperty, + EnumProperty, + IntProperty, + IntVectorProperty, + PointerProperty, + StringProperty, + ) class POVData(PropertyGroup): distance = FloatProperty() - location = FloatVectorProperty(subtype='TRANSLATION') - rotation = FloatVectorProperty(subtype='QUATERNION', - size=4) + location = FloatVectorProperty( + subtype='TRANSLATION' + ) + rotation = FloatVectorProperty( + subtype='QUATERNION', + size=4 + ) name = StringProperty() - perspective = EnumProperty(items=[('PERSP', '', ''), - ('ORTHO', '', ''), - ('CAMERA', '', '')]) + perspective = EnumProperty( + items=[('PERSP', '', ''), + ('ORTHO', '', ''), + ('CAMERA', '', '')] + ) lens = FloatProperty() clip_start = FloatProperty() clip_end = FloatProperty() @@ -42,11 +52,14 @@ class LayersData(PropertyGroup): class DisplayData(PropertyGroup): name = StringProperty() - viewport_shade = EnumProperty(items=[('BOUNDBOX', 'BOUNDBOX', 'BOUNDBOX'), - ('WIREFRAME', 'WIREFRAME', 'WIREFRAME'), - ('SOLID', 'SOLID', 'SOLID'), - ('TEXTURED', 'TEXTURED', 'TEXTURED'), - ('RENDERED', 'RENDERED', 'RENDERED')]) + viewport_shade = EnumProperty( + items=[('BOUNDBOX', 'BOUNDBOX', 'BOUNDBOX'), + ('WIREFRAME', 'WIREFRAME', 'WIREFRAME'), + ('SOLID', 'SOLID', 'SOLID'), + ('TEXTURED', 'TEXTURED', 'TEXTURED'), + ('MATERIAL', 'MATERIAL', 'MATERIAL'), + ('RENDERED', 'RENDERED', 'RENDERED')] + ) show_only_render = BoolProperty() show_outline_selected = BoolProperty() show_all_objects_origin = BoolProperty() @@ -58,9 +71,11 @@ class DisplayData(PropertyGroup): grid_lines = IntProperty() grid_scale = FloatProperty() grid_subdivisions = IntProperty() - material_mode = EnumProperty(items=[('TEXTURE_FACE', '', ''), - ('MULTITEXTURE', '', ''), - ('GLSL', '', '')]) + material_mode = EnumProperty( + items=[('TEXTURE_FACE', '', ''), + ('MULTITEXTURE', '', ''), + ('GLSL', '', '')] + ) show_textured_solid = BoolProperty() quad_view = BoolProperty() lock_rotation = BoolProperty() @@ -69,22 +84,43 @@ class DisplayData(PropertyGroup): class ViewData(PropertyGroup): - pov = PointerProperty(type=POVData) - layers = PointerProperty(type=LayersData) - display = PointerProperty(type=DisplayData) + pov = PointerProperty( + type=POVData + ) + layers = PointerProperty( + type=LayersData + ) + display = PointerProperty( + type=DisplayData + ) name = StringProperty() class StoredViewsData(PropertyGroup): - pov_list = CollectionProperty(type=POVData) - layers_list = CollectionProperty(type=LayersData) - display_list = CollectionProperty(type=DisplayData) - view_list = CollectionProperty(type=ViewData) - mode = EnumProperty(name="Mode", - items=[('VIEW', 'View', ''), - ('POV', 'POV', ''), - ('LAYERS', 'Layers', ''), - ('DISPLAY', 'Display', '')], - default='VIEW') - current_indices = IntVectorProperty(size=4, default=[-1, -1, -1, -1]) - view_modified = BoolProperty(default=False) + pov_list = CollectionProperty( + type=POVData + ) + layers_list = CollectionProperty( + type=LayersData + ) + display_list = CollectionProperty( + type=DisplayData + ) + view_list = CollectionProperty( + type=ViewData + ) + mode = EnumProperty( + name="Mode", + items=[('VIEW', "View", "3D View settings"), + ('POV', "POV", "POV settings"), + ('LAYERS', "Layers", "Layers settings"), + ('DISPLAY', "Display", "Display settings")], + default='VIEW' + ) + current_indices = IntVectorProperty( + size=4, + default=[-1, -1, -1, -1] + ) + view_modified = BoolProperty( + default=False + ) diff --git a/stored_views/ui.py b/stored_views/ui.py index f1d7df12..09f0b15c 100644 --- a/stored_views/ui.py +++ b/stored_views/ui.py @@ -1,49 +1,72 @@ -''' +# gpl authors: nfloyd, Francesco Siddi + import logging module_logger = logging.getLogger(__name__) -''' import bpy import blf - from . import core +from bpy.types import ( + Operator, + Panel, + ) + +""" + If view name display is enabled, + it will check periodically if the view has been modified + since last set. + get_preferences_timer() is the time in seconds between these checks. + It can be increased, if the view become sluggish + It is set in the add-on preferences +""" + -# If view name display is enabled, -# it will check periodically if the view has been modified -# since last set. -# VIEW_MODIFIED_TIMER is the time in seconds between these checks. -# It can be increased, if the view become sluggish -VIEW_MODIFIED_TIMER = 1 -# TODO: expose refresh rate to ui??? -# TODO: ui for import/export +# Utility function get_preferences_timer for update of 3d view draw +def get_preferences_timer(): + # replace the key if the add-on name changes + # TODO: expose refresh rate to ui??? + addon = bpy.context.user_preferences.addons["stored_views"] + timer_update = (addon.preferences.view_3d_update_rate if addon else False) + + return timer_update def init_draw(context=None): - if context == None: + if context is None: context = bpy.context + if "stored_views_osd" not in context.window_manager: context.window_manager["stored_views_osd"] = False + if not context.window_manager["stored_views_osd"]: context.window_manager["stored_views_osd"] = True bpy.ops.stored_views.draw() def _draw_callback_px(self, context): + if context.area and context.area.type == 'VIEW_3D': + r_width = text_location = context.region.width + r_height = context.region.height + font_id = 0 # TODO: need to find out how best to get font_id - r_width = context.region.width - r_height = context.region.height - font_id = 0 # TODO: need to find out how best to get font_id + blf.size(font_id, 11, context.user_preferences.system.dpi) + text_size = blf.dimensions(0, self.view_name) - blf.size(font_id, 11, 72) - text_size = blf.dimensions(0, self.view_name) + # compute the text location + text_location = 0 + overlap = context.user_preferences.system.use_region_overlap + if overlap: + for region in context.area.regions: + if region.type == "UI": + text_location = r_width - region.width - text_x = r_width - text_size[0] - 10 - text_y = r_height - text_size[1] - 8 - blf.position(font_id, text_x, text_y, 0) - blf.draw(font_id, self.view_name) + text_x = text_location - text_size[0] - 10 + text_y = r_height - text_size[1] - 8 + blf.position(font_id, text_x, text_y, 0) + blf.draw(font_id, self.view_name) -class VIEW3D_stored_views_draw(bpy.types.Operator): +class VIEW3D_stored_views_draw(Operator): bl_idname = "stored_views.draw" bl_label = "Show current" bl_description = "Toggle the display current view name in the view 3D" @@ -56,7 +79,7 @@ class VIEW3D_stored_views_draw(bpy.types.Operator): VIEW3D_stored_views_draw._handle = bpy.types.SpaceView3D.draw_handler_add( _draw_callback_px, (self, context), 'WINDOW', 'POST_PIXEL') VIEW3D_stored_views_draw._timer = \ - context.window_manager.event_timer_add(VIEW_MODIFIED_TIMER, context.window) + context.window_manager.event_timer_add(get_preferences_timer(), context.window) @staticmethod def handle_remove(context): @@ -69,22 +92,22 @@ class VIEW3D_stored_views_draw(bpy.types.Operator): @classmethod def poll(cls, context): - #return context.mode=='OBJECT' + # return context.mode == 'OBJECT' return True def modal(self, context, event): if context.area: context.area.tag_redraw() - if not context.area.type or context.area.type != "VIEW_3D": + if not context.area or context.area.type != "VIEW_3D": return {"PASS_THROUGH"} data = core.DataStore() stored_views = context.scene.stored_views if len(data.list) > 0 and \ - data.current_index >= 0 and \ - not stored_views.view_modified: + data.current_index >= 0 and \ + not stored_views.view_modified: if not stored_views.view_modified: sv = data.list[data.current_index] @@ -100,15 +123,18 @@ class VIEW3D_stored_views_draw(bpy.types.Operator): elif data.mode == 'DISPLAY': is_modified = core.Display.is_modified(context, sv) if is_modified: -# module_logger.debug('view modified - index: %s name: %s' % (data.current_index, sv.name)) + module_logger.debug( + 'view modified - index: %s name: %s' % (data.current_index, sv.name) + ) self.view_name = "" stored_views.view_modified = is_modified - return {"PASS_THROUGH"} + return {"PASS_THROUGH"} else: -# module_logger.debug('exit') + module_logger.debug('exit') context.window_manager["stored_views_osd"] = False VIEW3D_stored_views_draw.handle_remove(context) + return {'FINISHED'} def execute(self, context): @@ -116,20 +142,21 @@ class VIEW3D_stored_views_draw(bpy.types.Operator): self.view_name = "" VIEW3D_stored_views_draw.handle_add(self, context) context.window_manager.modal_handler_add(self) - return {"RUNNING_MODAL"} + return {"RUNNING_MODAL"} else: - self.report({"WARNING"}, "View3D not found, can't run operator") + self.report({"WARNING"}, "View3D not found. Operation Cancelled") + return {"CANCELLED"} -class VIEW3D_PT_properties_stored_views(bpy.types.Panel): +class VIEW3D_PT_properties_stored_views(Panel): bl_label = "Stored Views" bl_space_type = "VIEW_3D" bl_region_type = "UI" def draw(self, context): -# logger = logging.getLogger('%s Properties panel' % __name__) + self.logger = logging.getLogger('%s Properties panel' % __name__) layout = self.layout if bpy.ops.view3d.stored_views_initialize.poll(): @@ -141,7 +168,7 @@ class VIEW3D_PT_properties_stored_views(bpy.types.Panel): # UI : mode col = layout.column(align=True) col.prop_enum(stored_views, "mode", 'VIEW') - row = layout.row() + row = layout.row(align=True) row.operator("view3d.camera_to_view", text="Camera To view") row.operator("stored_views.newcamera", text="New Camera To view") @@ -154,6 +181,13 @@ class VIEW3D_PT_properties_stored_views(bpy.types.Panel): row = layout.row() row.operator("stored_views.save").index = -1 + # IO Operators + if core.get_preferences(): + row = layout.row(align=True) + row.operator("stored_views.import_from_scene", text="Import from Scene") + row.operator("stored_views.import", text="", icon="IMPORT") + row.operator("stored_views.export", text="", icon="EXPORT") + data_store = core.DataStore() list = data_store.list # UI : items list @@ -178,7 +212,7 @@ class VIEW3D_PT_properties_stored_views(bpy.types.Panel): else: icon_string = "OUTLINER_DATA_CAMERA" if mode == 'LAYERS': - if list[i].lock_camera_and_layers == True: + if list[i].lock_camera_and_layers is True: icon_string = 'SCENE_DATA' else: icon_string = 'RENDERLAYERS' @@ -186,6 +220,8 @@ class VIEW3D_PT_properties_stored_views(bpy.types.Panel): shade = list[i].viewport_shade if shade == 'TEXTURED': icon_string = 'TEXTURE_SHADED' + if shade == 'MATERIAL': + icon_string = 'MATERIAL_DATA' elif shade == 'SOLID': icon_string = 'SOLID' elif shade == 'WIREFRAME': @@ -197,7 +233,7 @@ class VIEW3D_PT_properties_stored_views(bpy.types.Panel): # stored view row subrow = box.row(align=True) # current view indicator - if data_store.current_index == i and context.scene.stored_views.view_modified == False: + if data_store.current_index == i and context.scene.stored_views.view_modified is False: subrow.label(text="", icon='SMALL_TRI_RIGHT_VEC') subrow.operator("stored_views.set", text="", icon=icon_string).index = i |