# SPDX-License-Identifier: MIT OR GPL-3.0-or-later import math from collections.abc import Iterator from contextlib import contextmanager from typing import Final, Optional from uuid import uuid4 import bpy from bpy.types import Context from .logger import get_logger logger = get_logger(__name__) class PartialProgress: def __init__( self, progress: "Progress", partial_start_ratio: float, partial_end_ratio: float ) -> None: self.progress: Final = progress self.partial_start_ratio: Final = partial_start_ratio self.partial_end_ratio: Final = partial_end_ratio def update(self, ratio: float) -> None: ratio = min(max(0.0, ratio), 1.0) self.progress.update( self.partial_start_ratio + ratio * (self.partial_end_ratio - self.partial_start_ratio) ) class Progress: active_progress_uuid: Optional[str] = None def __init__(self, context: Context, *, show_progress: bool) -> None: self.context: Final = context self.show_progress: Final = show_progress self.uuid: Final = uuid4().hex self.last_ratio = 0.0 def partial_progress(self, partial_end_ratio: float) -> PartialProgress: partial_end_ratio = min(max(0.0, partial_end_ratio), 1.0) return PartialProgress(self, self.last_ratio, partial_end_ratio) def update(self, ratio: float) -> None: ratio = min(max(0.0, ratio), 1.0) self.last_ratio = ratio if self.active_progress_uuid != self.uuid: logger.error( "Progress.update() called from different progress active=%s self=%s", self.active_progress_uuid, self.uuid, ) return if not self.show_progress: return if bpy.app.version >= (5, 0): progress_value = ratio * 9999 else: # The mouse cursor becomes a four-digit number, and there is an area # where values from 0 to 9999 can be displayed. However, if the # progress rate is directly converted to a number from 0 to 9999, the # last two digits will frequently round-trip, making the progress # difficult to understand. Therefore, only the last two digits display # area is used to show the progress rate as a number from 0 to 99. progress_value = math.floor(ratio * 99) self.context.window_manager.progress_update(progress_value) @contextmanager def create_progress( context: Context, *, show_progress: bool = True ) -> Iterator[Progress]: saved_progress_uuid = Progress.active_progress_uuid try: if show_progress and Progress.active_progress_uuid is None: context.window_manager.progress_begin(0, 9999) progress = Progress(context, show_progress=show_progress) Progress.active_progress_uuid = progress.uuid yield progress finally: Progress.active_progress_uuid = saved_progress_uuid if show_progress and Progress.active_progress_uuid is None: context.window_manager.progress_end()