shinestacker 1.6.1__tar.gz → 1.8.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of shinestacker might be problematic. Click here for more details.

Files changed (175) hide show
  1. {shinestacker-1.6.1 → shinestacker-1.8.0}/.github/workflows/release.yml +10 -5
  2. {shinestacker-1.6.1 → shinestacker-1.8.0}/CHANGELOG.md +38 -3
  3. {shinestacker-1.6.1/src/shinestacker.egg-info → shinestacker-1.8.0}/PKG-INFO +4 -4
  4. {shinestacker-1.6.1 → shinestacker-1.8.0}/README.md +3 -3
  5. shinestacker-1.8.0/scripts/build_release.py +210 -0
  6. shinestacker-1.8.0/scripts/create_macos_icon.py +58 -0
  7. {shinestacker-1.6.1 → shinestacker-1.8.0/scripts}/shinestacker-inno-setup.iss +10 -6
  8. shinestacker-1.8.0/src/shinestacker/_version.py +1 -0
  9. shinestacker-1.8.0/src/shinestacker/algorithms/corrections.py +26 -0
  10. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/algorithms/stack.py +9 -0
  11. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/algorithms/stack_framework.py +35 -16
  12. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/algorithms/utils.py +5 -1
  13. shinestacker-1.8.0/src/shinestacker/app/args_parser_opts.py +66 -0
  14. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/app/gui_utils.py +19 -2
  15. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/app/main.py +16 -27
  16. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/app/project.py +12 -23
  17. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/app/retouch.py +12 -25
  18. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/app/settings_dialog.py +46 -3
  19. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/config/settings.py +4 -1
  20. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/core/core_utils.py +2 -2
  21. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/core/framework.py +7 -2
  22. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/core/logging.py +2 -2
  23. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/gui/action_config_dialog.py +72 -45
  24. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/gui/gui_run.py +1 -2
  25. shinestacker-1.8.0/src/shinestacker/gui/ico/shinestacker.icns +0 -0
  26. shinestacker-1.8.0/src/shinestacker/gui/img/dark/close-round-line-icon.png +0 -0
  27. shinestacker-1.8.0/src/shinestacker/gui/img/dark/forward-button-icon.png +0 -0
  28. shinestacker-1.8.0/src/shinestacker/gui/img/dark/play-button-round-icon.png +0 -0
  29. shinestacker-1.8.0/src/shinestacker/gui/img/dark/plus-round-line-icon.png +0 -0
  30. shinestacker-1.8.0/src/shinestacker/gui/img/dark/shinestacker_bkg.png +0 -0
  31. shinestacker-1.8.0/src/shinestacker/gui/img/light/shinestacker_bkg.png +0 -0
  32. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/gui/main_window.py +20 -7
  33. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/gui/menu_manager.py +18 -7
  34. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/gui/new_project.py +0 -2
  35. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/gui/tab_widget.py +16 -10
  36. shinestacker-1.8.0/src/shinestacker/retouch/adjustments.py +98 -0
  37. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/base_filter.py +62 -7
  38. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/denoise_filter.py +1 -1
  39. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/image_editor_ui.py +26 -4
  40. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/unsharp_mask_filter.py +13 -28
  41. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/vignetting_filter.py +1 -1
  42. {shinestacker-1.6.1 → shinestacker-1.8.0/src/shinestacker.egg-info}/PKG-INFO +4 -4
  43. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker.egg-info/SOURCES.txt +13 -5
  44. shinestacker-1.6.1/scripts/build_release.py +0 -112
  45. shinestacker-1.6.1/src/shinestacker/_version.py +0 -1
  46. shinestacker-1.6.1/src/shinestacker/app/args_parser_opts.py +0 -27
  47. shinestacker-1.6.1/src/shinestacker/gui/ico/focus_stack_bkg.png +0 -0
  48. shinestacker-1.6.1/src/shinestacker/gui/ico/shinestacker.icns +0 -0
  49. {shinestacker-1.6.1 → shinestacker-1.8.0}/.coveragerc +0 -0
  50. {shinestacker-1.6.1 → shinestacker-1.8.0}/.flake8 +0 -0
  51. {shinestacker-1.6.1 → shinestacker-1.8.0}/.github/workflows/ci-multiplatform.yml +0 -0
  52. {shinestacker-1.6.1 → shinestacker-1.8.0}/.github/workflows/pylint.yml +0 -0
  53. {shinestacker-1.6.1 → shinestacker-1.8.0}/.github/workflows/pypi-publish.yml +0 -0
  54. {shinestacker-1.6.1 → shinestacker-1.8.0}/.gitignore +0 -0
  55. {shinestacker-1.6.1 → shinestacker-1.8.0}/.pylintrc +0 -0
  56. {shinestacker-1.6.1 → shinestacker-1.8.0}/.readthedocs.yaml +0 -0
  57. {shinestacker-1.6.1 → shinestacker-1.8.0}/LICENSE +0 -0
  58. {shinestacker-1.6.1 → shinestacker-1.8.0}/MANIFEST.in +0 -0
  59. {shinestacker-1.6.1 → shinestacker-1.8.0}/THIRD_PARTY_LICENSES.txt +0 -0
  60. {shinestacker-1.6.1 → shinestacker-1.8.0}/docs/alignment.md +0 -0
  61. {shinestacker-1.6.1 → shinestacker-1.8.0}/docs/api.md +0 -0
  62. {shinestacker-1.6.1 → shinestacker-1.8.0}/docs/balancing.md +0 -0
  63. {shinestacker-1.6.1 → shinestacker-1.8.0}/docs/conf.py +0 -0
  64. {shinestacker-1.6.1 → shinestacker-1.8.0}/docs/focus_stacking.md +0 -0
  65. {shinestacker-1.6.1 → shinestacker-1.8.0}/docs/gui.md +0 -0
  66. {shinestacker-1.6.1 → shinestacker-1.8.0}/docs/index.md +0 -0
  67. {shinestacker-1.6.1 → shinestacker-1.8.0}/docs/job.md +0 -0
  68. {shinestacker-1.6.1 → shinestacker-1.8.0}/docs/main.md +0 -0
  69. {shinestacker-1.6.1 → shinestacker-1.8.0}/docs/multilayer.md +0 -0
  70. {shinestacker-1.6.1 → shinestacker-1.8.0}/docs/noise.md +0 -0
  71. {shinestacker-1.6.1 → shinestacker-1.8.0}/docs/requirements.txt +0 -0
  72. {shinestacker-1.6.1 → shinestacker-1.8.0}/docs/vignetting.md +0 -0
  73. {shinestacker-1.6.1 → shinestacker-1.8.0}/img/coffee.gif +0 -0
  74. {shinestacker-1.6.1 → shinestacker-1.8.0}/img/coffee_stack.jpg +0 -0
  75. {shinestacker-1.6.1 → shinestacker-1.8.0}/img/extreme-vignetting.jpg +0 -0
  76. {shinestacker-1.6.1 → shinestacker-1.8.0}/img/flies.gif +0 -0
  77. {shinestacker-1.6.1 → shinestacker-1.8.0}/img/flies_stack.jpg +0 -0
  78. {shinestacker-1.6.1 → shinestacker-1.8.0}/img/flow-diagram.png +0 -0
  79. {shinestacker-1.6.1 → shinestacker-1.8.0}/img/gui-finder.png +0 -0
  80. {shinestacker-1.6.1 → shinestacker-1.8.0}/img/gui-project-new.png +0 -0
  81. {shinestacker-1.6.1 → shinestacker-1.8.0}/img/gui-project-run.png +0 -0
  82. {shinestacker-1.6.1 → shinestacker-1.8.0}/img/gui-retouch.png +0 -0
  83. {shinestacker-1.6.1 → shinestacker-1.8.0}/index.html +0 -0
  84. {shinestacker-1.6.1 → shinestacker-1.8.0}/pyproject.toml +0 -0
  85. {shinestacker-1.6.1 → shinestacker-1.8.0}/requirements.txt +0 -0
  86. {shinestacker-1.6.1 → shinestacker-1.8.0}/scripts/git-rev-list.sh +0 -0
  87. {shinestacker-1.6.1 → shinestacker-1.8.0}/scripts/hooks/hook-IPython.py +0 -0
  88. {shinestacker-1.6.1 → shinestacker-1.8.0}/scripts/hooks/hook-PySide6.py +0 -0
  89. {shinestacker-1.6.1 → shinestacker-1.8.0}/scripts/hooks/hook-opencv.py +0 -0
  90. {shinestacker-1.6.1 → shinestacker-1.8.0}/scripts/hooks/hook-tests.py +0 -0
  91. {shinestacker-1.6.1 → shinestacker-1.8.0}/scripts/scan_imports.py +0 -0
  92. {shinestacker-1.6.1 → shinestacker-1.8.0}/scripts/validate-tomli.py +0 -0
  93. {shinestacker-1.6.1 → shinestacker-1.8.0}/setup.cfg +0 -0
  94. {shinestacker-1.6.1/scripts → shinestacker-1.8.0}/shinestacker-inno-setup.iss +0 -0
  95. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/__init__.py +0 -0
  96. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/algorithms/__init__.py +0 -0
  97. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/algorithms/align.py +0 -0
  98. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/algorithms/align_auto.py +0 -0
  99. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/algorithms/align_parallel.py +0 -0
  100. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/algorithms/balance.py +0 -0
  101. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/algorithms/base_stack_algo.py +0 -0
  102. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/algorithms/denoise.py +0 -0
  103. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/algorithms/depth_map.py +0 -0
  104. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/algorithms/exif.py +0 -0
  105. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/algorithms/multilayer.py +0 -0
  106. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/algorithms/noise_detection.py +0 -0
  107. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/algorithms/pyramid.py +0 -0
  108. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/algorithms/pyramid_auto.py +0 -0
  109. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/algorithms/pyramid_tiles.py +0 -0
  110. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/algorithms/sharpen.py +0 -0
  111. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/algorithms/vignetting.py +0 -0
  112. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/algorithms/white_balance.py +0 -0
  113. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/app/__init__.py +0 -0
  114. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/app/about_dialog.py +0 -0
  115. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/app/help_menu.py +0 -0
  116. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/app/open_frames.py +0 -0
  117. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/config/__init__.py +0 -0
  118. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/config/app_config.py +0 -0
  119. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/config/config.py +0 -0
  120. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/config/constants.py +0 -0
  121. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/config/gui_constants.py +0 -0
  122. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/core/__init__.py +0 -0
  123. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/core/colors.py +0 -0
  124. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/core/exceptions.py +0 -0
  125. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/gui/__init__.py +0 -0
  126. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/gui/action_config.py +0 -0
  127. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/gui/base_form_dialog.py +0 -0
  128. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/gui/colors.py +0 -0
  129. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/gui/config_dialog.py +0 -0
  130. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/gui/flow_layout.py +0 -0
  131. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/gui/folder_file_selection.py +0 -0
  132. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/gui/gui_images.py +0 -0
  133. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/gui/gui_logging.py +0 -0
  134. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/gui/ico/shinestacker.ico +0 -0
  135. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/gui/ico/shinestacker.png +0 -0
  136. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/gui/ico/shinestacker.svg +0 -0
  137. {shinestacker-1.6.1/src/shinestacker/gui/img → shinestacker-1.8.0/src/shinestacker/gui/img/light}/close-round-line-icon.png +0 -0
  138. {shinestacker-1.6.1/src/shinestacker/gui/img → shinestacker-1.8.0/src/shinestacker/gui/img/light}/forward-button-icon.png +0 -0
  139. {shinestacker-1.6.1/src/shinestacker/gui/img → shinestacker-1.8.0/src/shinestacker/gui/img/light}/play-button-round-icon.png +0 -0
  140. {shinestacker-1.6.1/src/shinestacker/gui/img → shinestacker-1.8.0/src/shinestacker/gui/img/light}/plus-round-line-icon.png +0 -0
  141. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/gui/project_controller.py +0 -0
  142. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/gui/project_converter.py +0 -0
  143. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/gui/project_editor.py +0 -0
  144. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/gui/project_model.py +0 -0
  145. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/gui/recent_file_manager.py +0 -0
  146. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/gui/select_path_widget.py +0 -0
  147. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/gui/sys_mon.py +0 -0
  148. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/gui/time_progress_bar.py +0 -0
  149. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/__init__.py +0 -0
  150. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/brush.py +0 -0
  151. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/brush_gradient.py +0 -0
  152. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/brush_preview.py +0 -0
  153. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/brush_tool.py +0 -0
  154. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/display_manager.py +0 -0
  155. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/exif_data.py +0 -0
  156. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/file_loader.py +0 -0
  157. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/filter_manager.py +0 -0
  158. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/icon_container.py +0 -0
  159. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/image_view_status.py +0 -0
  160. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/image_viewer.py +0 -0
  161. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/io_gui_handler.py +0 -0
  162. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/io_threads.py +0 -0
  163. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/layer_collection.py +0 -0
  164. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/overlaid_view.py +0 -0
  165. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/paint_area_manager.py +0 -0
  166. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/shortcuts_help.py +0 -0
  167. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/sidebyside_view.py +0 -0
  168. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/transformation_manager.py +0 -0
  169. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/undo_manager.py +0 -0
  170. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/view_strategy.py +0 -0
  171. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker/retouch/white_balance_filter.py +0 -0
  172. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker.egg-info/dependency_links.txt +0 -0
  173. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker.egg-info/entry_points.txt +0 -0
  174. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker.egg-info/requires.txt +0 -0
  175. {shinestacker-1.6.1 → shinestacker-1.8.0}/src/shinestacker.egg-info/top_level.txt +0 -0
