I just had a quick question about providing undo via your API.

Issue #1843 closed
Joe Morris @ FAST Animation Studio Toolz created an issue

I'm using all the code that you provided just like taught in your samples but when I make a pretty involved operator using your APII think the reason that I'm not able to undo my operator when I run it is because I am calling an external API

i'm not really versed in this could you let me know if there's something you could provide or have provided that would allow users of your api to undo operations when they're creating an operator from your functions.

Comments (6)

  1. Thomas Larsson repo owner

    You add the undo feature with bl_options, like this:

    class ImportDAZ(bpy.types.operator):
        bl_idname = "daz.import_daz"
        bl_label = "Import DAZ"
        bl_description = "Load a native DAZ file"
        bl_options = {'UNDO', 'PRESET'}
    

  2. Joe Morris @ FAST Animation Studio Toolz reporter

    OKI already knew about BL options undo ……………are you saying that the actual contents of BL options should contain undo and that preset… which I've never seen before…. Or is it just your common coding practice to use the preset item in BL options and you're just showing me about the undo… just to note that I already use undo and the Register item in my BL options and it doesn't allow for undoing code written with your operators called in class methods….and the execute function

  3. Thomas Larsson repo owner

    No, you can add one or the other, or other keywords as well. The PRESET keyword enables operator presets, UNDO enables undo.

  4. Joe Morris @ FAST Animation Studio Toolz reporter

    So does the preset button fix it? If it doesn't that still brings me back to the issue that the API for DIFFEOMORPHIC should in theory Allow undoing code when you run operators from it inside another operator do you have any relevant functions for the operators that you provide in the API that are not class methods because I noticed that my undo was hampered when that was the case have you added register undo to all the operators that you provide in your api is there a chance you could check that because I don't have any issue on doing actions that have my own operators inside an operator if both have you know the undo bl option and all of the relevant functions are class methods …..now not sure the issue is on your end though this was GPT 's guess as one of 5 possibilities I just saw it important to bring to your attention you know essentially I'm only running easy import as and import action and merge rigs… Is there a chance she could scan my code and let me know if there's any way that I've set up the DIFFEOMORPHIC functionality wrong to where when I run this code which I think is written right undo isn't available.….. Then we could figure out whether the issue is on your end my end or maybe it's just some sort of weird blender python anomaly I haven't witnessed something like that but G P T suggests that that could happen too...

    class FAST_OT_diffeomorphic_import(bpy.types.Operator):
        """Internal...Used to import DAZ STUDIO assets"""
    
        bl_idname = "fast.diffeomorphic_import"
        bl_label = "Diffeomorphic Import"
        bl_options = {"REGISTER", "UNDO"}
    
        file_path: bpy.props.StringProperty(subtype="FILE_PATH")
    
        def select_object(obj):
            try:
                bpy.ops.object.select_all(action="DESELECT")
                obj.select_set(True)
                bpy.context.view_layer.objects.active = obj
            except:
                return False
            return True
    
        def set_mode(self, mode):
            if bpy.context.object and bpy.context.object.mode != mode:
                try:
                    bpy.ops.object.mode_set(mode=mode)
                except:
                    return False
            return True
    
        def get_highest_empty(self, empty1, empty2):
            """Returns the empty with the higher Z value and the other empty."""
            if empty1.location.z > empty2.location.z:
                return empty1, empty2
            return empty2, empty1
    
    
        def get_diffeomorphic_paths_two(self):
            # Base directory for Blender's scripts
            scripts_path = bpy.utils.resource_path("USER")
            addon_folder = "FAST"
    
            # Base directory for addon
            base_addon_directory = os.path.join(
                scripts_path, "scripts", "addons", addon_folder
            )
    
            # Construct the various directory paths
            pose_save_directory_path = os.path.join(
                base_addon_directory, "daz_studio_save", "pose"
            )
            settings_path = os.path.join(
                base_addon_directory, "diffeomorphic_settings", "EVEE.json"
            )
            temp_settings_path = os.path.join(
                base_addon_directory, "diffeomorphic_settings", "temp.json"
            )
    
            return pose_save_directory_path, settings_path, temp_settings_path
    
        def delete_files(self):
            try:
                manager = bpy.context.preferences.addons[__name__].preferences.Prop
                for file_path in [
                    manager.diffeomorphic_object_duf,
                    manager.diffeomorphic_object_dbz,
                    manager.diffeomorphic_object_duf_png,
                    manager.diffeomorphic_pose_duf,
                    manager.diffeomorphic_pose_duf_png,
                    manager.diffeomorphic_audio_file,
                ]:
                    if file_path:
                        try:
                            os.remove(file_path)
                            print_color("AB", "\nDeleted file:")
                            print_color("AR", f"{file_path}")
                        except Exception as e:
                            captured_traceback = get_exception_traceback_str(e)
    
                            if (
                                ".wav" in captured_traceback
                                or ".mp3" in captured_traceback
                                or ".ogg" in captured_traceback
                            ):
                                manager.diffeomorphic_allow_information = 1
                            else:
                                manager.diffeomorphic_allow_information = 2
                                print_color("AB", "Failed to delete file:")
                                print_color("AR", f"{file_path} - {e}")
    
                            # This will print the captured traceback, decorated with '=' lines for clarity
                            print("")
                            print("\nCaptured Traceback:\n")
                            print(captured_traceback)
    
            except OSError as e:
                if e.errno == 13:  # PermissionError
                    print(f"\nPermission denied when trying to delete file: {file_path}")
                    manager.monitor_daz_studio_save_directory = False
                else:
                    print(f"\nFailed to delete file_2: {file_path} - {e}")
                    manager.monitor_daz_studio_save_directory = False
    
        # dffF
        def detect_hair_advanced(self):
            potential_hair_objects = set()
    
            # Original logic for detection using object name and polygon count.
            for obj in bpy.data.objects:
                if obj.type == "MESH" and (
                    "hair" in obj.name.lower() and len(obj.data.polygons) > 30000
                ):
                    potential_hair_objects.add(obj)
    
            # Advanced check for detection using image texture node names.
            for obj in bpy.data.objects:
                if obj.type == "MESH":
                    for slot in obj.material_slots:
                        if "hair" in slot.name.lower() and not (
                            "eyebrow" in slot.name.lower() or "eyelash" in slot.name.lower()
                        ):
                            potential_hair_objects.add(obj)
                            continue
    
                        if slot.material:
                            for node in slot.material.node_tree.nodes:
                                if (
                                    isinstance(node, bpy.types.ShaderNodeTexImage)
                                    and node.image
                                    and "hair" in node.image.name.lower()
                                ):
                                    potential_hair_objects.add(obj)
                                    break
    
                    for vg in obj.vertex_groups:
                        if "hair" in vg.name.lower() and not (
                            "eyebrow" in vg.name.lower() or "eyelash" in vg.name.lower()
                        ):
                            potential_hair_objects.add(obj)
                            break
    
            return potential_hair_objects  # Return the set of detected hair objects
    
        def disable_hair(self, collection_name):
            manager = bpy.context.preferences.addons[__name__].preferences.Prop
            print("Disabling hair...")
    
            if manager.diffeomorphic_disable_hair:
                potential_hair_objects = self.detect_hair_advanced()
    
                for ob in bpy.data.collections[collection_name].objects:
                    if potential_hair_objects is not None:
                        if ob in potential_hair_objects:
                            poly_count = len(ob.data.polygons)
                            if poly_count > 30000:
                                ob.hide_viewport = True
                                print(
                                    f"'{ob.name}' with {poly_count} polygons is hidden in the viewport."
                                )
    
        def get_collection_name(self):
            manager = bpy.context.preferences.addons[__name__].preferences.Prop
    
            try:
                # Get the current active object
                rig = bpy.context.view_layer.objects.active
    
                # Check if there's no active object or if the active object isn't an armature
                if not rig or rig.type != "ARMATURE":
                    print(
                        f"No active rig or the active object is not an armature. Active object: {rig.name if rig else 'None'}"
                    )
                    return None
    
                # Get the collections the rig is a part of
                rig_collections = rig.users_collection
    
                # If the rig is part of any collection, return the name of the last one
                if rig_collections:
                    return rig_collections[-1].name
    
            except Exception as e:
                print(f"An error occurred in get_collection_name: {e}")
                manager.monitor_daz_studio_save_directory = False
    
            return None
    
        def merge_rigs(self):
            manager = bpy.context.preferences.addons[__name__].preferences.Prop
            print("Merging rigs...")
            # Get the collection name
            collection_name = self.get_collection_name()
            # Deselect all objects
            if manager.diffeomorphic_disable_hair:
                self.disable_hair(collection_name)
    
            bpy.ops.object.select_all(action="DESELECT")
    
            # Select all armatures in characters collection
            for ob in bpy.data.collections[collection_name].objects:
                if ob.type == "ARMATURE":
                    print("Armature Name:", ob.name)
                    # Ensure the object can be selected
                    ob.hide_select = False
                    ob.select_set(True)
    
            # Find the rig with a corresponding mesh in the collection
            rig = None
            for obj in bpy.data.collections[collection_name].objects:
                if obj.type == "ARMATURE":
                    rig_name = obj.name
                    print("Rig Name:", rig_name)
                    mesh_name = rig_name + " Mesh"
                    print("Mesh Name:", mesh_name)
                    if bpy.data.objects.get(mesh_name):
                        rig = obj
                        break
    
            if rig:
                print("Rig found:", rig.name)
                bpy.context.view_layer.objects.active = rig
    
                try:
                    # Merge rigs
                    print("Merging rigs - Starting merge operation")
                    bpy.ops.daz.merge_rigs(
                        separateCharacters=manager.diffeomorphic_separate_characters,
                        useMergeNonConforming=manager.diffeomorphic_merge_non_conforming,
                    )
                    print("Merging rigs - Merge operation completed")
                except ModuleNotFoundError:
                    print("The 'import_daz' module was not found!")
                    return {"CANCELLED"}
    
            else:
                print("Rig not found")
    
        def setup_camera_on_head(self):
            manager = bpy.context.preferences.addons[__name__].preferences.Prop
    
            # Set the scene to frame 1
            bpy.context.scene.frame_set(1)
    
            # Ensure we are in object mode
            if not self.set_mode("OBJECT"):
                return
    
            if (
                not bpy.context.active_object
                or bpy.context.active_object.type != "ARMATURE"
            ):
                raise ValueError("Armature not selected.")
    
            armature = bpy.context.active_object
    
            # Additional checks:
            # 1. Verify that the armature has a bone named "head"
            if "head" not in armature.pose.bones:
                print("The selected armature doesn't have a bone named 'head'.")
                bpy.ops.fast.hide_console()
                draw_centered_info(
                    "Import Errors?? Be sure you added your DAZ Asset Libraries.", width=326
                )
                return
    
            # Get global coordinates of the head and tail of the "head" bone
            head_bone = armature.pose.bones["head"]
            head_global = armature.matrix_world @ head_bone.head
            tail_global = armature.matrix_world @ head_bone.tail
    
            # Add empties
            bpy.ops.object.empty_add(location=head_global)
            empty_head = bpy.context.active_object
            bpy.ops.object.empty_add(location=tail_global)
            empty_tail = bpy.context.active_object
    
            # Determine higher empty and delete the lower one
            higher_empty, lower_empty = self.get_highest_empty(empty_head, empty_tail)
            bpy.data.objects.remove(lower_empty, do_unlink=True)
    
            # Switch back to object mode to add and set up the camera
            # Ensure we are in object mode
            if not self.set_mode("OBJECT"):
                return
    
            bpy.ops.object.camera_add()
            self.camera = bpy.context.active_object
            bpy.context.scene.camera = self.camera
            self.camera.data.show_passepartout = True
            self.camera.data.passepartout_alpha = 1.0
    
            # Set the camera's location and rotation based on the higher empty's position
            self.camera.location = higher_empty.location
            self.camera.rotation_euler.x = 1.5708  # 90 degrees in radians
    
            # Zero out the camera's location values after positioning
            self.camera.location.x -= manager.diffeomorphic_camera_x_offset
            self.camera.location.y -= (
                manager.diffeomorphic_camera_y_offset
            )  # Offset in the negative Y direction
    
            # Delete the higher empty
            bpy.data.objects.remove(higher_empty, do_unlink=True)
    
            print("All operations completed.")
    
    
        def finishing_character(self, context, save_path):
    
            # # try:
            manager = bpy.context.preferences.addons[__name__].preferences.Prop
    
            # Get the active object
            rig = bpy.context.active_object  # your rig object
    
            # Check if the active object is indeed an armature
            if not isinstance(rig, bpy.types.Object) or rig.type != "ARMATURE":
                print_color("AR", "\nSelected object is not a rig!")
                return False
    
            # Check if the rig is a Genesis rig
            if is_genesis_rig(rig, adjust_print=True):
                # The function will print the rig type if it is a Genesis rig
                pass
            else:
                print_color("AR", "Not a Genesis rig!\n")
    
            # Get the location of the character's rig
            rig_location = rig.location.copy()
    
            # Store the current active collection
            current_collection = bpy.context.view_layer.active_layer_collection
    
            # Switch to the Scene Collection
            bpy.context.view_layer.active_layer_collection = (
                bpy.context.view_layer.layer_collection
            )
    
            # Generate a unique name for the empty object
            unique_name = rig.name + "_empty_" + str(uuid.uuid4())[:8]
    
            # Check if the object name already exists in the 'Scene Collection'
            scene_collection = bpy.context.scene.collection
            if unique_name not in scene_collection.objects:
                # If not, create the empty
                bpy.ops.object.empty_add(type="PLAIN_AXES", location=rig_location)
                empty = bpy.context.active_object
                empty.name = unique_name
                print("Empty.name:", empty.name)
            else:
                print(
                    f"Object {unique_name} already exists in Scene Collection. Skipping addition."
                )
                empty = scene_collection.objects[unique_name]
    
            # Switch back to the original active collection
            bpy.context.view_layer.active_layer_collection = current_collection
    
            # Parent the rig to the empty
            rig.select_set(True)
            empty.select_set(True)
            bpy.context.view_layer.objects.active = empty
            bpy.ops.object.parent_set(type="OBJECT")
            bpy.ops.object.select_all(action="DESELECT")
            empty.select_set(True)
    
            ### ADD MULTIPLE LIGHT SETUP
            # Check if the collection "FAST Lighting Setup" doesn't exist in the scene
            if "FAST Lighting Setup" not in bpy.data.collections:
                bpy.ops.fast.multiple_light_setup()
    
            if manager.optimized_map:
                bpy.ops.fast.custom_normal(custom=True)
    
            if manager.daz_studio_extra_setup == True:
                bpy.ops.fast.save_and_enter_material_preview_mode()
    
                for area in bpy.context.screen.areas:
                    if area.type == "VIEW_3D":
                        for space in area.spaces:
                            if space.type == "VIEW_3D":
                                # Turn off overlays
                                space.overlay.show_overlays = False
    
                                # Turn on Scene Lights and Scene World if they're off
                                if not space.shading.use_scene_lights_render:
                                    space.shading.use_scene_lights_render = True
                                if not space.shading.use_scene_world_render:
                                    space.shading.use_scene_world_render = True
    
                        break
    
                scene = bpy.context.scene
                transparent = scene.render.film_transparent = True
                print("\nTransparency set = ", transparent)
    
            bpy.ops.object.select_all(action="DESELECT")
    
            rig.select_set(True)
    
            bpy.context.view_layer.objects.active = rig
    
            try:
                self.setup_camera_on_head()
            except Exception as e:
                manager.monitor_daz_studio_save_directory = False
                print(f"An unexpected error occurred: {e}")
                return False
    
            # # Ensure the camera is selected (it should be if the previous function worked as intended)
            self.camera.select_set(True)
    
            # Select the rig as well
            rig.select_set(True)
    
            # Select the empty
            empty.select_set(True)
    
            # Set the empty as the active object
            bpy.context.view_layer.objects.active = empty
    
            # Now, parent both selected objects (camera and rig) to the active object (empty)
            bpy.ops.object.parent_set(type="OBJECT")
    
            # Deselect all objects
            bpy.ops.object.select_all(action="DESELECT")
    
            # Set the empty as the active object again if needed
            bpy.context.view_layer.objects.active = self.camera
            for window in bpy.context.window_manager.windows:
                screen = window.screen
                for area in screen.areas:
                    if area.type == "VIEW_3D":
                        # Find the WINDOW region within the 3D View area
                        region = next((r for r in area.regions if r.type == "WINDOW"), None)
                        if region:
                            # Prepare the override context for the 3D view
                            override = bpy.context.copy()
                            override["window"] = window
                            override["screen"] = screen
                            override["area"] = area
                            override["region"] = region
                            # override["space_data"] = area.spaces.active
    
                            # Set the active camera and object
                            bpy.context.scene.camera = self.camera
                            bpy.context.view_layer.objects.active = self.camera
                            self.camera.select_set(True)
    
                            with bpy.context.temp_override(**override):
                                time.sleep(0.5)  # Half a second delay
                                bpy.ops.view3d.view_camera()
                                bpy.ops.view3d.view_center_camera()
    
    
                                # Store the original view matrix
                                view_matrix = context.region_data.view_matrix.copy()
                                # Execute the view operations
                                bpy.ops.view3d.view_all()
                                bpy.ops.view3d.view_lock_clear()
    
                                # Restore the original view matrix
                                context.region_data.view_matrix = view_matrix   
                                area.spaces.active.region_3d.view_perspective = "CAMERA"       
    
            # Set the default message
            text = "No Face Anim??  Enable All Morphs & Read Morphs Tooltips.\nImport Errors?? Be sure you added your DAZ Asset Libraries."
    
            if manager.diffeomorphic_allow_information == 1:
                # Additional text and formatting are added if diffeomorphic_allow_information equals 1
                additional_text = (
                    "=================================================\n"
                    "ERROR: Cannot delete audio file. It's likely in use by DAZ Studio.\n"
                    "Close DAZ Studio & delete audio file manually from DAZ_STUDIO_SAVE/AUDIO folder.\n"
                    "Your backup is SAFE, backup op copies files, circumventing 'file in use' error."
                    "\n=================================================="
                )
                full_text = f"{text}\n{additional_text}"
                manager.diffeomorphic_allow_information = 0
                bpy.ops.fast.hide_console()
    
                # Call draw_centered_info with wider width and offset for full message
                draw_centered_info(full_text, width=475)
            elif manager.diffeomorphic_allow_information == 2:
                text = (
                    f"{text}\n"
                    "====================================\n"
                    "ERROR: Unexpected issue here! Please check System Console!"
                    "\n===================================="
                )
                manager.diffeomorphic_allow_information = 0
                bpy.ops.fast.hide_console()
                draw_centered_info(text, width=326)
            else:
                bpy.ops.fast.hide_console()
                draw_centered_info(text, width=330)
    
            manager.monitor_daz_studio_save_directory = False
            bpy.ops.wm.save_as_mainfile(filepath=save_path)
    
            # except Exception as e:
            #     manager.monitor_daz_studio_save_directory = False
            #     print("Error in finishing character:", e)
            #     return False
            return True
    
        def finishing_environment(
            self,
            duf_file_start_path,
            dbz_file_start_path,
            duf_png_file_start_path,
            duf_file_name_without_ext,
            temp_settings_path,
        ):
            manager = bpy.context.preferences.addons[__name__].preferences.Prop
    
            # Set the manager variables
            manager.diffeomorphic_object_duf = duf_file_start_path
            manager.diffeomorphic_object_dbz = dbz_file_start_path
            manager.diffeomorphic_object_duf_png = duf_png_file_start_path
            manager.diffeomorphic_pose_duf = ""
            manager.diffeomorphic_pose_duf_png = ""
            manager.diffeomorphic_audio_file = ""
            manager.diffeomorphic_file_name = duf_file_name_without_ext
            # resetting back to their original state, they were both set to false in the monitor function
            manager.diffeomorphic_import_action = manager.diffeomorphic_import_action_temp
            manager.diffeomorphic_add_audio = manager.diffeomorphic_add_audio_temp
            print("")
            bpy.ops.wm.save_userpref()
    
            try:
                # Check if there's an active object and if it has bones attribute
                rig = bpy.context.active_object  # your rig object
                if rig and hasattr(rig, "bones"):
                    if is_genesis_rig(rig):
                        print_color(
                            "RW",
                            "To successfully import GENESIS...Set MESH FITTING to DBZ FILE on panel.",
                            shift=True,
                        )
                        print_color(
                            "RW",
                            "Your files are still safely inside DAZ Studio save folderready to be reimported.",
                            shift=True,
                        )
                        manager.monitor_daz_studio_save_directory = False
                        if manager.re_import_in_progress:
                            self.delete_files()
                            manager.re_import_in_progress = False
                        return False
    
                if manager.re_import_in_progress:
                    self.delete_files()
                    manager.re_import_in_progress = False
                else:
                    backup_and_zip_files()
    
                if manager.diffeomorphic_object_duf:
                    print_color("AG", "\nmanager.diffeomorphic_object_duf:")
                    print_color("AR", f"{manager.diffeomorphic_object_duf}\n")
    
                if manager.diffeomorphic_object_dbz:
                    print_color("AG", "manager.diffeomorphic_object_dbz:")
                    print_color("AR", f"{manager.diffeomorphic_object_dbz}\n")
    
                if manager.diffeomorphic_object_duf_png:
                    print_color("AG", "manager.diffeomorphic_object_duf_png:")
                    print_color("AR", f"{manager.diffeomorphic_object_duf_png}\n")
    
                # print("")
    
                try:
                    if manager.diffeomorphic_load_fast_settings:
                        # Restore the original settings file
                        if os.path.exists(temp_settings_path):
                            # Load the temporary settings file
                            bpy.ops.daz.load_settings_file(filepath=temp_settings_path)
                            print_color("AB", "\nTemp settings loaded:")
    
                            # Delete the temporary settings file
                            if os.path.exists(temp_settings_path):
                                os.remove(temp_settings_path)
                            print_color("AB", "\nTemporary settings deleted.")
                        else:
                            print_color("AR", "\nTemporary settings not found:")
                except Exception as ex:
                    traceback.print_exc()
                    self.report({"ERROR"}, f"Error occurred while restoring settings: {ex}")
                    manager.monitor_daz_studio_save_directory = False
                    return False
    
                if manager.optimized_map:
                    bpy.ops.fast.custom_normal(custom=True)
    
                if manager.daz_studio_extra_setup == True:
                    bpy.ops.fast.save_and_enter_material_preview_mode()
    
                    for area in bpy.context.screen.areas:
                        if area.type == "VIEW_3D":
                            for space in area.spaces:
                                if space.type == "VIEW_3D":
                                    # Turn off overlays
                                    space.overlay.show_overlays = False
    
                                    # Turn on Scene Lights and Scene World if they're off
                                    if not space.shading.use_scene_lights_render:
                                        space.shading.use_scene_lights_render = True
                                    if not space.shading.use_scene_world_render:
                                        space.shading.use_scene_world_render = True
    
                            break
    
                    scene = bpy.context.scene
                    transparent = scene.render.film_transparent = True
                    print("\nTransparency set = ", transparent)
    
                print_color(
                    "AR",
                    "\nERROR: Be sure you added your DAZ Asset Libraries in the DAZ STUDIO tab of FAST Preferences.",
                )
                if manager.diffeomorphic_reminders:
                    bpy.ops.fast.hide_console()
                    draw_centered_info(
                        "Import Errors?? Be sure you added your DAZ Asset Libraries.",
                        width=350,
                    )
                manager.monitor_daz_studio_save_directory = False
    
                pass
            except Exception as e:
                manager.monitor_daz_studio_save_directory = False
                print("Error in finishing environment:", e)
                return False
            return True
    
        def execute(self, context):
            try:
                import import_daz
            except ModuleNotFoundError:
                self.report({"ERROR"}, "The 'import_daz' module was not found!")
                manager.monitor_daz_studio_save_directory = False
                return {"CANCELLED"}
    
            bpy.ops.fast.show_console()
    
            manager = context.preferences.addons[__name__].preferences.Prop
            fps = float(manager.diffeomorphic_fps)
            for window in bpy.context.window_manager.windows:
                for area in window.screen.areas:
                    area.tag_redraw()
    
            # Now use the function to get all the paths
            (
                pose_save_directory_path,
                settings_path,
                temp_settings_path,
            ) = self.get_diffeomorphic_paths_two()
    
            # Set the directory to the folder where this blend file is saved
            auto_save_path = bpy.data.filepath
            directory = os.path.dirname(auto_save_path)
    
            # If file unsaved then directory will be ""
            if directory == "":
                # Set the directory to the user's desktop
                directory = os.path.join(os.path.expanduser("~"), "Desktop")
    
            file_name = bpy.path.basename(bpy.context.blend_data.filepath)
            if file_name == "":
                file_name = "diffeomorphic_import_save.blend"
                save_path = os.path.join(directory, file_name)
                # print('save_path:', save_path)
            else:
                save_path = auto_save_path
    
            bpy.ops.wm.save_as_mainfile(filepath=save_path)
    
            try:
                if manager.diffeomorphic_load_fast_settings:
                    # Handling temp_settings_path
                    if os.path.exists(temp_settings_path):
                        os.remove(temp_settings_path)
    
                    # Save the current settings to a temporary file
                    bpy.ops.daz.save_settings_file(
                        filepath=temp_settings_path, check_existing=False
                    )
                    print_color("AB", "\nTemporary settings path:\n", temp_settings_path)
                    print_color("AB", "\nSettings path:\n", settings_path, "\n")
                    # Load your desired settings file
                    if os.path.exists(settings_path):
                        bpy.ops.daz.load_settings_file(filepath=settings_path)
                        print_color("AB", "\nCustom settings loaded.")
                    else:
                        print_color("AR", "\nCustom settings not found.")
    
            except AttributeError as ae:
                if "bpy.ops.daz.save_settings_file" in str(
                    ae
                ) and "could not be found" in str(ae):
                    print(
                        "Enable DIFFEOMORPHIC in Blender add-on preferences and try again",
                        312,
                    )
                else:
                    # For any other AttributeError not related to daz.save_settings_file
                    traceback.print_exc()
                    self.report(
                        {"ERROR"}, f"Error occurred while processing settings: {ae}"
                    )
                    manager.monitor_daz_studio_save_directory = False
                    return {"CANCELLED"}
    
            except Exception as ex:
                traceback.print_exc()
                self.report({"ERROR"}, f"Error occurred while processing settings: {ex}")
                manager.monitor_daz_studio_save_directory = False
                return {"CANCELLED"}
    
            # print("manager.diffeomorphic", manager.diffeomorphic)
            if manager.diffeomorphic:
                if self.file_path:
                    duf_file_start_path = self.file_path
    
                    # Extract the filename from the file path
                    duf_file_base_name = os.path.basename(duf_file_start_path)
                    # Extract the directory path and the file name
                    orig_duf_file_dir_path = os.path.dirname(duf_file_start_path)
                    # Remove the existing extension
                    duf_file_name_without_ext = os.path.splitext(duf_file_base_name)[0]
    
                    dbz_file_base_name = duf_file_name_without_ext + ".dbz"
                    duf_png_file_base_name = duf_file_name_without_ext + ".duf.png"
    
                    dbz_file_start_path = os.path.join(
                        orig_duf_file_dir_path, dbz_file_base_name
                    )
                    duf_png_file_start_path = os.path.join(
                        orig_duf_file_dir_path, duf_png_file_base_name
                    )
    
                if manager.diffeomorphic_import_action == True:
                    # Check for DUF files in the 'POSE' subdirectory
                    pose_dir = os.path.join(orig_duf_file_dir_path, "pose")
                    # If 'POSE' directory doesn't exist create one & return from function, then folder set up right, But we return because no Pose Preset was there
                    if not os.path.isdir(pose_dir):
                        # Create the 'POSE' directory if it doesn't exist
                        os.makedirs(pose_dir)
                        print(
                            "No 'POSE' subdirectory found. I made one for you, Save a Pose Preset there & try again"
                        )
                        manager.monitor_daz_studio_save_directory = False
                        return {"CANCELLED"}
    
                    pose_files = [
                        f for f in os.listdir(pose_dir) if f.lower().endswith(".duf")
                    ]
                    pose_dir = pose_dir.replace("\\", "/")
    
                    # Find .duf files in the directory
                    pose_files = [
                        f for f in os.listdir(pose_dir) if f.lower().endswith(".duf")
                    ]
                    print_color("AG", f"\nFound POSE files: {pose_files}", new_line=True)
                    print_color("AG", f"\nBeginning DAZ import...", new_line=True)
    
                    if len(pose_files) != 1:
                        # bpy.ops.fast.pose_message_box('INVOKE_DEFAULT')
                        manager.monitor_daz_studio_save_directory = False
                        return {"CANCELLED"}
    
                    action_file = os.path.join(pose_dir, pose_files[0]).replace("\\", "/")
    
                try:
                    import_daz.set_silent_mode(False)
                    import_daz.set_selection([duf_file_start_path])
                except ModuleNotFoundError:
                    self.report({"ERROR"}, "The 'import_daz' module was not found!")
                    manager.monitor_daz_studio_save_directory = False
                    return {"CANCELLED"}
    
                while not os.path.exists(dbz_file_start_path):
                    time.sleep(1)  # Sleep for 1 second before checking again
    
                import re  # Regular expression module for pattern searching
    
                is_genesis = is_character_file(duf_file_start_path)
    
                try:
                    if manager.diffeomorphic_fit_meshes in ["SHARED", "UNIQUE"]:
                        print_color("AG", "\nAttempting", new_line=False)
                        print_color("AB", " easy_import_daz ", new_line=False)
                        print_color("AG", "with", new_line=False)
                        print_color("AR", " MESH FITTING ", new_line=False)
                        print_color("AG", "setting as ", new_line=False)
                        print_color("AR", manager.diffeomorphic_fit_meshes)
    
                        if is_genesis == True:
                            print_color(
                                "AR",
                                "\nMESH FITTING setting is 'UNIQUE/SHARED' and this is a Genesis character. Canceling function.",
                            )
                            manager.monitor_daz_studio_save_directory = False
                            manager.re_import_in_progress = False
                            return {"CANCELLED"}
    
                        bpy.ops.daz.easy_import_daz(
                            fitMeshes=manager.diffeomorphic_fit_meshes
                        )
                    elif manager.diffeomorphic_fit_meshes in ["MORPHED", "DBZFILE"]:
                        print_color("AG", "\nAttempting", new_line=False)
                        print_color("AB", " easy_import_daz ", new_line=False)
                        print_color("AG", "with", new_line=False)
                        print_color("AR", " MESH FITTING ", new_line=False)
                        print_color("AG", "setting as ", new_line=False)
                        print_color("AR", manager.diffeomorphic_fit_meshes)
    
                        if is_genesis == False:
                            print_color(
                                "AR",
                                "\nMESH FITTING setting is 'MORPHED/DBZFILE' and this is not a Genesis character. Canceling function.\n",
                            )
                            manager.monitor_daz_studio_save_directory = False
                            manager.re_import_in_progress = False
                            return {"CANCELLED"}
    
                        bpy.ops.daz.easy_import_daz(
                            useUnits=manager.diffeomorphic_units,
                            useExpressions=manager.diffeomorphic_expressions,
                            useVisemes=manager.diffeomorphic_visemes,
                            useBody=manager.diffeomorphic_body,
                            useFacs=manager.diffeomorphic_facs,
                            useFacsdetails=manager.diffeomorphic_facs_details,
                            useFacsexpr=manager.diffeomorphic_facs_expressions,
                            fitMeshes=manager.diffeomorphic_fit_meshes,
                        )
                    else:
                        return {"CANCELLED"}
    
                    print("\nDAZ operation completed successfully!")
    
                except Exception as e:
                    error_messages = traceback.format_exc()
                    print("\nError Encountered:\n", error_messages)
                    manager.monitor_daz_studio_save_directory = False
                    # Resetting back here in case of an exception
                    manager.diffeomorphic_import_action = (
                        manager.diffeomorphic_import_action_temp
                    )
                    manager.diffeomorphic_add_audio = manager.diffeomorphic_add_audio_temp
                    print("\nReset the properties...\n")
    
                # If neither 'MORPHED' nor 'DBZFILE' are selected for fitMeshes, use the operator with only fitMeshes.
                if manager.diffeomorphic_fit_meshes in ["MORPHED", "DBZFILE"]:
                    collection_name = self.get_collection_name()
    
                    if collection_name is None:
                        print_color(
                            "AR",
                            "\nERROR: Be sure you added your DAZ Asset Libraries in the DAZ STUDIO tab of FAST Preferences.",
                        )
                        manager.monitor_daz_studio_save_directory = False
    
                        return {"CANCELLED"}
    
                    result = self.merge_rigs()
    
                    if result == {"CANCELLED"}:
                        self.report({"ERROR"}, "Rig merging failed!")
                        manager.monitor_daz_studio_save_directory = False
                        return {"CANCELLED"}
    
                    if manager.diffeomorphic_import_action:
                        # Use the user-specified action name if it's not an empty string
                        if manager.diffeomorphic_action_name != "":
                            actionName = manager.diffeomorphic_action_name.replace(" ", "_")
                        else:
                            # Extract the action name from the pose file without extension
                            action_name_without_ext = os.path.splitext(pose_files[0])[0]
                            # Remove spaces in the action name
                            action_name_without_ext = action_name_without_ext.replace(
                                " ", "_"
                            )
                            # Use the extracted action name
                            actionName = action_name_without_ext
    
                        try:
                            import_daz.set_selection([action_file])
                            bpy.ops.daz.import_action(
                                fps=fps,
                                firstFrame=manager.diffeomorphic_first_frame,
                                lastFrame=manager.diffeomorphic_last_frame,
                                makeNewAction=True,
                                actionName=actionName,
                            )
    
                        except ModuleNotFoundError:
                            self.report({"ERROR"}, "The 'import_daz' module was not found!")
                            manager.monitor_daz_studio_save_directory = False
                            return {"CANCELLED"}
    
                        # Copy and move any pose file to bak
    
                        for f in pose_files:
                            manager.diffeomorphic_pose_duf = os.path.join(
                                pose_save_directory_path, f
                            )
    
                        pose_png_files = [
                            f
                            for f in os.listdir(pose_dir)
                            if f.lower().endswith(".duf.png")
                        ]
                        for f in pose_png_files:
                            pose_png_file_path = os.path.join(pose_dir, f)
                            if os.path.isfile(pose_png_file_path):
                                manager.diffeomorphic_pose_duf_png = os.path.join(
                                    pose_save_directory_path, f
                                )
    
                            else:
                                print(
                                    f"\nPOSE DUF.PNG file: {f} not found. Skipping move operation."
                                )
                    else:
                        manager.diffeomorphic_pose_duf = ""
                        manager.diffeomorphic_pose_duf_png = ""
    
                    # Set the manager variables
                    manager.diffeomorphic_object_duf = duf_file_start_path
                    manager.diffeomorphic_object_dbz = dbz_file_start_path
                    manager.diffeomorphic_object_duf_png = duf_png_file_start_path
                    manager.diffeomorphic_file_name = duf_file_name_without_ext
    
                    if manager.re_import_in_progress:
                        print("Re-import in progress...")
                        self.delete_files()
                        manager.re_import_in_progress = False
                    else:
                        print("Re-import not in progress...")
                        backup_and_zip_files()
                        bpy.context.scene.render.fps = int(fps)
    
                    # Saving user preferences after the variables printed below are created to make reimport easy
                    print("")
                    bpy.ops.wm.save_userpref()
    
                    if manager.diffeomorphic_object_duf:
                        print_color("AG", "\nmanager.diffeomorphic_object_duf:")
                        print_color("AR", f"{manager.diffeomorphic_object_duf}\n")
    
                    if manager.diffeomorphic_object_dbz:
                        print_color("AG", "manager.diffeomorphic_object_dbz:")
                        print_color("AR", f"{manager.diffeomorphic_object_dbz}\n")
    
                    if manager.diffeomorphic_object_duf_png:
                        print_color("AG", "manager.diffeomorphic_object_duf_png:")
                        print_color("AR", f"{manager.diffeomorphic_object_duf_png}\n")
    
                    if manager.diffeomorphic_pose_duf:
                        print_color("AG", "manager.diffeomorphic_pose_duf:")
                        print_color("AR", f"{manager.diffeomorphic_pose_duf}\n")
                    elif manager.diffeomorphic_pose_duf_png:
                        print_color("AG", "manager.diffeomorphic_pose_duf_png:")
                        print_color("AR", f"{manager.diffeomorphic_pose_duf_png}\n")
                    else:
                        print_color("AR", "No animation added\n")
    
                    if manager.diffeomorphic_audio_file:
                        print_color("AG", "manager.diffeomorphic_audio_file:")
                        print_color("AR", f"{manager.diffeomorphic_audio_file}\n")
                    else:
                        print_color("AR", "No audio added\n")
    
                    try:
                        if manager.diffeomorphic_load_fast_settings:
                            # Restore the original settings file
                            if os.path.exists(temp_settings_path):
                                # Load the temporary settings file
                                bpy.ops.daz.load_settings_file(filepath=temp_settings_path)
                                print_color("AG", "\nTemporary settings loaded:")
    
                                # Delete the temporary settings file
                                if os.path.exists(temp_settings_path):
                                    os.remove(temp_settings_path)
                                print_color("AR", "\nTemporary settings deleted.")
                            else:
                                print_color("AR", "\nTemporary settings not found:")
                    except Exception as ex:
                        traceback.print_exc()
                        self.report(
                            {"ERROR"}, f"Error occurred while restoring settings: {ex}"
                        )
                        manager.monitor_daz_studio_save_directory = False
                        return {"CANCELLED"}
    
                    if not self.finishing_character(context, save_path):
                        return {"CANCELLED"}
    
                else:
                    if not self.finishing_environment(
                        duf_file_start_path,
                        dbz_file_start_path,
                        duf_png_file_start_path,
                        duf_file_name_without_ext,
                        temp_settings_path,
                    ):
                        return {"CANCELLED"}
    
                print_color("AG", "\nSaving file to Backup directory as: ", new_line=False)
                print_color("AR", "daz_studio_import_end.blend")
                bpy.ops.fast.backup_file(
                    overwrite=True, filename="daz_studio_import_complete.blend"
                )
    
            return {"FINISHED"}
    
    
    def handled_daz_settings_1():
        manager = bpy.context.preferences.addons[__name__].preferences.Prop
        scripts_path = bpy.utils.resource_path("USER")
        addon_folder = "FAST"
    
        # Base directory for addon
        base_addon_directory = os.path.join(scripts_path, "scripts", "addons", addon_folder)
    
        # Construct the various directory paths
        settings_path = os.path.join(
            base_addon_directory, "diffeomorphic_settings", "EVEE.json"
        )
        temp_settings_path = os.path.join(
            base_addon_directory, "diffeomorphic_settings", "temp.json"
        )
    
        try:
            if manager.diffeomorphic_load_fast_settings:
                # Handling temp_settings_path
                if os.path.exists(temp_settings_path):
                    os.remove(temp_settings_path)
    
                # Save the current settings to a temporary file
                bpy.ops.daz.save_settings_file(
                    filepath=temp_settings_path, check_existing=False
                )
                print_color("AB", "\nTemporary settings path:\n", temp_settings_path)
                print_color("AB", "\nSettings path:\n", settings_path, "\n")
                # Load your desired settings file
                if os.path.exists(settings_path):
                    bpy.ops.daz.load_settings_file(filepath=settings_path)
                    print_color("AB", "\nCustom settings loaded.")
                else:
                    print_color("AR", "\nCustom settings not found.")
    
        except AttributeError as ae:
            if "bpy.ops.daz.save_settings_file" in str(ae) and "could not be found" in str(
                ae
            ):
                print(
                    "Enable DIFFEOMORPHIC in Blender add-on preferences and try again", 312
                )
            else:
                # For any other AttributeError not related to daz.save_settings_file
                traceback.print_exc()
                bpy.context.window_manager.monitor_daz_studio_save_directory = False
                return {"CANCELLED"}
    
        except Exception as ex:
            traceback.print_exc()
            bpy.context.window_manager.monitor_daz_studio_save_directory = False
            return {"CANCELLED"}
    
    
    def handled_daz_settings_2():
        manager = bpy.context.preferences.addons[__name__].preferences.Prop
        scripts_path = bpy.utils.resource_path("USER")
        addon_folder = "FAST"
    
        # Base directory for addon
        base_addon_directory = os.path.join(scripts_path, "scripts", "addons", addon_folder)
    
        # Construct the various directory paths
        temp_settings_path = os.path.join(
            base_addon_directory, "diffeomorphic_settings", "temp.json"
        )
    
        try:
            if manager.diffeomorphic_load_fast_settings:
                # Restore the original settings file
                if os.path.exists(temp_settings_path):
                    # Load the temporary settings file
                    bpy.ops.daz.load_settings_file(filepath=temp_settings_path)
                    print_color("AG", "\nTemporary settings loaded:")
    
                    # Delete the temporary settings file
                    if os.path.exists(temp_settings_path):
                        os.remove(temp_settings_path)
                        print_color("AR", "\nTemporary settings deleted.\n")
                else:
                    print_color("AR", "\nTemporary settings not found:")
        except Exception as ex:
            traceback.print_exc()
            bpy.context.window_manager.monitor_daz_studio_save_directory = False
            return {"CANCELLED"}
    

  5. Log in to comment