Coverage for paintmaskanimator/progress.py: 95%

33 statements  

« prev     ^ index     » next       coverage.py v7.17.0, created at 2026-11-02 04:50 +0000

1"""Modal progress counter for long, frame-by-frame operations. 

2 

3Shared UI plumbing, not a feature: exports, scope runs, tween generation, layer 

4merges and used-colour scans all show the same "n / total" dialog. It used to 

5live inside ``main_window_line_ops.py``, which meant eight unrelated modules 

6reached into the line-tool module for it. It is a free function set here so any 

7of them can use it without depending on a feature module. 

8 

9``parent`` is the window the dialog is modal to. 

10""" 

11from __future__ import annotations 

12 

13from PySide6.QtCore import Qt 

14from PySide6.QtWidgets import QApplication, QProgressDialog, QWidget 

15 

16from .i18n import tr 

17 

18 

19def create_counter( 

20 parent: QWidget, title: str, total: int, label: str | None = None, 

21 cancellable: bool = False, 

22) -> QProgressDialog: 

23 """Show a modal counter dialog and return it.""" 

24 total = max(1, int(total)) 

25 dialog = QProgressDialog( 

26 label or title, 

27 tr("中止") if cancellable else "", 

28 0, 

29 total, 

30 parent, 

31 ) 

32 dialog.setWindowTitle(title) 

33 if not cancellable: 

34 dialog.setCancelButton(None) 

35 dialog.setWindowModality(Qt.WindowModality.WindowModal) 

36 dialog.setMinimumDuration(0) 

37 dialog.setAutoClose(False) 

38 dialog.setAutoReset(False) 

39 dialog.setMinimumWidth(330) 

40 dialog.setValue(0) 

41 dialog.show() 

42 QApplication.processEvents() 

43 return dialog 

44 

45 

46def update_counter(dialog: QProgressDialog | None, value: int, total: int, label: str) -> None: 

47 """Advance the counter. A ``None`` dialog is a no-op, so callers need no guard.""" 

48 if dialog is None: 48 ↛ 49line 48 didn't jump to line 49 because the condition on line 48 was never true

49 return 

50 value = max(0, min(int(total), int(value))) 

51 dialog.setLabelText(f"{label}\n{value} / {int(total)}") 

52 dialog.setValue(value) 

53 QApplication.processEvents() 

54 

55 

56def close_counter(dialog: QProgressDialog | None) -> None: 

57 """Finish and dispose of the counter. A ``None`` dialog is a no-op.""" 

58 if dialog is None: 

59 return 

60 dialog.setValue(dialog.maximum()) 

61 dialog.close() 

62 dialog.deleteLater() 

63 QApplication.processEvents()