@@ -40,18 +40,20 @@ jobs:
40
40
  pip install -e .[dev]
41
41
 
42
42
  - name: Build and package release
43
- working-directory: scripts
44
43
  run: |
45
- python build_release.py
44
+ python scripts/build_release.py
46
45
 
47
46
  - name: Upload artifacts
48
47
  uses: actions/upload-artifact@v4
49
48
  with:
50
49
  name: shinestacker-${{ matrix.os }}
51
50
  path: |
52
- ${{ matrix.os == 'windows-latest' && 'dist/shinestacker-release.zip' || 'dist/shinestacker-release.tar.gz' }}
51
+ ${{ matrix.os == 'windows-latest' && 'dist/shinestacker-release.zip' || '' }}
53
52
  ${{ matrix.os == 'windows-latest' && 'dist/*.exe' || '' }}
53
+ ${{ matrix.os == 'ubuntu-latest' && 'dist/shinestacker-release.tar.gz' || '' }}
54
+ ${{ matrix.os == 'macos-latest' && 'dist/shinestacker-release.dmg' || '' }}
54
55
  if-no-files-found: ignore
56
+
55
57
  create-release:
56
58
  needs: publish-release
57
59
  runs-on: ubuntu-latest
@@ -64,8 +66,11 @@ jobs:
64
66
  - name: Prepare release assets
