shinestacker 1.6.1__tar.gz → 1.7.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 (170) hide show
  1. {shinestacker-1.6.1 → shinestacker-1.7.0}/.github/workflows/release.yml +10 -1
  2. {shinestacker-1.6.1 → shinestacker-1.7.0}/CHANGELOG.md +21 -3
  3. {shinestacker-1.6.1/src/shinestacker.egg-info → shinestacker-1.7.0}/PKG-INFO +2 -2
  4. {shinestacker-1.6.1 → shinestacker-1.7.0}/README.md +1 -1
  5. shinestacker-1.7.0/scripts/build_release.py +215 -0
  6. shinestacker-1.7.0/scripts/create_macos_icon.py +58 -0
  7. {shinestacker-1.6.1 → shinestacker-1.7.0/scripts}/shinestacker-inno-setup.iss +10 -6
  8. shinestacker-1.7.0/src/shinestacker/_version.py +1 -0
  9. shinestacker-1.7.0/src/shinestacker/algorithms/corrections.py +26 -0
  10. shinestacker-1.7.0/src/shinestacker/app/args_parser_opts.py +66 -0
  11. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/app/gui_utils.py +19 -2
  12. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/app/main.py +16 -27
  13. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/app/project.py +12 -23
  14. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/app/retouch.py +12 -25
  15. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/core/core_utils.py +1 -2
  16. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/core/logging.py +2 -2
  17. shinestacker-1.7.0/src/shinestacker/gui/ico/shinestacker.icns +0 -0
  18. shinestacker-1.7.0/src/shinestacker/gui/ico/shinestacker_bkg.png +0 -0
  19. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/tab_widget.py +1 -5
  20. shinestacker-1.7.0/src/shinestacker/retouch/adjustments.py +93 -0
  21. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/base_filter.py +62 -7
  22. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/denoise_filter.py +1 -1
  23. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/image_editor_ui.py +26 -4
  24. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/unsharp_mask_filter.py +13 -28
  25. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/vignetting_filter.py +1 -1
  26. {shinestacker-1.6.1 → shinestacker-1.7.0/src/shinestacker.egg-info}/PKG-INFO +2 -2
  27. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker.egg-info/SOURCES.txt +4 -1
  28. shinestacker-1.6.1/scripts/build_release.py +0 -112
  29. shinestacker-1.6.1/src/shinestacker/_version.py +0 -1
  30. shinestacker-1.6.1/src/shinestacker/app/args_parser_opts.py +0 -27
  31. shinestacker-1.6.1/src/shinestacker/gui/ico/focus_stack_bkg.png +0 -0
  32. shinestacker-1.6.1/src/shinestacker/gui/ico/shinestacker.icns +0 -0
  33. {shinestacker-1.6.1 → shinestacker-1.7.0}/.coveragerc +0 -0
  34. {shinestacker-1.6.1 → shinestacker-1.7.0}/.flake8 +0 -0
  35. {shinestacker-1.6.1 → shinestacker-1.7.0}/.github/workflows/ci-multiplatform.yml +0 -0
  36. {shinestacker-1.6.1 → shinestacker-1.7.0}/.github/workflows/pylint.yml +0 -0
  37. {shinestacker-1.6.1 → shinestacker-1.7.0}/.github/workflows/pypi-publish.yml +0 -0
  38. {shinestacker-1.6.1 → shinestacker-1.7.0}/.gitignore +0 -0
  39. {shinestacker-1.6.1 → shinestacker-1.7.0}/.pylintrc +0 -0
  40. {shinestacker-1.6.1 → shinestacker-1.7.0}/.readthedocs.yaml +0 -0
  41. {shinestacker-1.6.1 → shinestacker-1.7.0}/LICENSE +0 -0
  42. {shinestacker-1.6.1 → shinestacker-1.7.0}/MANIFEST.in +0 -0
  43. {shinestacker-1.6.1 → shinestacker-1.7.0}/THIRD_PARTY_LICENSES.txt +0 -0
  44. {shinestacker-1.6.1 → shinestacker-1.7.0}/docs/alignment.md +0 -0
  45. {shinestacker-1.6.1 → shinestacker-1.7.0}/docs/api.md +0 -0
  46. {shinestacker-1.6.1 → shinestacker-1.7.0}/docs/balancing.md +0 -0
  47. {shinestacker-1.6.1 → shinestacker-1.7.0}/docs/conf.py +0 -0
  48. {shinestacker-1.6.1 → shinestacker-1.7.0}/docs/focus_stacking.md +0 -0
  49. {shinestacker-1.6.1 → shinestacker-1.7.0}/docs/gui.md +0 -0
  50. {shinestacker-1.6.1 → shinestacker-1.7.0}/docs/index.md +0 -0
  51. {shinestacker-1.6.1 → shinestacker-1.7.0}/docs/job.md +0 -0
  52. {shinestacker-1.6.1 → shinestacker-1.7.0}/docs/main.md +0 -0
  53. {shinestacker-1.6.1 → shinestacker-1.7.0}/docs/multilayer.md +0 -0
  54. {shinestacker-1.6.1 → shinestacker-1.7.0}/docs/noise.md +0 -0
  55. {shinestacker-1.6.1 → shinestacker-1.7.0}/docs/requirements.txt +0 -0
  56. {shinestacker-1.6.1 → shinestacker-1.7.0}/docs/vignetting.md +0 -0
  57. {shinestacker-1.6.1 → shinestacker-1.7.0}/img/coffee.gif +0 -0
  58. {shinestacker-1.6.1 → shinestacker-1.7.0}/img/coffee_stack.jpg +0 -0
  59. {shinestacker-1.6.1 → shinestacker-1.7.0}/img/extreme-vignetting.jpg +0 -0
  60. {shinestacker-1.6.1 → shinestacker-1.7.0}/img/flies.gif +0 -0
  61. {shinestacker-1.6.1 → shinestacker-1.7.0}/img/flies_stack.jpg +0 -0
  62. {shinestacker-1.6.1 → shinestacker-1.7.0}/img/flow-diagram.png +0 -0
  63. {shinestacker-1.6.1 → shinestacker-1.7.0}/img/gui-finder.png +0 -0
  64. {shinestacker-1.6.1 → shinestacker-1.7.0}/img/gui-project-new.png +0 -0
  65. {shinestacker-1.6.1 → shinestacker-1.7.0}/img/gui-project-run.png +0 -0
  66. {shinestacker-1.6.1 → shinestacker-1.7.0}/img/gui-retouch.png +0 -0
  67. {shinestacker-1.6.1 → shinestacker-1.7.0}/index.html +0 -0
  68. {shinestacker-1.6.1 → shinestacker-1.7.0}/pyproject.toml +0 -0
  69. {shinestacker-1.6.1 → shinestacker-1.7.0}/requirements.txt +0 -0
  70. {shinestacker-1.6.1 → shinestacker-1.7.0}/scripts/git-rev-list.sh +0 -0
  71. {shinestacker-1.6.1 → shinestacker-1.7.0}/scripts/hooks/hook-IPython.py +0 -0
  72. {shinestacker-1.6.1 → shinestacker-1.7.0}/scripts/hooks/hook-PySide6.py +0 -0
  73. {shinestacker-1.6.1 → shinestacker-1.7.0}/scripts/hooks/hook-opencv.py +0 -0
  74. {shinestacker-1.6.1 → shinestacker-1.7.0}/scripts/hooks/hook-tests.py +0 -0
  75. {shinestacker-1.6.1 → shinestacker-1.7.0}/scripts/scan_imports.py +0 -0
  76. {shinestacker-1.6.1 → shinestacker-1.7.0}/scripts/validate-tomli.py +0 -0
  77. {shinestacker-1.6.1 → shinestacker-1.7.0}/setup.cfg +0 -0
  78. {shinestacker-1.6.1/scripts → shinestacker-1.7.0}/shinestacker-inno-setup.iss +0 -0
  79. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/__init__.py +0 -0
  80. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/__init__.py +0 -0
  81. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/align.py +0 -0
  82. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/align_auto.py +0 -0
  83. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/align_parallel.py +0 -0
  84. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/balance.py +0 -0
  85. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/base_stack_algo.py +0 -0
  86. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/denoise.py +0 -0
  87. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/depth_map.py +0 -0
  88. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/exif.py +0 -0
  89. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/multilayer.py +0 -0
  90. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/noise_detection.py +0 -0
  91. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/pyramid.py +0 -0
  92. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/pyramid_auto.py +0 -0
  93. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/pyramid_tiles.py +0 -0
  94. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/sharpen.py +0 -0
  95. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/stack.py +0 -0
  96. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/stack_framework.py +0 -0
  97. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/utils.py +0 -0
  98. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/vignetting.py +0 -0
  99. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/white_balance.py +0 -0
  100. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/app/__init__.py +0 -0
  101. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/app/about_dialog.py +0 -0
  102. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/app/help_menu.py +0 -0
  103. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/app/open_frames.py +0 -0
  104. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/app/settings_dialog.py +0 -0
  105. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/config/__init__.py +0 -0
  106. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/config/app_config.py +0 -0
  107. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/config/config.py +0 -0
  108. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/config/constants.py +0 -0
  109. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/config/gui_constants.py +0 -0
  110. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/config/settings.py +0 -0
  111. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/core/__init__.py +0 -0
  112. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/core/colors.py +0 -0
  113. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/core/exceptions.py +0 -0
  114. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/core/framework.py +0 -0
  115. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/__init__.py +0 -0
  116. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/action_config.py +0 -0
  117. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/action_config_dialog.py +0 -0
  118. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/base_form_dialog.py +0 -0
  119. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/colors.py +0 -0
  120. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/config_dialog.py +0 -0
  121. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/flow_layout.py +0 -0
  122. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/folder_file_selection.py +0 -0
  123. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/gui_images.py +0 -0
  124. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/gui_logging.py +0 -0
  125. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/gui_run.py +0 -0
  126. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/ico/shinestacker.ico +0 -0
  127. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/ico/shinestacker.png +0 -0
  128. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/ico/shinestacker.svg +0 -0
  129. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/img/close-round-line-icon.png +0 -0
  130. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/img/forward-button-icon.png +0 -0
  131. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/img/play-button-round-icon.png +0 -0
  132. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/img/plus-round-line-icon.png +0 -0
  133. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/main_window.py +0 -0
  134. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/menu_manager.py +0 -0
  135. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/new_project.py +0 -0
  136. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/project_controller.py +0 -0
  137. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/project_converter.py +0 -0
  138. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/project_editor.py +0 -0
  139. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/project_model.py +0 -0
  140. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/recent_file_manager.py +0 -0
  141. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/select_path_widget.py +0 -0
  142. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/sys_mon.py +0 -0
  143. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/time_progress_bar.py +0 -0
  144. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/__init__.py +0 -0
  145. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/brush.py +0 -0
  146. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/brush_gradient.py +0 -0
  147. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/brush_preview.py +0 -0
  148. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/brush_tool.py +0 -0
  149. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/display_manager.py +0 -0
  150. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/exif_data.py +0 -0
  151. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/file_loader.py +0 -0
  152. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/filter_manager.py +0 -0
  153. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/icon_container.py +0 -0
  154. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/image_view_status.py +0 -0
  155. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/image_viewer.py +0 -0
  156. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/io_gui_handler.py +0 -0
  157. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/io_threads.py +0 -0
  158. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/layer_collection.py +0 -0
  159. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/overlaid_view.py +0 -0
  160. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/paint_area_manager.py +0 -0
  161. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/shortcuts_help.py +0 -0
  162. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/sidebyside_view.py +0 -0
  163. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/transformation_manager.py +0 -0
  164. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/undo_manager.py +0 -0
  165. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/view_strategy.py +0 -0
  166. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/white_balance_filter.py +0 -0
  167. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker.egg-info/dependency_links.txt +0 -0
  168. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker.egg-info/entry_points.txt +0 -0
  169. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker.egg-info/requires.txt +0 -0
  170. {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker.egg-info/top_level.txt +0 -0
@@ -49,9 +49,13 @@ jobs:
49
49
  with:
50
50
  name: shinestacker-${{ matrix.os }}
51
51
  path: |
52
- ${{ matrix.os == 'windows-latest' && 'dist/shinestacker-release.zip' || 'dist/shinestacker-release.tar.gz' }}
52
+ ${{ matrix.os == 'windows-latest' && 'dist/shinestacker-release.zip' || '' }}
53
53
  ${{ matrix.os == 'windows-latest' && 'dist/*.exe' || '' }}
54
+ ${{ matrix.os == 'ubuntu-latest' && 'dist/shinestacker-release.tar.gz' || '' }}
55
+ ${{ matrix.os == 'macos-latest' && 'dist/shinestacker-release.tar.gz' || '' }}
56
+ ${{ matrix.os == 'macos-latest' && 'dist/shinestacker-release.dmg' || '' }}
54
57
  if-no-files-found: ignore
58
+
55
59
  create-release:
56
60
  needs: publish-release
57
61
  runs-on: ubuntu-latest
@@ -64,8 +68,12 @@ jobs:
64
68
  - name: Prepare release assets
65
69
  run: |
66
70
  mkdir -p release_assets
71
+ # Linux
67
72
  cp artifacts/shinestacker-ubuntu-latest/shinestacker-release.tar.gz release_assets/shinestacker-ubuntu.tar.gz
73
+ # macOS - include both formats
68
74
  cp artifacts/shinestacker-macos-latest/shinestacker-release.tar.gz release_assets/shinestacker-macos.tar.gz
75
+ cp artifacts/shinestacker-macos-latest/shinestacker-release.dmg release_assets/shinestacker-macos.dmg
76
+ # Windows
69
77
  cp artifacts/shinestacker-windows-latest/shinestacker-release.zip release_assets/shinestacker-windows.zip
70
78
  if ls artifacts/shinestacker-windows-latest/shinestacker-setup.exe 1> /dev/null 2>&1; then
71
79
  cp artifacts/shinestacker-windows-latest/shinestacker-setup.exe release_assets/
@@ -79,5 +87,6 @@ jobs:
79
87
  files: |
80
88
  release_assets/shinestacker-ubuntu.tar.gz
81
89
  release_assets/shinestacker-macos.tar.gz
90
+ release_assets/shinestacker-macos.dmg
82
91
  release_assets/shinestacker-windows.zip
83
92
  release_assets/shinestacker-setup.exe
@@ -2,15 +2,33 @@
2
2
 
3
3
  This page reports the main releases only and the main changes therein.
4
4
 
5
+ ## [v1.7.0] - 2025-10-04
6
+ ** New image adjustment actions and macOS dmg installer **
7
+
8
+ ### Added
9
+ - luminosity and contrast adjustment action
10
+ - saturation and vibrance adjustment action
11
+ - macOS dmg installer
12
+
13
+ ### Changed
14
+ - improved windows installer
15
+ - white balance moved from filters to edit > adjust menu
16
+ - minor GUI cosmetic improvements
17
+ - code refactoring
18
+
19
+ -----
20
+
5
21
  ## [v1.6.1] - 2025-10-01
6
- ** Unreleased updates **
22
+ ** Performance improvements **
23
+
24
+ ### Added
25
+ - windows installer
7
26
 
8
27
  ### Changed
9
28
  - improved display update performance by refreshing only the painted area
10
29
  - multiple frame import now runs in a separate thread, avoiding UI freezes
11
30
  - reduced dependencies and code refactored for more robust architecture
12
- - added windows installer
13
- - dropped examples and test images reduces distribution file size
31
+ - dropped examples and test images to reduce distribution file size
14
32
 
15
33
  -----
16
34
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shinestacker
3
- Version: 1.6.1
3
+ Version: 1.7.0
4
4
  Summary: ShineStacker
5
5
  Author-email: Luca Lista <luka.lista@gmail.com>
6
6
  License-Expression: LGPL-3.0
@@ -85,7 +85,7 @@ 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 have expanded the ```tar.gz``` under ```Downloads```):
89
89
  ```bash
90
90
  xattr -cr ~/Downloads/shinestacker/shinestacker.app
91
91
  ```
