shinestacker 1.3.1__py3-none-any.whl → 1.5.0__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.

Files changed (38) hide show
  1. shinestacker/_version.py +1 -1
  2. shinestacker/algorithms/align.py +198 -18
  3. shinestacker/algorithms/align_parallel.py +17 -1
  4. shinestacker/algorithms/balance.py +23 -13
  5. shinestacker/algorithms/noise_detection.py +3 -1
  6. shinestacker/algorithms/utils.py +21 -10
  7. shinestacker/algorithms/vignetting.py +2 -0
  8. shinestacker/app/main.py +1 -1
  9. shinestacker/config/gui_constants.py +7 -2
  10. shinestacker/core/core_utils.py +10 -1
  11. shinestacker/gui/action_config.py +172 -7
  12. shinestacker/gui/action_config_dialog.py +246 -285
  13. shinestacker/gui/gui_run.py +2 -2
  14. shinestacker/gui/main_window.py +14 -5
  15. shinestacker/gui/menu_manager.py +26 -2
  16. shinestacker/gui/project_controller.py +4 -0
  17. shinestacker/gui/recent_file_manager.py +93 -0
  18. shinestacker/retouch/base_filter.py +13 -15
  19. shinestacker/retouch/brush_preview.py +3 -1
  20. shinestacker/retouch/brush_tool.py +11 -11
  21. shinestacker/retouch/display_manager.py +43 -59
  22. shinestacker/retouch/image_editor_ui.py +161 -82
  23. shinestacker/retouch/image_view_status.py +65 -0
  24. shinestacker/retouch/image_viewer.py +95 -431
  25. shinestacker/retouch/io_gui_handler.py +12 -2
  26. shinestacker/retouch/layer_collection.py +3 -0
  27. shinestacker/retouch/overlaid_view.py +215 -0
  28. shinestacker/retouch/shortcuts_help.py +13 -3
  29. shinestacker/retouch/sidebyside_view.py +477 -0
  30. shinestacker/retouch/transformation_manager.py +43 -0
  31. shinestacker/retouch/undo_manager.py +22 -3
  32. shinestacker/retouch/view_strategy.py +557 -0
  33. {shinestacker-1.3.1.dist-info → shinestacker-1.5.0.dist-info}/METADATA +7 -7
  34. {shinestacker-1.3.1.dist-info → shinestacker-1.5.0.dist-info}/RECORD +38 -32
  35. {shinestacker-1.3.1.dist-info → shinestacker-1.5.0.dist-info}/WHEEL +0 -0
  36. {shinestacker-1.3.1.dist-info → shinestacker-1.5.0.dist-info}/entry_points.txt +0 -0
  37. {shinestacker-1.3.1.dist-info → shinestacker-1.5.0.dist-info}/licenses/LICENSE +0 -0
  38. {shinestacker-1.3.1.dist-info → shinestacker-1.5.0.dist-info}/top_level.txt +0 -0
@@ -1,16 +1,15 @@
1
1
  # pylint: disable=C0114, C0115, C0116, E0611, R0913, R0917, R0915, R0912
2
- # pylint: disable=E0606, W0718, R1702, W0102, W0221, R0914
2
+ # pylint: disable=E0606, W0718, R1702, W0102, W0221, R0914, C0302
3
3
  import os
4
4
  import traceback
5
- from PySide6.QtWidgets import (QWidget, QPushButton, QHBoxLayout, QLabel, QScrollArea, QSizePolicy,
6
- QMessageBox, QStackedWidget, QFormLayout, QDialog, QTabWidget)
7
5
  from PySide6.QtCore import Qt, QTimer
6
+ from PySide6.QtWidgets import (QWidget, QPushButton, QHBoxLayout, QLabel, QScrollArea, QMessageBox,
7
+ QStackedWidget, QFormLayout, QDialog)
8
8
  from .. config.constants import constants
9
9
  from .. algorithms.align import validate_align_config
10
- from .project_model import ActionConfig
11
10
  from .base_form_dialog import create_form_layout
12
11
  from . action_config import (
13
- FieldBuilder, ActionConfigurator,
12
+ DefaultActionConfigurator,
14
13
  FIELD_TEXT, FIELD_ABS_PATH, FIELD_REL_PATH, FIELD_FLOAT,
15
14
  FIELD_INT, FIELD_INT_TUPLE, FIELD_BOOL, FIELD_COMBO, FIELD_REF_IDX
16
15
  )
@@ -18,7 +17,7 @@ from .folder_file_selection import FolderFileSelectionWidget
18
17
 
19
18
 
20
19
  class ActionConfigDialog(QDialog):
21
- def __init__(self, action: ActionConfig, current_wd, parent=None):
20
+ def __init__(self, action, current_wd, parent=None):
22
21
  super().__init__(parent)
23
22
  self.setWindowTitle(f"Configure {action.type_name}")
24
23
  self.form_layout = create_form_layout(self)
@@ -85,7 +84,7 @@ class ActionConfigDialog(QDialog):
85
84
  frame_geometry.moveCenter(center_point)
86
85
  self.move(frame_geometry.topLeft())
87
86
 
