示例#1
0
def preSolve_updateProgress(prog_fn, status_fn):
    LOG.debug('preSolve_updateProgress')
    # Start up solver
    collectionutils.run_progress_func(prog_fn, 0)
    ts = solveresult.format_timestamp(time.time())
    collectionutils.run_status_func(status_fn, 'Solve start (%s)' % ts)
    api_state.set_solver_running(True)
    return
def preSolve_updateProgress(prog_fn, status_fn):
    """
    Initialise solver is running, and send info to the Maya GUI before
    a solve starts.

    :param prog_fn: Function to use for printing progress messages.
    :type prog_fn: callable or None

    :param status_fn: Function to use for printing status messages.
    :type status_fn: callable or None
    """
    # Start up solver
    collectionutils.run_progress_func(prog_fn, 0)
    ts = solveresult.format_timestamp(time.time())
    collectionutils.run_status_func(status_fn, 'Solve start (%s)' % ts)
    api_state.set_solver_running(True)
    return
示例#3
0
def postSolve_setUpdateProgress(progress_min,
                                progress_value,
                                progress_max,
                                solres,  # SolveResult or None
                                prog_fn, status_fn):
    LOG.debug(
        'postSolve_setUpdateProgress: '
        'progress_min=%r '
        'progress_value=%r '
        'progress_max=%r '
        'solres=%r '
        'prog_fn=%r '
        'status_fn=%r',
        progress_min,
        progress_value,
        progress_max,
        solres,
        prog_fn, status_fn)
    stop_solving = False

    # Update progress
    ratio = float(progress_value) / float(progress_max)
    percent = float(progress_min) + (ratio * (100.0 - progress_min))
    collectionutils.run_progress_func(prog_fn, int(percent))

    # Check if the user wants to stop solving.
    if solres is None:
        cmd_cancel = False
    else:
        cmd_cancel = solres.get_user_interrupted()
    gui_cancel = api_state.get_user_interrupt()
    if cmd_cancel is True or gui_cancel is True:
        msg = 'Cancelled by User'
        api_state.set_user_interrupt(False)
        collectionutils.run_status_func(status_fn, 'WARNING: ' + msg)
        LOG.warning(msg)
        stop_solving = True
    if (solres is not None) and (solres.get_success() is False):
        msg = 'Solver failed!!!'
        collectionutils.run_status_func(status_fn, 'ERROR: ' + msg)
        LOG.error(msg)
    return stop_solving