@@ -53,7 +53,7 @@ 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 have expanded the ```tar.gz``` under ```Downloads```):
57
57
  ```bash
58
58
  xattr -cr ~/Downloads/shinestacker/shinestacker.app
59
59
  ```
@@ -0,0 +1,215 @@
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
+ os.chdir("../")
11
+ project_root = Path(__file__).resolve().parent.parent
12
+ dist_dir = project_root / "dist"
13
+ project_name = "shinestacker"
14
+ app_name = "shinestacker"
15
+ hooks_dir = "scripts/hooks"
16
+ hook_files = list(Path(hooks_dir).glob("hook-*.py"))
17
+ for hook in hook_files:
18
+ print(f" - {hook.name}")
19
+ return project_root, dist_dir, project_name, app_name
20
+
21
+
22
+ def build_pyinstaller_command(sys_name, dist_dir, project_name, app_name, hooks_dir):
23
+ if sys_name == 'darwin':
24
+ return [
25
+ "pyinstaller", "--windowed",
26
+ f"--name={app_name}",
27
+ f"--distpath={dist_dir}",
28
+ "--paths=src",
29
+ "--icon=src/shinestacker/gui/ico/shinestacker.icns",
30
+ "--argv-emulation",
31
+ f"--additional-hooks-dir={hooks_dir}",
32
+ f"--collect-all={project_name}",
33
+ "--collect-data=imagecodecs",
34
+ "--collect-submodules=imagecodecs",
35
+ "--copy-metadata=imagecodecs",
36
+ "src/shinestacker/app/main.py"
37
+ ]
38
+ elif sys_name == 'windows':
39
+ return [
40
+ "pyinstaller", "--onedir", "--windowed",
41
+ f"--name={app_name}",
42
+ f"--distpath={dist_dir}",
43
+ "--paths=src",
44
+ "--icon=src/shinestacker/gui/ico/shinestacker.ico",
45
+ f"--collect-all={project_name}",
46
+ "--collect-data=imagecodecs", "--collect-submodules=imagecodecs",
47
+ "--copy-metadata=imagecodecs", f"--additional-hooks-dir={hooks_dir}",
48
+ "src/shinestacker/app/main.py"
49
+ ]
50
+ else:
51
+ return [
52
+ "pyinstaller", "--onedir",
53
+ f"--name={app_name}",
54
+ f"--distpath={dist_dir}",
55
+ "--paths=src",
56
+ f"--collect-all={project_name}",
57
+ "--collect-data=imagecodecs", "--collect-submodules=imagecodecs",
58
+ "--copy-metadata=imagecodecs", f"--additional-hooks-dir={hooks_dir}",
59
+ "src/shinestacker/app/main.py"
60
+ ]
61
+
62
+
63
+ def package_windows(dist_dir, app_name):
64
+ shutil.make_archive(
65
+ base_name=str(dist_dir / "shinestacker-release"),
66
+ format="zip",
67
+ root_dir=dist_dir,
68
+ base_dir=app_name
69
+ )
70
+
71
+
72
+ def package_macos(dist_dir, app_name, project_root):
73
+ app_bundle = dist_dir / f"{app_name}.app"
74
+ if not app_bundle.exists():
75
+ print(f"ERROR: .app bundle not found at {app_bundle}")
76
+ return
77
+ version = get_version(project_root)
78
+ build_number = version.replace('.', '') + '0' # Convert x.y.z -> xyz0
79
+ info_plist_template = project_root / "scripts" / "Info.plist"
80
+ info_plist_target = app_bundle / "Contents" / "Info.plist"
81
+ if info_plist_template.exists():
82
+ print("Processing Info.plist...")
83
+ with open(info_plist_template, 'r') as f:
84
+ plist_content = f.read()
85
+ plist_content = plist_content.replace('{{VERSION}}', version)
86
+ plist_content = plist_content.replace('{{BUILD_NUMBER}}', build_number)
87
+ info_plist_target.parent.mkdir(parents=True, exist_ok=True)
88
+ with open(info_plist_target, 'w') as f:
89
+ f.write(plist_content)
90
+ print(f"Info.plist created at: {info_plist_target}")
91
+ else:
92
+ print(f"WARNING: Info.plist template not found at {info_plist_template}")
93
+ icon_source = project_root / "src" / "shinestacker" / "gui" / "ico" / "shinestacker.icns"
94
+ dmg_temp_dir = dist_dir / "dmg_temp"
95
+ if dmg_temp_dir.exists():
96
+ shutil.rmtree(dmg_temp_dir)
97
+ shutil.copytree(app_bundle, dmg_temp_dir / app_bundle.name, symlinks=True)
98
+ os.symlink("/Applications", dmg_temp_dir / "Applications")
99
+ dmg_path = dist_dir / f"{app_name}-release.dmg"
100
+ dmg_cmd = [
101
+ "hdiutil", "create",
102
+ "-volname", app_name,
103
+ "-srcfolder", str(dmg_temp_dir),
104
+ "-ov", str(dmg_path),
105
+ "-format", "UDBZ",
106
+ "-fs", "HFS+"
107
+ ]
108
+ subprocess.run(dmg_cmd, check=True)
109
+ print(f"Created DMG: {dmg_path.name}")
110
+ if icon_source.exists():
111
+ print("Setting custom icon...")
112
+ try:
113
+ subprocess.run(["sips", "-i", str(icon_source)], check=True)
114
+ subprocess.run(["DeRez", "-only", "icns", str(icon_source)],
115
+ stdout=open("/tmp/icon.r", "w"), check=True)
116
+ subprocess.run(["Rez", "-append", "/tmp/icon.r", "-o", str(dmg_path)], check=True)
117
+ subprocess.run(["SetFile", "-a", "C", str(dmg_path)], check=True)
118
+ print("Custom icon set successfully!")
119
+ except subprocess.CalledProcessError as e:
120
+ print(f"Could not set custom icon: {e}")
121
+ shutil.rmtree(dmg_temp_dir)
122
+ archive_path = dist_dir / "shinestacker-release.tar.gz"
123
+ with tarfile.open(archive_path, "w:gz") as tar:
124
+ tar.add(app_bundle, arcname=app_bundle.name, recursive=True)
125
+ print(f"Created tar.gz: {archive_path.name}")
126
+
127
+
128
+ def package_linux(dist_dir, app_name):
129
+ archive_path = dist_dir / "shinestacker-release.tar.gz"
130
+ linux_app_dir = dist_dir / app_name
131
+ if linux_app_dir.exists():
132
+ with tarfile.open(archive_path, "w:gz") as tar:
133
+ tar.add(linux_app_dir, arcname=app_name, recursive=True)
134
+ print(f"Packaged Linux application: {app_name}")
135
+ else:
136
+ print(f"ERROR: Linux app directory not found at {linux_app_dir}")
137
+
138
+
139
+ def get_version(project_root):
140
+ version_file = project_root / "src" / "shinestacker" / "_version.py"
141
+ version = "0.0.0"
142
+ if version_file.exists():
143
+ with open(version_file, 'r') as f:
144
+ content = f.read()
145
+ import re
146
+ match = re.search(r"__version__\s*=\s*['\"]([^'\"]+)['\"]", content)
147
+ if match:
148
+ version = match.group(1)
149
+ return version
150
+
151
+
152
+ def create_windows_installer(project_root, dist_dir):
153
+ inno_paths = [
154
+ r"C:\Program Files (x86)\Inno Setup 6\ISCC.exe",
155
+ r"C:\Program Files (x86)\Inno Setup 5\ISCC.exe",
156
+ r"C:\Program Files\Inno Setup 6\ISCC.exe",
157
+ r"C:\Program Files\Inno Setup 5\ISCC.exe"
158
+ ]
159
+ iscc_exe = None
160
+ for path in inno_paths:
161
+ if os.path.exists(path):
162
+ iscc_exe = path
163
+ break
164
+ if not iscc_exe:
165
+ try:
166
+ subprocess.run(["choco", "--version"], check=True, capture_output=True)
167
+ subprocess.run(["choco", "install", "innosetup", "-y",
168
+ "--no-progress", "--accept-license"], check=True)
169
+ for path in inno_paths:
170
+ if os.path.exists(path):
171
+ iscc_exe = path
172
+ break
173
+ except (subprocess.CalledProcessError, FileNotFoundError):
174
+ pass
175
+ if iscc_exe:
176
+ iss_script_source = project_root / "scripts" / "shinestacker-inno-setup.iss"
177
+ iss_script_temp = project_root / "shinestacker-inno-setup.iss"
178
+ if iss_script_source.exists():
179
+ version = get_version(project_root)
180
+ with open(iss_script_source, 'r') as f:
181
+ iss_content = f.read()
182
+ old_version_line = f'#define MyAppVersion "{"x.x.x"}"'
183
+ new_version_line = f'#define MyAppVersion "{version}"'
184
+ iss_content = iss_content.replace(old_version_line, new_version_line)
185
+ with open(iss_script_temp, 'w') as f:
186
+ f.write(iss_content)
187
+ subprocess.run([iscc_exe, str(iss_script_temp)], check=True)
188
+ iss_script_temp.unlink()
189
+ if dist_dir.exists():
190
+ installer_files = list(dist_dir.glob("*.exe"))
191
+ if installer_files:
192
+ print(f"Installer created: {installer_files[0].name}")
193
+
194
+
195
+ def main():
196
+ project_root, dist_dir, project_name, app_name = setup_environment()
197
+ sys_name = platform.system().lower()
198
+ hooks_dir = "scripts/hooks"
199
+ pyinstaller_cmd = build_pyinstaller_command(
200
+ sys_name, dist_dir, project_name, app_name, hooks_dir)
201
+ print(" ".join(pyinstaller_cmd))
202
+ subprocess.run(pyinstaller_cmd, check=True)
203
+ if sys_name == 'windows':
204
+ package_windows(dist_dir, app_name)
205
+ elif sys_name == 'darwin':
206
+ package_macos(dist_dir, app_name, project_root)
207
+ else:
208
+ package_linux(dist_dir, app_name)
209
+ if sys_name == 'windows':
210
+ print("=== CREATING WINDOWS INSTALLER ===")
211
+ create_windows_installer(project_root, dist_dir)
212
+
213
+
214
+ if __name__ == "__main__":
215
+ 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.7.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)
@@ -0,0 +1,66 @@
1
+ # pylint: disable=C0114, C0116
2
+ import sys
3
+
4
+
5
+ def add_project_arguments(parser):
6
+ parser.add_argument('-x', '--expert', action='store_true', help='''
7
+ expert options are visible by default.
8
+ ''')
9
+ parser.add_argument('-n', '--no-new-project', dest='new-project',
10
+ action='store_false', default=True, help='''
11
+ Do not open new project dialog at startup (default: open).
12
+ ''')
13
+
14
+
15
+ def add_retouch_arguments(parser):
16
+ parser.add_argument('-p', '--path', nargs='?', help='''
17
+ import frames from one or more directories.
18
+ Multiple directories can be specified separated by ';'.
19
+ ''')
20
+ view_group = parser.add_mutually_exclusive_group()
21
+ view_group.add_argument('-v1', '--view-overlaid', action='store_true', help='''
22
+ set overlaid view.
23
+ ''')
24
+ view_group.add_argument('-v2', '--view-side-by-side', action='store_true', help='''
25
+ set side-by-side view.
26
+ ''')
27
+ view_group.add_argument('-v3', '--view-top-bottom', action='store_true', help='''
28
+ set top-bottom view.
29
+ ''')
30
+
31
+
32
+ def extract_positional_filename():
33
+ positional_filename = None
34
+ filtered_args = []
35
+ for arg in sys.argv[1:]:
36
+ if not arg.startswith('-') and not positional_filename:
37
+ positional_filename = arg
38
+ else:
39
+ filtered_args.append(arg)
40
+ return positional_filename, filtered_args
41
+
42
+
43
+ def setup_filename_argument(parser, use_const=True):
44
+ if use_const:
45
+ parser.add_argument('-f', '--filename', nargs='?', const=True, help='''
46
+ filename to open. Can be a project file or image file.
47
+ Multiple files can be specified separated by ';'.
48
+ ''')
49
+ else:
50
+ parser.add_argument('-f', '--filename', nargs='?', help='''
51
+ filename to open. Can be a project file or image file.
52
+ Multiple files can be specified separated by ';'.
53
+ ''')
54
+
55
+
56
+ def process_filename_argument(args, positional_filename):
57
+ filename = args.get('filename')
58
+ if positional_filename and not filename:
59
+ filename = positional_filename
60
+ if filename is True:
61
+ if positional_filename:
62
+ filename = positional_filename
63
+ else:
64
+ print("Error: -f flag used but no filename provided", file=sys.stderr)
65
+ sys.exit(1)
66
+ return filename
@@ -1,12 +1,15 @@
1
1
  # pylint: disable=C0114, C0116, E0611, R0913, R0917