88
- def get_configurator(self, action_type: str) -> ActionConfigurator:
87
+ def get_configurator(self, action_type):
89
88
  configurators = {
90
89
  constants.ACTION_JOB: JobConfigurator,
91
90
  constants.ACTION_COMBO: CombinedActionsConfigurator,
@@ -115,83 +114,9 @@ class ActionConfigDialog(QDialog):
115
114
  return self.parent().expert_options
116
115
 
117
116
 
118
- class NoNameActionConfigurator(ActionConfigurator):
119
- def __init__(self, expert, current_wd):
120
- super().__init__(expert, current_wd)
121
- self.builder = None
122
-
123
- def get_builder(self):
124
- return self.builder
125
-
126
- def update_params(self, params):
127
- return self.builder.update_params(params)
128
-
129
- def add_bold_label(self, label):
130
- label = QLabel(label)
131
- label.setStyleSheet("font-weight: bold")
132
- self.add_row(label)
133
-
134
- def add_row(self, row):
135
- self.builder.main_layout.addRow(row)
136
-
137
- def add_field(self, tag, field_type, label,
138
- required=False, add_to_layout=None, **kwargs):
139
- return self.builder.add_field(tag, field_type, label, required, add_to_layout, **kwargs)
140
-
141
- def labelled_widget(self, label, widget):
142
- row = QWidget()
143
- layout = QHBoxLayout()
144
- layout.setContentsMargins(2, 2, 2, 2)
145
- layout.setSpacing(8)
146
- label_widget = QLabel(label)
147
- label_widget.setFixedWidth(120)
148
- label_widget.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
149
- layout.addWidget(label_widget)
150
- layout.addWidget(widget)
151
- layout.setStretch(0, 1)
152
- layout.setStretch(1, 3)
153
- row.setLayout(layout)
154
- return row
155
-
156
- def add_labelled_row(self, label, widget):
157
- self.add_row(self.labelled_widget(label, widget))
158
-
159
- def create_tab_widget(self, layout):
160
- tab_widget = QTabWidget()
161
- layout.addRow(tab_widget)
162
- return tab_widget
163
-
164
- def add_tab(self, tab_widget, title):
165
- tab = QWidget()
166
- tab_layout = QFormLayout()
167
- tab_layout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
168
- tab_layout.setRowWrapPolicy(QFormLayout.DontWrapRows)
169
- tab_layout.setFormAlignment(Qt.AlignLeft | Qt.AlignTop)
170
- tab_layout.setLabelAlignment(Qt.AlignLeft)
171
- tab.setLayout(tab_layout)
172
- tab_widget.addTab(tab, title)
173
- return tab_layout
174
-
175
- def add_field_to_layout(self, layout, tag, field_type, label, required=False, **kwargs):
176
- return self.add_field(tag, field_type, label, required, add_to_layout=layout, **kwargs)
177
-
178
- def add_bold_label_to_layout(self, layout, label):
179
- label_widget = QLabel(label)
180
- label_widget.setStyleSheet("font-weight: bold")
181
- layout.addRow(label_widget)
182
- return label_widget
183
-
184
-
185
- class DefaultActionConfigurator(NoNameActionConfigurator):
186
- def create_form(self, layout, action, tag='Action'):
187
- self.builder = FieldBuilder(layout, action, self.current_wd)
188
- self.add_field(
189
- 'name', FIELD_TEXT, f'{tag} name', required=True)
190
-
191
-
192
117
  class JobConfigurator(DefaultActionConfigurator):
193
118
  def __init__(self, expert, current_wd):
194
- super().__init__(expert, current_wd)
119
+ super().__init__(expert, current_wd, expert_toggle=False)
195
120
  self.working_path_label = None
196
121
  self.input_path_label = None
197
122
  self.frames_label = None
@@ -291,10 +216,10 @@ class NoiseDetectionConfigurator(DefaultActionConfigurator):
291
216
  required=False, size=3,
292
217
  default=constants.DEFAULT_CHANNEL_THRESHOLDS,
293
218
  labels=constants.RGB_LABELS, min_val=[1] * 3, max_val=[1000] * 3)
294
- if self.expert:
295
- self.add_field(
296
- 'blur_size', FIELD_INT, 'Blur size (px)', required=False,
297
- default=constants.DEFAULT_BLUR_SIZE, min_val=1, max_val=50)
219
+ self.add_field(
220
+ 'blur_size', FIELD_INT, 'Blur size (px)', required=False,
221
+ expert=True,
222
+ default=constants.DEFAULT_BLUR_SIZE, min_val=1, max_val=50)
298
223
  self.add_field(
299
224
  'file_name', FIELD_TEXT, 'File name', required=False,
300
225
  default=constants.DEFAULT_NOISE_MAP_FILENAME,
@@ -334,15 +259,17 @@ class FocusStackBaseConfigurator(DefaultActionConfigurator):
334
259
  self.create_algorithm_tab(self.algorithm_tab_layout)
335
260
 
336
261
  def create_general_tab(self, layout):
337
- if self.expert:
338
- self.add_field_to_layout(
339
- layout, 'working_path', FIELD_ABS_PATH, 'Working path', required=False)
340
- self.add_field_to_layout(
341
- layout, 'input_path', FIELD_REL_PATH, 'Input path', required=False,
342
- placeholder='relative to working path')
343
- self.add_field_to_layout(
344
- layout, 'output_path', FIELD_REL_PATH, 'Output path', required=False,
345
- placeholder='relative to working path')
262
+ self.add_field_to_layout(
263
+ layout, 'working_path', FIELD_ABS_PATH, 'Working path', required=False,
264
+ expert=True)
265
+ self.add_field_to_layout(
266
+ layout, 'input_path', FIELD_REL_PATH, 'Input path', required=False,
267
+ expert=True,
268
+ placeholder='relative to working path')
269
+ self.add_field_to_layout(
270
+ layout, 'output_path', FIELD_REL_PATH, 'Output path', required=False,
271
+ expert=True,
272
+ placeholder='relative to working path')
346
273
  self.add_field_to_layout(
347
274
  layout, 'scratch_output_dir', FIELD_BOOL, 'Scratch output dir.',
348
275
  required=False, default=True)
@@ -355,12 +282,7 @@ class FocusStackBaseConfigurator(DefaultActionConfigurator):
355
282
  default=constants.STACK_ALGO_DEFAULT)
356
283
  q_pyramid, q_depthmap = QWidget(), QWidget()
357
284
  for q in [q_pyramid, q_depthmap]:
358
- tab_layout = QFormLayout()
359
- tab_layout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
360
- tab_layout.setRowWrapPolicy(QFormLayout.DontWrapRows)
361
- tab_layout.setFormAlignment(Qt.AlignLeft | Qt.AlignTop)
362
- tab_layout.setLabelAlignment(Qt.AlignLeft)
363
- q.setLayout(tab_layout)
285
+ q.setLayout(self.create_tab_layout())
364
286
  stacked = QStackedWidget()
365
287
  stacked.addWidget(q_pyramid)
366
288
  stacked.addWidget(q_depthmap)
@@ -373,55 +295,63 @@ class FocusStackBaseConfigurator(DefaultActionConfigurator):
373
295
  stacked.setCurrentWidget(q_depthmap)
374
296
 
375
297
  change()
376
- if self.expert:
377
- self.add_field_to_layout(
378
- q_pyramid.layout(), 'pyramid_min_size', FIELD_INT, 'Minimum size (px)',
379
- required=False, default=constants.DEFAULT_PY_MIN_SIZE, min_val=2, max_val=256)
380
- self.add_field_to_layout(
381
- q_pyramid.layout(), 'pyramid_kernel_size', FIELD_INT, 'Kernel size (px)',
382
- required=False, default=constants.DEFAULT_PY_KERNEL_SIZE, min_val=3, max_val=21)
383
- self.add_field_to_layout(
384
- q_pyramid.layout(), 'pyramid_gen_kernel', FIELD_FLOAT, 'Gen. kernel',
385
- required=False, default=constants.DEFAULT_PY_GEN_KERNEL,
386
- min_val=0.0, max_val=2.0)
387
- self.add_field_to_layout(
388
- q_pyramid.layout(), 'pyramid_float_type', FIELD_COMBO, 'Precision', required=False,
389
- options=self.FLOAT_OPTIONS, values=constants.VALID_FLOATS,
390
- default=dict(zip(constants.VALID_FLOATS,
391
- self.FLOAT_OPTIONS))[constants.DEFAULT_PY_FLOAT])
392
- mode = self.add_field_to_layout(
393
- q_pyramid.layout(), 'pyramid_mode', FIELD_COMBO, 'Mode',
394
- required=False, options=self.MODE_OPTIONS, values=constants.PY_VALID_MODES,
395
- default=dict(zip(constants.PY_VALID_MODES,
396
- self.MODE_OPTIONS))[constants.DEFAULT_PY_MODE])
397
- memory_limit = self.add_field_to_layout(
398
- q_pyramid.layout(), 'pyramid_memory_limit', FIELD_FLOAT,
399
- 'Memory limit (approx., GBytes)',
400
- required=False, default=constants.DEFAULT_PY_MEMORY_LIMIT_GB,
401
- min_val=1.0, max_val=64.0)
402
- max_threads = self.add_field_to_layout(
403
- q_pyramid.layout(), 'pyramid_max_threads', FIELD_INT, 'Max num. of cores',
404
- required=False, default=constants.DEFAULT_PY_MAX_THREADS,
405
- min_val=1, max_val=64)
406
- tile_size = self.add_field_to_layout(
407
- q_pyramid.layout(), 'pyramid_tile_size', FIELD_INT, 'Tile size (px)',
408
- required=False, default=constants.DEFAULT_PY_TILE_SIZE,
409
- min_val=128, max_val=2048)
410
- n_tiled_layers = self.add_field_to_layout(
411
- q_pyramid.layout(), 'pyramid_n_tiled_layers', FIELD_INT, 'Num. tiled layers',
412
- required=False, default=constants.DEFAULT_PY_N_TILED_LAYERS,
413
- min_val=0, max_val=6)
414
-
415
- def change_mode():
416
- text = mode.currentText()
417
- enabled = text == self.MODE_OPTIONS[2]
418
- tile_size.setEnabled(enabled)
419
- n_tiled_layers.setEnabled(enabled)
420
- memory_limit.setEnabled(text == self.MODE_OPTIONS[0])
421
- max_threads.setEnabled(text != self.MODE_OPTIONS[1])
422
-
423
- mode.currentIndexChanged.connect(change_mode)
424
- change_mode()
298
+ self.add_field_to_layout(
299
+ q_pyramid.layout(), 'pyramid_min_size', FIELD_INT, 'Minimum size (px)',
300
+ expert=True,
301
+ required=False, default=constants.DEFAULT_PY_MIN_SIZE, min_val=2, max_val=256)
302
+ self.add_field_to_layout(
303
+ q_pyramid.layout(), 'pyramid_kernel_size', FIELD_INT, 'Kernel size (px)',
304
+ expert=True,
305
+ required=False, default=constants.DEFAULT_PY_KERNEL_SIZE, min_val=3, max_val=21)
306
+ self.add_field_to_layout(
307
+ q_pyramid.layout(), 'pyramid_gen_kernel', FIELD_FLOAT, 'Gen. kernel',
308
+ expert=True,
309
+ required=False, default=constants.DEFAULT_PY_GEN_KERNEL,
310
+ min_val=0.0, max_val=2.0)
311
+ self.add_field_to_layout(
312
+ q_pyramid.layout(), 'pyramid_float_type', FIELD_COMBO, 'Precision', required=False,
313
+ expert=True,
314
+ options=self.FLOAT_OPTIONS, values=constants.VALID_FLOATS,
315
+ default=dict(zip(constants.VALID_FLOATS,
316
+ self.FLOAT_OPTIONS))[constants.DEFAULT_PY_FLOAT])
317
+ mode = self.add_field_to_layout(
318
+ q_pyramid.layout(), 'pyramid_mode', FIELD_COMBO, 'Mode',
319
+ expert=True,
320
+ required=False, options=self.MODE_OPTIONS, values=constants.PY_VALID_MODES,
321
+ default=dict(zip(constants.PY_VALID_MODES,
322
+ self.MODE_OPTIONS))[constants.DEFAULT_PY_MODE])
323
+ memory_limit = self.add_field_to_layout(
324
+ q_pyramid.layout(), 'pyramid_memory_limit', FIELD_FLOAT,
325
+ 'Memory limit (approx., GBytes)',
326
+ expert=True,
327
+ required=False, default=constants.DEFAULT_PY_MEMORY_LIMIT_GB,
328
+ min_val=1.0, max_val=64.0)
329
+ max_threads = self.add_field_to_layout(
330
+ q_pyramid.layout(), 'pyramid_max_threads', FIELD_INT, 'Max num. of cores',
331
+ expert=True,
332
+ required=False, default=constants.DEFAULT_PY_MAX_THREADS,
333
+ min_val=1, max_val=64)
334
+ tile_size = self.add_field_to_layout(
335
+ q_pyramid.layout(), 'pyramid_tile_size', FIELD_INT, 'Tile size (px)',
336
+ expert=True,
337
+ required=False, default=constants.DEFAULT_PY_TILE_SIZE,
338
+ min_val=128, max_val=2048)
339
+ n_tiled_layers = self.add_field_to_layout(
340
+ q_pyramid.layout(), 'pyramid_n_tiled_layers', FIELD_INT, 'Num. tiled layers',
341
+ expert=True,
342
+ required=False, default=constants.DEFAULT_PY_N_TILED_LAYERS,
343
+ min_val=0, max_val=6)
344
+
345
+ def change_mode():
346
+ text = mode.currentText()
347
+ enabled = text == self.MODE_OPTIONS[2]
348
+ tile_size.setEnabled(enabled)
349
+ n_tiled_layers.setEnabled(enabled)
350
+ memory_limit.setEnabled(text == self.MODE_OPTIONS[0])
351
+ max_threads.setEnabled(text != self.MODE_OPTIONS[1])
352
+
353
+ mode.currentIndexChanged.connect(change_mode)
354
+ change_mode()
425
355
 
426
356
  self.add_field_to_layout(
427
357
  q_depthmap.layout(), 'depthmap_energy', FIELD_COMBO, 'Energy', required=False,
@@ -433,29 +363,34 @@ class FocusStackBaseConfigurator(DefaultActionConfigurator):
433
363
  options=self.MAP_TYPE_OPTIONS, values=constants.VALID_DM_MAP,
434
364
  default=dict(zip(constants.VALID_DM_MAP,
435
365
  self.MAP_TYPE_OPTIONS))[constants.DEFAULT_DM_MAP])
436
- if self.expert:
437
- self.add_field_to_layout(
438
- q_depthmap.layout(), 'depthmap_kernel_size', FIELD_INT, 'Kernel size (px)',
439
- required=False, default=constants.DEFAULT_DM_KERNEL_SIZE, min_val=3, max_val=21)
440
- self.add_field_to_layout(
441
- q_depthmap.layout(), 'depthmap_blur_size', FIELD_INT, 'Blurl size (px)',
442
- required=False, default=constants.DEFAULT_DM_BLUR_SIZE, min_val=1, max_val=21)
443
- self.add_field_to_layout(
444
- q_depthmap.layout(), 'depthmap_smooth_size', FIELD_INT, 'Smooth size (px)',
445
- required=False, default=constants.DEFAULT_DM_SMOOTH_SIZE, min_val=0, max_val=256)
446
- self.add_field_to_layout(
447
- q_depthmap.layout(), 'depthmap_temperature', FIELD_FLOAT, 'Temperature',
448
- required=False, default=constants.DEFAULT_DM_TEMPERATURE,
449
- min_val=0, max_val=1, step=0.05)
450
- self.add_field_to_layout(
451
- q_depthmap.layout(), 'depthmap_levels', FIELD_INT, 'Levels', required=False,
452
- default=constants.DEFAULT_DM_LEVELS, min_val=2, max_val=6)
453
- self.add_field_to_layout(
454
- q_depthmap.layout(), 'depthmap_float_type', FIELD_COMBO,
455
- 'Precision', required=False,
456
- options=self.FLOAT_OPTIONS, values=constants.VALID_FLOATS,
457
- default=dict(zip(constants.VALID_FLOATS,
458
- self.FLOAT_OPTIONS))[constants.DEFAULT_DM_FLOAT])
366
+ self.add_field_to_layout(
367
+ q_depthmap.layout(), 'depthmap_kernel_size', FIELD_INT, 'Kernel size (px)',
368
+ expert=True,
369
+ required=False, default=constants.DEFAULT_DM_KERNEL_SIZE, min_val=3, max_val=21)
370
+ self.add_field_to_layout(
371
+ q_depthmap.layout(), 'depthmap_blur_size', FIELD_INT, 'Blurl size (px)',
372
+ expert=True,
373
+ required=False, default=constants.DEFAULT_DM_BLUR_SIZE, min_val=1, max_val=21)
374
+ self.add_field_to_layout(
375
+ q_depthmap.layout(), 'depthmap_smooth_size', FIELD_INT, 'Smooth size (px)',
376
+ expert=True,
377
+ required=False, default=constants.DEFAULT_DM_SMOOTH_SIZE, min_val=0, max_val=256)
378
+ self.add_field_to_layout(
379
+ q_depthmap.layout(), 'depthmap_temperature', FIELD_FLOAT, 'Temperature',
380
+ expert=True,
381
+ required=False, default=constants.DEFAULT_DM_TEMPERATURE,
382
+ min_val=0, max_val=1, step=0.05)
383
+ self.add_field_to_layout(
384
+ q_depthmap.layout(), 'depthmap_levels', FIELD_INT, 'Levels', required=False,
385
+ expert=True,
386
+ default=constants.DEFAULT_DM_LEVELS, min_val=2, max_val=6)
387
+ self.add_field_to_layout(
388
+ q_depthmap.layout(), 'depthmap_float_type', FIELD_COMBO,
389
+ 'Precision', required=False,
390
+ expert=True,
391
+ options=self.FLOAT_OPTIONS, values=constants.VALID_FLOATS,
392
+ default=dict(zip(constants.VALID_FLOATS,
393
+ self.FLOAT_OPTIONS))[constants.DEFAULT_DM_FLOAT])
459
394
  layout.addRow(stacked)