65
67
  run: |
66
68
  mkdir -p release_assets
69
+ # Linux
67
70
  cp artifacts/shinestacker-ubuntu-latest/shinestacker-release.tar.gz release_assets/shinestacker-ubuntu.tar.gz
68
- cp artifacts/shinestacker-macos-latest/shinestacker-release.tar.gz release_assets/shinestacker-macos.tar.gz
71
+ # macOS
72
+ cp artifacts/shinestacker-macos-latest/shinestacker-release.dmg release_assets/shinestacker-macos.dmg
73
+ # Windows
69
74
  cp artifacts/shinestacker-windows-latest/shinestacker-release.zip release_assets/shinestacker-windows.zip
70
75
  if ls artifacts/shinestacker-windows-latest/shinestacker-setup.exe 1> /dev/null 2>&1; then
71
76
  cp artifacts/shinestacker-windows-latest/shinestacker-setup.exe release_assets/
@@ -78,6 +83,6 @@ jobs:
78
83
  draft: true
79
84
  files: |
80
85
  release_assets/shinestacker-ubuntu.tar.gz
81
- release_assets/shinestacker-macos.tar.gz
86
+ release_assets/shinestacker-macos.dmg
82
87
  release_assets/shinestacker-windows.zip
83
88
  release_assets/shinestacker-setup.exe
