shinestacker 1.2.1__py3-none-any.whl → 1.3.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.
- shinestacker/_version.py +1 -1
- shinestacker/algorithms/align.py +126 -94
- shinestacker/algorithms/align_auto.py +64 -0
- shinestacker/algorithms/align_parallel.py +296 -0
- shinestacker/algorithms/balance.py +3 -1
- shinestacker/algorithms/base_stack_algo.py +11 -2
- shinestacker/algorithms/multilayer.py +8 -8
- shinestacker/algorithms/noise_detection.py +10 -10
- shinestacker/algorithms/pyramid.py +4 -4
- shinestacker/algorithms/pyramid_auto.py +16 -10
- shinestacker/algorithms/pyramid_tiles.py +19 -11
- shinestacker/algorithms/stack.py +21 -17
- shinestacker/algorithms/stack_framework.py +97 -46
- shinestacker/algorithms/vignetting.py +13 -10
- shinestacker/app/main.py +7 -3
- shinestacker/config/constants.py +60 -25
- shinestacker/config/gui_constants.py +1 -1
- shinestacker/core/core_utils.py +4 -0
- shinestacker/core/framework.py +104 -23
- shinestacker/gui/action_config.py +4 -5
- shinestacker/gui/action_config_dialog.py +152 -12
- shinestacker/gui/base_form_dialog.py +2 -2
- shinestacker/gui/folder_file_selection.py +101 -0
- shinestacker/gui/gui_run.py +12 -10
- shinestacker/gui/main_window.py +6 -1
- shinestacker/gui/new_project.py +171 -73
- shinestacker/gui/project_controller.py +10 -6
- shinestacker/gui/project_converter.py +4 -2
- shinestacker/gui/project_editor.py +37 -27
- shinestacker/gui/select_path_widget.py +1 -1
- shinestacker/gui/sys_mon.py +96 -0
- shinestacker/gui/time_progress_bar.py +4 -3
- shinestacker/retouch/exif_data.py +1 -1
- shinestacker/retouch/image_editor_ui.py +2 -0
- {shinestacker-1.2.1.dist-info → shinestacker-1.3.0.dist-info}/METADATA +6 -6
- {shinestacker-1.2.1.dist-info → shinestacker-1.3.0.dist-info}/RECORD +40 -36
- {shinestacker-1.2.1.dist-info → shinestacker-1.3.0.dist-info}/WHEEL +0 -0
- {shinestacker-1.2.1.dist-info → shinestacker-1.3.0.dist-info}/entry_points.txt +0 -0
- {shinestacker-1.2.1.dist-info → shinestacker-1.3.0.dist-info}/licenses/LICENSE +0 -0
- {shinestacker-1.2.1.dist-info → shinestacker-1.3.0.dist-info}/top_level.txt +0 -0
|
@@ -134,7 +134,8 @@ class Vignetting(SubAction):
|
|
|
134
134
|
self.corrections = None
|
|
135
135
|
|
|
136
136
|
def run_frame(self, idx, _ref_idx, img_0):
|
|
137
|
-
self.process.
|
|
137
|
+
self.process.print_message(
|
|
138
|
+
color_str(f"{self.process.idx_tot_str(idx)}: compute vignetting", "cyan"))
|
|
138
139
|
h, w = img_0.shape[:2]
|
|
139
140
|
self.w_2, self.h_2 = w / 2, h / 2
|
|
140
141
|
self.r_max = np.sqrt((w / 2)**2 + (h / 2)**2)
|
|
@@ -153,12 +154,13 @@ class Vignetting(SubAction):
|
|
|
153
154
|
return img_0
|
|
154
155
|
self.v0 = sigmoid_model(0, *params)
|
|
155
156
|
i0_fit, k_fit, r0_fit = params
|
|
156
|
-
self.process.
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
157
|
+
self.process.print_message(
|
|
158
|
+
color_str(f"{self.process.idx_tot_str(idx)}: vignetting model parameters: ", "cyan") +
|
|
159
|
+
color_str(f"i0={i0_fit / 2:.4f}, "
|
|
160
|
+
f"k={k_fit * self.r_max:.4f}, "
|
|
161
|
+
f"r0={r0_fit / self.r_max:.4f}",
|
|
162
|
+
"light_blue"),
|
|
163
|
+
level=logging.DEBUG)
|
|
162
164
|
if self.plot_correction:
|
|
163
165
|
plt.figure(figsize=constants.PLT_FIG_SIZE)
|
|
164
166
|
plt.plot(radii, intensities, label="image mean intensity")
|
|
@@ -175,12 +177,13 @@ class Vignetting(SubAction):
|
|
|
175
177
|
save_plot(plot_path)
|
|
176
178
|
plt.close('all')
|
|
177
179
|
self.process.callback(
|
|
178
|
-
|
|
180
|
+
constants.CALLBACK_SAVE_PLOT, self.process.id,
|
|
179
181
|
f"{self.process.name}: intensity\nframe {idx_str}", plot_path)
|
|
180
182
|
for i, p in enumerate(self.percentiles):
|
|
181
183
|
self.corrections[i][idx] = fsolve(lambda x: sigmoid_model(x, *params) /
|
|
182
184
|
self.v0 - p, r0_fit)[0]
|
|
183
|
-
self.process.
|
|
185
|
+
self.process.print_message(
|
|
186
|
+
color_str(f"{self.process.idx_tot_str(idx)}: correct vignetting", "cyan"))
|
|
184
187
|
return correct_vignetting(
|
|
185
188
|
img_0, self.max_correction, self.black_threshold, None, params, self.v0,
|
|
186
189
|
subsample, self.fast_subsampling)
|
|
@@ -224,5 +227,5 @@ class Vignetting(SubAction):
|
|
|
224
227
|
f"{self.process.name}-r0.pdf"
|
|
225
228
|
save_plot(plot_path)
|
|
226
229
|
plt.close('all')
|
|
227
|
-
self.process.callback(
|
|
230
|
+
self.process.callback(constants.CALLBACK_SAVE_PLOT, self.process.id,
|
|
228
231
|
f"{self.process.name}: vignetting", plot_path)
|
shinestacker/app/main.py
CHANGED
|
@@ -142,9 +142,12 @@ class MainApp(QMainWindow):
|
|
|
142
142
|
return app_menu
|
|
143
143
|
|
|
144
144
|
def quit(self):
|
|
145
|
-
self.retouch_window.quit()
|
|
146
|
-
|
|
145
|
+
if not self.retouch_window.quit():
|
|
146
|
+
return False
|
|
147
|
+
if not self.project_window.quit():
|
|
148
|
+
return False
|
|
147
149
|
self.close()
|
|
150
|
+
return True
|
|
148
151
|
|
|
149
152
|
def switch_app(self, index):
|
|
150
153
|
self.stacked_widget.setCurrentIndex(index)
|
|
@@ -192,7 +195,8 @@ class MainApp(QMainWindow):
|
|
|
192
195
|
class Application(QApplication):
|
|
193
196
|
def event(self, event):
|
|
194
197
|
if event.type() == QEvent.Quit and event.spontaneous():
|
|
195
|
-
self.
|
|
198
|
+
if not self.quit():
|
|
199
|
+
return True
|
|
196
200
|
return super().event(event)
|
|
197
201
|
|
|
198
202
|
|
shinestacker/config/constants.py
CHANGED
|
@@ -37,19 +37,25 @@ class _Constants:
|
|
|
37
37
|
ACTION_VIGNETTING = "Vignetting"
|
|
38
38
|
ACTION_ALIGNFRAMES = "AlignFrames"
|
|
39
39
|
ACTION_BALANCEFRAMES = "BalanceFrames"
|
|
40
|
-
SUB_ACTION_TYPES = [
|
|
41
|
-
|
|
40
|
+
SUB_ACTION_TYPES = [ACTION_ALIGNFRAMES, ACTION_BALANCEFRAMES,
|
|
41
|
+
ACTION_VIGNETTING, ACTION_MASKNOISE]
|
|
42
42
|
STACK_ALGO_PYRAMID = 'Pyramid'
|
|
43
43
|
STACK_ALGO_DEPTH_MAP = 'Depth map'
|
|
44
44
|
STACK_ALGO_OPTIONS = [STACK_ALGO_PYRAMID, STACK_ALGO_DEPTH_MAP]
|
|
45
45
|
STACK_ALGO_DEFAULT = STACK_ALGO_PYRAMID
|
|
46
|
-
DEFAULT_PLOTS_PATH = 'plots'
|
|
47
46
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
ACTION_ICONS = {
|
|
48
|
+
ACTION_JOB: '',
|
|
49
|
+
ACTION_COMBO: '',
|
|
50
|
+
ACTION_NOISEDETECTION: '',
|
|
51
|
+
ACTION_FOCUSSTACK: '',
|
|
52
|
+
ACTION_FOCUSSTACKBUNCH: '',
|
|
53
|
+
ACTION_MULTILAYER: '',
|
|
54
|
+
ACTION_MASKNOISE: '',
|
|
55
|
+
ACTION_VIGNETTING: '',
|
|
56
|
+
ACTION_ALIGNFRAMES: '',
|
|
57
|
+
ACTION_BALANCEFRAMES: ''
|
|
58
|
+
}
|
|
53
59
|
|
|
54
60
|
PATH_SEPARATOR = ';'
|
|
55
61
|
|
|
@@ -60,10 +66,39 @@ class _Constants:
|
|
|
60
66
|
LOG_COLOR_LEVEL_2 = 'magenta'
|
|
61
67
|
LOG_COLOR_LEVEL_3 = 'cyan'
|
|
62
68
|
|
|
69
|
+
STATUS_RUNNING = 1
|
|
70
|
+
STATUS_PAUSED = 2
|
|
71
|
+
STATUS_STOPPED = 3
|
|
72
|
+
|
|
73
|
+
RUN_COMPLETED = 0
|
|
74
|
+
RUN_ONGOING = 1
|
|
75
|
+
RUN_FAILED = 2
|
|
76
|
+
RUN_STOPPED = 3
|
|
77
|
+
|
|
78
|
+
CALLBACK_BEFORE_ACTION = 'before_action'
|
|
79
|
+
CALLBACK_AFTER_ACTION = 'after_action'
|
|
80
|
+
CALLBACK_STEP_COUNTS = 'step_counts'
|
|
81
|
+
CALLBACK_BEGIN_STEPS = 'begin_steps'
|
|
82
|
+
CALLBACK_END_STEPS = 'end_steps'
|
|
83
|
+
CALLBACK_AFTER_STEP = 'after_step'
|
|
84
|
+
CALLBACK_CHECK_RUNNING = 'check_running'
|
|
85
|
+
CALLBACK_SAVE_PLOT = 'save_plot'
|
|
86
|
+
CALLBACK_OPEN_APP = 'open_app'
|
|
87
|
+
|
|
63
88
|
DEFAULT_FILE_REVERSE_ORDER = False
|
|
64
89
|
DEFAULT_MULTILAYER_FILE_REVERSE_ORDER = True
|
|
65
90
|
MULTILAYER_WARNING_MEM_GB = 1
|
|
66
91
|
|
|
92
|
+
DEFAULT_PLOTS_PATH = 'plots'
|
|
93
|
+
DEFAULT_MAX_FWK_THREADS = 8
|
|
94
|
+
DEFAULT_MAX_FWK_CHUNK_SUBMIT = True
|
|
95
|
+
|
|
96
|
+
FIELD_SUBSAMPLE_VALUES_1 = [2, 3, 4, 6, 8, 12, 16, 24, 32]
|
|
97
|
+
FIELD_SUBSAMPLE_OPTIONS_1 = [f"1/{n} × 1/{n}" for n in FIELD_SUBSAMPLE_VALUES_1]
|
|
98
|
+
FIELD_SUBSAMPLE_VALUES = [0, 1] + FIELD_SUBSAMPLE_VALUES_1
|
|
99
|
+
FIELD_SUBSAMPLE_OPTIONS = ['Auto', 'Full resolution'] + FIELD_SUBSAMPLE_OPTIONS_1
|
|
100
|
+
FIELD_SUBSAMPLE_DEFAULT = FIELD_SUBSAMPLE_VALUES[0]
|
|
101
|
+
|
|
67
102
|
DEFAULT_NOISE_MAP_FILENAME = "noise-map/hot_pixels.png"
|
|
68
103
|
DEFAULT_NOISE_MAX_FRAMES = 10
|
|
69
104
|
DEFAULT_MN_KERNEL_SIZE = 3
|
|
@@ -104,9 +139,9 @@ class _Constants:
|
|
|
104
139
|
NOKNN_METHODS = {'detectors': [DETECTOR_ORB, DETECTOR_SURF, DETECTOR_AKAZE, DETECTOR_BRISK],
|
|
105
140
|
'descriptors': [DESCRIPTOR_ORB, DESCRIPTOR_AKAZE, DESCRIPTOR_BRISK]}
|
|
106
141
|
|
|
107
|
-
DEFAULT_DETECTOR =
|
|
108
|
-
DEFAULT_DESCRIPTOR =
|
|
109
|
-
DEFAULT_MATCHING_METHOD =
|
|
142
|
+
DEFAULT_DETECTOR = DETECTOR_ORB
|
|
143
|
+
DEFAULT_DESCRIPTOR = DESCRIPTOR_ORB
|
|
144
|
+
DEFAULT_MATCHING_METHOD = MATCHING_NORM_HAMMING
|
|
110
145
|
DEFAULT_FLANN_IDX_KDTREE = 2
|
|
111
146
|
DEFAULT_FLANN_TREES = 5
|
|
112
147
|
DEFAULT_FLANN_CHECKS = 50
|
|
@@ -124,7 +159,13 @@ class _Constants:
|
|
|
124
159
|
DEFAULT_ALIGN_SUBSAMPLE = 0
|
|
125
160
|
DEFAULT_ALIGN_RES_TARGET_MPX = 2
|
|
126
161
|
DEFAULT_ALIGN_FAST_SUBSAMPLING = False
|
|
127
|
-
DEFAULT_ALIGN_MIN_GOOD_MATCHES =
|
|
162
|
+
DEFAULT_ALIGN_MIN_GOOD_MATCHES = 20
|
|
163
|
+
ALIGN_VALID_MODES = ['auto', 'sequential', 'parallel']
|
|
164
|
+
DEFAULT_ALIGN_MODE = 'auto'
|
|
165
|
+
DEFAULT_ALIGN_MEMORY_LIMIT_GB = 8
|
|
166
|
+
DEFAULT_ALIGN_MAX_THREADS = min(os.cpu_count() or 4, 8)
|
|
167
|
+
DEFAULT_ALIGN_CHUNK_SUBMIT = True
|
|
168
|
+
DEFAULT_ALIGN_BW_MATCHING = False
|
|
128
169
|
|
|
129
170
|
BALANCE_LINEAR = "LINEAR"
|
|
130
171
|
BALANCE_GAMMA = "GAMMA"
|
|
@@ -162,13 +203,13 @@ class _Constants:
|
|
|
162
203
|
DEFAULT_STACK_PREFIX = "stack_"
|
|
163
204
|
DEFAULT_BUNCH_PREFIX = "bunch_"
|
|
164
205
|
|
|
165
|
-
DEFAULT_DM_FLOAT = FLOAT_32
|
|
166
206
|
DM_ENERGY_LAPLACIAN = "laplacian"
|
|
167
207
|
DM_ENERGY_SOBEL = "sobel"
|
|
168
208
|
DM_MAP_AVERAGE = "average"
|
|
169
209
|
DM_MAP_MAX = "max"
|
|
170
210
|
VALID_DM_MAP = [DM_MAP_AVERAGE, DM_MAP_MAX]
|
|
171
211
|
VALID_DM_ENERGY = [DM_ENERGY_LAPLACIAN, DM_ENERGY_SOBEL]
|
|
212
|
+
DEFAULT_DM_FLOAT = FLOAT_32
|
|
172
213
|
DEFAULT_DM_MAP = DM_MAP_AVERAGE
|
|
173
214
|
DEFAULT_DM_ENERGY = DM_ENERGY_LAPLACIAN
|
|
174
215
|
DEFAULT_DM_KERNEL_SIZE = 5
|
|
@@ -185,22 +226,16 @@ class _Constants:
|
|
|
185
226
|
DEFAULT_PY_N_TILED_LAYERS = 2
|
|
186
227
|
DEFAULT_PY_MEMORY_LIMIT_GB = 8
|
|
187
228
|
DEFAULT_PY_MAX_THREADS = min(os.cpu_count() or 4, 8)
|
|
188
|
-
DEFAULT_PY_MODE = 'auto'
|
|
189
229
|
PY_VALID_MODES = ['auto', 'memory', 'tiled']
|
|
190
|
-
|
|
230
|
+
DEFAULT_PY_MODE = 'auto'
|
|
231
|
+
DEFAULT_PY_MAX_TILE_SIZE = 4096
|
|
232
|
+
DEFAULT_PY_MIN_TILE_SIZE = 128
|
|
233
|
+
DEFAULT_PY_MIN_N_TILED_LAYERS = 1
|
|
234
|
+
PY_MEMORY_OVERHEAD = 2.5
|
|
191
235
|
|
|
192
|
-
DEFAULT_PLOT_STACK_BUNCH =
|
|
236
|
+
DEFAULT_PLOT_STACK_BUNCH = True
|
|
193
237
|
DEFAULT_PLOT_STACK = True
|
|
194
238
|
|
|
195
|
-
STATUS_RUNNING = 1
|
|
196
|
-
STATUS_PAUSED = 2
|
|
197
|
-
STATUS_STOPPED = 3
|
|
198
|
-
|
|
199
|
-
RUN_COMPLETED = 0
|
|
200
|
-
RUN_ONGOING = 1
|
|
201
|
-
RUN_FAILED = 2
|
|
202
|
-
RUN_STOPPED = 3
|
|
203
|
-
|
|
204
239
|
def __setattr__aux(self, name, value):
|
|
205
240
|
raise AttributeError(f"Can't reassign constant '{name}'")
|
|
206
241
|
|
|
@@ -16,7 +16,7 @@ class _GuiConstants:
|
|
|
16
16
|
NEW_PROJECT_ALIGN_FRAMES = True
|
|
17
17
|
NEW_PROJECT_BALANCE_FRAMES = True
|
|
18
18
|
NEW_PROJECT_BUNCH_STACK = False
|
|
19
|
-
NEW_PROJECT_BUNCH_FRAMES = {'min':
|
|
19
|
+
NEW_PROJECT_BUNCH_FRAMES = {'min': 1, 'max': 20}
|
|
20
20
|
NEW_PROJECT_BUNCH_OVERLAP = {'min': 0, 'max': 10}
|
|
21
21
|
NEW_PROJECT_FOCUS_STACK_PYRAMID = True
|
|
22
22
|
NEW_PROJECT_FOCUS_STACK_DEPTH_MAP = False
|
shinestacker/core/core_utils.py
CHANGED
shinestacker/core/framework.py
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
# pylint: disable=C0114, C0115, C0116, R0917, R0913, R0902
|
|
1
|
+
# pylint: disable=C0114, C0115, C0116, R0917, R0913, R0902, W0718
|
|
2
2
|
import time
|
|
3
3
|
import logging
|
|
4
|
+
import traceback
|
|
5
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
4
6
|
from .. config.constants import constants
|
|
5
7
|
from .. config.config import config
|
|
6
8
|
from .colors import color_str
|
|
7
9
|
from .logging import setup_logging
|
|
8
|
-
from .core_utils import make_tqdm_bar
|
|
10
|
+
from .core_utils import make_tqdm_bar, make_chunks
|
|
9
11
|
from .exceptions import RunStopException
|
|
10
12
|
|
|
11
13
|
LINE_UP = "\r\033[A"
|
|
12
|
-
TRAILING_SPACES = " " *
|
|
14
|
+
TRAILING_SPACES = " " * 50
|
|
13
15
|
|
|
14
16
|
|
|
15
17
|
class TqdmCallbacks:
|
|
@@ -62,7 +64,7 @@ def elapsed_time_str(start):
|
|
|
62
64
|
return f"{hh:02d}:{mm:02d}:{ss:05.2f}s"
|
|
63
65
|
|
|
64
66
|
|
|
65
|
-
class
|
|
67
|
+
class TaskBase:
|
|
66
68
|
def __init__(self, name, enabled=True):
|
|
67
69
|
self.id = -1
|
|
68
70
|
self.name = name
|
|
@@ -77,8 +79,7 @@ class JobBase:
|
|
|
77
79
|
self.begin_r, self.end_r = LINE_UP, None
|
|
78
80
|
|
|
79
81
|
def callback(self, key, *args):
|
|
80
|
-
|
|
81
|
-
if has_callbacks and self.callbacks is not None:
|
|
82
|
+
if self.callbacks is not None:
|
|
82
83
|
callback = self.callbacks.get(key, None)
|
|
83
84
|
if callback:
|
|
84
85
|
return callback(*args)
|
|
@@ -92,9 +93,9 @@ class JobBase:
|
|
|
92
93
|
if not self.enabled:
|
|
93
94
|
self.get_logger().warning(color_str(self.name + ": entire job disabled",
|
|
94
95
|
constants.LOG_COLOR_ALERT))
|
|
95
|
-
self.callback(
|
|
96
|
+
self.callback(constants.CALLBACK_BEFORE_ACTION, self.id, self.name)
|
|
96
97
|
self.run_core()
|
|
97
|
-
self.callback(
|
|
98
|
+
self.callback(constants.CALLBACK_AFTER_ACTION, self.id, self.name)
|
|
98
99
|
msg_name = color_str(self.name + ":", constants.LOG_COLOR_LEVEL_JOB, "bold")
|
|
99
100
|
msg_time = color_str(f"elapsed time: {elapsed_time_str(self._t0)}",
|
|
100
101
|
constants.LOG_COLOR_LEVEL_JOB)
|
|
@@ -146,9 +147,9 @@ class JobBase:
|
|
|
146
147
|
self.sub_message(msg, level, self.end_r, self.begin_r, False)
|
|
147
148
|
|
|
148
149
|
|
|
149
|
-
class Job(
|
|
150
|
+
class Job(TaskBase):
|
|
150
151
|
def __init__(self, name, logger_name=None, log_file='', callbacks=None, **kwargs):
|
|
151
|
-
|
|
152
|
+
TaskBase.__init__(self, name, **kwargs)
|
|
152
153
|
self.action_counter = 0
|
|
153
154
|
self.__actions = []
|
|
154
155
|
if logger_name is None:
|
|
@@ -163,7 +164,7 @@ class Job(JobBase):
|
|
|
163
164
|
def init(self, a):
|
|
164
165
|
pass
|
|
165
166
|
|
|
166
|
-
def add_action(self, a:
|
|
167
|
+
def add_action(self, a: TaskBase):
|
|
167
168
|
a.id = self.action_counter
|
|
168
169
|
self.action_counter += 1
|
|
169
170
|
a.logger = self.logger
|
|
@@ -183,47 +184,127 @@ class Job(JobBase):
|
|
|
183
184
|
self.get_logger().warning(color_str(a.name + f": {msg} disabled",
|
|
184
185
|
constants.LOG_COLOR_ALERT))
|
|
185
186
|
else:
|
|
186
|
-
if self.callback(
|
|
187
|
+
if self.callback(constants.CALLBACK_CHECK_RUNNING,
|
|
188
|
+
self.id, self.name) is False:
|
|
187
189
|
raise RunStopException(self.name)
|
|
188
190
|
a.run()
|
|
189
191
|
|
|
190
192
|
|
|
191
|
-
class
|
|
193
|
+
class SequentialTask(TaskBase):
|
|
192
194
|
def __init__(self, name, enabled=True, **kwargs):
|
|
193
|
-
|
|
195
|
+
self.max_threads = kwargs.pop('max_threads', constants.DEFAULT_MAX_FWK_THREADS)
|
|
196
|
+
self.chunk_submit = kwargs.pop('chunk_submit', constants.DEFAULT_MAX_FWK_CHUNK_SUBMIT)
|
|
197
|
+
TaskBase.__init__(self, name, enabled, **kwargs)
|
|
194
198
|
self.total_action_counts = None
|
|
195
199
|
self.current_action_count = None
|
|
200
|
+
self.begin_steps = 0
|
|
196
201
|
|
|
197
202
|
def set_counts(self, counts):
|
|
198
203
|
self.total_action_counts = counts
|
|
199
|
-
self.callback(
|
|
204
|
+
self.callback(constants.CALLBACK_STEP_COUNTS,
|
|
205
|
+
self.id, self.name, self.total_action_counts)
|
|
206
|
+
|
|
207
|
+
def add_begin_steps(self, steps):
|
|
208
|
+
self.begin_steps += steps
|
|
200
209
|
|
|
201
210
|
def begin(self):
|
|
202
|
-
self.callback(
|
|
211
|
+
self.callback(constants.CALLBACK_BEGIN_STEPS, self.id, self.name)
|
|
203
212
|
|
|
204
213
|
def end(self):
|
|
205
|
-
self.callback(
|
|
214
|
+
self.callback(constants.CALLBACK_END_STEPS, self.id, self.name)
|
|
206
215
|
|
|
207
216
|
def __iter__(self):
|
|
208
217
|
self.current_action_count = 0
|
|
209
218
|
return self
|
|
210
219
|
|
|
211
|
-
def run_step(self):
|
|
220
|
+
def run_step(self, action_count=-1):
|
|
212
221
|
pass
|
|
213
222
|
|
|
214
223
|
def __next__(self):
|
|
215
224
|
if self.current_action_count < self.total_action_counts:
|
|
216
|
-
self.run_step()
|
|
225
|
+
self.run_step(self.current_action_count)
|
|
217
226
|
x = self.current_action_count
|
|
218
227
|
self.current_action_count += 1
|
|
219
228
|
return x
|
|
220
229
|
raise StopIteration
|
|
221
230
|
|
|
231
|
+
def check_running(self):
|
|
232
|
+
if self.callback(constants.CALLBACK_CHECK_RUNNING,
|
|
233
|
+
self.id, self.name) is False:
|
|
234
|
+
raise RunStopException(self.name)
|
|
235
|
+
|
|
236
|
+
def after_step(self, step=-1):
|
|
237
|
+
if step == -1:
|
|
238
|
+
step = self.current_action_count + self.begin_steps
|
|
239
|
+
self.callback(constants.CALLBACK_AFTER_STEP, self.id, self.name, step)
|
|
240
|
+
|
|
241
|
+
def run_core_serial(self):
|
|
242
|
+
for _ in iter(self):
|
|
243
|
+
self.after_step()
|
|
244
|
+
self.check_running()
|
|
245
|
+
|
|
246
|
+
def idx_tot_str(self, idx):
|
|
247
|
+
return f"{idx + 1}/{self.total_action_counts}"
|
|
248
|
+
|
|
249
|
+
def run_core_parallel_single_chunk(self, idx_chunk):
|
|
250
|
+
with ThreadPoolExecutor(max_workers=self.max_threads) as executor:
|
|
251
|
+
future_to_index = {}
|
|
252
|
+
for idx in idx_chunk:
|
|
253
|
+
self.print_message(color_str(
|
|
254
|
+
f"submit processing step: {self.idx_tot_str(idx)}",
|
|
255
|
+
constants.LOG_COLOR_LEVEL_1))
|
|
256
|
+
future = executor.submit(self.run_step, idx)
|
|
257
|
+
future_to_index[future] = idx
|
|
258
|
+
self.check_running()
|
|
259
|
+
for future in as_completed(future_to_index):
|
|
260
|
+
idx = future_to_index[future]
|
|
261
|
+
try:
|
|
262
|
+
result = future.result()
|
|
263
|
+
if result:
|
|
264
|
+
self.print_message(color_str(
|
|
265
|
+
f"completed processing step: {self.idx_tot_str(idx)}",
|
|
266
|
+
constants.LOG_COLOR_LEVEL_1))
|
|
267
|
+
else:
|
|
268
|
+
self.print_message(color_str(
|
|
269
|
+
f"failed processing step: {self.idx_tot_str(idx)}",
|
|
270
|
+
constants.LOG_COLOR_WARNING))
|
|
271
|
+
self.current_action_count += 1
|
|
272
|
+
self.after_step()
|
|
273
|
+
self.check_running()
|
|
274
|
+
except RunStopException as e:
|
|
275
|
+
raise e
|
|
276
|
+
except Exception as e:
|
|
277
|
+
traceback.print_tb(e.__traceback__)
|
|
278
|
+
self.print_message(color_str(
|
|
279
|
+
f"failed processing step: {idx + 1}: {str(e)}",
|
|
280
|
+
constants.LOG_COLOR_ALERT))
|
|
281
|
+
|
|
282
|
+
def run_core_parallel(self):
|
|
283
|
+
self.current_action_count = 0
|
|
284
|
+
self.run_core_parallel_single_chunk(list(range(self.total_action_counts)))
|
|
285
|
+
|
|
286
|
+
def run_core_parallel_chunks(self):
|
|
287
|
+
self.current_action_count = 0
|
|
288
|
+
action_idx_list = list(range(self.total_action_counts))
|
|
289
|
+
max_chunck_size = self.max_threads
|
|
290
|
+
action_idx_chunks = make_chunks(action_idx_list, max_chunck_size)
|
|
291
|
+
for idx_chunk in action_idx_chunks:
|
|
292
|
+
self.run_core_parallel_single_chunk(idx_chunk)
|
|
293
|
+
|
|
222
294
|
def run_core(self):
|
|
223
295
|
self.print_message(color_str('begin run', constants.LOG_COLOR_LEVEL_2), end='\n')
|
|
224
296
|
self.begin()
|
|
225
|
-
|
|
226
|
-
self.
|
|
227
|
-
|
|
228
|
-
|
|
297
|
+
if self.run_sequential():
|
|
298
|
+
self.run_core_serial()
|
|
299
|
+
else:
|
|
300
|
+
if self.chunk_submit:
|
|
301
|
+
self.run_core_parallel_chunks()
|
|
302
|
+
else:
|
|
303
|
+
self.run_core_parallel()
|
|
229
304
|
self.end()
|
|
305
|
+
|
|
306
|
+
def sequential_processing(self):
|
|
307
|
+
return False
|
|
308
|
+
|
|
309
|
+
def run_sequential(self):
|
|
310
|
+
return self.sequential_processing() or self.max_threads == 1
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# pylint: disable=C0114, C0115, C0116, E0611, R0913, R0917, R0915, R0912
|
|
2
|
-
# pylint: disable=E0606, W0718, R1702, W0102, W0221
|
|
2
|
+
# pylint: disable=E0606, W0718, R1702, W0102, W0221, R0914, E1121
|
|
3
3
|
import traceback
|
|
4
4
|
from abc import ABC, abstractmethod
|
|
5
|
-
from typing import Dict, Any
|
|
6
5
|
import os.path
|
|
7
6
|
from PySide6.QtWidgets import (QPushButton, QHBoxLayout, QFileDialog, QLabel, QComboBox,
|
|
8
7
|
QMessageBox, QSizePolicy, QLineEdit, QSpinBox,
|
|
@@ -128,7 +127,7 @@ class FieldBuilder:
|
|
|
128
127
|
spinbox = widget.layout().itemAt(1 + i * 2).widget()
|
|
129
128
|
spinbox.setValue(default[i])
|
|
130
129
|
elif field['type'] == FIELD_COMBO:
|
|
131
|
-
widget.setCurrentText(default)
|
|
130
|
+
widget.setCurrentText(str(default))
|
|
132
131
|
|
|
133
132
|
def get_path_widget(self, widget):
|
|
134
133
|
return widget.layout().itemAt(0).widget()
|
|
@@ -145,7 +144,7 @@ class FieldBuilder:
|
|
|
145
144
|
parent = parent.parent
|
|
146
145
|
return ''
|
|
147
146
|
|
|
148
|
-
def update_params(self, params
|
|
147
|
+
def update_params(self, params):
|
|
149
148
|
for tag, field in self.fields.items():
|
|
150
149
|
if field['type'] == FIELD_TEXT:
|
|
151
150
|
params[tag] = field['widget'].text()
|
|
@@ -224,7 +223,7 @@ class FieldBuilder:
|
|
|
224
223
|
self.action.params.get(tag, ''),
|
|
225
224
|
kwargs.get('placeholder', ''),
|
|
226
225
|
tag.replace('_', ' ')
|
|
227
|
-
)
|
|
226
|
+
)
|
|
228
227
|
|
|
229
228
|
def create_rel_path_field(self, tag, **kwargs):
|
|
230
229
|
value = self.action.params.get(tag, kwargs.get('default', ''))
|