460
395
  combo.currentIndexChanged.connect(change)
461
396
 
@@ -463,16 +398,17 @@ class FocusStackBaseConfigurator(DefaultActionConfigurator):
463
398
  class FocusStackConfigurator(FocusStackBaseConfigurator):
464
399
  def create_form(self, layout, action):
465
400
  super().create_form(layout, action)
466
- if self.expert:
467
- self.add_field_to_layout(
468
- self.general_tab_layout, 'exif_path', FIELD_REL_PATH,
469
- 'Exif data path', required=False,
470
- placeholder='relative to working path')
471
- self.add_field_to_layout(
472
- self.general_tab_layout, 'prefix', FIELD_TEXT,
473
- 'Output filename prefix', required=False,
474
- default=constants.DEFAULT_STACK_PREFIX,
475
- placeholder=constants.DEFAULT_STACK_PREFIX)
401
+ self.add_field_to_layout(
402
+ self.general_tab_layout, 'exif_path', FIELD_REL_PATH,
403
+ 'Exif data path', required=False,
404
+ expert=True,
405
+ placeholder='relative to working path')
406
+ self.add_field_to_layout(
407
+ self.general_tab_layout, 'prefix', FIELD_TEXT,
408
+ 'Output filename prefix', required=False,
409
+ expert=True,
410
+ default=constants.DEFAULT_STACK_PREFIX,
411
+ placeholder=constants.DEFAULT_STACK_PREFIX)
476
412
  self.add_field_to_layout(
477
413
  self.general_tab_layout, 'plot_stack', FIELD_BOOL, 'Plot stack', required=False,
478
414
  default=constants.DEFAULT_PLOT_STACK)
@@ -495,21 +431,22 @@ class FocusStackBunchConfigurator(FocusStackBaseConfigurator):
495
431
  class MultiLayerConfigurator(DefaultActionConfigurator):
496
432
  def create_form(self, layout, action):
497
433
  super().create_form(layout, action)
498
- if self.expert:
499
- self.add_field(
500
- 'working_path', FIELD_ABS_PATH, 'Working path', required=False)
434
+ self.add_field(
435
+ 'working_path', FIELD_ABS_PATH, 'Working path', required=False,
436
+ expert=True)
501
437
  self.add_field(
502
438
  'input_path', FIELD_REL_PATH,
503
439
  f'Input path (separate by {constants.PATH_SEPARATOR})',
504
440
  required=False, multiple_entries=True,
505
441
  placeholder='relative to working path')
506
- if self.expert:
507
- self.add_field(
508
- 'output_path', FIELD_REL_PATH, 'Output path', required=False,
509
- placeholder='relative to working path')
510
- self.add_field(
511
- 'exif_path', FIELD_REL_PATH, 'Exif data path', required=False,
512
- placeholder='relative to working path')
442
+ self.add_field(
443
+ 'output_path', FIELD_REL_PATH, 'Output path', required=False,
444
+ expert=True,
445
+ placeholder='relative to working path')
446
+ self.add_field(
447
+ 'exif_path', FIELD_REL_PATH, 'Exif data path', required=False,
448
+ expert=True,
449
+ placeholder='relative to working path')
513
450
  self.add_field(
514
451
  'scratch_output_dir', FIELD_BOOL, 'Scratch output dir.',
515
452
  required=False, default=True)
@@ -521,38 +458,45 @@ class MultiLayerConfigurator(DefaultActionConfigurator):
521
458
  class CombinedActionsConfigurator(DefaultActionConfigurator):
522
459
  def create_form(self, layout, action):
523
460
  super().create_form(layout, action)
524
- if self.expert:
525
- self.add_field(
526
- 'working_path', FIELD_ABS_PATH, 'Working path', required=False)
527
- self.add_field(
528
- 'input_path', FIELD_REL_PATH, 'Input path', required=False,
529
- must_exist=True, placeholder='relative to working path')
530
- self.add_field(
531
- 'output_path', FIELD_REL_PATH, 'Output path', required=False,
532
- placeholder='relative to working path')
461
+ self.add_field(
462
+ 'working_path', FIELD_ABS_PATH, 'Working path', required=False,
463
+ expert=True)
464
+ self.add_field(
465
+ 'input_path', FIELD_REL_PATH, 'Input path', required=False,
466
+ expert=True,
467
+ must_exist=True, placeholder='relative to working path')
468
+ self.add_field(
469
+ 'output_path', FIELD_REL_PATH, 'Output path', required=False,
470
+ expert=True,
471
+ placeholder='relative to working path')
533
472
  self.add_field(
534
473
  'scratch_output_dir', FIELD_BOOL, 'Scratch output dir.',
535
474
  required=False, default=True)
536
- if self.expert:
537
- self.add_field(
538
- 'plot_path', FIELD_REL_PATH, 'Plots path', required=False,
539
- default="plots", placeholder='relative to working path')
540
- self.add_field(
541
- 'resample', FIELD_INT, 'Resample frame stack', required=False,
542
- default=1, min_val=1, max_val=100)
543
- self.add_field(
544
- 'reference_index', FIELD_REF_IDX, 'Reference frame', required=False,
545
- default=0)
546
- self.add_field(
547
- 'step_process', FIELD_BOOL, 'Step process', required=False,
548
- default=True)
549
- self.add_field(
550
- 'max_threads', FIELD_INT, 'Max num. of cores',
551
- required=False, default=constants.DEFAULT_MAX_FWK_THREADS,
552
- min_val=1, max_val=64)
553
- self.add_field(
554
- 'chunk_submit', FIELD_BOOL, 'Submit in chunks',
555
- required=False, default=constants.DEFAULT_MAX_FWK_CHUNK_SUBMIT)
475
+ self.add_field(
476
+ 'plot_path', FIELD_REL_PATH, 'Plots path', required=False,
477
+ expert=True,
478
+ default="plots", placeholder='relative to working path')
479
+ self.add_field(
480
+ 'resample', FIELD_INT, 'Resample frame stack', required=False,
481
+ expert=True,
482
+ default=1, min_val=1, max_val=100)
483
+ self.add_field(
484
+ 'reference_index', FIELD_REF_IDX, 'Reference frame', required=False,
485
+ expert=True,
486
+ default=0)
487
+ self.add_field(
488
+ 'step_process', FIELD_BOOL, 'Step process', required=False,
489
+ expert=True,
490
+ default=True)
491
+ self.add_field(
492
+ 'max_threads', FIELD_INT, 'Max num. of cores',
493
+ required=False, default=constants.DEFAULT_MAX_FWK_THREADS,
494
+ expert=True,
495
+ min_val=1, max_val=64)
496
+ self.add_field(
497
+ 'chunk_submit', FIELD_BOOL, 'Submit in chunks',
498
+ expert=True,
499
+ required=False, default=constants.DEFAULT_MAX_FWK_CHUNK_SUBMIT)
556
500
 
557
501
 
558
502
  class MaskNoiseConfigurator(DefaultActionConfigurator):
@@ -563,13 +507,14 @@ class MaskNoiseConfigurator(DefaultActionConfigurator):
563
507
  path_type='file', must_exist=True,
564
508
  default=constants.DEFAULT_NOISE_MAP_FILENAME,
565
509
  placeholder=constants.DEFAULT_NOISE_MAP_FILENAME)
566
- if self.expert:
567
- self.add_field(
568
- 'kernel_size', FIELD_INT, 'Kernel size', required=False,
569
- default=constants.DEFAULT_MN_KERNEL_SIZE, min_val=1, max_val=10)
570
- self.add_field(
571
- 'method', FIELD_COMBO, 'Interpolation method', required=False,
572
- options=['Mean', 'Median'], default='Mean')
510
+ self.add_field(
511
+ 'kernel_size', FIELD_INT, 'Kernel size', required=False,
512
+ expert=True,
513
+ default=constants.DEFAULT_MN_KERNEL_SIZE, min_val=1, max_val=10)
514
+ self.add_field(
515
+ 'method', FIELD_COMBO, 'Interpolation method', required=False,
516
+ expert=True,
517
+ options=['Mean', 'Median'], default='Mean')
573
518
 
574
519
 
575
520
  class SubsampleActionConfigurator(DefaultActionConfigurator):
@@ -583,12 +528,14 @@ class SubsampleActionConfigurator(DefaultActionConfigurator):
583
528
  add_to_layout = self.builder.main_layout
584
529
  self.subsample_field = self.add_field(
585
530
  'subsample', FIELD_COMBO, 'Subsample', required=False,
531
+ expert=True,
586
532
  options=constants.FIELD_SUBSAMPLE_OPTIONS,
587
533
  values=constants.FIELD_SUBSAMPLE_VALUES,
588
534
  default=constants.FIELD_SUBSAMPLE_DEFAULT,
589
535
  add_to_layout=add_to_layout)
590
536
  self.fast_subsampling_field = self.add_field(
591
537
  'fast_subsampling', FIELD_BOOL, 'Fast subsampling', required=False,
538
+ expert=True,
592
539
  default=constants.DEFAULT_ALIGN_FAST_SUBSAMPLING,
593
540
  add_to_layout=add_to_layout)
594
541
 
@@ -657,13 +604,12 @@ class AlignFramesConfigurator(SubsampleActionConfigurator):
657
604
  self.descriptor_field = None
658
605
  self.matching_method_field = None
659
606
  self.tab_widget = self.create_tab_widget(layout)
660
- if self.expert:
661
- feature_layout = self.add_tab(self.tab_widget, "Feature extraction")
662
- self.create_feature_tab(feature_layout)
663
- transform_layout = self.add_tab(self.tab_widget, "Transform")
664
- self.create_transform_tab(transform_layout)
665
- border_layout = self.add_tab(self.tab_widget, "Border")
666
- self.create_border_tab(border_layout)
607
+ feature_layout = self.add_tab(self.tab_widget, "Feature extraction")
608
+ self.create_feature_tab(feature_layout)
609
+ transform_layout = self.add_tab(self.tab_widget, "Transform")
610
+ self.create_transform_tab(transform_layout)
611
+ border_layout = self.add_tab(self.tab_widget, "Border")
612
+ self.create_border_tab(border_layout)
667
613
  misc_layout = self.add_tab(self.tab_widget, "Miscellanea")
668
614
  self.create_miscellanea_tab(misc_layout)
669
615
 
@@ -699,18 +645,22 @@ class AlignFramesConfigurator(SubsampleActionConfigurator):
699
645
  self.matching_method_field.currentIndexChanged.connect(self.change_match_config)