@@ -2,15 +2,50 @@
2
2
 
3
3
  This page reports the main releases only and the main changes therein.
4
4
 
5
+ ## [v1.8.0] - 2025-10-08
6
+ ** Minor improvements and accessibility fix **
7
+
8
+ ### Added
9
+ - temporary disk space can be cleaned up with a new option to scratch output files at the end of a job
10
+
11
+ ### Fixed
12
+ - new project dialog displays well also with dark background settings
13
+
14
+ ### Changed
15
+ - icons now adapt automatically to light or dark desktop theme
16
+ - additional alignment parameters added to default settings
17
+ - minor GUI stability fix
18
+ - redundant macOS .tar.gz installer removed, replaced by .dmg image
19
+
20
+ -----
21
+
22
+ ## [v1.7.0] - 2025-10-04
23
+ ** New image adjustment actions and macOS dmg image installer **
24
+
25
+ ### Added
26
+ - luminosity and contrast adjustment action
27
+ - saturation and vibrance adjustment action
28
+ - macOS dmg installer
29
+
30
+ ### Changed
31
+ - improved windows installer
32
+ - white balance moved from filters to edit > adjust menu
33
+ - minor GUI cosmetic improvements
34
+ - code refactoring
35
+
36
+ -----
37
+
5
38
  ## [v1.6.1] - 2025-10-01
6
- ** Unreleased updates **
39
+ ** Performance improvements **
40
+
41
+ ### Added
42
+ - windows installer
7
43
 
8
44
  ### Changed
9
45
  - improved display update performance by refreshing only the painted area
10
46
  - multiple frame import now runs in a separate thread, avoiding UI freezes
11
47
  - reduced dependencies and code refactored for more robust architecture
12
- - added windows installer
13
- - dropped examples and test images reduces distribution file size
48
+ - dropped examples and test images to reduce distribution file size
14
49
 
15
50
  -----
16
51
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shinestacker
3
- Version: 1.6.1
3
+ Version: 1.8.0
4
4
  Summary: ShineStacker
5
5
  Author-email: Luca Lista <luka.lista@gmail.com>
6
6
  License-Expression: LGPL-3.0
@@ -85,11 +85,11 @@ In order to prevent this, follow the instructions below:
85
85
  1. Download the compressed archive ```shinestacker-macos.tar.gz``` in your ```Download``` folder.
86
86
  2. Double-click the archive to uncompress it. You will find a new folder ```shinestacker```.
87
87
  3. Open a terminal (*Applications > Utilities > Terminal*)
88
- 4. Type the folliwng command on the terminal:
88
+ 4. Type the folliwng command on the terminal (assuming you installed the app from the ```dmg``` image under ```Applications```):
89
89
  ```bash
90
- xattr -cr ~/Downloads/shinestacker/shinestacker.app
90
+ xattr -cr /Applications/shinestacker/shinestacker.app
91
91
  ```
92
- 5. Now you can double-click the Sine Stacker icon app in the ```shiestacker``` folder and it should run.
92
+ 5. Now you can double-click the Sine Stacker icon app and it should run.
93
93
 
94
94
  macOS adds a quarantine flag to all files downloaded from the internet. The above command removes that flag while preserving all other application functionality.
95
95
 
@@ -53,11 +53,11 @@ In order to prevent this, follow the instructions below:
53
53
  1. Download the compressed archive ```shinestacker-macos.tar.gz``` in your ```Download``` folder.
54
54
  2. Double-click the archive to uncompress it. You will find a new folder ```shinestacker```.
55
55
  3. Open a terminal (*Applications > Utilities > Terminal*)
56
- 4. Type the folliwng command on the terminal:
56
+ 4. Type the folliwng command on the terminal (assuming you installed the app from the ```dmg``` image under ```Applications```):
57
57
  ```bash
58
- xattr -cr ~/Downloads/shinestacker/shinestacker.app
58
+ xattr -cr /Applications/shinestacker/shinestacker.app
59
59
  ```
60
- 5. Now you can double-click the Sine Stacker icon app in the ```shiestacker``` folder and it should run.
60
+ 5. Now you can double-click the Sine Stacker icon app and it should run.
61
61
 
62
62
  macOS adds a quarantine flag to all files downloaded from the internet. The above command removes that flag while preserving all other application functionality.
63
63
 