2
2
  import os
3
3
  import sys
4
- from PySide6.QtCore import QCoreApplication, QProcess
5
- from PySide6.QtGui import QAction
4
+ import logging
5
+ from PySide6.QtCore import Qt, QCoreApplication, QProcess
6
+ from PySide6.QtGui import QAction, QIcon
6
7
  from shinestacker.config.constants import constants
7
8
  from shinestacker.config.config import config
9
+ from shinestacker.config.settings import StdPathFile
8
10
  from shinestacker.app.about_dialog import show_about_dialog
9
11
  from shinestacker.app.settings_dialog import show_settings_dialog
12
+ from shinestacker.core.logging import setup_logging
10
13
 
11
14
 
12
15
  def disable_macos_special_menu_items():
@@ -71,3 +74,17 @@ def set_css_style(app):
71
74
  }
72
75
  """
73
76
  app.setStyleSheet(css_style)
77
+
78
+
79
+ def make_app(application_class):
80
+ setup_logging(console_level=logging.DEBUG, file_level=logging.DEBUG, disable_console=True,
81
+ log_file=StdPathFile('shinestacker.log').get_file_path())
82
+ app = application_class(sys.argv)
83
+ if config.DONT_USE_NATIVE_MENU:
84
+ app.setAttribute(Qt.AA_DontUseNativeMenuBar)
85
+ else:
86
+ disable_macos_special_menu_items()
87
+ icon_path = f"{os.path.dirname(__file__)}/../gui/ico/shinestacker.png"
88
+ app.setWindowIcon(QIcon(icon_path))
89
+ set_css_style(app)
90
+ return app
@@ -1,27 +1,26 @@
1
1
  # pylint: disable=C0114, C0115, C0116, C0413, E0611, R0903, E1121, W0201, R0915, R0912
2
2
  import sys
3
- import os
4
- import logging
5
3
  import argparse
6
4
  import matplotlib
7
5
  import matplotlib.backends.backend_pdf
8
6
  matplotlib.use('agg')
9
7
  from PySide6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QStackedWidget,
10
8
  QMenu, QMessageBox, QDialog, QLabel, QListWidget, QPushButton)
11
- from PySide6.QtGui import QAction, QIcon, QGuiApplication
12
- from PySide6.QtCore import Qt, QEvent, QTimer, Signal
9
+ from PySide6.QtGui import QAction, QGuiApplication
10
+ from PySide6.QtCore import QEvent, QTimer, Signal
13
11
  from shinestacker.config.config import config
14
12
  config.init(DISABLE_TQDM=True, COMBINED_APP=True, DONT_USE_NATIVE_MENU=True)
15
13
  from shinestacker.config.constants import constants
16
- from shinestacker.config.settings import StdPathFile
17
- from shinestacker.core.logging import setup_logging
18
14
  from shinestacker.gui.main_window import MainWindow
19
15
  from shinestacker.retouch.image_editor_ui import ImageEditorUI
20
- from shinestacker.app.gui_utils import (
21
- disable_macos_special_menu_items, fill_app_menu, set_css_style)
16
+ from shinestacker.app.gui_utils import fill_app_menu
22
17
  from shinestacker.app.help_menu import add_help_action
23
18
  from shinestacker.app.open_frames import open_frames
24
- from shinestacker.app.args_parser_opts import add_project_arguments, add_retouch_arguments
19
+ from shinestacker.app.args_parser_opts import (
20
+ add_project_arguments, add_retouch_arguments, extract_positional_filename,
21
+ setup_filename_argument, process_filename_argument
22
+ )
23
+ from shinestacker.app.gui_utils import make_app
25
24
 
26
25
 
27
26
  class SelectionDialog(QDialog):
@@ -206,15 +205,12 @@ class Application(QApplication):
206
205
 
207
206
 
208
207
  def main():
208
+ positional_filename, filtered_args = extract_positional_filename()
209
209
  parser = argparse.ArgumentParser(
210
- prog=f'{constants.APP_STRING.lower()}-retouch',
210
+ prog=f'{constants.APP_STRING.lower()}',
211
211
  description='Focus stacking App.',
212
212
  epilog=f'This app is part of the {constants.APP_STRING} package.')
213
- parser.add_argument('-f', '--filename', nargs='?', help='''
214
- if a single file is specified, it can be either a project or an image.
215
- Multiple frames can be specified as a list of files.
216
- Multiple files can be specified separated by ';'.
217
- ''')
213
+ setup_filename_argument(parser, use_const=True)
218
214
  app_group = parser.add_mutually_exclusive_group()
219
215
  app_group.add_argument('-j', '--project', action='store_true', help='''
220
216
  open project window at startup instead of project windows (default).
@@ -224,24 +220,17 @@ open retouch window at startup instead of project windows.
224
220
  ''')
225
221
  add_project_arguments(parser)
226
222
  add_retouch_arguments(parser)
227
- args = vars(parser.parse_args(sys.argv[1:]))
228
- filename = args['filename']
223
+ args = vars(parser.parse_args(filtered_args))
224
+ filename = process_filename_argument(args, positional_filename)
229
225
  path = args['path']
230
226
  if filename and path:
231
227
  print("can't specify both arguments --filename and --path", file=sys.stderr)
232
228
  sys.exit(1)
233
- setup_logging(console_level=logging.DEBUG, file_level=logging.DEBUG, disable_console=True,
234
- log_file=StdPathFile('shinestacker.log').get_file_path())
235
- app = Application(sys.argv)
236
- if config.DONT_USE_NATIVE_MENU:
237
- app.setAttribute(Qt.AA_DontUseNativeMenuBar)
238
- else:
239
- disable_macos_special_menu_items()
240
- icon_path = f"{os.path.dirname(__file__)}/../gui/ico/shinestacker.png"
241
- app.setWindowIcon(QIcon(icon_path))
229
+
230
+ app = make_app(Application)
242
231
  main_app = MainApp()
243
232
  app.main_app = main_app
244
- set_css_style(app)
233
+
245
234
  main_app.show()
246
235
  main_app.activateWindow()
247
236
  if args['expert']: