shinestacker 1.5.2__py3-none-any.whl → 1.5.4__py3-none-any.whl
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/_version.py +1 -1
- shinestacker/app/main.py +1 -1
- shinestacker/app/project.py +1 -1
- shinestacker/app/retouch.py +1 -1
- shinestacker/config/gui_constants.py +1 -1
- shinestacker/gui/main_window.py +10 -0
- shinestacker/gui/menu_manager.py +14 -10
- shinestacker/gui/project_controller.py +3 -0
- shinestacker/retouch/image_editor_ui.py +45 -14
- shinestacker/retouch/io_gui_handler.py +3 -0
- shinestacker/retouch/overlaid_view.py +6 -43
- shinestacker/retouch/sidebyside_view.py +35 -29
- shinestacker/retouch/view_strategy.py +181 -63
- shinestacker/retouch/white_balance_filter.py +5 -6
- {shinestacker-1.5.2.dist-info → shinestacker-1.5.4.dist-info}/METADATA +1 -1
- {shinestacker-1.5.2.dist-info → shinestacker-1.5.4.dist-info}/RECORD +21 -21
- /shinestacker/app/{args.py → args_parser_opts.py} +0 -0
- {shinestacker-1.5.2.dist-info → shinestacker-1.5.4.dist-info}/WHEEL +0 -0
- {shinestacker-1.5.2.dist-info → shinestacker-1.5.4.dist-info}/entry_points.txt +0 -0
- {shinestacker-1.5.2.dist-info → shinestacker-1.5.4.dist-info}/licenses/LICENSE +0 -0
- {shinestacker-1.5.2.dist-info → shinestacker-1.5.4.dist-info}/top_level.txt +0 -0
shinestacker/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = '1.5.
|
|
1
|
+
__version__ = '1.5.4'
|
shinestacker/app/main.py
CHANGED
|
@@ -20,7 +20,7 @@ from shinestacker.app.gui_utils import (
|
|
|
20
20
|
disable_macos_special_menu_items, fill_app_menu, set_css_style)
|
|
21
21
|
from shinestacker.app.help_menu import add_help_action
|
|
22
22
|
from shinestacker.app.open_frames import open_frames
|
|
23
|
-
from .
|
|
23
|
+
from shinestacker.app.args_parser_opts import add_project_arguments, add_retouch_arguments
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
class SelectionDialog(QDialog):
|
shinestacker/app/project.py
CHANGED
|
@@ -17,7 +17,7 @@ from shinestacker.gui.main_window import MainWindow
|
|
|
17
17
|
from shinestacker.app.gui_utils import (
|
|
18
18
|
disable_macos_special_menu_items, fill_app_menu, set_css_style)
|
|
19
19
|
from shinestacker.app.help_menu import add_help_action
|
|
20
|
-
from .
|
|
20
|
+
from shinestacker.app.args_parser_opts import add_project_arguments
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class ProjectApp(MainWindow):
|
shinestacker/app/retouch.py
CHANGED
|
@@ -13,7 +13,7 @@ from shinestacker.app.gui_utils import (
|
|
|
13
13
|
disable_macos_special_menu_items, fill_app_menu, set_css_style)
|
|
14
14
|
from shinestacker.app.help_menu import add_help_action
|
|
15
15
|
from shinestacker.app.open_frames import open_frames
|
|
16
|
-
from .
|
|
16
|
+
from shinestacker.app.args_parser_opts import add_retouch_arguments
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class RetouchApp(ImageEditorUI):
|
|
@@ -26,7 +26,7 @@ class _GuiConstants:
|
|
|
26
26
|
'outer': (255, 0, 0, 200),
|
|
27
27
|
'inner': (255, 0, 0, 150),
|
|
28
28
|
'gradient_end': (255, 0, 0, 0),
|
|
29
|
-
'pen': (255,
|
|
29
|
+
'pen': (255, 255, 255, 200),
|
|
30
30
|
'preview': (255, 180, 180),
|
|
31
31
|
'cursor_inner': (255, 0, 0, 120),
|
|
32
32
|
'preview_inner': (255, 255, 255, 150)
|
shinestacker/gui/main_window.py
CHANGED
|
@@ -154,9 +154,14 @@ class MainWindow(QMainWindow, LogManager):
|
|
|
154
154
|
self.menu_manager.set_enabled_sub_actions_gui)
|
|
155
155
|
self.project_controller.add_recent_file_requested.connect(
|
|
156
156
|
self.menu_manager.add_recent_file)
|
|
157
|
+
self.project_controller.set_enabled_file_open_close_actions_requested.connect(
|
|
158
|
+
self.set_enabled_file_open_close_actions)
|
|
159
|
+
|
|
157
160
|
self.menu_manager.open_file_requested.connect(
|
|
158
161
|
self.project_controller.open_project)
|
|
159
162
|
|
|
163
|
+
self.set_enabled_file_open_close_actions(False)
|
|
164
|
+
|
|
160
165
|
def modified(self):
|
|
161
166
|
return self.project_editor.modified()
|
|
162
167
|
|
|
@@ -571,3 +576,8 @@ class MainWindow(QMainWindow, LogManager):
|
|
|
571
576
|
self.menu_manager.set_enabled_sub_actions_gui(enable_sub_actions)
|
|
572
577
|
else:
|
|
573
578
|
self.menu_manager.set_enabled_sub_actions_gui(False)
|
|
579
|
+
|
|
580
|
+
def set_enabled_file_open_close_actions(self, enabled):
|
|
581
|
+
for action in self.findChildren(QAction):
|
|
582
|
+
if action.property("requires_file"):
|
|
583
|
+
action.setEnabled(enabled)
|
shinestacker/gui/menu_manager.py
CHANGED
|
@@ -60,8 +60,10 @@ class MenuManager(QObject):
|
|
|
60
60
|
def get_icon(self, icon):
|
|
61
61
|
return QIcon(os.path.join(self.script_dir, f"img/{icon}.png"))
|
|
62
62
|
|
|
63
|
-
def action(self, name):
|
|
63
|
+
def action(self, name, requires_file=False):
|
|
64
64
|
action = QAction(name, self.parent)
|
|
65
|
+
if requires_file:
|
|
66
|
+
action.setProperty("requires_file", True)
|
|
65
67
|
shortcut = self.shortcuts.get(name, '')
|
|
66
68
|
if shortcut:
|
|
67
69
|
action.setShortcut(shortcut)
|
|
@@ -110,20 +112,20 @@ class MenuManager(QObject):
|
|
|
110
112
|
self.undo_action.setEnabled(False)
|
|
111
113
|
menu.addAction(self.undo_action)
|
|
112
114
|
for name in ["&Cut", "Cop&y", "&Paste", "Duplicate"]:
|
|
113
|
-
menu.addAction(self.action(name))
|
|
114
|
-
self.delete_element_action = self.action("Delete")
|
|
115
|
+
menu.addAction(self.action(name, requires_file=True))
|
|
116
|
+
self.delete_element_action = self.action("Delete", requires_file=True)
|
|
115
117
|
self.delete_element_action.setEnabled(False)
|
|
116
118
|
menu.addAction(self.delete_element_action)
|
|
117
119
|
menu.addSeparator()
|
|
118
120
|
for name in ["Move &Up", "Move &Down"]:
|
|
119
|
-
menu.addAction(self.action(name))
|
|
121
|
+
menu.addAction(self.action(name, requires_file=True))
|
|
120
122
|
menu.addSeparator()
|
|
121
|
-
self.enable_action = self.action("E&nable")
|
|
123
|
+
self.enable_action = self.action("E&nable", requires_file=True)
|
|
122
124
|
menu.addAction(self.enable_action)
|
|
123
|
-
self.disable_action = self.action("Di&sable")
|
|
125
|
+
self.disable_action = self.action("Di&sable", requires_file=True)
|
|
124
126
|
menu.addAction(self.disable_action)
|
|
125
127
|
for name in ["Enable All", "Disable All"]:
|
|
126
|
-
menu.addAction(self.action(name))
|
|
128
|
+
menu.addAction(self.action(name, requires_file=True))
|
|
127
129
|
|
|
128
130
|
def add_view_menu(self):
|
|
129
131
|
menu = self.menubar.addMenu("&View")
|
|
@@ -133,13 +135,13 @@ class MenuManager(QObject):
|
|
|
133
135
|
|
|
134
136
|
def add_job_menu(self):
|
|
135
137
|
menu = self.menubar.addMenu("&Jobs")
|
|
136
|
-
self.add_job_action = self.action("Add Job")
|
|
138
|
+
self.add_job_action = self.action("Add Job", requires_file=True)
|
|
137
139
|
menu.addAction(self.add_job_action)
|
|
138
140
|
menu.addSeparator()
|
|
139
|
-
self.run_job_action = self.action("Run Job")
|
|
141
|
+
self.run_job_action = self.action("Run Job", requires_file=True)
|
|
140
142
|
self.run_job_action.setEnabled(False)
|
|
141
143
|
menu.addAction(self.run_job_action)
|
|
142
|
-
self.run_all_jobs_action = self.action("Run All Jobs")
|
|
144
|
+
self.run_all_jobs_action = self.action("Run All Jobs", requires_file=True)
|
|
143
145
|
self.set_enabled_run_all_jobs(False)
|
|
144
146
|
menu.addAction(self.run_all_jobs_action)
|
|
145
147
|
|
|
@@ -148,6 +150,7 @@ class MenuManager(QObject):
|
|
|
148
150
|
add_action_menu = QMenu("Add Action", self.parent)
|
|
149
151
|
for action in constants.ACTION_TYPES:
|
|
150
152
|
entry_action = QAction(action, self.parent)
|
|
153
|
+
entry_action.setProperty("requires_file", True)
|
|
151
154
|
entry_action.triggered.connect({
|
|
152
155
|
constants.ACTION_COMBO: self.add_action_combined_actions,
|
|
153
156
|
constants.ACTION_NOISEDETECTION: self.add_action_noise_detection,
|
|
@@ -161,6 +164,7 @@ class MenuManager(QObject):
|
|
|
161
164
|
self.sub_action_menu_entries = []
|
|
162
165
|
for action in constants.SUB_ACTION_TYPES:
|
|
163
166
|
entry_action = QAction(action, self.parent)
|
|
167
|
+
entry_action.setProperty("requires_file", True)
|
|
164
168
|
entry_action.triggered.connect({
|
|
165
169
|
constants.ACTION_MASKNOISE: self.add_sub_action_make_noise,
|
|
166
170
|
constants.ACTION_VIGNETTING: self.add_sub_action_vignetting,
|
|
@@ -21,6 +21,7 @@ class ProjectController(QObject):
|
|
|
21
21
|
enable_save_actions_requested = Signal(bool)
|
|
22
22
|
enable_sub_actions_requested = Signal(bool)
|
|
23
23
|
add_recent_file_requested = Signal(str)
|
|
24
|
+
set_enabled_file_open_close_actions_requested = Signal(bool)
|
|
24
25
|
|
|
25
26
|
def __init__(self, parent):
|
|
26
27
|
super().__init__(parent)
|
|
@@ -138,6 +139,7 @@ class ProjectController(QObject):
|
|
|
138
139
|
self.clear_action_list()
|
|
139
140
|
self.mark_as_modified(False)
|
|
140
141
|
self.project_editor.reset_undo()
|
|
142
|
+
self.set_enabled_file_open_close_actions_requested.emit(False)
|
|
141
143
|
|
|
142
144
|
def new_project(self):
|
|
143
145
|
if not self.check_unsaved_changes():
|
|
@@ -250,6 +252,7 @@ class ProjectController(QObject):
|
|
|
250
252
|
project = Project.from_dict(json_obj['project'])
|
|
251
253
|
if project is None:
|
|
252
254
|
raise RuntimeError(f"Project from file {file_path} produced a null project.")
|
|
255
|
+
self.set_enabled_file_open_close_actions_requested.emit(True)
|
|
253
256
|
self.set_project(project)
|
|
254
257
|
self.mark_as_modified(False)
|
|
255
258
|
self.add_recent_file_requested.emit(abs_file_path)
|
|
@@ -229,6 +229,8 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
|
|
|
229
229
|
self.io_gui_handler.mark_as_modified_requested.connect(self.mark_as_modified)
|
|
230
230
|
self.io_gui_handler.change_layer_requested.connect(self.change_layer)
|
|
231
231
|
self.io_gui_handler.add_recent_file_requested.connect(self.add_recent_file)
|
|
232
|
+
self.io_gui_handler.set_enabled_file_open_close_actions_requested.connect(
|
|
233
|
+
self.set_enabled_file_open_close_actions)
|
|
232
234
|
self.brush_tool.setup_ui(self.brush, self.brush_preview_widget, self.image_viewer,
|
|
233
235
|
self.brush_size_slider, self.hardness_slider, self.opacity_slider,
|
|
234
236
|
self.flow_slider)
|
|
@@ -277,22 +279,26 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
|
|
|
277
279
|
|
|
278
280
|
transf_menu = QMenu("&Transform")
|
|
279
281
|
rotate_90_cw_action = QAction(gui_constants.ROTATE_90_CW_LABEL, self)
|
|
280
|
-
|
|
282
|
+
rotate_90_cw_action.setProperty("requires_file", True)
|
|
281
283
|
rotate_90_cw_action.triggered.connect(lambda: self.transformation_manager.rotate_90_cw())
|
|
284
|
+
transf_menu.addAction(rotate_90_cw_action)
|
|
282
285
|
rotate_90_ccw_action = QAction(gui_constants.ROTATE_90_CCW_LABEL, self)
|
|
283
|
-
|
|
286
|
+
rotate_90_ccw_action.setProperty("requires_file", True)
|
|
284
287
|
rotate_90_ccw_action.triggered.connect(lambda: self.transformation_manager.rotate_90_ccw())
|
|
288
|
+
transf_menu.addAction(rotate_90_ccw_action)
|
|
285
289
|
rotate_180_action = QAction(gui_constants.ROTATE_180_LABEL, self)
|
|
286
290
|
rotate_180_action.triggered.connect(lambda: self.transformation_manager.rotate_180())
|
|
291
|
+
rotate_180_action.setProperty("requires_file", True)
|
|
287
292
|
transf_menu.addAction(rotate_180_action)
|
|
288
293
|
edit_menu.addMenu(transf_menu)
|
|
289
294
|
|
|
290
295
|
edit_menu.addSeparator()
|
|
291
296
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
297
|
+
copy_current_to_master_action = QAction("Copy Current Layer to Master", self)
|
|
298
|
+
copy_current_to_master_action.setShortcut("Ctrl+M")
|
|
299
|
+
copy_current_to_master_action.setProperty("requires_file", True)
|
|
300
|
+
copy_current_to_master_action.triggered.connect(self.copy_layer_to_master)
|
|
301
|
+
edit_menu.addAction(copy_current_to_master_action)
|
|
296
302
|
|
|
297
303
|
view_menu = menubar.addMenu("&View")
|
|
298
304
|
|
|
@@ -351,15 +357,18 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
|
|
|
351
357
|
}
|
|
352
358
|
brush_action = self.cursor_style_actions['brush']
|
|
353
359
|
brush_action.setCheckable(True)
|
|
360
|
+
brush_action.setProperty("requires_file", True)
|
|
354
361
|
brush_action.triggered.connect(lambda: set_cursor_style('brush'))
|
|
355
362
|
cursor_menu.addAction(brush_action)
|
|
356
363
|
|
|
357
364
|
preview_action = self.cursor_style_actions['preview']
|
|
365
|
+
preview_action.setProperty("requires_file", True)
|
|
358
366
|
preview_action.setCheckable(True)
|
|
359
367
|
preview_action.triggered.connect(lambda: set_cursor_style('preview'))
|
|
360
368
|
cursor_menu.addAction(preview_action)
|
|
361
369
|
|
|
362
370
|
outline_action = self.cursor_style_actions['outline']
|
|
371
|
+
outline_action.setProperty("requires_file", True)
|
|
363
372
|
outline_action.setCheckable(True)
|
|
364
373
|
outline_action.triggered.connect(lambda: set_cursor_style('outline'))
|
|
365
374
|
cursor_menu.addAction(outline_action)
|
|
@@ -382,37 +391,44 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
|
|
|
382
391
|
|
|
383
392
|
zoom_in_action = QAction("Zoom In", self)
|
|
384
393
|
zoom_in_action.setShortcut("Ctrl++")
|
|
394
|
+
zoom_in_action.setProperty("requires_file", True)
|
|
385
395
|
zoom_in_action.triggered.connect(self.image_viewer.zoom_in)
|
|
386
396
|
view_menu.addAction(zoom_in_action)
|
|
387
397
|
|
|
388
398
|
zoom_out_action = QAction("Zoom Out", self)
|
|
389
399
|
zoom_out_action.setShortcut("Ctrl+-")
|
|
400
|
+
zoom_out_action.setProperty("requires_file", True)
|
|
390
401
|
zoom_out_action.triggered.connect(self.image_viewer.zoom_out)
|
|
391
402
|
view_menu.addAction(zoom_out_action)
|
|
392
403
|
|
|
393
404
|
adapt_action = QAction("Adapt to Screen", self)
|
|
394
405
|
adapt_action.setShortcut("Ctrl+0")
|
|
406
|
+
adapt_action.setProperty("requires_file", True)
|
|
395
407
|
adapt_action.triggered.connect(self.image_viewer.reset_zoom)
|
|
396
408
|
view_menu.addAction(adapt_action)
|
|
397
409
|
|
|
398
410
|
actual_size_action = QAction("Actual Size", self)
|
|
399
411
|
actual_size_action.setShortcut("Ctrl+R")
|
|
412
|
+
actual_size_action.setProperty("requires_file", True)
|
|
400
413
|
actual_size_action.triggered.connect(self.image_viewer.actual_size)
|
|
401
414
|
view_menu.addAction(actual_size_action)
|
|
402
415
|
view_menu.addSeparator()
|
|
403
416
|
|
|
404
417
|
self.view_master_action = QAction("View Master", self)
|
|
405
418
|
self.view_master_action.setShortcut("M")
|
|
419
|
+
self.view_master_action.setProperty("requires_file", True)
|
|
406
420
|
self.view_master_action.triggered.connect(self.set_view_master)
|
|
407
421
|
view_menu.addAction(self.view_master_action)
|
|
408
422
|
|
|
409
423
|
self.view_individual_action = QAction("View Individual", self)
|
|
410
424
|
self.view_individual_action.setShortcut("L")
|
|
425
|
+
self.view_individual_action.setProperty("requires_file", True)
|
|
411
426
|
self.view_individual_action.triggered.connect(self.set_view_individual)
|
|
412
427
|
view_menu.addAction(self.view_individual_action)
|
|
413
428
|
|
|
414
429
|
self.toggle_view_master_individual_action = QAction("Toggle Master/Individual", self)
|
|
415
430
|
self.toggle_view_master_individual_action.setShortcut("T")
|
|
431
|
+
self.toggle_view_master_individual_action.setProperty("requires_file", True)
|
|
416
432
|
self.toggle_view_master_individual_action.triggered.connect(
|
|
417
433
|
self.toggle_view_master_individual)
|
|
418
434
|
view_menu.addAction(self.toggle_view_master_individual_action)
|
|
@@ -421,11 +437,13 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
|
|
|
421
437
|
self.set_strategy('overlaid')
|
|
422
438
|
|
|
423
439
|
sort_asc_action = QAction("Sort Layers A-Z", self)
|
|
424
|
-
sort_asc_action.
|
|
440
|
+
sort_asc_action.setProperty("requires_file", True)
|
|
441
|
+
sort_asc_action.triggered.connect(lambda: self.sort_layers_ui('asc'))
|
|
425
442
|
view_menu.addAction(sort_asc_action)
|
|
426
443
|
|
|
427
444
|
sort_desc_action = QAction("Sort Layers Z-A", self)
|
|
428
|
-
sort_desc_action.
|
|
445
|
+
sort_desc_action.setProperty("requires_file", True)
|
|
446
|
+
sort_desc_action.triggered.connect(lambda: self.sort_layers_ui('desc'))
|
|
429
447
|
view_menu.addAction(sort_desc_action)
|
|
430
448
|
|
|
431
449
|
view_menu.addSeparator()
|
|
@@ -433,15 +451,19 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
|
|
|
433
451
|
filter_menu = menubar.addMenu("&Filter")
|
|
434
452
|
filter_menu.setObjectName("Filter")
|
|
435
453
|
denoise_action = QAction("Denoise", self)
|
|
454
|
+
denoise_action.setProperty("requires_file", True)
|
|
436
455
|
denoise_action.triggered.connect(self.denoise_filter)
|
|
437
456
|
filter_menu.addAction(denoise_action)
|
|
438
457
|
unsharp_mask_action = QAction("Unsharp Mask", self)
|
|
458
|
+
unsharp_mask_action.setProperty("requires_file", True)
|
|
439
459
|
unsharp_mask_action.triggered.connect(self.unsharp_mask)
|
|
440
460
|
filter_menu.addAction(unsharp_mask_action)
|
|
441
461
|
white_balance_action = QAction("White Balance", self)
|
|
462
|
+
white_balance_action.setProperty("requires_file", True)
|
|
442
463
|
white_balance_action.triggered.connect(self.white_balance)
|
|
443
464
|
filter_menu.addAction(white_balance_action)
|
|
444
465
|
vignetting_action = QAction("Vignetting Correction", self)
|
|
466
|
+
vignetting_action.setProperty("requires_file", True)
|
|
445
467
|
vignetting_action.triggered.connect(self.vignetting_correction)
|
|
446
468
|
filter_menu.addAction(vignetting_action)
|
|
447
469
|
|
|
@@ -462,20 +484,29 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
|
|
|
462
484
|
prev_layer.activated.connect(self.prev_layer)
|
|
463
485
|
next_layer = QShortcut(QKeySequence(Qt.Key_Down), self, context=Qt.ApplicationShortcut)
|
|
464
486
|
next_layer.activated.connect(self.next_layer)
|
|
487
|
+
|
|
488
|
+
self.set_enabled_file_open_close_actions(False)
|
|
465
489
|
self.installEventFilter(self)
|
|
466
490
|
|
|
491
|
+
def set_enabled_view_toggles(self, enabled):
|
|
492
|
+
self.view_master_action.setEnabled(enabled)
|
|
493
|
+
self.view_individual_action.setEnabled(enabled)
|
|
494
|
+
self.toggle_view_master_individual_action.setEnabled(enabled)
|
|
495
|
+
|
|
467
496
|
def set_strategy(self, strategy):
|
|
468
497
|
self.image_viewer.set_strategy(strategy)
|
|
469
|
-
enable_shortcuts = strategy == 'overlaid'
|
|
470
498
|
self.display_manager.view_mode = 'master'
|
|
471
499
|
self.highlight_master_thumbnail(gui_constants.THUMB_MASTER_HI_COLOR)
|
|
472
|
-
self.
|
|
473
|
-
self.view_individual_action.setEnabled(enable_shortcuts)
|
|
474
|
-
self.toggle_view_master_individual_action.setEnabled(enable_shortcuts)
|
|
500
|
+
self.set_enabled_view_toggles(strategy == 'overlaid')
|
|
475
501
|
for label, mode in self.view_mode_actions.items():
|
|
476
502
|
mode.setEnabled(label != strategy)
|
|
477
503
|
mode.setChecked(label == strategy)
|
|
478
504
|
|
|
505
|
+
def set_enabled_file_open_close_actions(self, enabled):
|
|
506
|
+
for action in self.findChildren(QAction):
|
|
507
|
+
if action.property("requires_file"):
|
|
508
|
+
action.setEnabled(enabled)
|
|
509
|
+
|
|
479
510
|
def update_title(self):
|
|
480
511
|
title = constants.APP_TITLE
|
|
481
512
|
if self.io_gui_handler is not None:
|
|
@@ -544,10 +575,10 @@ class ImageEditorUI(QMainWindow, LayerCollectionHandler):
|
|
|
544
575
|
def save_master_only(self, _checked):
|
|
545
576
|
self.update_title()
|
|
546
577
|
|
|
547
|
-
def
|
|
578
|
+
def sort_layers_ui(self, order):
|
|
548
579
|
self.sort_layers(order)
|
|
549
580
|
self.display_manager.update_thumbnails()
|
|
550
|
-
self.change_layer(self.
|
|
581
|
+
self.change_layer(self.current_layer_idx())
|
|
551
582
|
|
|
552
583
|
def change_layer(self, layer_idx):
|
|
553
584
|
if 0 <= layer_idx < self.number_of_layers():
|
|
@@ -17,6 +17,7 @@ class IOGuiHandler(QObject, LayerCollectionHandler):
|
|
|
17
17
|
mark_as_modified_requested = Signal(bool)
|
|
18
18
|
change_layer_requested = Signal(int)
|
|
19
19
|
add_recent_file_requested = Signal(str)
|
|
20
|
+
set_enabled_file_open_close_actions_requested = Signal(bool)
|
|
20
21
|
|
|
21
22
|
def __init__(self, layer_collection, undo_manager, parent):
|
|
22
23
|
QObject.__init__(self, parent)
|
|
@@ -165,6 +166,7 @@ class IOGuiHandler(QObject, LayerCollectionHandler):
|
|
|
165
166
|
self.change_layer_requested.emit(0)
|
|
166
167
|
self.status_message_requested.emit(message)
|
|
167
168
|
self.update_title_requested.emit()
|
|
169
|
+
self.set_enabled_file_open_close_actions_requested.emit(True)
|
|
168
170
|
self.add_recent_file_requested.emit(self.current_file_path_master)
|
|
169
171
|
|
|
170
172
|
def save_file(self):
|
|
@@ -277,4 +279,5 @@ class IOGuiHandler(QObject, LayerCollectionHandler):
|
|
|
277
279
|
self.display_manager.thumbnail_list.clear()
|
|
278
280
|
self.display_manager.update_thumbnails()
|
|
279
281
|
self.update_title_requested.emit()
|
|
282
|
+
self.set_enabled_file_open_close_actions_requested.emit(False)
|
|
280
283
|
self.status_message_requested.emit("File closed")
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# pylint: disable=C0114, C0115, C0116, E0611, E1101, R0904, R0912, R0914, R0902, E0202
|
|
2
2
|
from PySide6.QtCore import Qt, QPointF, QEvent, QRectF
|
|
3
|
-
from .. config.gui_constants import gui_constants
|
|
4
3
|
from .view_strategy import ViewStrategy, ImageGraphicsViewBase, ViewSignals
|
|
5
4
|
|
|
6
5
|
|
|
@@ -61,41 +60,9 @@ class OverlaidView(ViewStrategy, ImageGraphicsViewBase, ViewSignals):
|
|
|
61
60
|
self.mouse_release_event(event)
|
|
62
61
|
super().mouseReleaseEvent(event)
|
|
63
62
|
|
|
64
|
-
# pylint: enable=R0801
|
|
65
63
|
def wheelEvent(self, event):
|
|
66
|
-
|
|
67
|
-
return
|
|
68
|
-
if event.source() == Qt.MouseEventNotSynthesized: # Physical mouse
|
|
69
|
-
if self.control_pressed:
|
|
70
|
-
self.brush_size_change_requested.emit(1 if event.angleDelta().y() > 0 else -1)
|
|
71
|
-
else:
|
|
72
|
-
zoom_in_factor = gui_constants.ZOOM_IN_FACTOR
|
|
73
|
-
zoom_out_factor = gui_constants.ZOOM_OUT_FACTOR
|
|
74
|
-
current_scale = self.get_current_scale()
|
|
75
|
-
if event.angleDelta().y() > 0: # Zoom in
|
|
76
|
-
new_scale = current_scale * zoom_in_factor
|
|
77
|
-
if new_scale <= self.max_scale():
|
|
78
|
-
self.scale(zoom_in_factor, zoom_in_factor)
|
|
79
|
-
self.set_zoom_factor(new_scale)
|
|
80
|
-
else: # Zoom out
|
|
81
|
-
new_scale = current_scale * zoom_out_factor
|
|
82
|
-
if new_scale >= self.min_scale():
|
|
83
|
-
self.scale(zoom_out_factor, zoom_out_factor)
|
|
84
|
-
self.set_zoom_factor(new_scale)
|
|
85
|
-
self.update_brush_cursor()
|
|
86
|
-
else: # Touchpad event - fallback for systems without gesture recognition
|
|
87
|
-
if not self.control_pressed:
|
|
88
|
-
delta = event.pixelDelta() or event.angleDelta() / 8
|
|
89
|
-
if delta:
|
|
90
|
-
self.scroll_view(self, delta.x(), delta.y())
|
|
91
|
-
else: # Control + touchpad scroll for zoom
|
|
92
|
-
zoom_in = event.angleDelta().y() > 0
|
|
93
|
-
if zoom_in:
|
|
94
|
-
self.zoom_in()
|
|
95
|
-
else:
|
|
96
|
-
self.zoom_out()
|
|
64
|
+
self.handle_wheel_event(event)
|
|
97
65
|
event.accept()
|
|
98
|
-
# pylint: disable=R0801
|
|
99
66
|
|
|
100
67
|
def enterEvent(self, event):
|
|
101
68
|
self.activateWindow()
|
|
@@ -114,10 +81,13 @@ class OverlaidView(ViewStrategy, ImageGraphicsViewBase, ViewSignals):
|
|
|
114
81
|
def set_mouse_callbacks(self, callbacks):
|
|
115
82
|
self.mousePressEvent = callbacks
|
|
116
83
|
|
|
84
|
+
def get_view_with_mouse(self, event=None):
|
|
85
|
+
return self
|
|
86
|
+
|
|
87
|
+
# pylint: enable=C0103
|
|
117
88
|
def show(self):
|
|
118
89
|
self.show_master()
|
|
119
90
|
super().show()
|
|
120
|
-
# pylint: enable=C0103
|
|
121
91
|
|
|
122
92
|
def event(self, event):
|
|
123
93
|
if event.type() == QEvent.Gesture:
|
|
@@ -126,14 +96,7 @@ class OverlaidView(ViewStrategy, ImageGraphicsViewBase, ViewSignals):
|
|
|
126
96
|
|
|
127
97
|
def setup_scene_image(self, pixmap, pixmap_item):
|
|
128
98
|
self.setSceneRect(QRectF(pixmap.rect()))
|
|
129
|
-
|
|
130
|
-
self.set_max_min_scales(img_width, img_height)
|
|
131
|
-
view_rect = self.viewport().rect()
|
|
132
|
-
scale_x = view_rect.width() / img_width
|
|
133
|
-
scale_y = view_rect.height() / img_height
|
|
134
|
-
scale_factor = min(scale_x, scale_y)
|
|
135
|
-
scale_factor = max(self.min_scale(), min(scale_factor, self.max_scale()))
|
|
136
|
-
self.set_zoom_factor(scale_factor)
|
|
99
|
+
_img_width, _img_height, scale_factor = self.setup_view_image(self, pixmap)
|
|
137
100
|
self.resetTransform()
|
|
138
101
|
self.scale(scale_factor, scale_factor)
|
|
139
102
|
self.centerOn(pixmap_item)
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# pylint: disable=C0114, C0115, C0116, R0904, R0915, E0611, R0902, R0911, R0914, E1003
|
|
2
|
+
import time
|
|
2
3
|
from PySide6.QtCore import Qt, Signal, QEvent, QRectF
|
|
3
4
|
from PySide6.QtGui import QCursor
|
|
4
5
|
from PySide6.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout, QFrame
|
|
6
|
+
|
|
5
7
|
from .view_strategy import ViewStrategy, ImageGraphicsViewBase, ViewSignals
|
|
6
8
|
|
|
7
9
|
|
|
@@ -58,6 +60,7 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
|
|
|
58
60
|
self.current_view.setFocusPolicy(Qt.NoFocus)
|
|
59
61
|
self.master_view.setFocusPolicy(Qt.NoFocus)
|
|
60
62
|
self.current_brush_cursor = None
|
|
63
|
+
self.last_color_update_time_current = 0
|
|
61
64
|
|
|
62
65
|
def setup_layout(self):
|
|
63
66
|
raise NotImplementedError("Subclasses must implement setup_layout")
|
|
@@ -227,25 +230,20 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
|
|
|
227
230
|
self.current_view.mousePressEvent = callbacks
|
|
228
231
|
# pylint: enable=C0103
|
|
229
232
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
delta = event.pixelDelta() or event.angleDelta() / 8
|
|
245
|
-
if delta:
|
|
246
|
-
self.scroll_view(self.master_view, delta.x(), delta.y())
|
|
247
|
-
self.scroll_view(self.current_view, delta.x(), delta.y())
|
|
248
|
-
# pylint: disable=R0801
|
|
233
|
+
def get_view_with_mouse(self, event=None):
|
|
234
|
+
if event is None:
|
|
235
|
+
mouse_pos_global = QCursor.pos()
|
|
236
|
+
else:
|
|
237
|
+
mouse_pos_global = event.globalPosition().toPoint()
|
|
238
|
+
mouse_pos_current = self.current_view.mapFromGlobal(mouse_pos_global)
|
|
239
|
+
mouse_pos_master = self.master_view.mapFromGlobal(mouse_pos_global)
|
|
240
|
+
current_has_mouse = self.current_view.rect().contains(mouse_pos_current)
|
|
241
|
+
master_has_mouse = self.master_view.rect().contains(mouse_pos_master)
|
|
242
|
+
if master_has_mouse:
|
|
243
|
+
return self.master_view
|
|
244
|
+
if current_has_mouse:
|
|
245
|
+
return self.current_view
|
|
246
|
+
return None
|
|
249
247
|
|
|
250
248
|
def _apply_zoom_to_view(self, view, factor):
|
|
251
249
|
view.scale(factor, factor)
|
|
@@ -311,9 +309,11 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
|
|
|
311
309
|
radius = size / 2
|
|
312
310
|
self.current_brush_cursor.setRect(
|
|
313
311
|
scene_pos.x() - radius, scene_pos.y() - radius, size, size)
|
|
312
|
+
self.update_current_cursor_color()
|
|
314
313
|
self.current_brush_cursor.show()
|
|
315
314
|
self.brush_cursor.setRect(
|
|
316
315
|
scene_pos.x() - radius, scene_pos.y() - radius, size, size)
|
|
316
|
+
self.update_master_cursor_color()
|
|
317
317
|
self.brush_cursor.show()
|
|
318
318
|
else:
|
|
319
319
|
self.brush_cursor.hide()
|
|
@@ -321,6 +321,19 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
|
|
|
321
321
|
self.master_view.setCursor(Qt.ArrowCursor)
|
|
322
322
|
self.current_view.setCursor(Qt.ArrowCursor)
|
|
323
323
|
|
|
324
|
+
def update_current_cursor_color(self):
|
|
325
|
+
self.update_cursor_color_based_on_background(
|
|
326
|
+
self.current_brush_cursor, self.current_layer(),
|
|
327
|
+
self.get_visible_current_image_region, self.get_current_pixmap,
|
|
328
|
+
self.update_color_time_current)
|
|
329
|
+
|
|
330
|
+
def update_color_time_current(self):
|
|
331
|
+
current_time = time.time()
|
|
332
|
+
if current_time - self.last_color_update_time_current < 0.2:
|
|
333
|
+
return False
|
|
334
|
+
self.last_color_update_time_current = current_time
|
|
335
|
+
return True
|
|
336
|
+
|
|
324
337
|
def handle_master_mouse_press(self, event):
|
|
325
338
|
self.setFocus()
|
|
326
339
|
self.mouse_press_event(event)
|
|
@@ -366,14 +379,7 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
|
|
|
366
379
|
pixmap = self.status.pixmap_master
|
|
367
380
|
self.master_view.setSceneRect(QRectF(pixmap.rect()))
|
|
368
381
|
self.pixmap_item_master.setPixmap(pixmap)
|
|
369
|
-
img_width, img_height =
|
|
370
|
-
self.set_max_min_scales(img_width, img_height)
|
|
371
|
-
view_rect = self.master_view.viewport().rect()
|
|
372
|
-
scale_x = view_rect.width() / img_width
|
|
373
|
-
scale_y = view_rect.height() / img_height
|
|
374
|
-
scale_factor = min(scale_x, scale_y)
|
|
375
|
-
scale_factor = max(self.min_scale(), min(scale_factor, self.max_scale()))
|
|
376
|
-
self.set_zoom_factor(scale_factor)
|
|
382
|
+
img_width, img_height, scale_factor = self.setup_view_image(self.master_view, pixmap)
|
|
377
383
|
self.master_view.resetTransform()
|
|
378
384
|
self.master_view.scale(scale_factor, scale_factor)
|
|
379
385
|
self.master_view.centerOn(self.pixmap_item_master)
|
|
@@ -422,9 +428,9 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
|
|
|
422
428
|
size = self.brush.size
|
|
423
429
|
radius = size / 2
|
|
424
430
|
self.current_brush_cursor.setRect(
|
|
425
|
-
scene_pos.x() - radius, scene_pos.y() - radius,
|
|
426
|
-
size, size)
|
|
431
|
+
scene_pos.x() - radius, scene_pos.y() - radius, size, size)
|
|
427
432
|
if self.brush_cursor.isVisible():
|
|
433
|
+
self.update_current_cursor_color()
|
|
428
434
|
self.current_brush_cursor.show()
|
|
429
435
|
else:
|
|
430
436
|
self.current_brush_cursor.hide()
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# pylint: disable=C0114, C0115, C0116, E0611, R0904, R0903, R0902, E1101, R0914, R0913, R0917
|
|
2
2
|
import math
|
|
3
|
+
import time
|
|
3
4
|
from abc import abstractmethod
|
|
4
5
|
import numpy as np
|
|
5
6
|
from PySide6.QtCore import Qt, QPointF, QTime, QPoint, Signal, QRectF
|
|
@@ -67,6 +68,7 @@ class BrushCursor(QGraphicsItemGroup):
|
|
|
67
68
|
|
|
68
69
|
def setRect(self, x, y, w, h):
|
|
69
70
|
self._rect = QRectF(x, y, w, h)
|
|
71
|
+
self._radius = min(w, h) / 2
|
|
70
72
|
self._create_arcs()
|
|
71
73
|
|
|
72
74
|
def rect(self):
|
|
@@ -95,6 +97,8 @@ class ImageGraphicsViewBase(QGraphicsView):
|
|
|
95
97
|
self.setRenderHint(QPainter.Antialiasing)
|
|
96
98
|
self.setRenderHint(QPainter.SmoothPixmapTransform)
|
|
97
99
|
self.setCursor(Qt.BlankCursor)
|
|
100
|
+
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
|
|
101
|
+
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
|
|
98
102
|
|
|
99
103
|
|
|
100
104
|
class ViewStrategy(LayerCollectionHandler):
|
|
@@ -116,6 +120,7 @@ class ViewStrategy(LayerCollectionHandler):
|
|
|
116
120
|
self.last_brush_pos = None
|
|
117
121
|
self.last_mouse_pos = None
|
|
118
122
|
self.last_update_time = QTime.currentTime()
|
|
123
|
+
self.last_color_update_time = 0
|
|
119
124
|
|
|
120
125
|
@abstractmethod
|
|
121
126
|
def create_pixmaps(self):
|
|
@@ -185,6 +190,10 @@ class ViewStrategy(LayerCollectionHandler):
|
|
|
185
190
|
def set_mouse_callbacks(self, callbacks):
|
|
186
191
|
pass
|
|
187
192
|
|
|
193
|
+
@abstractmethod
|
|
194
|
+
def get_view_with_mouse(self, event=None):
|
|
195
|
+
pass
|
|
196
|
+
|
|
188
197
|
def hide_brush_cursor(self):
|
|
189
198
|
if self.brush_cursor:
|
|
190
199
|
self.brush_cursor.hide()
|
|
@@ -236,6 +245,8 @@ class ViewStrategy(LayerCollectionHandler):
|
|
|
236
245
|
|
|
237
246
|
def set_cursor_style(self, style):
|
|
238
247
|
self.cursor_style = style
|
|
248
|
+
if style == 'preview':
|
|
249
|
+
self.show_brush_preview()
|
|
239
250
|
self.update_brush_cursor()
|
|
240
251
|
|
|
241
252
|
def get_cursor_style(self):
|
|
@@ -315,6 +326,17 @@ class ViewStrategy(LayerCollectionHandler):
|
|
|
315
326
|
return QImage(memoryview(array), width, height, 3 * width, QImage.Format_RGB888)
|
|
316
327
|
return QImage()
|
|
317
328
|
|
|
329
|
+
def setup_view_image(self, view, pixmap):
|
|
330
|
+
img_width, img_height = pixmap.width(), pixmap.height()
|
|
331
|
+
self.set_max_min_scales(img_width, img_height)
|
|
332
|
+
view_rect = view.viewport().rect()
|
|
333
|
+
scale_x = view_rect.width() / img_width
|
|
334
|
+
scale_y = view_rect.height() / img_height
|
|
335
|
+
scale_factor = min(scale_x, scale_y)
|
|
336
|
+
scale_factor = max(self.min_scale(), min(scale_factor, self.max_scale()))
|
|
337
|
+
self.set_zoom_factor(scale_factor)
|
|
338
|
+
return img_width, img_height, scale_factor
|
|
339
|
+
|
|
318
340
|
def create_scene(self, view):
|
|
319
341
|
scene = QGraphicsScene()
|
|
320
342
|
view.setScene(scene)
|
|
@@ -336,21 +358,6 @@ class ViewStrategy(LayerCollectionHandler):
|
|
|
336
358
|
gui_constants.MIN_ZOOMED_IMG_HEIGHT / img_height))
|
|
337
359
|
self.set_max_scale(gui_constants.MAX_ZOOMED_IMG_PX_SIZE)
|
|
338
360
|
|
|
339
|
-
def zoom_in(self):
|
|
340
|
-
if self.empty():
|
|
341
|
-
return
|
|
342
|
-
master_view = self.get_master_view()
|
|
343
|
-
old_center = master_view.mapToScene(master_view.viewport().rect().center())
|
|
344
|
-
current_scale = self.get_current_scale()
|
|
345
|
-
new_scale = current_scale * gui_constants.ZOOM_IN_FACTOR
|
|
346
|
-
if new_scale <= self.max_scale():
|
|
347
|
-
for view in self.get_views():
|
|
348
|
-
view.scale(gui_constants.ZOOM_IN_FACTOR, gui_constants.ZOOM_IN_FACTOR)
|
|
349
|
-
self.set_zoom_factor(new_scale)
|
|
350
|
-
master_view.centerOn(old_center)
|
|
351
|
-
self.update_brush_cursor()
|
|
352
|
-
self.update_cursor_pen_width()
|
|
353
|
-
|
|
354
361
|
def apply_zoom(self):
|
|
355
362
|
if self.empty():
|
|
356
363
|
return
|
|
@@ -359,20 +366,98 @@ class ViewStrategy(LayerCollectionHandler):
|
|
|
359
366
|
scale_factor = self.zoom_factor() / current_scale
|
|
360
367
|
view.scale(scale_factor, scale_factor)
|
|
361
368
|
|
|
362
|
-
def
|
|
369
|
+
def center_image(self, view):
|
|
370
|
+
view.horizontalScrollBar().setValue(self.status.h_scroll)
|
|
371
|
+
view.verticalScrollBar().setValue(self.status.v_scroll)
|
|
372
|
+
|
|
373
|
+
def set_scroll_and_center(self, view, delta):
|
|
374
|
+
self.status.set_scroll(
|
|
375
|
+
view.horizontalScrollBar().value() + int(delta.x() * self.zoom_factor()),
|
|
376
|
+
view.verticalScrollBar().value() + int(delta.y() * self.zoom_factor()))
|
|
377
|
+
self.center_image(view)
|
|
378
|
+
|
|
379
|
+
def apply_zoom_and_center(self, view, new_scale, ref_pos, old_center):
|
|
380
|
+
self.set_zoom_factor(new_scale)
|
|
381
|
+
self.apply_zoom()
|
|
382
|
+
new_center = view.mapToScene(ref_pos)
|
|
383
|
+
delta = old_center - new_center
|
|
384
|
+
self.set_scroll_and_center(view, delta)
|
|
385
|
+
|
|
386
|
+
def handle_pinch_gesture(self, pinch):
|
|
387
|
+
master_view = self.get_master_view()
|
|
388
|
+
if pinch.state() == Qt.GestureStarted:
|
|
389
|
+
self.pinch_start_scale = self.zoom_factor()
|
|
390
|
+
self.pinch_center_view = pinch.centerPoint()
|
|
391
|
+
self.pinch_center_scene = master_view.mapToScene(self.pinch_center_view.toPoint())
|
|
392
|
+
self.gesture_active = True
|
|
393
|
+
elif pinch.state() == Qt.GestureUpdated:
|
|
394
|
+
new_scale = self.pinch_start_scale * pinch.totalScaleFactor()
|
|
395
|
+
new_scale = max(self.min_scale(), min(new_scale, self.max_scale()))
|
|
396
|
+
if abs(new_scale - self.zoom_factor()) > 0.01:
|
|
397
|
+
old_center = self.pinch_center_scene
|
|
398
|
+
ref_pos = self.pinch_center_view.toPoint()
|
|
399
|
+
self.apply_zoom_and_center(master_view, new_scale, ref_pos, old_center)
|
|
400
|
+
elif pinch.state() in (Qt.GestureFinished, Qt.GestureCanceled):
|
|
401
|
+
self.gesture_active = False
|
|
402
|
+
self.update_cursor_pen_width()
|
|
403
|
+
|
|
404
|
+
def do_zoom(self, new_scale, view):
|
|
363
405
|
if self.empty():
|
|
364
406
|
return
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
407
|
+
if not self.min_scale() <= new_scale <= self.max_scale():
|
|
408
|
+
return
|
|
409
|
+
if view is None:
|
|
410
|
+
view = self.get_master_view()
|
|
411
|
+
global_pos = QCursor.pos()
|
|
412
|
+
ref_pos = view.mapFromGlobal(global_pos)
|
|
413
|
+
old_center = view.mapToScene(ref_pos)
|
|
414
|
+
self.apply_zoom_and_center(view, new_scale, ref_pos, old_center)
|
|
415
|
+
self.update_cursor_pen_width()
|
|
416
|
+
|
|
417
|
+
def handle_wheel_event(self, event):
|
|
418
|
+
if self.empty() or self.gesture_active:
|
|
419
|
+
return
|
|
420
|
+
if event.source() == Qt.MouseEventNotSynthesized: # Physical mouse
|
|
421
|
+
if self.control_pressed:
|
|
422
|
+
self.brush_size_change_requested.emit(1 if event.angleDelta().y() > 0 else -1)
|
|
423
|
+
else:
|
|
424
|
+
self.handle_zoom_wheel(self.get_view_with_mouse(event), event)
|
|
374
425
|
self.update_brush_cursor()
|
|
375
|
-
|
|
426
|
+
else:
|
|
427
|
+
self.handle_wheel_touchpad_event(event)
|
|
428
|
+
|
|
429
|
+
def handle_wheel_touchpad_event(self, event):
|
|
430
|
+
if not self.control_pressed:
|
|
431
|
+
delta = event.pixelDelta() or event.angleDelta() / 8
|
|
432
|
+
if delta:
|
|
433
|
+
self.scroll_view(self.get_view_with_mouse(event), delta.x(), delta.y())
|
|
434
|
+
else:
|
|
435
|
+
zoom_in = event.angleDelta().y() > 0
|
|
436
|
+
if zoom_in:
|
|
437
|
+
self.zoom_in()
|
|
438
|
+
else:
|
|
439
|
+
self.zoom_out()
|
|
440
|
+
|
|
441
|
+
def handle_zoom_wheel(self, view, event):
|
|
442
|
+
if view is None:
|
|
443
|
+
return
|
|
444
|
+
current_scale = self.get_current_scale()
|
|
445
|
+
if event.angleDelta().y() > 0:
|
|
446
|
+
new_scale = current_scale * gui_constants.ZOOM_IN_FACTOR
|
|
447
|
+
else:
|
|
448
|
+
new_scale = current_scale * gui_constants.ZOOM_OUT_FACTOR
|
|
449
|
+
new_scale = max(self.min_scale(), min(new_scale, self.max_scale()))
|
|
450
|
+
self.do_zoom(new_scale, view)
|
|
451
|
+
|
|
452
|
+
def zoom_in(self):
|
|
453
|
+
self.do_zoom(
|
|
454
|
+
self.get_current_scale() * gui_constants.ZOOM_IN_FACTOR,
|
|
455
|
+
self.get_view_with_mouse())
|
|
456
|
+
|
|
457
|
+
def zoom_out(self):
|
|
458
|
+
self.do_zoom(
|
|
459
|
+
self.get_current_scale() * gui_constants.ZOOM_OUT_FACTOR,
|
|
460
|
+
self.get_view_with_mouse())
|
|
376
461
|
|
|
377
462
|
def reset_zoom(self):
|
|
378
463
|
if self.empty():
|
|
@@ -402,9 +487,12 @@ class ViewStrategy(LayerCollectionHandler):
|
|
|
402
487
|
self.update_cursor_pen_width()
|
|
403
488
|
|
|
404
489
|
def setup_simple_brush_style(self, center_x, center_y, radius):
|
|
490
|
+
if self.brush_cursor:
|
|
491
|
+
pen = self.brush_cursor.pen()
|
|
492
|
+
else:
|
|
493
|
+
pen = QPen(QColor(*gui_constants.BRUSH_COLORS['pen']), self.current_line_width())
|
|
405
494
|
gradient = create_default_brush_gradient(center_x, center_y, radius, self.brush)
|
|
406
|
-
self.brush_cursor.setPen(
|
|
407
|
-
self.current_line_width()))
|
|
495
|
+
self.brush_cursor.setPen(pen)
|
|
408
496
|
self.brush_cursor.setBrush(QBrush(gradient))
|
|
409
497
|
|
|
410
498
|
def create_circle(self, scene, line_style=Qt.SolidLine):
|
|
@@ -469,9 +557,58 @@ class ViewStrategy(LayerCollectionHandler):
|
|
|
469
557
|
self.brush_preview.update(scene_pos, int(size))
|
|
470
558
|
else:
|
|
471
559
|
self.hide_brush_preview()
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
560
|
+
self.update_master_cursor_color()
|
|
561
|
+
if self.cursor_style == 'brush':
|
|
562
|
+
self.setup_simple_brush_style(scene_pos.x(), scene_pos.y(), radius)
|
|
563
|
+
if not self.scrolling:
|
|
564
|
+
self.show_brush_cursor()
|
|
565
|
+
|
|
566
|
+
def update_color_time(self):
|
|
567
|
+
current_time = time.time()
|
|
568
|
+
if current_time - self.last_color_update_time < 0.2:
|
|
569
|
+
return False
|
|
570
|
+
self.last_color_update_time = current_time
|
|
571
|
+
return True
|
|
572
|
+
|
|
573
|
+
def update_master_cursor_color(self):
|
|
574
|
+
self.update_cursor_color_based_on_background(
|
|
575
|
+
self.brush_cursor, self.master_layer(),
|
|
576
|
+
self.get_visible_image_region, self.get_master_pixmap,
|
|
577
|
+
self.update_color_time)
|
|
578
|
+
|
|
579
|
+
def update_cursor_color_based_on_background(
|
|
580
|
+
self, cursor, layer,
|
|
581
|
+
visible_region, get_pixmap, update_timer):
|
|
582
|
+
if not update_timer():
|
|
583
|
+
return
|
|
584
|
+
cursor_rect = cursor.rect()
|
|
585
|
+
image_region = visible_region()
|
|
586
|
+
if image_region and cursor_rect.intersects(image_region):
|
|
587
|
+
intersect_rect = cursor_rect.intersected(image_region)
|
|
588
|
+
top_left = get_pixmap().mapFromScene(intersect_rect.topLeft())
|
|
589
|
+
bottom_right = get_pixmap().mapFromScene(intersect_rect.bottomRight())
|
|
590
|
+
x1, y1 = max(0, int(top_left.x())), max(0, int(top_left.y()))
|
|
591
|
+
x2, y2 = min(layer.shape[1], int(bottom_right.x())), \
|
|
592
|
+
min(layer.shape[0], int(bottom_right.y()))
|
|
593
|
+
if x2 > x1 and y2 > y1:
|
|
594
|
+
region = layer[y1:y2, x1:x2]
|
|
595
|
+
if region.size > 10000:
|
|
596
|
+
step = int(math.sqrt(region.size / 100))
|
|
597
|
+
region = region[::step, ::step]
|
|
598
|
+
if region.ndim == 3:
|
|
599
|
+
luminosity = np.dot(region[..., :3], [0.299, 0.587, 0.114])
|
|
600
|
+
avg_luminosity = np.mean(luminosity)
|
|
601
|
+
else:
|
|
602
|
+
avg_luminosity = np.mean(region)
|
|
603
|
+
if region.dtype == np.uint16:
|
|
604
|
+
avg_luminosity /= 256.0
|
|
605
|
+
if avg_luminosity < 128:
|
|
606
|
+
new_color = QColor(255, 255, 255)
|
|
607
|
+
else:
|
|
608
|
+
new_color = QColor(0, 0, 0)
|
|
609
|
+
current_pen = cursor.pen()
|
|
610
|
+
current_pen.setColor(new_color)
|
|
611
|
+
cursor.setPen(current_pen)
|
|
475
612
|
|
|
476
613
|
def position_on_image(self, pos):
|
|
477
614
|
master_view = self.get_master_view()
|
|
@@ -485,11 +622,20 @@ class ViewStrategy(LayerCollectionHandler):
|
|
|
485
622
|
return None
|
|
486
623
|
master_view = self.get_master_view()
|
|
487
624
|
master_pixmap = self.get_master_pixmap()
|
|
488
|
-
pixmap = self.get_master_pixmap()
|
|
489
625
|
view_rect = master_view.viewport().rect()
|
|
490
626
|
scene_rect = master_view.mapToScene(view_rect).boundingRect()
|
|
491
627
|
image_rect = master_pixmap.mapFromScene(scene_rect).boundingRect().toRect()
|
|
492
|
-
return image_rect.intersected(
|
|
628
|
+
return image_rect.intersected(master_pixmap.boundingRect().toRect())
|
|
629
|
+
|
|
630
|
+
def get_visible_current_image_region(self):
|
|
631
|
+
if self.empty():
|
|
632
|
+
return None
|
|
633
|
+
current_view = self.get_current_view()
|
|
634
|
+
current_pixmap = self.get_current_pixmap()
|
|
635
|
+
view_rect = current_view.viewport().rect()
|
|
636
|
+
scene_rect = current_view.mapToScene(view_rect).boundingRect()
|
|
637
|
+
image_rect = current_pixmap.mapFromScene(scene_rect).boundingRect().toRect()
|
|
638
|
+
return image_rect.intersected(current_pixmap.boundingRect().toRect())
|
|
493
639
|
|
|
494
640
|
def get_visible_image_portion(self):
|
|
495
641
|
if self.has_no_master_layer():
|
|
@@ -548,10 +694,6 @@ class ViewStrategy(LayerCollectionHandler):
|
|
|
548
694
|
self.status.set_scroll(view.horizontalScrollBar().value(),
|
|
549
695
|
view.verticalScrollBar().value())
|
|
550
696
|
|
|
551
|
-
def center_image(self, view):
|
|
552
|
-
view.horizontalScrollBar().setValue(self.status.h_scroll)
|
|
553
|
-
view.verticalScrollBar().setValue(self.status.v_scroll)
|
|
554
|
-
|
|
555
697
|
def mouse_move_event(self, event):
|
|
556
698
|
if self.empty():
|
|
557
699
|
return
|
|
@@ -598,7 +740,8 @@ class ViewStrategy(LayerCollectionHandler):
|
|
|
598
740
|
self.last_brush_pos = event.position()
|
|
599
741
|
self.brush_operation_started.emit(event.position().toPoint())
|
|
600
742
|
self.dragging = True
|
|
601
|
-
self.
|
|
743
|
+
if not self.scrolling:
|
|
744
|
+
self.show_brush_cursor()
|
|
602
745
|
|
|
603
746
|
def mouse_release_event(self, event):
|
|
604
747
|
if self.empty():
|
|
@@ -617,28 +760,3 @@ class ViewStrategy(LayerCollectionHandler):
|
|
|
617
760
|
elif self.dragging:
|
|
618
761
|
self.dragging = False
|
|
619
762
|
self.brush_operation_ended.emit()
|
|
620
|
-
|
|
621
|
-
def handle_pinch_gesture(self, pinch):
|
|
622
|
-
master_view = self.get_master_view()
|
|
623
|
-
if pinch.state() == Qt.GestureStarted:
|
|
624
|
-
self.pinch_start_scale = self.zoom_factor()
|
|
625
|
-
self.pinch_center_view = pinch.centerPoint()
|
|
626
|
-
self.pinch_center_scene = master_view.mapToScene(self.pinch_center_view.toPoint())
|
|
627
|
-
self.gesture_active = True
|
|
628
|
-
elif pinch.state() == Qt.GestureUpdated:
|
|
629
|
-
new_scale = self.pinch_start_scale * pinch.totalScaleFactor()
|
|
630
|
-
new_scale = max(self.min_scale(), min(new_scale, self.max_scale()))
|
|
631
|
-
if abs(new_scale - self.zoom_factor()) > 0.01:
|
|
632
|
-
self.set_zoom_factor(new_scale)
|
|
633
|
-
self.apply_zoom()
|
|
634
|
-
new_center = master_view.mapToScene(self.pinch_center_view.toPoint())
|
|
635
|
-
delta = self.pinch_center_scene - new_center
|
|
636
|
-
h_scroll = master_view.horizontalScrollBar().value() + \
|
|
637
|
-
int(delta.x() * self.zoom_factor())
|
|
638
|
-
v_scroll = master_view.verticalScrollBar().value() + \
|
|
639
|
-
int(delta.y() * self.zoom_factor())
|
|
640
|
-
self.status.set_scroll(h_scroll, v_scroll)
|
|
641
|
-
self.center_image(master_view)
|
|
642
|
-
elif pinch.state() in (Qt.GestureFinished, Qt.GestureCanceled):
|
|
643
|
-
self.gesture_active = False
|
|
644
|
-
self.update_cursor_pen_width()
|
|
@@ -49,7 +49,6 @@ class WhiteBalanceFilter(BaseFilter):
|
|
|
49
49
|
self.value_labels[name] = val_label
|
|
50
50
|
row_layout.addLayout(sliders_layout)
|
|
51
51
|
layout.addLayout(row_layout)
|
|
52
|
-
|
|
53
52
|
rbg_layout = QHBoxLayout()
|
|
54
53
|
rbg_layout.addWidget(QLabel("RBG hex:"))
|
|
55
54
|
self.rgb_hex = QLineEdit(self.hex_color(self.initial_val))
|
|
@@ -58,7 +57,6 @@ class WhiteBalanceFilter(BaseFilter):
|
|
|
58
57
|
rbg_layout.addWidget(self.rgb_hex)
|
|
59
58
|
rbg_layout.addStretch(1)
|
|
60
59
|
layout.addLayout(rbg_layout)
|
|
61
|
-
|
|
62
60
|
pick_button = QPushButton("Pick Color")
|
|
63
61
|
layout.addWidget(pick_button)
|
|
64
62
|
self.create_base_widgets(
|
|
@@ -126,17 +124,18 @@ class WhiteBalanceFilter(BaseFilter):
|
|
|
126
124
|
bgr = self.get_pixel_color_at(
|
|
127
125
|
pos, radius=int(self.image_viewer.get_brush().size))
|
|
128
126
|
rgb = (bgr[2], bgr[1], bgr[0])
|
|
127
|
+
QApplication.restoreOverrideCursor()
|
|
128
|
+
self.image_viewer.unsetCursor()
|
|
129
|
+
self.image_viewer.strategy.set_mouse_callbacks(self.original_mouse_press)
|
|
130
|
+
self.filter_gui_set_enabled_requested.emit(True)
|
|
131
|
+
self.image_viewer.hide_brush_preview()
|
|
129
132
|
new_filter = WhiteBalanceFilter(
|
|
130
133
|
self.name, self.parent(), self.image_viewer, self.layer_collection,
|
|
131
134
|
self.undo_manager)
|
|
132
135
|
new_filter.run_with_preview(init_val=rgb)
|
|
133
|
-
QApplication.restoreOverrideCursor()
|
|
134
|
-
self.image_viewer.unsetCursor()
|
|
135
|
-
self.image_viewer.strategy.set_mouse_callbacks(self.original_mouse_press)
|
|
136
136
|
self.image_viewer.set_cursor_style(self.original_cursor_style)
|
|
137
137
|
self.image_viewer.show_brush_cursor()
|
|
138
138
|
self.image_viewer.show_brush_preview()
|
|
139
|
-
self.filter_gui_set_enabled_requested.emit(True)
|
|
140
139
|
|
|
141
140
|
def reset_rgb(self):
|
|
142
141
|
for name, slider in self.sliders.items():
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
shinestacker/__init__.py,sha256=uq2fjAw2z_6TpH3mOcWFZ98GoEPRsNhTAK8N0MMm_e8,448
|
|
2
|
-
shinestacker/_version.py,sha256=
|
|
2
|
+
shinestacker/_version.py,sha256=HfPNw49U2WWQOQlRuPSUUE8x8nkPx62dtjaGJfbhyTk,21
|
|
3
3
|
shinestacker/algorithms/__init__.py,sha256=1FwVJ3w9GGbFFkjYJRUedTvcdE4j0ieSgaH9RC9iCY4,877
|
|
4
4
|
shinestacker/algorithms/align.py,sha256=mb44u-YxZI1TTSHz81nRpX_2c8awlOhnGrK0LyfTQeQ,33543
|
|
5
5
|
shinestacker/algorithms/align_auto.py,sha256=pJetw6zZEWQLouzcelkI8gD4cPiOp887ePXzVbm0E6Q,3800
|
|
@@ -22,17 +22,17 @@ shinestacker/algorithms/vignetting.py,sha256=gJOv-FN3GnTgaVn70W_6d-qbw3WmqinDiO9
|
|
|
22
22
|
shinestacker/algorithms/white_balance.py,sha256=PMKsBtxOSn5aRr_Gkx1StHS4eN6kBN2EhNnhg4UG24g,501
|
|
23
23
|
shinestacker/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
24
|
shinestacker/app/about_dialog.py,sha256=pkH7nnxUP8yc0D3vRGd1jRb5cwi1nDVbQRk_OC9yLk8,4144
|
|
25
|
-
shinestacker/app/
|
|
25
|
+
shinestacker/app/args_parser_opts.py,sha256=TlfMR5GBd6Zz9wZNrN7CGJ1_hm1FnLZn5EoP5mkRHrk,776
|
|
26
26
|
shinestacker/app/gui_utils.py,sha256=fSpkwPXTON_l676UHdAnJNrGq7BPbSlPOiHpOF_LZaI,2519
|
|
27
27
|
shinestacker/app/help_menu.py,sha256=g8lKG_xZmXtNQaC3SIRzyROKVWva_PLEgZsQWh6zUcQ,499
|
|
28
|
-
shinestacker/app/main.py,sha256=
|
|
28
|
+
shinestacker/app/main.py,sha256=UkWz4jvaBPH4Fs-arHe8H0NqKVTA4B_1e9BRn9S5oFo,10963
|
|
29
29
|
shinestacker/app/open_frames.py,sha256=bsu32iJSYJQLe_tQQbvAU5DuMDVX6MRuNdE7B5lojZc,1488
|
|
30
|
-
shinestacker/app/project.py,sha256=
|
|
31
|
-
shinestacker/app/retouch.py,sha256=
|
|
30
|
+
shinestacker/app/project.py,sha256=_kopeyb8e5JAA3XjNXoCshlYBfT5Avw-kTqiJN4GVlo,2805
|
|
31
|
+
shinestacker/app/retouch.py,sha256=On2oV2QonfMziQAvRkO6qw-h6E6wWQE0svKzZQKHeyg,2687
|
|
32
32
|
shinestacker/config/__init__.py,sha256=aXxi-LmAvXd0daIFrVnTHE5OCaYeK1uf1BKMr7oaXQs,197
|
|
33
33
|
shinestacker/config/config.py,sha256=eBko2D3ADhLTIm9X6hB_a_WsIjwgfE-qmBVkhP1XSvc,1636
|
|
34
34
|
shinestacker/config/constants.py,sha256=EEdr7pZg4JpbIjUWaP7kJQfTuBB85FN739myDNAfn8A,8301
|
|
35
|
-
shinestacker/config/gui_constants.py,sha256=
|
|
35
|
+
shinestacker/config/gui_constants.py,sha256=55Qr0KzrTx8eQHCkT-EVabSiD59VvLZIh5o2cA9028s,2791
|
|
36
36
|
shinestacker/core/__init__.py,sha256=IUEIx6SQ3DygDEHN3_E6uKpHjHtUa4a_U_1dLd_8yEU,484
|
|
37
37
|
shinestacker/core/colors.py,sha256=kr_tJA1iRsdck2JaYDb2lS-codZ4Ty9gdu3kHfiWvuM,1340
|
|
38
38
|
shinestacker/core/core_utils.py,sha256=1LYj19Dfc9jZN9-4dlf1paximDH5WZYa7DXvKr7R7QY,1719
|
|
@@ -49,10 +49,10 @@ shinestacker/gui/folder_file_selection.py,sha256=IYWfZQFkoD5iO7zJ7BxVVDP9F3Dc0EX
|
|
|
49
49
|
shinestacker/gui/gui_images.py,sha256=k39DpdsxcmYoRdHNNZj6OpFAas0GOHS4JSG542wfheg,5728
|
|
50
50
|
shinestacker/gui/gui_logging.py,sha256=kiZcrC2AFYCWgPZo0O5SKw-E5cFrezwf4anS3HjPuNw,8168
|
|
51
51
|
shinestacker/gui/gui_run.py,sha256=zr7x4BVmM0n_ZRsSEaJVVKvHSWHuwhftgkUvgeg90gU,15767
|
|
52
|
-
shinestacker/gui/main_window.py,sha256=
|
|
53
|
-
shinestacker/gui/menu_manager.py,sha256=
|
|
52
|
+
shinestacker/gui/main_window.py,sha256=zOxIRn6urmnmfDOR1JJ3n6xsT1qryOt1s7kZxwh-qYI,25202
|
|
53
|
+
shinestacker/gui/menu_manager.py,sha256=legmYEpQxuzEQoDhxMUWiwCcYTXwd-uRfAju-Nymy8g,11664
|
|
54
54
|
shinestacker/gui/new_project.py,sha256=z8e3EhRMB-KtoPwYQSiKLSOQ2dS0-Okm7zVw21B7zy8,16391
|
|
55
|
-
shinestacker/gui/project_controller.py,sha256=
|
|
55
|
+
shinestacker/gui/project_controller.py,sha256=7vSyoxepplJrf0VsbPrZkMqtHW6rtPgEOZgdPsOPVoQ,16490
|
|
56
56
|
shinestacker/gui/project_converter.py,sha256=Gmna0HwbvACcXiX74TaQYumif8ZV8sZ2APLTMM-L1mU,7436
|
|
57
57
|
shinestacker/gui/project_editor.py,sha256=lSgQ42IoaobHs-NQQWT88Qhg5l7nu5ejxAO5VgIupr8,25498
|
|
58
58
|
shinestacker/gui/project_model.py,sha256=eRUmH3QmRzDtPtZoxgT6amKzN8_5XzwjHgEJeL-_JOE,4263
|
|
@@ -82,24 +82,24 @@ shinestacker/retouch/exif_data.py,sha256=LF-fRXW-reMq-xJ_QRE5j8DC2LVGKIlC6MR3QbC
|
|
|
82
82
|
shinestacker/retouch/file_loader.py,sha256=z02-A8_uDZxayI1NFTxT2GVUvEBWStchX9hlN1o5-0U,4784
|
|
83
83
|
shinestacker/retouch/filter_manager.py,sha256=tOGIWj5HjViL1-iXHkd91X-sZ1c1G531pDmLO0x6zx0,866
|
|
84
84
|
shinestacker/retouch/icon_container.py,sha256=6gw1HO1bC2FrdB4dc_iH81DQuLjzuvRGksZ2hKLT9yA,585
|
|
85
|
-
shinestacker/retouch/image_editor_ui.py,sha256
|
|
85
|
+
shinestacker/retouch/image_editor_ui.py,sha256=-UVWeFSnXyHOG7XEIl9wNhiCjH5R0UFeM2VOg5R6ozs,33435
|
|
86
86
|
shinestacker/retouch/image_view_status.py,sha256=bdIhsXiYXm7eyjkTGWkw5PRShzaF_by-g7daqgmhwjM,1858
|
|
87
87
|
shinestacker/retouch/image_viewer.py,sha256=H8w-ORug1aKf7X3FeSX4lQV-a0IewZ9OVG1-50BK4cE,4452
|
|
88
|
-
shinestacker/retouch/io_gui_handler.py,sha256=
|
|
88
|
+
shinestacker/retouch/io_gui_handler.py,sha256=UOnrFT00s0075Ng_yJACJX9TP8UT9mQOWXwQwNAfpMw,12079
|
|
89
89
|
shinestacker/retouch/io_manager.py,sha256=JUAA--AK0mVa1PTErJTnBFjaXIle5Qs7Ow0Wkd8at0o,2437
|
|
90
90
|
shinestacker/retouch/layer_collection.py,sha256=fZlGrkm9-Ycc7AOzFSpImhafiTieBeCZRk-UlvlFHbo,5819
|
|
91
|
-
shinestacker/retouch/overlaid_view.py,sha256=
|
|
91
|
+
shinestacker/retouch/overlaid_view.py,sha256=KBwuzC2OQsK7rdtFAKrSvXrHrEV1SiOoZhxPCQLGkvg,6734
|
|
92
92
|
shinestacker/retouch/shortcuts_help.py,sha256=BFWTT5QvodqMhqa_9LI25hZqjICfckgyWG4fGrGzvnM,4283
|
|
93
|
-
shinestacker/retouch/sidebyside_view.py,sha256=
|
|
93
|
+
shinestacker/retouch/sidebyside_view.py,sha256=Ipx5ckaAuEv6fnxg4RpENj00ew6-M2jLsnTKUcYKcAk,18055
|
|
94
94
|
shinestacker/retouch/transformation_manager.py,sha256=NSHGUF-JFv4Y81gSvizjQCTp49TLo1so7c0WoUElO08,1812
|
|
95
95
|
shinestacker/retouch/undo_manager.py,sha256=cKUkqnJtnJ-Hq-LQs5Bv49FC6qkG6XSw9oCVySJ8jS0,4312
|
|
96
96
|
shinestacker/retouch/unsharp_mask_filter.py,sha256=Iapc8UmSVpj3V0LcJq_38P5qerRqTevMynbbk5Rk6iE,3634
|
|
97
|
-
shinestacker/retouch/view_strategy.py,sha256=
|
|
97
|
+
shinestacker/retouch/view_strategy.py,sha256=u9oOB-fxap_ijNJ3O3Ev5OMLH-bDZNGj4hyBkJUDeeo,27993
|
|
98
98
|
shinestacker/retouch/vignetting_filter.py,sha256=JhFr6OVIripQzSJrZEG4lxq7wBsmpofLqJQ-aP2bKw8,3789
|
|
99
|
-
shinestacker/retouch/white_balance_filter.py,sha256=
|
|
100
|
-
shinestacker-1.5.
|
|
101
|
-
shinestacker-1.5.
|
|
102
|
-
shinestacker-1.5.
|
|
103
|
-
shinestacker-1.5.
|
|
104
|
-
shinestacker-1.5.
|
|
105
|
-
shinestacker-1.5.
|
|
99
|
+
shinestacker/retouch/white_balance_filter.py,sha256=UaH4yxG3fU4vPutBAkV5oTXIQyUTN09x0uTywAzv3sY,8286
|
|
100
|
+
shinestacker-1.5.4.dist-info/licenses/LICENSE,sha256=pWgb-bBdsU2Gd2kwAXxketnm5W_2u8_fIeWEgojfrxs,7651
|
|
101
|
+
shinestacker-1.5.4.dist-info/METADATA,sha256=1cFUICQsSTKF1T8L7sNiT7XQ_UkwXlIMRr64bym9TvA,6978
|
|
102
|
+
shinestacker-1.5.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
103
|
+
shinestacker-1.5.4.dist-info/entry_points.txt,sha256=SY6g1LqtMmp23q1DGwLUDT_dhLX9iss8DvWkiWLyo_4,166
|
|
104
|
+
shinestacker-1.5.4.dist-info/top_level.txt,sha256=MhijwnBVX5psfsyX8JZjqp3SYiWPsKe69f3Gnyze4Fw,13
|
|
105
|
+
shinestacker-1.5.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|