@@ -0,0 +1,210 @@
1
+ import os
2
+ import shutil
3
+ import tarfile
4
+ import subprocess
5
+ from pathlib import Path
6
+ import platform
7
+
8
+
9
+ def setup_environment():
10
+ project_root = Path(__file__).resolve().parent.parent
11
+ dist_dir = project_root / "dist"
12
+ project_name = "shinestacker"
13
+ app_name = "shinestacker"
14
+ hooks_dir = "scripts/hooks"
15
+ hook_files = list(Path(hooks_dir).glob("hook-*.py"))
16
+ for hook in hook_files:
17
+ print(f" - {hook.name}")
18
+ return project_root, dist_dir, project_name, app_name
19
+
20
+
21
+ def build_pyinstaller_command(sys_name, dist_dir, project_name, app_name, hooks_dir):
22
+ if sys_name == 'darwin':
23
+ return [
24
+ "pyinstaller", "--windowed",
25
+ f"--name={app_name}",
26
+ f"--distpath={dist_dir}",
27
+ "--paths=src",
28
+ "--icon=src/shinestacker/gui/ico/shinestacker.icns",
29
+ "--argv-emulation",
30
+ f"--additional-hooks-dir={hooks_dir}",
31
+ f"--collect-all={project_name}",
32
+ "--collect-data=imagecodecs",
33
+ "--collect-submodules=imagecodecs",
34
+ "--copy-metadata=imagecodecs",
35
+ "src/shinestacker/app/main.py"
36
+ ]
37
+ elif sys_name == 'windows':
38
+ return [
39
+ "pyinstaller", "--onedir", "--windowed",
40
+ f"--name={app_name}",
41
+ f"--distpath={dist_dir}",
42
+ "--paths=src",
43
+ "--icon=src/shinestacker/gui/ico/shinestacker.ico",
44
+ f"--collect-all={project_name}",
45
+ "--collect-data=imagecodecs", "--collect-submodules=imagecodecs",
46
+ "--copy-metadata=imagecodecs", f"--additional-hooks-dir={hooks_dir}",
47
+ "src/shinestacker/app/main.py"
48
+ ]
49
+ else:
50
+ return [
51
+ "pyinstaller", "--onedir",
52
+ f"--name={app_name}",
53
+ f"--distpath={dist_dir}",
54
+ "--paths=src",
55
+ f"--collect-all={project_name}",
56
+ "--collect-data=imagecodecs", "--collect-submodules=imagecodecs",
57
+ "--copy-metadata=imagecodecs", f"--additional-hooks-dir={hooks_dir}",
58
+ "src/shinestacker/app/main.py"
59
+ ]
60
+
61
+
62
+ def package_windows(dist_dir, app_name):
63
+ shutil.make_archive(
64
+ base_name=str(dist_dir / "shinestacker-release"),
65
+ format="zip",
66
+ root_dir=dist_dir,
67
+ base_dir=app_name
68
+ )
69
+
70
+
71
+ def package_macos(dist_dir, app_name, project_root):
72
+ app_bundle = dist_dir / f"{app_name}.app"
73
+ if not app_bundle.exists():
74
+ print(f"ERROR: .app bundle not found at {app_bundle}")
75
+ return
76
+ version = get_version(project_root)
77
+ build_number = version.replace('.', '') + '0' # Convert x.y.z -> xyz0
78
+ info_plist_template = project_root / "scripts" / "Info.plist"
79
+ info_plist_target = app_bundle / "Contents" / "Info.plist"
80
+ if info_plist_template.exists():
81
+ print("Processing Info.plist...")
82
+ with open(info_plist_template, 'r') as f:
83
+ plist_content = f.read()
84
+ plist_content = plist_content.replace('{{VERSION}}', version)
85
+ plist_content = plist_content.replace('{{BUILD_NUMBER}}', build_number)
86
+ info_plist_target.parent.mkdir(parents=True, exist_ok=True)
87
+ with open(info_plist_target, 'w') as f:
88
+ f.write(plist_content)
89
+ print(f"Info.plist created at: {info_plist_target}")
90
+ else:
91
+ print(f"WARNING: Info.plist template not found at {info_plist_template}")
92
+ icon_source = project_root / "src" / "shinestacker" / "gui" / "ico" / "shinestacker.icns"
93
+ dmg_temp_dir = dist_dir / "dmg_temp"
94
+ if dmg_temp_dir.exists():
95
+ shutil.rmtree(dmg_temp_dir)
96
+ shutil.copytree(app_bundle, dmg_temp_dir / app_bundle.name, symlinks=True)
97
+ os.symlink("/Applications", dmg_temp_dir / "Applications")
98
+ dmg_path = dist_dir / f"{app_name}-release.dmg"
99
+ dmg_cmd = [
100
+ "hdiutil", "create",
101
+ "-volname", app_name,
102
+ "-srcfolder", str(dmg_temp_dir),
103
+ "-ov", str(dmg_path),
104
+ "-format", "UDBZ",
105
+ "-fs", "HFS+"
106
+ ]
107
+ subprocess.run(dmg_cmd, check=True)
108
+ print(f"Created DMG: {dmg_path.name}")
109
+ if icon_source.exists():
110
+ print("Setting custom icon...")
111
+ try:
112
+ subprocess.run(["sips", "-i", str(icon_source)], check=True)
113
+ subprocess.run(["DeRez", "-only", "icns", str(icon_source)],
114
+ stdout=open("/tmp/icon.r", "w"), check=True)
115
+ subprocess.run(["Rez", "-append", "/tmp/icon.r", "-o", str(dmg_path)], check=True)
116
+ subprocess.run(["SetFile", "-a", "C", str(dmg_path)], check=True)
117
+ print("Custom icon set successfully!")
118
+ except subprocess.CalledProcessError as e:
119
+ print(f"Could not set custom icon: {e}")
120
+ shutil.rmtree(dmg_temp_dir)
121
+
122
+
123
+ def package_linux(dist_dir, app_name):
124
+ archive_path = dist_dir / "shinestacker-release.tar.gz"
125
+ linux_app_dir = dist_dir / app_name
126
+ if linux_app_dir.exists():
127
+ with tarfile.open(archive_path, "w:gz") as tar:
128
+ tar.add(linux_app_dir, arcname=app_name, recursive=True)
129
+ print(f"Packaged Linux application: {app_name}")
130
+ else:
131
+ print(f"ERROR: Linux app directory not found at {linux_app_dir}")
132
+
133
+
134
+ def get_version(project_root):
135
+ version_file = project_root / "src" / "shinestacker" / "_version.py"
136
+ version = "0.0.0"
137
+ if version_file.exists():
138
+ with open(version_file, 'r') as f:
139
+ content = f.read()
140
+ import re
141
+ match = re.search(r"__version__\s*=\s*['\"]([^'\"]+)['\"]", content)
142
+ if match:
143
+ version = match.group(1)
144
+ return version
145
+
146
+
147
+ def create_windows_installer(project_root, dist_dir):
148
+ inno_paths = [
149
+ r"C:\Program Files (x86)\Inno Setup 6\ISCC.exe",
150
+ r"C:\Program Files (x86)\Inno Setup 5\ISCC.exe",
151
+ r"C:\Program Files\Inno Setup 6\ISCC.exe",
152
+ r"C:\Program Files\Inno Setup 5\ISCC.exe"
153
+ ]
154
+ iscc_exe = None
155
+ for path in inno_paths:
156
+ if os.path.exists(path):
157
+ iscc_exe = path
158
+ break
159
+ if not iscc_exe:
160
+ try:
161
+ subprocess.run(["choco", "--version"], check=True, capture_output=True)
162
+ subprocess.run(["choco", "install", "innosetup", "-y",
163
+ "--no-progress", "--accept-license"], check=True)
164
+ for path in inno_paths:
165
+ if os.path.exists(path):
166
+ iscc_exe = path
167
+ break
168
+ except (subprocess.CalledProcessError, FileNotFoundError):
169
+ pass
170
+ if iscc_exe:
171
+ iss_script_source = project_root / "scripts" / "shinestacker-inno-setup.iss"
172
+ iss_script_temp = project_root / "shinestacker-inno-setup.iss"
173
+ if iss_script_source.exists():
174
+ version = get_version(project_root)
175
+ with open(iss_script_source, 'r') as f:
176
+ iss_content = f.read()
177
+ old_version_line = f'#define MyAppVersion "{"x.x.x"}"'
178
+ new_version_line = f'#define MyAppVersion "{version}"'
179
+ iss_content = iss_content.replace(old_version_line, new_version_line)
180
+ with open(iss_script_temp, 'w') as f:
181
+ f.write(iss_content)
182
+ subprocess.run([iscc_exe, str(iss_script_temp)], check=True)
183
+ iss_script_temp.unlink()
184
+ if dist_dir.exists():
185
+ installer_files = list(dist_dir.glob("*.exe"))
186
+ if installer_files:
187
+ print(f"Installer created: {installer_files[0].name}")
188
+
189
+
190
+ def main():
191
+ project_root, dist_dir, project_name, app_name = setup_environment()
192
+ sys_name = platform.system().lower()
193
+ hooks_dir = "scripts/hooks"
194
+ pyinstaller_cmd = build_pyinstaller_command(
195
+ sys_name, dist_dir, project_name, app_name, hooks_dir)
196
+ print(" ".join(pyinstaller_cmd))
197
+ subprocess.run(pyinstaller_cmd, check=True)
198
+ if sys_name == 'windows':
199
+ package_windows(dist_dir, app_name)
200
+ elif sys_name == 'darwin':
201
+ package_macos(dist_dir, app_name, project_root)
202
+ else:
203
+ package_linux(dist_dir, app_name)
204
+ if sys_name == 'windows':
205
+ print("=== CREATING WINDOWS INSTALLER ===")
206
+ create_windows_installer(project_root, dist_dir)
207
+
208
+
209
+ if __name__ == "__main__":
210
+ main()
@@ -0,0 +1,58 @@
1
+ import os
2
+ import shutil
3
+ import subprocess
4
+ from pathlib import Path
5
+
6
+ def create_macos_icon():
7
+ script_dir = Path(__file__).parent
8
+ project_root = script_dir.parent
9
+ png_source = project_root / "src" / "shinestacker" / "gui" / "ico" / "shinestacker.png"
10
+ icns_output = project_root / "src" / "shinestacker" / "gui" / "ico" / "shinestacker.icns"
11
+
12
+ if not png_source.exists():
13
+ print(f"ERROR: Source PNG not found at {png_source}")
14
+ return False
15
+
16
+ print(f"Creating macOS icon from {png_source}")
17
+
18
+ iconset_dir = project_root / "src" / "shinestacker" / "gui" / "ico" / "shinestacker.iconset"
19
+ if iconset_dir.exists():
20
+ shutil.rmtree(iconset_dir)
21
+ iconset_dir.mkdir()
22
+
23
+ sizes = [
24
+ ("16x16", 16),
25
+ ("16x16@2x", 32),
26
+ ("32x32", 32),
27
+ ("32x32@2x", 64),
28
+ ("128x128", 128),
29
+ ("128x128@2x", 256),
30
+ ("256x256", 256),
31
+ ("256x256@2x", 512),
32
+ ("512x512", 512),
33
+ ("512x512@2x", 1024)
34
+ ]
35
+
36
+ for name, size in sizes:
37
+ output_file = iconset_dir / f"icon_{name}.png"
38
+ print(f" Creating {name}...")
39
+ subprocess.run([
40
+ "sips", "-z", str(size), str(size),
41
+ str(png_source),
42
+ "--out", str(output_file)
43
+ ], check=True)
44
+
45
+ print("Converting to .icns format...")
46
+ subprocess.run([
47
+ "iconutil", "-c", "icns",
48
+ str(iconset_dir),
49
+ "-o", str(icns_output)
50
+ ], check=True)
51
+
52
+ shutil.rmtree(iconset_dir)
53
+ print(f"SUCCESS: Created {icns_output}")
54
+ return True
55
+
56
+
57
+ if __name__ == "__main__":
58
+ create_macos_icon()
@@ -1,5 +1,5 @@
1
1
  #define MyAppName "ShineStacker"