示例#4
0
    def execute(
            self,
            verbose=False,
            # TODO: Refactor arguments into a 'Preferences' object
            # to hold the different preferences.
            refresh=False,
            force_update=False,
            do_isolate=False,
            # TODO: Allow a dict to be passed to the function
            # specifying the object type and the visibility status
            # during solving. This allows us to turn on/off any
            # object type during solving. If an argument is not
            # given or is None, the object type visibility will
            # not be changed.
            display_image_plane=False,
            prog_fn=None,
            status_fn=None,
            info_fn=None):
        """
        Compile the collection, then pass that data to the 'mmSolver' command.

        The mmSolver command will return a list of strings, which will then be
        passed to the SolveResult class so the user can query the raw data
        using an interface.

        :param verbose: Print extra solver information while a solve is
                        running.
        :type verbose: bool

        :param refresh: Should the solver refresh the viewport while
                        solving?
        :type refresh: bool

        :param force_update: Force updating the DG network, to help the
                             solver in case of a Maya evaluation DG bug.
        :type force_update: bool

        :param do_isolate: Isolate only solving objects while running solve.
        :type do_isolate: bool

        :param display_image_plane: Display Image Planes while solving?
        :type display_image_plane: bool

        :param prog_fn: The function used report progress messages to
                        the user.
        :type prog_fn: callable or None

        :param status_fn: The function used to report status messages
                          to the user.
        :type status_fn: callable or None

        :param info_fn: The function used to report information
                        messages to the user.
        :type info_fn: callable or None

        :return: List of SolveResults from the executed collection.
        :rtype: [SolverResult, ..]
        """
        start_time = time.time()

        # Ensure the plug-in is loaded, so we fail before trying to run.
        api_utils.load_plugin()

        # If 'refresh' is 'on' change all viewports to 'isolate
        # selected' on only the markers and bundles being solved. This
        # will speed up computations, especially per-frame solving as
        # it will not re-compute any invisible nodes (such as rigs or
        # image planes).
        panel_objs = {}
        panel_img_pl_vis = {}
        panels = viewport_utils.get_all_model_panels()
        if refresh is True:
            for panel in panels:
                if do_isolate is True:
                    state = maya.cmds.isolateSelect(panel,
                                                    query=True,
                                                    state=True)
                    nodes = None
                    if state is True:
                        nodes = viewport_utils.get_isolated_nodes(panel)
                    panel_objs[panel] = nodes
                panel_img_pl_vis[
                    panel] = viewport_utils.get_image_plane_visibility(panel)

        # Save current frame, to revert to later on.
        cur_frame = maya.cmds.currentTime(query=True)

        try:
            collectionutils.run_progress_func(prog_fn, 0)
            ts = solveresult.format_timestamp(time.time())
            collectionutils.run_status_func(status_fn, 'Solve start (%s)' % ts)
            api_state.set_solver_running(True)

            # Check for validity
            solres_list = []
            if self.is_valid() is False:
                LOG.warning('Collection not valid: %r', self.get_node())
                return solres_list
            kwargs_list = self._compile()
            collectionutils.run_progress_func(prog_fn, 1)

            # Isolate all nodes used in all of the kwargs to be run.
            # Note; This assumes the isolated objects are visible, but
            # they may actually be hidden.
            if refresh is True:
                s = time.time()
                if do_isolate is True:
                    isolate_nodes = set()
                    for kwargs in kwargs_list:
                        isolate_nodes |= collectionutils.generate_isolate_nodes(
                            kwargs)
                    if len(isolate_nodes) == 0:
                        raise excep.NotValid
                    isolate_node_list = list(isolate_nodes)
                    for panel in panels:
                        viewport_utils.set_isolated_nodes(
                            panel, isolate_node_list, True)

                for panel in panels:
                    viewport_utils.set_image_plane_visibility(
                        panel, display_image_plane)
                e = time.time()
                LOG.debug('Perform Pre-Isolate; time=%r', e - s)

            # Set the first current time to the frame before current.
            # This is to help trigger evaluations on the 'current
            # frame', if the current frame is the same as the first
            # frame.
            frame_list = []
            for kwargs in kwargs_list:
                frame_list += kwargs.get('frame', [])
            frame_list = list(set(frame_list))
            frame_list = list(sorted(frame_list))
            is_whole_solve_single_frame = len(frame_list) == 1
            if is_whole_solve_single_frame is False:
                s = time.time()
                maya.cmds.currentTime(
                    cur_frame - 1,
                    edit=True,
                    update=force_update,
                )
                e = time.time()
                LOG.debug('Update previous of current time; time=%r', e - s)

            # Run Solver...
            marker_nodes = []
            start = 0
            total = len(kwargs_list)
            for i, kwargs in enumerate(kwargs_list):
                frame = kwargs.get('frame')
                collectionutils.run_status_func(info_fn,
                                                'Evaluating frames %r' % frame)
                if frame is None or len(frame) == 0:
                    raise excep.NotValid

                # Write solver flags to a debug file.
                debug_file_path = kwargs.get('debugFile', None)
                if debug_file_path is not None:
                    options_file_path = debug_file_path.replace(
                        '.log', '.flags')
                    text = pprint.pformat(kwargs)
                    with open(options_file_path, 'w') as file_:
                        file_.write(text)

                # HACK: Overriding the verbosity, irrespective of what
                # the solver verbosity value is set to.
                if verbose is True:
                    kwargs['verbose'] = True

                # HACK for single frame solves.
                save_node_attrs = []
                is_single_frame = collectionutils.is_single_frame(kwargs)
                if is_single_frame is True:
                    save_node_attrs = collectionutils.disconnect_animcurves(
                        kwargs)

                # Run Solver Maya plug-in command
                solve_data = maya.cmds.mmSolver(**kwargs)

                # Revert special HACK for single frame solves
                if is_single_frame is True:
                    collectionutils.reconnect_animcurves(
                        kwargs, save_node_attrs)

                # Create SolveResult.
                solres = solveresult.SolveResult(solve_data)
                solres_list.append(solres)

                # Update progress
                ratio = float(i) / float(total)
                percent = float(start) + (ratio * (100.0 - start))
                collectionutils.run_progress_func(prog_fn, int(percent))

                # Check if the user wants to stop solving.
                cmd_cancel = solres.get_user_interrupted()
                gui_cancel = api_state.get_user_interrupt()
                if cmd_cancel is True or gui_cancel is True:
                    msg = 'Cancelled by User'
                    api_state.set_user_interrupt(False)
                    collectionutils.run_status_func(status_fn,
                                                    'WARNING: ' + msg)
                    LOG.warning(msg)
                    break
                if solres.get_success() is False:
                    msg = 'Solver failed!!!'
                    collectionutils.run_status_func(status_fn, 'ERROR: ' + msg)
                    LOG.error(msg)

                marker_nodes += [x[0] for x in kwargs.get('marker', [])]

                # Refresh the Viewport.
                if refresh is True:
                    # TODO: If we solve per-frame without "refresh"
                    # on, then we get wacky solvers
                    # per-frame. Interestingly, the 'force_update'
                    # does not seem to make a difference, just the
                    # 'maya.cmds.refresh' call.
                    #
                    # Test scene file:
                    # ./tests/data/scenes/mmSolverBasicSolveD_before.ma
                    s = time.time()
                    maya.cmds.currentTime(
                        frame[0],
                        edit=True,
                        update=force_update,
                    )
                    maya.cmds.refresh()
                    e = time.time()
                    LOG.debug('Refresh Viewport; time=%r', e - s)
        finally:
            if refresh is True:
                s = time.time()
                for panel, objs in panel_objs.items():
                    if objs is None:
                        # No original objects, disable 'isolate
                        # selected' after resetting the objects.
                        if do_isolate is True:
                            viewport_utils.set_isolated_nodes(panel, [], False)
                        img_pl_vis = panel_img_pl_vis.get(panel, True)
                        viewport_utils.set_image_plane_visibility(
                            panel, img_pl_vis)
                    else:
                        if do_isolate is True:
                            viewport_utils.set_isolated_nodes(
                                panel, list(objs), True)
                e = time.time()
                LOG.debug('Finally; reset isolate selected; time=%r', e - s)

            collectionutils.run_status_func(status_fn, 'Solve Ended')
            collectionutils.run_progress_func(prog_fn, 100)
            api_state.set_solver_running(False)

            maya.cmds.currentTime(cur_frame, edit=True, update=True)

        # Store output information of the solver.
        end_time = time.time()
        duration = end_time - start_time
        self._set_last_solve_timestamp(end_time)
        self._set_last_solve_duration(duration)
        self._set_last_solve_results(solres_list)
        return solres_list