700
646
  self.add_field_to_layout(
701
647
  layout, 'flann_idx_kdtree', FIELD_INT, 'Flann idx kdtree', required=False,
648
+ expert=True,
702
649
  default=constants.DEFAULT_FLANN_IDX_KDTREE,
703
650
  min_val=0, max_val=10)
704
651
  self.add_field_to_layout(
705
652
  layout, 'flann_trees', FIELD_INT, 'Flann trees', required=False,
653
+ expert=True,
706
654
  default=constants.DEFAULT_FLANN_TREES,
707
655
  min_val=0, max_val=10)
708
656
  self.add_field_to_layout(
709
657
  layout, 'flann_checks', FIELD_INT, 'Flann checks', required=False,
658
+ expert=True,
710
659
  default=constants.DEFAULT_FLANN_CHECKS,
711
660
  min_val=0, max_val=1000)
712
661
  self.add_field_to_layout(
713
662
  layout, 'threshold', FIELD_FLOAT, 'Threshold', required=False,
663
+ expert=True,
714
664
  default=constants.DEFAULT_ALIGN_THRESHOLD,
715
665
  min_val=0, max_val=1, step=0.05)
716
666
  self.add_subsample_fields(add_to_layout=layout)
@@ -727,9 +677,11 @@ class AlignFramesConfigurator(SubsampleActionConfigurator):
727
677
  default=constants.DEFAULT_ALIGN_METHOD)
728
678
  rans_threshold = self.add_field_to_layout(
729
679
  layout, 'rans_threshold', FIELD_FLOAT, 'RANSAC threshold (px)', required=False,
680
+ expert=True,
730
681
  default=constants.DEFAULT_RANS_THRESHOLD, min_val=0, max_val=20, step=0.1)
731
682
  self.add_field_to_layout(
732
683
  layout, 'min_good_matches', FIELD_INT, "Min. good matches", required=False,
684
+ expert=True,
733
685
  default=constants.DEFAULT_ALIGN_MIN_GOOD_MATCHES, min_val=0, max_val=500)
734
686
 
735
687
  def change_method():
@@ -744,13 +696,16 @@ class AlignFramesConfigurator(SubsampleActionConfigurator):
744
696
  self.add_field_to_layout(
745
697
  layout, 'align_confidence', FIELD_FLOAT, 'Confidence (%)',
746
698
  required=False, decimals=1,
699
+ expert=True,
747
700
  default=constants.DEFAULT_ALIGN_CONFIDENCE,
748
701
  min_val=70.0, max_val=100.0, step=0.1)
749
702
  refine_iters = self.add_field_to_layout(
750
703
  layout, 'refine_iters', FIELD_INT, 'Refinement iterations (Rigid)', required=False,
704
+ expert=True,
751
705
  default=constants.DEFAULT_REFINE_ITERS, min_val=0, max_val=1000)
752
706
  max_iters = self.add_field_to_layout(
753
707
  layout, 'max_iters', FIELD_INT, 'Max. iterations (Homography)', required=False,
708
+ expert=True,
754
709
  default=constants.DEFAULT_ALIGN_MAX_ITERS, min_val=0, max_val=5000)
755
710
 
756
711
  def change_transform():
@@ -766,6 +721,7 @@ class AlignFramesConfigurator(SubsampleActionConfigurator):
766
721
  change_transform()
767
722
  self.add_field_to_layout(
768
723
  layout, 'abort_abnormal', FIELD_BOOL, 'Abort on abnormal transf.',
724
+ expert=True,
769
725
  required=False, default=constants.DEFAULT_ALIGN_ABORT_ABNORMAL)
770
726
 
771
727
  def create_border_tab(self, layout):
@@ -778,46 +734,49 @@ class AlignFramesConfigurator(SubsampleActionConfigurator):
778
734
  self.add_field_to_layout(
779
735
  layout, 'border_value', FIELD_INT_TUPLE,
780
736
  'Border value (if constant)', required=False, size=4,
737
+ expert=True,
781
738
  default=constants.DEFAULT_BORDER_VALUE,
782
739
  labels=constants.RGBA_LABELS,
783
740
  min_val=constants.DEFAULT_BORDER_VALUE, max_val=[255] * 4)
784
741
  self.add_field_to_layout(
785
742
  layout, 'border_blur', FIELD_FLOAT, 'Border blur', required=False,
743
+ expert=True,
786
744
  default=constants.DEFAULT_BORDER_BLUR,
787
745
  min_val=0, max_val=1000, step=1)
788
746
 
789
747
  def create_miscellanea_tab(self, layout):
790
748
  self.add_bold_label_to_layout(layout, "Miscellanea:")