2
- #define MyAppVersion "1.6.1"
2
+ #define MyAppVersion "x.x.x"
3
3
  #define MyAppPublisher "Luca Lista"
4
4
  #define MyAppURL "https://shinestacker.wordpress.com/"
5
5
  #define MyAppExeName "shinestacker.exe"
@@ -34,30 +34,34 @@ LicenseFile=.\LICENSE
34
34
  ; Uncomment the following line to run in non administrative install mode (install for current user only).
35
35
  ;PrivilegesRequired=lowest
36
36
  OutputBaseFilename=shinestacker-setup
37
- OutputDir=.\dist
37
+ OutputDir=dist
38
38
  VersionInfoVersion={#MyAppVersion}
39
39
  VersionInfoCompany={#MyAppPublisher}
40
- SetupIconFile=.\src\shinestacker\gui\ico\shinestacker.ico
40
+ SetupIconFile=src\shinestacker\gui\ico\shinestacker.ico
41
41
  SolidCompression=yes
42
42
  WizardStyle=modern
43
43
 
44
44
  [Languages]
45
45
  Name: "english"; MessagesFile: "compiler:Default.isl"
46
46
 
47
+ [Messages]
48
+ WelcomeLabel1=Welcome to the {#MyAppName} Setup Wizard
49
+ WelcomeLabel2=This wizard will install {#MyAppName} {#MyAppVersion}, an open-source focus stacking application developed by Luca Lista.%n%nThe source code is available on GitHub.
50
+ FinishedLabel=Setup has finished installing {#MyAppName} on your computer. The application may be launched by selecting the installed shortcuts.%n%nClick Finish to exit Setup.
51
+
47
52
  [Tasks]
48
53
  Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
49
54
 
50
55
  [Files]
51
56
  ; Copy the entire shinestacker folder structure that contains both the exe and _internal
52
- Source: ".\dist\shinestacker\shinestacker\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
53
- Source: ".\examples\*"; DestDir: "{app}\examples"; Flags: ignoreversion recursesubdirs
57
+ Source: "dist\shinestacker\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
54
58
  ; NOTE: Don't use "Flags: ignoreversion" on any shared system files
55
59
 
56
60
  [Registry]
57
61
  Root: HKA; Subkey: "Software\Classes\{#MyAppAssocExt}\OpenWithProgids"; ValueType: string; ValueName: "{#MyAppAssocKey}"; ValueData: ""; Flags: uninsdeletevalue
58
62
  Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}"; ValueType: string; ValueName: ""; ValueData: "{#MyAppAssocName}"; Flags: uninsdeletekey
59
63
  Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\{#MyAppExeName},0"
60
- Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" ""%1"""
64
+ Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" -f ""%1"""
61
65
 
62
66
  [Icons]
63
67
  Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
@@ -0,0 +1 @@
1
+ __version__ = '1.8.0'
@@ -0,0 +1,26 @@
1
+ # pylint: disable=C0114, C0115, C0116, E1101
2
+ import numpy as np
3
+ import cv2
4
+ from ..config.constants import constants
5
+
6
+
7
+ def gamma_correction(img, gamma):
8
+ max_px_val = constants.MAX_UINT8 if img.dtype == np.uint8 else constants.MAX_UINT16
9
+ ar = np.arange(0, max_px_val + 1, dtype=np.float64)
10
+ lut = (((ar / max_px_val) ** (1.0 / gamma)) * max_px_val).astype(img.dtype)
11
+ return cv2.LUT(img, lut) if img.dtype == np.uint8 else np.take(lut, img)
12
+
13
+
14
+ def contrast_correction(img, k):
15
+ max_px_val = constants.MAX_UINT8 if img.dtype == np.uint8 else constants.MAX_UINT16
16
+ ar = np.arange(0, max_px_val + 1, dtype=np.float64)
17
+ x = 2.0 * (ar / max_px_val) - 1.0
18
+ # f(x) = x * exp(k) / (1 + (exp(k) - 1)|x|), -1 < x < +1
19
+ # note that: f(f(x, k), -k) = x
20
+ exp_k = np.exp(k)
21
+ numerator = x * exp_k
22
+ denominator = 1 + (exp_k - 1) * np.abs(x)
23
+ corrected = numerator / denominator
24
+ corrected = (corrected + 1.0) * 0.5 * max_px_val
25
+ lut = np.clip(corrected, 0, max_px_val).astype(img.dtype)
26
+ return cv2.LUT(img, lut) if img.dtype == np.uint8 else np.take(lut, img)
@@ -65,6 +65,9 @@ class FocusStackBase(TaskBase, ImageSequenceManager):
65
65
  if self.exif_path != '':
66
66
  self.exif_path = os.path.join(working_path, self.exif_path)
67
67
 
68
+ def end_job(self):
69
+ ImageSequenceManager.end_job(self)
70
+
68
71
 
69
72
  def get_bunches(collection, n_frames, n_overlap):
70
73
  bunches = [collection[x:x + n_frames]
@@ -100,6 +103,9 @@ class FocusStackBunch(SequentialTask, FocusStackBase):
100
103
  def end(self):
101
104
  SequentialTask.end(self)
102
105
 
106
+ def end_job(self):
107
+ FocusStackBase.end_job(self)
108
+
103
109
  def run_step(self, action_count=-1):
104
110
  self.print_message(
105
111
  color_str(f"fusing bunch: {action_count + 1}/{self.total_action_counts}",
@@ -126,3 +132,6 @@ class FocusStack(FocusStackBase):
126
132
 
127
133
  def init(self, job, _working_path=''):
128
134
  FocusStackBase.init(self, job, self.working_path)
135
+
136
+ def end_job(self):
137
+ FocusStackBase.end_job(self)
@@ -46,7 +46,8 @@ class StackJob(Job):
46
46
  class ImageSequenceManager:
47
47
  def __init__(self, name, input_path='', output_path='', working_path='',
48
48
  plot_path=constants.DEFAULT_PLOTS_PATH,
49
- scratch_output_dir=True, resample=1,
49
+ scratch_output_dir=True, delete_output_at_end=False,
50
+ resample=1,
50
51
  reverse_order=constants.DEFAULT_FILE_REVERSE_ORDER, **_kwargs):
51
52
  self.name = name
52
53
  self.working_path = working_path
@@ -56,6 +57,7 @@ class ImageSequenceManager:
56
57
  self._resample = resample
57
58
  self.reverse_order = reverse_order
58
59
  self.scratch_output_dir = scratch_output_dir
60
+ self.delete_output_at_end = delete_output_at_end
59
61
  self.enabled = None
60
62
  self.base_message = ''
61
63
  self._input_full_path = None
@@ -122,6 +124,24 @@ class ImageSequenceManager:
122
124
  constants.LOG_COLOR_LEVEL_2))
123
125
  self.base_message = color_str(self.name, constants.LOG_COLOR_LEVEL_1, "bold")
124
126
 
127
+ def scratch_outout_folder(self):
128
+ if self.enabled:
129
+ output_dir = self.output_full_path()
130
+ list_dir = os.listdir(output_dir)
131
+ n_files = len(list_dir)
132
+ if n_files > 0:
133
+ for filename in list_dir:
134
+ file_path = os.path.join(output_dir, filename)
135
+ if os.path.isfile(file_path):
136
+ os.remove(file_path)
137
+ self.print_message(
138
+ color_str(f"output directory {self.output_path} content erased",
139
+ 'yellow'))
140
+ else:
141
+ self.print_message(
142
+ color_str(f"module disabled, output directory {self.output_path}"
143
+ " not scratched", 'yellow'))
144
+
125
145
  def init(self, job):
126
146
  if self.working_path == '':
127
147
  self.working_path = job.working_path
@@ -130,22 +150,10 @@ class ImageSequenceManager:
130
150
  if not os.path.exists(output_dir):
131
151
  os.makedirs(output_dir)
132
152
  else:
133
- list_dir = os.listdir(output_dir)
134
- if len(list_dir) > 0:
153
+ if len(os.listdir(output_dir)):
135
154
  if self.scratch_output_dir:
136
- if self.enabled:
137
- for filename in list_dir:
138
- file_path = os.path.join(output_dir, filename)
139
- if os.path.isfile(file_path):
140
- os.remove(file_path)
141
- self.print_message(
142
- color_str(f": output directory {self.output_path} content erased",
143
- 'yellow'))
144
- else:
145
- self.print_message(
146
- color_str(f": module disabled, output directory {self.output_path}"
147
- " not scratched", 'yellow'))
148
- else:
155
+ self.scratch_outout_folder()
156
+ elif self.enabled:
149
157
  self.print_message(
150
158
  color_str(
151
159
  f": output directory {self.output_path} not empty, "
@@ -168,6 +176,11 @@ class ImageSequenceManager:
168
176
  self._input_filepaths.append(filepath)
169
177
  job.add_action_path(self.output_path)
170
178
 
179
+ def end_job(self):
180
+ if self.delete_output_at_end:
181
+ self.scratch_outout_folder()
182
+ os.rmdir(self.output_full_path())
183
+
171
184
  def folder_list_str(self):
172
185
  if isinstance(self.input_full_path(), list):
173
186
  file_list = ", ".join(
@@ -206,6 +219,9 @@ class ReferenceFrameTask(SequentialTask, ImageSequenceManager):
206
219
  def end(self):
207
220
  SequentialTask.end(self)
208
221
 
222
+ def end_job(self):
223
+ ImageSequenceManager.end_job(self)
224
+
209
225
  def run_frame(self, _idx, _ref_idx):
210
226
  return None
211
227
 
@@ -323,6 +339,9 @@ class CombinedActions(ReferenceFrameTask):
323
339
  if a.enabled:
324
340
  a.end()
325
341
 
342
+ def end_job(self):
343
+ ReferenceFrameTask.end_job(self)
344
+
326
345
  def sequential_processing(self):
327
346
  for a in self._actions:
328
347
  if a.sequential_processing():
@@ -140,8 +140,12 @@ def save_plot(filename, fig=None):
140
140
  if not os.path.isdir(dir_path):
141
141
  os.makedirs(dir_path)
142
142
  if fig is None:
143
+ logging_level = logging.getLogger().level
144
+ logger = logging.getLogger()
145
+ logger.setLevel(logging.WARNING)
143
146
  fig = plt.gcf()
144
- fig.savefig(filename, dpi=150)
147
+ fig.savefig(filename, dpi=150)
148
+ logger.setLevel(logging_level)
145
149
  if config.JUPYTER_NOTEBOOK:
146
150
  plt.show()
147
151
  plt.close(fig)