示例#5
0
def execute(col,
            options=None,
            validate_mode=None,
            log_level=None,
            prog_fn=None,
            status_fn=None,
            info_fn=None):
    """
    Compile the collection, then pass that data to the 'mmSolver' command.

    The mmSolver command will return a list of strings, which will then be
    passed to the SolveResult class so the user can query the raw data
    using an interface.

    :param col: The Collection to execute.
    :type col: Collection

    :param options: The options for the execution.
    :type options: ExecuteOptions

    :param validate_mode: How should the solve validate? Must be one of
                          the VALIDATE_MODE_VALUE_LIST values.
    :type validate_mode: str or None

    :param log_level: The log level for the execution.
    :type log_level: str

    :param prog_fn: The function used report progress messages to
                    the user.
    :type prog_fn: callable or None

    :param status_fn: The function used to report status messages
                      to the user.
    :type status_fn: callable or None

    :param info_fn: The function used to report information
                    messages to the user.
    :type info_fn: callable or None

    :return: List of SolveResults from the executed collection.
    :rtype: [SolverResult, ..]
    """
    if options is None:
        options = createExecuteOptions()
    if validate_mode is None:
        validate_mode = const.VALIDATE_MODE_NONE_VALUE
    assert validate_mode in const.VALIDATE_MODE_VALUE_LIST
    if log_level is None:
        log_level = const.LOG_LEVEL_DEFAULT
    assert isinstance(log_level, (str, unicode))

    start_time = time.time()

    # Ensure the plug-in is loaded, so we fail before trying to run.
    api_utils.load_plugin()
    assert 'mmSolver' in dir(maya.cmds)

    vp2_state = viewport_utils.get_viewport2_active_state()

    panels = viewport_utils.get_all_model_panels()
    panel_objs, panel_node_type_vis = preSolve_queryViewportState(
        options, panels
    )

    # Save current frame, to revert to later on.
    cur_frame = maya.cmds.currentTime(query=True)

    try:
        if options.disable_viewport_two is True:
            viewport_utils.set_viewport2_active_state(False)
        preSolve_updateProgress(prog_fn, status_fn)

        # Check for validity and compile actions.
        solres_list = []
        withtest = validate_mode in [const.VALIDATE_MODE_PRE_VALIDATE_VALUE,
                                     const.VALIDATE_MODE_AT_RUNTIME_VALUE]
        col_node = col.get_node()
        sol_list = col.get_solver_list()
        mkr_list = col.get_marker_list()
        attr_list = col.get_attribute_list()
        try:
            s = time.time()
            action_list, vaction_list = api_compile.collection_compile(
                col_node,
                sol_list,
                mkr_list,
                attr_list,
                withtest=withtest,
                prog_fn=prog_fn,
                status_fn=status_fn
            )
            e = time.time()
            LOG.debug('compile time (execute): %r', e - s)
        except excep.NotValid as e:
            LOG.warning(e)
            return solres_list
        collectionutils.run_progress_func(prog_fn, 1)

        if validate_mode == 'pre_validate':
            valid, msg, metrics_list = _run_validate_action_list(vaction_list)
            if valid is not True:
                LOG.warning(msg)
                return solres_list

        # Prepare frame solve
        preSolve_setIsolatedNodes(action_list, options, panels)
        preSolve_triggerEvaluation(action_list, cur_frame, options)

        # Run Solver Actions...
        start = 0
        total = len(action_list)
        for i, (action, vaction) in enumerate(zip(action_list, vaction_list)):
            if isinstance(vaction, api_action.Action) and validate_mode == 'at_runtime':
                valid, message, metrics = _run_validate_action(vaction)
                if valid is not True:
                    LOG.warn(message)
                    return

            func, args, kwargs = api_action.action_to_components(action)
            func_is_mmsolver = api_action.action_func_is_mmSolver(action)

            save_node_attrs = None
            is_single_frame = None
            if func_is_mmsolver is True:
                frame = kwargs.get('frame')
                collectionutils.run_status_func(info_fn, 'Evaluating frames %r' % frame)
                if frame is None or len(frame) == 0:
                    raise excep.NotValid

                # Write solver flags to a debug file.
                debug_file_path = kwargs.get('debugFile', None)
                if debug_file_path is not None:
                    options_file_path = debug_file_path.replace('.log', '.flags')
                    text = pprint.pformat(kwargs)
                    with open(options_file_path, 'w') as file_:
                        file_.write(text)

                # Overriding the verbosity, irrespective of what
                # the solver verbosity value is set to.
                if log_level is not None and log_level.lower() == 'verbose':
                    kwargs['verbose'] = True

                # HACK for single frame solves.
                save_node_attrs = []
                is_single_frame = collectionutils.is_single_frame(kwargs)
                if is_single_frame is True:
                    save_node_attrs = collectionutils.disconnect_animcurves(kwargs)

            # Run Solver Maya plug-in command
            solve_data = func(*args, **kwargs)

            # Revert special HACK for single frame solves
            if func_is_mmsolver is True:
                if is_single_frame is True:
                    collectionutils.reconnect_animcurves(kwargs, save_node_attrs)

            # Create SolveResult.
            solres = None
            if solve_data is not None and func_is_mmsolver is True:
                solres = solveresult.SolveResult(solve_data)
                solres_list.append(solres)

            # Update Progress
            interrupt = postSolve_setUpdateProgress(
                start, i, total, solres,
                prog_fn, status_fn
            )
            if interrupt is True:
                break

            # Refresh the Viewport.
            if func_is_mmsolver is True:
                frame = kwargs.get('frame')
                postSolve_refreshViewport(options, frame)
    finally:
        postSolve_setViewportState(
            options, panel_objs, panel_node_type_vis
        )
        collectionutils.run_status_func(status_fn, 'Solve Ended')
        collectionutils.run_progress_func(prog_fn, 100)
        api_state.set_solver_running(False)
        if options.disable_viewport_two is True:
            viewport_utils.set_viewport2_active_state(vp2_state)
        maya.cmds.currentTime(
            cur_frame,
            edit=True,
            update=options.force_update
        )

    # Store output information of the solver.
    end_time = time.time()
    duration = end_time - start_time
    col._set_last_solve_timestamp(end_time)
    col._set_last_solve_duration(duration)
    col._set_last_solve_results(solres_list)
    return solres_list
