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.
- {shinestacker-1.6.1 → shinestacker-1.7.0}/.github/workflows/release.yml +10 -1
- {shinestacker-1.6.1 → shinestacker-1.7.0}/CHANGELOG.md +21 -3
- {shinestacker-1.6.1/src/shinestacker.egg-info → shinestacker-1.7.0}/PKG-INFO +2 -2
- {shinestacker-1.6.1 → shinestacker-1.7.0}/README.md +1 -1
- shinestacker-1.7.0/scripts/build_release.py +215 -0
- shinestacker-1.7.0/scripts/create_macos_icon.py +58 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0/scripts}/shinestacker-inno-setup.iss +10 -6
- shinestacker-1.7.0/src/shinestacker/_version.py +1 -0
- shinestacker-1.7.0/src/shinestacker/algorithms/corrections.py +26 -0
- shinestacker-1.7.0/src/shinestacker/app/args_parser_opts.py +66 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/app/gui_utils.py +19 -2
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/app/main.py +16 -27
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/app/project.py +12 -23
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/app/retouch.py +12 -25
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/core/core_utils.py +1 -2
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/core/logging.py +2 -2
- shinestacker-1.7.0/src/shinestacker/gui/ico/shinestacker.icns +0 -0
- shinestacker-1.7.0/src/shinestacker/gui/ico/shinestacker_bkg.png +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/tab_widget.py +1 -5
- shinestacker-1.7.0/src/shinestacker/retouch/adjustments.py +93 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/base_filter.py +62 -7
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/denoise_filter.py +1 -1
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/image_editor_ui.py +26 -4
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/unsharp_mask_filter.py +13 -28
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/vignetting_filter.py +1 -1
- {shinestacker-1.6.1 → shinestacker-1.7.0/src/shinestacker.egg-info}/PKG-INFO +2 -2
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker.egg-info/SOURCES.txt +4 -1
- shinestacker-1.6.1/scripts/build_release.py +0 -112
- shinestacker-1.6.1/src/shinestacker/_version.py +0 -1
- shinestacker-1.6.1/src/shinestacker/app/args_parser_opts.py +0 -27
- shinestacker-1.6.1/src/shinestacker/gui/ico/focus_stack_bkg.png +0 -0
- shinestacker-1.6.1/src/shinestacker/gui/ico/shinestacker.icns +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/.coveragerc +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/.flake8 +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/.github/workflows/ci-multiplatform.yml +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/.github/workflows/pylint.yml +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/.github/workflows/pypi-publish.yml +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/.gitignore +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/.pylintrc +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/.readthedocs.yaml +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/LICENSE +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/MANIFEST.in +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/THIRD_PARTY_LICENSES.txt +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/docs/alignment.md +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/docs/api.md +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/docs/balancing.md +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/docs/conf.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/docs/focus_stacking.md +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/docs/gui.md +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/docs/index.md +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/docs/job.md +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/docs/main.md +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/docs/multilayer.md +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/docs/noise.md +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/docs/requirements.txt +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/docs/vignetting.md +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/img/coffee.gif +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/img/coffee_stack.jpg +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/img/extreme-vignetting.jpg +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/img/flies.gif +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/img/flies_stack.jpg +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/img/flow-diagram.png +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/img/gui-finder.png +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/img/gui-project-new.png +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/img/gui-project-run.png +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/img/gui-retouch.png +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/index.html +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/pyproject.toml +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/requirements.txt +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/scripts/git-rev-list.sh +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/scripts/hooks/hook-IPython.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/scripts/hooks/hook-PySide6.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/scripts/hooks/hook-opencv.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/scripts/hooks/hook-tests.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/scripts/scan_imports.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/scripts/validate-tomli.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/setup.cfg +0 -0
- {shinestacker-1.6.1/scripts → shinestacker-1.7.0}/shinestacker-inno-setup.iss +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/__init__.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/__init__.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/align.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/align_auto.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/align_parallel.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/balance.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/base_stack_algo.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/denoise.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/depth_map.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/exif.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/multilayer.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/noise_detection.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/pyramid.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/pyramid_auto.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/pyramid_tiles.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/sharpen.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/stack.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/stack_framework.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/utils.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/vignetting.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/algorithms/white_balance.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/app/__init__.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/app/about_dialog.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/app/help_menu.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/app/open_frames.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/app/settings_dialog.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/config/__init__.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/config/app_config.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/config/config.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/config/constants.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/config/gui_constants.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/config/settings.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/core/__init__.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/core/colors.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/core/exceptions.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/core/framework.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/__init__.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/action_config.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/action_config_dialog.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/base_form_dialog.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/colors.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/config_dialog.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/flow_layout.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/folder_file_selection.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/gui_images.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/gui_logging.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/gui_run.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/ico/shinestacker.ico +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/ico/shinestacker.png +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/ico/shinestacker.svg +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/img/close-round-line-icon.png +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/img/forward-button-icon.png +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/img/play-button-round-icon.png +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/img/plus-round-line-icon.png +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/main_window.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/menu_manager.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/new_project.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/project_controller.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/project_converter.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/project_editor.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/project_model.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/recent_file_manager.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/select_path_widget.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/sys_mon.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/gui/time_progress_bar.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/__init__.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/brush.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/brush_gradient.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/brush_preview.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/brush_tool.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/display_manager.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/exif_data.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/file_loader.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/filter_manager.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/icon_container.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/image_view_status.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/image_viewer.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/io_gui_handler.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/io_threads.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/layer_collection.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/overlaid_view.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/paint_area_manager.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/shortcuts_help.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/sidebyside_view.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/transformation_manager.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/undo_manager.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/view_strategy.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker/retouch/white_balance_filter.py +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker.egg-info/dependency_links.txt +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker.egg-info/entry_points.txt +0 -0
- {shinestacker-1.6.1 → shinestacker-1.7.0}/src/shinestacker.egg-info/requires.txt +0 -0
- {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' || '
|
|
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
|
-
**
|
|
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
|
-
-
|
|
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.
|
|
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 "
|
|
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
|
|
37
|
+
OutputDir=dist
|
|
38
38
|
VersionInfoVersion={#MyAppVersion}
|
|
39
39
|
VersionInfoCompany={#MyAppPublisher}
|
|
40
|
-
SetupIconFile
|
|
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: "
|
|
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
|
-
|
|
5
|
-
from PySide6.
|
|
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,
|
|
12
|
-
from PySide6.QtCore import
|
|
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
|
|
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()}
|
|
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
|
|
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(
|
|
228
|
-
filename = args
|
|
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
|
-
|
|
234
|
-
|
|
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
|
-
|
|
233
|
+
|
|
245
234
|
main_app.show()
|
|
246
235
|
main_app.activateWindow()
|
|
247
236
|
if args['expert']:
|