791
- if self.expert:
792
- mode = self.add_field_to_layout(
793
- layout, 'mode', FIELD_COMBO, 'Mode',
794
- required=False, options=self.MODE_OPTIONS, values=constants.ALIGN_VALID_MODES,
795
- default=dict(zip(constants.ALIGN_VALID_MODES,
796
- self.MODE_OPTIONS))[constants.DEFAULT_ALIGN_MODE])
797
- memory_limit = self.add_field_to_layout(
798
- layout, 'memory_limit', FIELD_FLOAT, 'Memory limit (approx., GBytes)',
799
- required=False, default=constants.DEFAULT_ALIGN_MEMORY_LIMIT_GB,
800
- min_val=1.0, max_val=64.0)
801
- max_threads = self.add_field_to_layout(
802
- layout, 'max_threads', FIELD_INT, 'Max num. of cores',
803
- required=False, default=constants.DEFAULT_ALIGN_MAX_THREADS,
804
- min_val=1, max_val=64)
805
- chunk_submit = self.add_field_to_layout(
806
- layout, 'chunk_submit', FIELD_BOOL, 'Submit in chunks',
807
- required=False, default=constants.DEFAULT_ALIGN_CHUNK_SUBMIT)
808
- bw_matching = self.add_field_to_layout(
809
- layout, 'bw_matching', FIELD_BOOL, 'Match using black & white',
810
- required=False, default=constants.DEFAULT_ALIGN_BW_MATCHING)
811
-
812
- def change_mode():
813
- text = mode.currentText()
814
- enabled = text != self.MODE_OPTIONS[1]
815
- memory_limit.setEnabled(enabled)
816
- max_threads.setEnabled(enabled)
817
- chunk_submit.setEnabled(enabled)
818
- bw_matching.setEnabled(enabled)
819
-
820
- mode.currentIndexChanged.connect(change_mode)
749
+ mode = self.add_field_to_layout(
750
+ layout, 'mode', FIELD_COMBO, 'Mode',
751
+ required=False, options=self.MODE_OPTIONS, values=constants.ALIGN_VALID_MODES,
752
+ default=dict(zip(constants.ALIGN_VALID_MODES,
753
+ self.MODE_OPTIONS))[constants.DEFAULT_ALIGN_MODE])
754
+ memory_limit = self.add_field_to_layout(
755
+ layout, 'memory_limit', FIELD_FLOAT, 'Memory limit (approx., GBytes)',
756
+ required=False, default=constants.DEFAULT_ALIGN_MEMORY_LIMIT_GB,
757
+ min_val=1.0, max_val=64.0)
758
+ max_threads = self.add_field_to_layout(
759
+ layout, 'max_threads', FIELD_INT, 'Max num. of cores',
760
+ required=False, default=constants.DEFAULT_ALIGN_MAX_THREADS,
761
+ min_val=1, max_val=64)
762
+ chunk_submit = self.add_field_to_layout(
763
+ layout, 'chunk_submit', FIELD_BOOL, 'Submit in chunks',
764
+ expert=True,
765
+ required=False, default=constants.DEFAULT_ALIGN_CHUNK_SUBMIT)
766
+ bw_matching = self.add_field_to_layout(
767
+ layout, 'bw_matching', FIELD_BOOL, 'Match using black & white',
768
+ expert=True,
769
+ required=False, default=constants.DEFAULT_ALIGN_BW_MATCHING)
770
+
771
+ def change_mode():
772
+ text = mode.currentText()
773
+ enabled = text != self.MODE_OPTIONS[1]
774
+ memory_limit.setEnabled(enabled)
775
+ max_threads.setEnabled(enabled)
776
+ chunk_submit.setEnabled(enabled)
777
+ bw_matching.setEnabled(enabled)
778
+
779
+ mode.currentIndexChanged.connect(change_mode)
821
780
 
822
781
  self.add_field_to_layout(
823
782
  layout, 'plot_summary', FIELD_BOOL, 'Plot summary',
@@ -851,16 +810,17 @@ class BalanceFramesConfigurator(SubsampleActionConfigurator):
851
810
 
852
811
  def create_form(self, layout, action):
853
812
  super().create_form(layout, action)
854
- if self.expert:
855
- self.add_field(
856
- 'mask_size', FIELD_FLOAT, 'Mask size', required=False,
857
- default=0, min_val=0, max_val=5, step=0.1)
858
- self.add_field(
859
- 'intensity_interval', FIELD_INT_TUPLE, 'Intensity range',
860
- required=False, size=2,
861
- default=[v for k, v in constants.DEFAULT_INTENSITY_INTERVAL.items()],
862
- labels=['min', 'max'], min_val=[-1] * 2, max_val=[65536] * 2)
863
- self.add_subsample_fields()
813
+ self.add_field(
814
+ 'mask_size', FIELD_FLOAT, 'Mask size', required=False,
815
+ expert=True,
816
+ default=0, min_val=0, max_val=5, step=0.1)
817
+ self.add_field(
818
+ 'intensity_interval', FIELD_INT_TUPLE, 'Intensity range',
819
+ required=False, size=2,
820
+ expert=True,
821
+ default=[v for k, v in constants.DEFAULT_INTENSITY_INTERVAL.items()],
822
+ labels=['min', 'max'], min_val=[-1] * 2, max_val=[65536] * 2)
823
+ self.add_subsample_fields()
864
824
  self.add_field(
865
825
  'corr_map', FIELD_COMBO, 'Correction map', required=False,
866
826
  options=self.CORRECTION_MAP_OPTIONS, values=constants.VALID_BALANCE,
@@ -881,15 +841,16 @@ class BalanceFramesConfigurator(SubsampleActionConfigurator):
881
841
  class VignettingConfigurator(SubsampleActionConfigurator):
882
842
  def create_form(self, layout, action):
883
843
  super().create_form(layout, action)
884
- if self.expert:
885
- self.add_field(
886
- 'r_steps', FIELD_INT, 'Radial steps', required=False,
887
- default=constants.DEFAULT_R_STEPS, min_val=1, max_val=1000)
888
- self.add_field(
889
- 'black_threshold', FIELD_INT, 'Black intensity threshold',
890
- required=False, default=constants.DEFAULT_BLACK_THRESHOLD,
891
- min_val=0, max_val=1000)
892
- self.add_subsample_fields()
844
+ self.add_field(
845
+ 'r_steps', FIELD_INT, 'Radial steps', required=False,
846
+ expert=True,
847
+ default=constants.DEFAULT_R_STEPS, min_val=1, max_val=1000)
848
+ self.add_field(
849
+ 'black_threshold', FIELD_INT, 'Black intensity threshold',
850
+ expert=True,
851
+ required=False, default=constants.DEFAULT_BLACK_THRESHOLD,
852
+ min_val=0, max_val=1000)
853
+ self.add_subsample_fields()
893
854
  self.add_field(
894
855
  'max_correction', FIELD_FLOAT, 'Max. correction', required=False,
895
856
  default=constants.DEFAULT_MAX_CORRECTION,