def postSolve_setUpdateProgress(progress_min,
                                progress_value,
                                progress_max,
                                solres,
                                prog_fn, status_fn):
    """
    Update the Maya GUI with progress information, and detects users
    wanting to cancel the solve.

    :param progress_min:
        Minimum progress number possible.
        Usually the number is 0.
    :type progress_min: int

    :param progress_value:
        The actual progress value.
        The value is usually between 0 and 100 (inclusive).
    :type progress_value: int

    :param progress_max:
        THe maximum progress number possible.
        Usually the number is 100.
    :type progress_max: int

    :param solres:
        The SolveResult object for the last solved state.
    :type solres: SolveResult or None

    :param prog_fn: The function used report progress messages to
                    the user.
    :type prog_fn: callable or None

    :param status_fn: The function used to report status messages
                      to the user.
    :type status_fn: callable or None

    :returns:
        Should the solver stop executing or not? Has the user
        cancelled the solve?
    :rtype: bool
    """
    stop_solving = False

    # Update progress
    ratio = float(progress_value) / float(progress_max)
    percent = float(progress_min) + (ratio * (100.0 - progress_min))
    collectionutils.run_progress_func(prog_fn, int(percent))

    # Check if the user wants to stop solving.
    if solres is None:
        cmd_cancel = False
    else:
        cmd_cancel = solres.get_user_interrupted()
    gui_cancel = api_state.get_user_interrupt()
    if cmd_cancel is True or gui_cancel is True:
        msg = 'Cancelled by User'
        api_state.set_user_interrupt(False)
        collectionutils.run_status_func(status_fn, 'WARNING: ' + msg)
        LOG.warn(msg)
        stop_solving = True
    if (solres is not None) and (solres.get_success() is False):
        msg = 'Solver failed!!!'
        collectionutils.run_status_func(status_fn, 'ERROR: ' + msg)
        LOG.error(msg)
    return stop_solving
示例#7
0
def execute(col,
            options=None,
            validate_mode=None,
            log_level=None,
            prog_fn=None,
            status_fn=None,
            info_fn=None):
    """
    Compile the collection, then pass that data to the 'mmSolver' command.

    The mmSolver command will return a list of strings, which will then be
    passed to the SolveResult class so the user can query the raw data
    using an interface.

    :param col: The Collection to execute.
    :type col: Collection

    :param options: The options for the execution.
    :type options: ExecuteOptions

    :param validate_mode: How should the solve validate? Must be one of
                          the VALIDATE_MODE_VALUE_LIST values.
    :type validate_mode: str or None

    :param log_level: The log level for the execution.
    :type log_level: str

    :param prog_fn: The function used report progress messages to
                    the user.
    :type prog_fn: callable or None

    :param status_fn: The function used to report status messages
                      to the user.
    :type status_fn: callable or None

    :param info_fn: The function used to report information
                    messages to the user.
    :type info_fn: callable or None

    :return: List of SolveResults from the executed collection.
    :rtype: [SolverResult, ..]
    """
    if options is None:
        options = createExecuteOptions()
    if validate_mode is None:
        validate_mode = const.VALIDATE_MODE_PRE_VALIDATE_VALUE
    assert validate_mode in const.VALIDATE_MODE_VALUE_LIST
    if log_level is None:
        log_level = const.LOG_LEVEL_DEFAULT
    assert isinstance(log_level, pycompat.TEXT_TYPE)
    validate_runtime = validate_mode == const.VALIDATE_MODE_AT_RUNTIME_VALUE
    validate_before = validate_mode == const.VALIDATE_MODE_PRE_VALIDATE_VALUE

    start_time = time.time()

    # Ensure the plug-in is loaded, so we (do not) fail before trying
    # to run.
    api_utils.load_plugin()
    assert 'mmSolver' in dir(maya.cmds)

    vp2_state = viewport_utils.get_viewport2_active_state()
    current_eval_manager_mode = maya.cmds.evaluationManager(query=True,
                                                            mode=True)

    panels = viewport_utils.get_all_model_panels()
    panel_objs, panel_node_type_vis = preSolve_queryViewportState(
        options, panels)

    # Save scene state, to revert to later on.
    cur_frame = maya.cmds.currentTime(query=True)
    prev_auto_key_state = maya.cmds.autoKeyframe(query=True, state=True)
    prev_cycle_check = maya.cmds.cycleCheck(query=True, evaluation=True)

    # State information needed to revert reconnect animation curves in
    # 'finally' block.
    kwargs = {}
    save_node_attrs = []
    func_is_mmsolver = False
    is_single_frame = False

    try:
        if options.disable_viewport_two is True:
            viewport_utils.set_viewport2_active_state(False)
        maya.cmds.autoKeyframe(edit=True, state=False)
        maya.cmds.evaluationManager(mode='off')
        maya.cmds.cycleCheck(evaluation=False)
        preSolve_updateProgress(prog_fn, status_fn)

        # Check for validity and compile actions.
        solres_list = []
        withtest = validate_mode in [
            const.VALIDATE_MODE_PRE_VALIDATE_VALUE,
            const.VALIDATE_MODE_AT_RUNTIME_VALUE
        ]
        sol_list = col.get_solver_list()
        mkr_list = col.get_marker_list()
        attr_list = col.get_attribute_list()
        try:
            action_list, vaction_list = api_compile.collection_compile(
                col,
                sol_list,
                mkr_list,
                attr_list,
                withtest=withtest,
                prog_fn=prog_fn,
                status_fn=status_fn)
        except excep.NotValid as e:
            LOG.warn(e)
            return solres_list
        collectionutils.run_progress_func(prog_fn, 1)

        vaction_state_list = []
        if validate_before is True:
            vaction_state_list = _run_validate_action_list(vaction_list)
            assert len(vaction_list) == len(vaction_state_list)

        # Prepare frame solve
        preSolve_setIsolatedNodes(action_list, options, panels)
        preSolve_triggerEvaluation(action_list, cur_frame, options)

        # Ensure prediction attributes are created and initialised.
        collectionutils.set_initial_prediction_attributes(
            col, attr_list, cur_frame)

        # Run Solver Actions...
        message_hashes = set()
        start = 0
        total = len(action_list)
        number_of_solves = 0
        for i, (action, vaction) in enumerate(zip(action_list, vaction_list)):
            state = None
            if len(vaction_state_list) > 0:
                # We have pre-computed the state list.
                state = vaction_state_list[i]
            if isinstance(vaction, api_action.Action) and validate_runtime:
                # We will calculate the state just-in-time.
                state = _run_validate_action(vaction)
            if state is not None:
                if state.status != const.ACTION_STATUS_SUCCESS:
                    assert isinstance(state, ActionState)
                    h = hash(state.message)
                    if h not in message_hashes:
                        LOG.warn(state.message)
                    message_hashes.add(h)
                    # Skip this action, since the test failed.
                    continue

            func, args, kwargs = api_action.action_to_components(action)
            func_is_mmsolver = api_action.action_func_is_mmSolver(action)

            if func_is_mmsolver is True:
                frame = kwargs.get('frame')
                collectionutils.run_status_func(info_fn,
                                                'Evaluating frames %r' % frame)
                if frame is None or len(frame) == 0:
                    raise excep.NotValid

                # Write solver flags to a debug file.
                debug_file_path = kwargs.get('debugFile', None)
                if debug_file_path is not None:
                    options_file_path = debug_file_path.replace(
                        '.log', '.flags')
                    text = pprint.pformat(kwargs)
                    with open(options_file_path, 'w') as file_:
                        file_.write(text)

                # Overriding the verbosity, irrespective of what the
                # solver verbosity value is set to.
                kwargs['verbose'] = False
                if log_level is not None and log_level.lower() == 'verbose':
                    kwargs['verbose'] = True

                # HACK for single frame solves.
                is_single_frame = collectionutils.is_single_frame(kwargs)
                if is_single_frame is True:
                    save_node_attrs = collectionutils.disconnect_animcurves(
                        kwargs)
                else:
                    # Reset the data structure so in the 'finally'
                    # block we can detect animcurves are not needing
                    # to be reset.
                    save_node_attrs = []

            # Run Solver Maya plug-in command
            solve_data = func(*args, **kwargs)

            # Revert special HACK for single frame solves
            if func_is_mmsolver is True:
                if is_single_frame is True:
                    collectionutils.reconnect_animcurves(
                        kwargs, save_node_attrs)
                    # Reset the data structure so in the 'finally'
                    # block we can detect animcurves are not needing
                    # to be reset.
                    save_node_attrs = []

            # Create SolveResult.
            solres = None
            if solve_data is not None and func_is_mmsolver is True:
                solres = solveresult.SolveResult(solve_data)
                solres_list.append(solres)

            if func_is_mmsolver is True and solres.get_success() is True:
                frame = kwargs.get('frame')
                if frame is None or len(frame) == 0:
                    raise excep.NotValid
                single_frame = frame[0]

                if number_of_solves == 0:
                    collectionutils.set_initial_prediction_attributes(
                        col, attr_list, single_frame)
                # Count number of solves, so we don't need to set the
                # initial prediction attributes again.
                number_of_solves += 1

                # Calculate the mean, variance values, and predict the
                # next attribute value.
                collectionutils.compute_attribute_value_prediction(
                    col,
                    attr_list,
                    single_frame,
                )

            # Update Progress
            interrupt = postSolve_setUpdateProgress(start, i, total, solres,
                                                    prog_fn, status_fn)
            if interrupt is True:
                break

            # Refresh the Viewport.
            if func_is_mmsolver is True:
                frame = kwargs.get('frame')
                postSolve_refreshViewport(options, frame)
    finally:
        # If something has gone wrong, or the user cancels the solver
        # without finishing, then we make sure to reconnect animcurves
        # that were disconnected for single frame solves.
        if func_is_mmsolver is True and is_single_frame is True:
            if len(save_node_attrs):
                collectionutils.reconnect_animcurves(kwargs, save_node_attrs)

        postSolve_setViewportState(options, panel_objs, panel_node_type_vis)
        collectionutils.run_status_func(status_fn, 'Solve Ended')
        collectionutils.run_progress_func(prog_fn, 100)
        maya.cmds.evaluationManager(mode=current_eval_manager_mode[0])
        maya.cmds.cycleCheck(evaluation=prev_cycle_check)
        maya.cmds.autoKeyframe(edit=True, state=prev_auto_key_state)
        api_state.set_solver_running(False)
        if options.disable_viewport_two is True:
            viewport_utils.set_viewport2_active_state(vp2_state)
        maya.cmds.currentTime(cur_frame,
                              edit=True,
                              update=options.force_update)

    # Store output information of the solver.
    end_time = time.time()
    duration = end_time - start_time
    col._set_last_solve_timestamp(end_time)
    col._set_last_solve_duration(duration)
    col._set_last_solve_results(solres_list)
    return solres_list