shinestacker 0.3.3__py3-none-any.whl → 0.3.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.

Files changed (71) hide show
  1. shinestacker/__init__.py +2 -1
  2. shinestacker/_version.py +1 -1
  3. shinestacker/algorithms/__init__.py +3 -2
  4. shinestacker/algorithms/align.py +102 -64
  5. shinestacker/algorithms/balance.py +89 -42
  6. shinestacker/algorithms/base_stack_algo.py +42 -0
  7. shinestacker/algorithms/core_utils.py +6 -6
  8. shinestacker/algorithms/denoise.py +4 -1
  9. shinestacker/algorithms/depth_map.py +28 -39
  10. shinestacker/algorithms/exif.py +43 -38
  11. shinestacker/algorithms/multilayer.py +48 -28
  12. shinestacker/algorithms/noise_detection.py +34 -23
  13. shinestacker/algorithms/pyramid.py +42 -42
  14. shinestacker/algorithms/sharpen.py +1 -0
  15. shinestacker/algorithms/stack.py +42 -41
  16. shinestacker/algorithms/stack_framework.py +111 -65
  17. shinestacker/algorithms/utils.py +12 -11
  18. shinestacker/algorithms/vignetting.py +48 -22
  19. shinestacker/algorithms/white_balance.py +1 -0
  20. shinestacker/app/about_dialog.py +6 -2
  21. shinestacker/app/app_config.py +1 -0
  22. shinestacker/app/gui_utils.py +20 -0
  23. shinestacker/app/help_menu.py +1 -0
  24. shinestacker/app/main.py +9 -18
  25. shinestacker/app/open_frames.py +5 -4
  26. shinestacker/app/project.py +5 -16
  27. shinestacker/app/retouch.py +5 -17
  28. shinestacker/core/colors.py +4 -4
  29. shinestacker/core/core_utils.py +1 -1
  30. shinestacker/core/exceptions.py +2 -1
  31. shinestacker/core/framework.py +46 -33
  32. shinestacker/core/logging.py +9 -10
  33. shinestacker/gui/action_config.py +253 -197
  34. shinestacker/gui/actions_window.py +32 -28
  35. shinestacker/gui/colors.py +1 -0
  36. shinestacker/gui/gui_images.py +7 -3
  37. shinestacker/gui/gui_logging.py +3 -2
  38. shinestacker/gui/gui_run.py +53 -38
  39. shinestacker/gui/main_window.py +69 -25
  40. shinestacker/gui/new_project.py +35 -2
  41. shinestacker/gui/project_converter.py +21 -20
  42. shinestacker/gui/project_editor.py +45 -52
  43. shinestacker/gui/project_model.py +15 -23
  44. shinestacker/retouch/{filter_base.py → base_filter.py} +7 -4
  45. shinestacker/retouch/brush.py +1 -0
  46. shinestacker/retouch/brush_gradient.py +17 -3
  47. shinestacker/retouch/brush_preview.py +14 -10
  48. shinestacker/retouch/brush_tool.py +28 -19
  49. shinestacker/retouch/denoise_filter.py +3 -2
  50. shinestacker/retouch/display_manager.py +11 -5
  51. shinestacker/retouch/exif_data.py +1 -0
  52. shinestacker/retouch/file_loader.py +13 -9
  53. shinestacker/retouch/filter_manager.py +1 -0
  54. shinestacker/retouch/image_editor.py +14 -48
  55. shinestacker/retouch/image_editor_ui.py +10 -5
  56. shinestacker/retouch/image_filters.py +4 -2
  57. shinestacker/retouch/image_viewer.py +33 -31
  58. shinestacker/retouch/io_gui_handler.py +25 -13
  59. shinestacker/retouch/io_manager.py +3 -2
  60. shinestacker/retouch/layer_collection.py +79 -23
  61. shinestacker/retouch/shortcuts_help.py +1 -0
  62. shinestacker/retouch/undo_manager.py +7 -0
  63. shinestacker/retouch/unsharp_mask_filter.py +3 -2
  64. shinestacker/retouch/white_balance_filter.py +11 -6
  65. {shinestacker-0.3.3.dist-info → shinestacker-0.3.4.dist-info}/METADATA +10 -4
  66. shinestacker-0.3.4.dist-info/RECORD +86 -0
  67. shinestacker-0.3.3.dist-info/RECORD +0 -85
  68. {shinestacker-0.3.3.dist-info → shinestacker-0.3.4.dist-info}/WHEEL +0 -0
  69. {shinestacker-0.3.3.dist-info → shinestacker-0.3.4.dist-info}/entry_points.txt +0 -0
  70. {shinestacker-0.3.3.dist-info → shinestacker-0.3.4.dist-info}/licenses/LICENSE +0 -0
  71. {shinestacker-0.3.3.dist-info → shinestacker-0.3.4.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,9 @@
1
+ # pylint: disable=C0114, C0115, C0116, E0611, R0914, R0912, R0915, W0718
1
2
  import os.path
2
3
  import os
4
+ import traceback
3
5
  import json
4
6
  import jsonpickle
5
- import traceback
6
7
  from PySide6.QtWidgets import QMessageBox, QFileDialog, QDialog
7
8
  from .. core.core_utils import get_app_base_path
8
9
  from .. config.constants import constants
@@ -31,7 +32,7 @@ class ActionsWindow(ProjectEditor):
31
32
 
32
33
  def mark_as_modified(self):
33
34
  self._modified_project = True
34
- self._project_buffer.append(self.project.clone())
35
+ self.project_buffer.append(self.project.clone())
35
36
  self.update_title()
36
37
 
37
38
  def close_project(self):
@@ -123,19 +124,21 @@ class ActionsWindow(ProjectEditor):
123
124
  if not self._check_unsaved_changes():
124
125
  return
125
126
  if file_path is False:
126
- file_path, _ = QFileDialog.getOpenFileName(self, "Open Project", "", "Project Files (*.fsp);;All Files (*)")
127
+ file_path, _ = QFileDialog.getOpenFileName(
128
+ self, "Open Project", "", "Project Files (*.fsp);;All Files (*)")
127
129
  if file_path:
128
130
  try:
129
131
  self._current_file = file_path
130
- self._current_file_wd = '' if os.path.isabs(file_path) else os.path.dirname(file_path)
132
+ self._current_file_wd = '' if os.path.isabs(file_path) \
133
+ else os.path.dirname(file_path)
131
134
  if not os.path.isabs(self._current_file_wd):
132
135
  self._current_file_wd = os.path.abspath(self._current_file_wd)
133
136
  self._current_file = os.path.basename(self._current_file)
134
- file = open(file_path, 'r')
137
+ with open(file_path, 'r', encoding="utf-8") as file:
138
+ json_obj = json.load(file)
135
139
  pp = file_path.split('/')
136
140
  if len(pp) > 1:
137
141
  os.chdir('/'.join(pp[:-1]))
138
- json_obj = json.load(file)
139
142
  project = Project.from_dict(json_obj['project'])
140
143
  if project is None:
141
144
  raise RuntimeError(f"Project from file {file_path} produced a null project.")
@@ -155,21 +158,23 @@ class ActionsWindow(ProjectEditor):
155
158
  if 'working_path' in job.params.keys():
156
159
  working_path = job.params['working_path']
157
160
  if not os.path.isdir(working_path):
158
- QMessageBox.warning(self, "Working path not found",
159
- f'''The working path specified in the project file for the job:
160
- "{job.params['name']}"
161
- was not found.\n
162
- Please, select a valid working path.''')
161
+ QMessageBox.warning(
162
+ self, "Working path not found",
163
+ f'''The working path specified in the project file for the job:
164
+ "{job.params['name']}"
165
+ was not found.\n
166
+ Please, select a valid working path.''')
163
167
  self.edit_action(job)
164
168
  for action in job.sub_actions:
165
169
  if 'working_path' in job.params.keys():
166
170
  working_path = job.params['working_path']
167
171
  if working_path != '' and not os.path.isdir(working_path):
168
- QMessageBox.warning(self, "Working path not found",
169
- f'''The working path specified in the project file for the job:
170
- "{job.params['name']}"
171
- was not found.\n
172
- Please, select a valid working path.''')
172
+ QMessageBox.warning(
173
+ self, "Working path not found",
174
+ f'''The working path specified in the project file for the job:
175
+ "{job.params['name']}"
176
+ was not found.\n
177
+ Please, select a valid working path.''')
173
178
  self.edit_action(action)
174
179
 
175
180
  def current_file_name(self):
@@ -182,7 +187,8 @@ class ActionsWindow(ProjectEditor):
182
187
  self.save_project_as()
183
188
 
184
189
  def save_project_as(self):
185
- file_path, _ = QFileDialog.getSaveFileName(self, "Save Project As", "", "Project Files (*.fsp);;All Files (*)")
190
+ file_path, _ = QFileDialog.getSaveFileName(
191
+ self, "Save Project As", "", "Project Files (*.fsp);;All Files (*)")
186
192
  if file_path:
187
193
  if not file_path.endswith('.fsp'):
188
194
  file_path += '.fsp'
@@ -198,9 +204,10 @@ class ActionsWindow(ProjectEditor):
198
204
  'project': self.project.to_dict(),
199
205
  'version': 1
200
206
  })
201
- path = f"{self._current_file_wd}/{file_path}" if self._current_file_wd != '' else file_path
202
- f = open(path, 'w')
203
- f.write(json_obj)
207
+ path = f"{self._current_file_wd}/{file_path}" \
208
+ if self._current_file_wd != '' else file_path
209
+ with open(path, 'w', encoding="utf-8") as f:
210
+ f.write(json_obj)
204
211
  self._modified_project = False
205
212
  except Exception as e:
206
213
  QMessageBox.critical(self, "Error", f"Cannot save file:\n{str(e)}")
@@ -215,12 +222,8 @@ class ActionsWindow(ProjectEditor):
215
222
  if reply == QMessageBox.Save:
216
223
  self.save_project()
217
224
  return True
218
- elif reply == QMessageBox.Discard:
219
- return True
220
- else:
221
- return False
222
- else:
223
- return True
225
+ return reply == QMessageBox.Discard
226
+ return True
224
227
 
225
228
  def on_job_edit(self, item):
226
229
  index = self.job_list.row(item)
@@ -257,7 +260,8 @@ class ActionsWindow(ProjectEditor):
257
260
  break
258
261
  if current_action:
259
262
  if not is_sub_action:
260
- self.set_enabled_sub_actions_gui(current_action.type_name == constants.ACTION_COMBO)
263
+ self.set_enabled_sub_actions_gui(
264
+ current_action.type_name == constants.ACTION_COMBO)
261
265
  dialog = ActionConfigDialog(current_action, self._current_file_wd, self)
262
266
  if dialog.exec() == QDialog.Accepted:
263
267
  self.on_job_selected(job_index)
@@ -273,7 +277,7 @@ class ActionsWindow(ProjectEditor):
273
277
  if self.job_list.hasFocus():
274
278
  current_action = job
275
279
  elif self.action_list.hasFocus():
276
- job_row, action_row, pos = self.get_current_action()
280
+ job_row, _action_row, pos = self.get_current_action()
277
281
  if pos.actions is not None:
278
282
  current_action = pos.action if not pos.is_sub_action else pos.sub_action
279
283
  if current_action is not None:
@@ -1,3 +1,4 @@
1
+ # pylint: disable=C0114, C0115, C0116, R0903, E0611
1
2
  from PySide6.QtGui import QColor
2
3
 
3
4
 
@@ -1,3 +1,4 @@
1
+ # pylint: disable=C0114, C0115, C0116, E0611, W0718, E1101, C0103
1
2
  import webbrowser
2
3
  import subprocess
3
4
  import os
@@ -75,7 +76,8 @@ class GuiImageView(QWidget):
75
76
  self.setLayout(self.layout)
76
77
  pixmap = QPixmap(file_path)
77
78
  if pixmap:
78
- scaled_pixmap = pixmap.scaledToWidth(gui_constants.GUI_IMG_WIDTH, Qt.SmoothTransformation)
79
+ scaled_pixmap = pixmap.scaledToWidth(
80
+ gui_constants.GUI_IMG_WIDTH, Qt.SmoothTransformation)
79
81
  self.image_label.setPixmap(scaled_pixmap)
80
82
  else:
81
83
  raise RuntimeError(f"Can't load file: {file_path}.")
@@ -112,7 +114,8 @@ class GuiOpenApp(QWidget):
112
114
  self.setLayout(self.layout)
113
115
  pixmap = QPixmap(file_path)
114
116
  if pixmap:
115
- scaled_pixmap = pixmap.scaledToWidth(gui_constants.GUI_IMG_WIDTH, Qt.SmoothTransformation)
117
+ scaled_pixmap = pixmap.scaledToWidth(
118
+ gui_constants.GUI_IMG_WIDTH, Qt.SmoothTransformation)
116
119
  self.image_label.setPixmap(scaled_pixmap)
117
120
  else:
118
121
  raise RuntimeError(f"Can't load file: {file_path}.")
@@ -134,7 +137,8 @@ class GuiOpenApp(QWidget):
134
137
  try:
135
138
  os.system(f"{self.app} -f {self.file_path} &")
136
139
  except Exception as e:
137
- raise RuntimeError(f"Can't open file {self.file_path} with app: {self.app}.\n{str(e)}")
140
+ raise RuntimeError(
141
+ f"Can't open file {self.file_path} with app: {self.app}.\n{str(e)}") from e
138
142
  else:
139
143
  app = None
140
144
  stacked_widget = self.parent().window().findChild(QStackedWidget)
@@ -1,3 +1,4 @@
1
+ # pylint: disable=C0114, C0115, C0116, E0611, W0212, R0903
1
2
  import logging
2
3
  from PySide6.QtWidgets import QWidget, QTextEdit, QMessageBox, QStatusBar
3
4
  from PySide6.QtGui import QTextCursor, QTextOption, QFont
@@ -87,8 +88,8 @@ class SimpleHtmlHandler(QObject, logging.Handler):
87
88
  try:
88
89
  msg = self.format(record)
89
90
  self.html_signal.emit(msg)
90
- except Exception as e:
91
- logging.error(f"Logging error: {e}")
91
+ except RuntimeError as e:
92
+ logging.error(msg=f"Logging error: {e}")
92
93
 
93
94
 
94
95
  class GuiLogger(QWidget):
@@ -1,3 +1,4 @@
1
+ # pylint: disable=C0114, C0115, C0116, E0611, R0903, R0915, R0914, R0917, R0913, R0902
1
2
  import time
2
3
  from PySide6.QtWidgets import (QWidget, QPushButton, QVBoxLayout, QHBoxLayout, QProgressBar,
3
4
  QMessageBox, QScrollArea, QSizePolicy, QFrame, QLabel, QComboBox)
@@ -50,6 +51,7 @@ class TimerProgressBar(QProgressBar):
50
51
  super().setValue(0)
51
52
  self.set_running_style()
52
53
  self._start_time = -1
54
+ self._current_time = -1
53
55
 
54
56
  def set_style(self, bar_color=None):
55
57
  if bar_color is None:
@@ -77,11 +79,11 @@ class TimerProgressBar(QProgressBar):
77
79
  m = (ss % 3600) // 60
78
80
  s = (ss % 3600) % 60
79
81
  x = secs - ss
80
- t_str = "{:02d}".format(s) + "{:.1f}s".format(x).lstrip('0')
82
+ t_str = f"{s:02d}" + f"{x:.1f}s".lstrip('0')
81
83
  if m > 0:
82
- t_str = "{:02d}:{}".format(m, t_str)
84
+ t_str = f"{m:02d}:{t_str}"
83
85
  if h > 0:
84
- t_str = "{:02d}:{}".format(h, t_str)
86
+ t_str = f"{h:02d}:{t_str}"
85
87
  if m > 0 or h > 0:
86
88
  t_str = t_str.lstrip('0')
87
89
  elif 0 < s < 10:
@@ -114,9 +116,11 @@ class TimerProgressBar(QProgressBar):
114
116
  self.check_time(self.maximum())
115
117
  self.setValue(self.maximum())
116
118
 
119
+ # pylint: disable=C0103
117
120
  def setValue(self, val):
118
121
  self.check_time(val)
119
122
  super().setValue(val)
123
+ # pylint: enable=C0103
120
124
 
121
125
  def set_running_style(self):
122
126
  self.set_style(action_running_color)
@@ -192,7 +196,9 @@ class RunWindow(QTextEditLogger):
192
196
  self.retouch_widget.setStyleSheet(BLUE_COMBO_STYLE)
193
197
  self.retouch_widget.addItems(options)
194
198
  self.retouch_widget.setEnabled(False)
195
- self.retouch_widget.currentIndexChanged.connect(lambda: self.retouch(self.retouch_paths[self.retouch_widget.currentIndex() - 1]))
199
+ self.retouch_widget.currentIndexChanged.connect(
200
+ lambda: self.retouch(
201
+ self.retouch_paths[self.retouch_widget.currentIndex() - 1]))
196
202
  self.status_bar.addPermanentWidget(self.retouch_widget)
197
203
 
198
204
  self.stop_button = QPushButton("Stop")
@@ -238,40 +244,40 @@ class RunWindow(QTextEditLogger):
238
244
  self.close_window_callback(self.id_str())
239
245
 
240
246
  @Slot(int, str)
241
- def handle_before_action(self, id, name):
242
- if 0 <= id < len(self.color_widgets[self.row_widget_id]):
243
- self.color_widgets[self.row_widget_id][id].set_color(*action_running_color.tuple())
247
+ def handle_before_action(self, run_id, _name):
248
+ if 0 <= run_id < len(self.color_widgets[self.row_widget_id]):
249
+ self.color_widgets[self.row_widget_id][run_id].set_color(*action_running_color.tuple())
244
250
  self.progress_bar.start(1)
245
- if id == -1:
251
+ if run_id == -1:
246
252
  self.progress_bar.set_running_style()
247
253
 
248
254
  @Slot(int, str)
249
- def handle_after_action(self, id, name):
250
- if 0 <= id < len(self.color_widgets[self.row_widget_id]):
251
- self.color_widgets[self.row_widget_id][id].set_color(*action_done_color.tuple())
255
+ def handle_after_action(self, run_id, _name):
256
+ if 0 <= run_id < len(self.color_widgets[self.row_widget_id]):
257
+ self.color_widgets[self.row_widget_id][run_id].set_color(*action_done_color.tuple())
252
258
  self.progress_bar.stop()
253
- if id == -1:
259
+ if run_id == -1:
254
260
  self.row_widget_id += 1
255
261
  self.progress_bar.set_done_style()
256
262
 
257
263
  @Slot(int, str, str)
258
- def handle_step_counts(self, id, name, steps):
264
+ def handle_step_counts(self, _run_id, _name, steps):
259
265
  self.progress_bar.start(steps)
260
266
 
261
267
  @Slot(int, str)
262
- def handle_begin_steps(self, id, name):
268
+ def handle_begin_steps(self, _run_id, _name):
263
269
  self.progress_bar.start(1)
264
270
 
265
271
  @Slot(int, str)
266
- def handle_end_steps(self, id, name):
272
+ def handle_end_steps(self, _run_id, _name):
267
273
  self.progress_bar.stop()
268
274
 
269
275
  @Slot(int, str, str)
270
- def handle_after_step(self, id, name, step):
276
+ def handle_after_step(self, _run_id, _name, step):
271
277
  self.progress_bar.setValue(step)
272
278
 
273
279
  @Slot(int, str, str)
274
- def handle_save_plot(self, id, name, path):
280
+ def handle_save_plot(self, _run_id, name, path):
275
281
  label = QLabel(name, self)
276
282
  label.setStyleSheet("QLabel {margin-top: 5px; font-weight: bold;}")
277
283
  self.image_layout.addWidget(label)
@@ -290,10 +296,12 @@ class RunWindow(QTextEditLogger):
290
296
  self.image_area_widget.setFixedWidth(needed_width)
291
297
  self.right_area.updateGeometry()
292
298
  self.image_area_widget.updateGeometry()
293
- QTimer.singleShot(0, lambda: self.right_area.verticalScrollBar().setValue(self.right_area.verticalScrollBar().maximum()))
299
+ QTimer.singleShot(
300
+ 0, lambda: self.right_area.verticalScrollBar().setValue(
301
+ self.right_area.verticalScrollBar().maximum()))
294
302
 
295
303
  @Slot(int, str, str, str)
296
- def handle_open_app(self, id, name, app, path):
304
+ def handle_open_app(self, _run_id, name, app, path):
297
305
  label = QLabel(name, self)
298
306
  label.setStyleSheet("QLabel {margin-top: 5px; font-weight: bold;}")
299
307
  self.image_layout.addWidget(label)
@@ -306,7 +314,9 @@ class RunWindow(QTextEditLogger):
306
314
  self.image_area_widget.setFixedWidth(needed_width)
307
315
  self.right_area.updateGeometry()
308
316
  self.image_area_widget.updateGeometry()
309
- QTimer.singleShot(0, lambda: self.right_area.verticalScrollBar().setValue(self.right_area.verticalScrollBar().maximum()))
317
+ QTimer.singleShot(
318
+ 0, lambda: self.right_area.verticalScrollBar().setValue(
319
+ self.right_area.verticalScrollBar().maximum()))
310
320
 
311
321
 
312
322
  class RunWorker(LogWorker):
@@ -336,40 +346,41 @@ class RunWorker(LogWorker):
336
346
  }
337
347
  self.tag = ""
338
348
 
339
- def before_action(self, id, name):
340
- self.before_action_signal.emit(id, name)
349
+ def before_action(self, run_id, name):
350
+ self.before_action_signal.emit(run_id, name)
341
351
 
342
- def after_action(self, id, name):
343
- self.after_action_signal.emit(id, name)
352
+ def after_action(self, run_id, name):
353
+ self.after_action_signal.emit(run_id, name)
344
354
 
345
- def step_counts(self, id, name, steps):
346
- self.step_counts_signal.emit(id, name, steps)
355
+ def step_counts(self, run_id, name, steps):
356
+ self.step_counts_signal.emit(run_id, name, steps)
347
357
 
348
- def begin_steps(self, id, name):
349
- self.begin_steps_signal.emit(id, name)
358
+ def begin_steps(self, run_id, name):
359
+ self.begin_steps_signal.emit(run_id, name)
350
360
 
351
- def end_steps(self, id, name):
352
- self.end_steps_signal.emit(id, name)
361
+ def end_steps(self, run_id, name):
362
+ self.end_steps_signal.emit(run_id, name)
353
363
 
354
- def after_step(self, id, name, step):
355
- self.after_step_signal.emit(id, name, step)
364
+ def after_step(self, run_id, name, step):
365
+ self.after_step_signal.emit(run_id, name, step)
356
366
 
357
- def save_plot(self, id, name, path):
358
- self.save_plot_signal.emit(id, name, path)
367
+ def save_plot(self, run_id, name, path):
368
+ self.save_plot_signal.emit(run_id, name, path)
359
369
 
360
- def open_app(self, id, name, app, path):
361
- self.open_app_signal.emit(id, name, app, path)
370
+ def open_app(self, run_id, name, app, path):
371
+ self.open_app_signal.emit(run_id, name, app, path)
362
372
 
363
- def check_running(self, id, name):
373
+ def check_running(self, _run_id, _name):
364
374
  return self.status == constants.STATUS_RUNNING
365
375
 
366
376
  def run(self):
377
+ # pylint: disable=line-too-long
367
378
  self.status_signal.emit(f"{self.tag} running...", constants.RUN_ONGOING, "", 0)
368
379
  self.html_signal.emit(f'''
369
380
  <div style="margin: 2px 0; font-family: {constants.LOG_FONTS_STR};">
370
381
  <span style="color: #{ColorPalette.DARK_BLUE.hex()}; font-style: italic; font-weigt: bold;">{self.tag} begins</span>
371
382
  </div>
372
- ''')
383
+ ''') # noqa
373
384
  status, error_message = self.do_run()
374
385
  if status == constants.RUN_FAILED:
375
386
  message = f"{self.tag} failed"
@@ -380,11 +391,15 @@ class RunWorker(LogWorker):
380
391
  elif status == constants.RUN_STOPPED:
381
392
  message = f"{self.tag} stopped"
382
393
  color = "#" + ColorPalette.DARK_RED.hex()
394
+ else:
395
+ message = ''
396
+ color = "#000000"
383
397
  self.html_signal.emit(f'''
384
398
  <div style="margin: 2px 0; font-family: {constants.LOG_FONTS_STR};">
385
399
  <span style="color: {color}; font-style: italic; font-weight: bold;">{message}</span>
386
400
  </div>
387
401
  ''')
402
+ # pylint: enable=line-too-long
388
403
  self.end_signal.emit(status, self.id_str, message)
389
404
  self.status_signal.emit(message, status, error_message, 0)
390
405
 
@@ -1,3 +1,4 @@
1
+ # pylint: disable=C0114, C0115, C0116, E0611, R0902, R0915, R0904, R0914, R0912, E1101, W0201
1
2
  import os
2
3
  import subprocess
3
4
  from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QTabWidget, QLabel, QMessageBox,
@@ -90,6 +91,7 @@ class TabWidgetWithPlaceholder(QWidget):
90
91
  else:
91
92
  self.stacked_widget.setCurrentIndex(0)
92
93
 
94
+ # pylint: disable=C0103
93
95
  def addTab(self, widget, label):
94
96
  result = self.tab_widget.addTab(widget, label)
95
97
  self.update_placeholder_visibility()
@@ -117,6 +119,7 @@ class TabWidgetWithPlaceholder(QWidget):
117
119
 
118
120
  def indexOf(self, widget):
119
121
  return self.tab_widget.indexOf(widget)
122
+ # pylint: enable=C0103
120
123
 
121
124
 
122
125
  class MainWindow(ActionsWindow, LogManager):
@@ -289,17 +292,44 @@ class MainWindow(ActionsWindow, LogManager):
289
292
  self.run_all_jobs_action.triggered.connect(self.run_all_jobs)
290
293
  menu.addAction(self.run_all_jobs_action)
291
294
 
295
+ def add_action_combined_actions(self):
296
+ self.add_action(constants.ACTION_COMBO)
297
+
298
+ def add_action_noise_detection(self):
299
+ self.add_action(constants.ACTION_NOISEDETECTION)
300
+
301
+ def add_action_focus_stack(self):
302
+ self.add_action(constants.ACTION_FOCUSSTACK)
303
+
304
+ def add_action_focus_stack_bunch(self):
305
+ self.add_action(constants.ACTION_FOCUSSTACKBUNCH)
306
+
307
+ def add_action_multilayer(self):
308
+ self.add_action(constants.ACTION_MULTILAYER)
309
+
310
+ def add_sub_action_make_noise(self):
311
+ self.add_sub_action(constants.ACTION_MASKNOISE)
312
+
313
+ def add_sub_action_vignetting(self):
314
+ self.add_sub_action(constants.ACTION_VIGNETTING)
315
+
316
+ def add_sub_action_align_frames(self):
317
+ self.add_sub_action(constants.ACTION_ALIGNFRAMES)
318
+
319
+ def add_sub_action_balance_frames(self):
320
+ self.add_sub_action(constants.ACTION_BALANCEFRAMES)
321
+
292
322
  def add_actions_menu(self, menubar):
293
323
  menu = menubar.addMenu("&Actions")
294
324
  add_action_menu = QMenu("Add Action", self)
295
325
  for action in constants.ACTION_TYPES:
296
326
  entry_action = QAction(action, self)
297
327
  entry_action.triggered.connect({
298
- constants.ACTION_COMBO: self.add_action_CombinedActions,
299
- constants.ACTION_NOISEDETECTION: self.add_action_NoiseDetection,
300
- constants.ACTION_FOCUSSTACK: self.add_action_FocusStack,
301
- constants.ACTION_FOCUSSTACKBUNCH: self.add_action_FocusStackBunch,
302
- constants.ACTION_MULTILAYER: self.add_action_MultiLayer
328
+ constants.ACTION_COMBO: self.add_action_combined_actions,
329
+ constants.ACTION_NOISEDETECTION: self.add_action_noise_detection,
330
+ constants.ACTION_FOCUSSTACK: self.add_action_focus_stack,
331
+ constants.ACTION_FOCUSSTACKBUNCH: self.add_action_focus_stack_bunch,
332
+ constants.ACTION_MULTILAYER: self.add_action_multilayer
303
333
  }[action])
304
334
  add_action_menu.addAction(entry_action)
305
335
  menu.addMenu(add_action_menu)
@@ -308,10 +338,10 @@ class MainWindow(ActionsWindow, LogManager):
308
338
  for action in constants.SUB_ACTION_TYPES:
309
339
  entry_action = QAction(action, self)
310
340
  entry_action.triggered.connect({
311
- constants.ACTION_MASKNOISE: self.add_sub_action_MakeNoise,
312
- constants.ACTION_VIGNETTING: self.add_sub_action_Vignetting,
313
- constants.ACTION_ALIGNFRAMES: self.add_sub_action_AlignFrames,
314
- constants.ACTION_BALANCEFRAMES: self.add_sub_action_BalanceFrames
341
+ constants.ACTION_MASKNOISE: self.add_sub_action_make_noise,
342
+ constants.ACTION_VIGNETTING: self.add_sub_action_vignetting,
343
+ constants.ACTION_ALIGNFRAMES: self.add_sub_action_align_frames,
344
+ constants.ACTION_BALANCEFRAMES: self.add_sub_action_balance_frames
315
345
  }[action])
316
346
  entry_action.setEnabled(False)
317
347
  self.sub_action_menu_entries.append(entry_action)
@@ -330,7 +360,8 @@ class MainWindow(ActionsWindow, LogManager):
330
360
  self.action_selector.setEnabled(False)
331
361
  toolbar.addWidget(self.action_selector)
332
362
  self.add_action_entry_action = QAction("Add Action", self)
333
- self.add_action_entry_action.setIcon(QIcon(os.path.join(self.script_dir, "img/plus-round-line-icon.png")))
363
+ self.add_action_entry_action.setIcon(
364
+ QIcon(os.path.join(self.script_dir, "img/plus-round-line-icon.png")))
334
365
  self.add_action_entry_action.setToolTip("Add action")
335
366
  self.add_action_entry_action.triggered.connect(self.add_action)
336
367
  self.add_action_entry_action.setEnabled(False)
@@ -340,7 +371,8 @@ class MainWindow(ActionsWindow, LogManager):
340
371
  self.sub_action_selector.setEnabled(False)
341
372
  toolbar.addWidget(self.sub_action_selector)
342
373
  self.add_sub_action_entry_action = QAction("Add Sub Action", self)
343
- self.add_sub_action_entry_action.setIcon(QIcon(os.path.join(self.script_dir, "img/plus-round-line-icon.png")))
374
+ self.add_sub_action_entry_action.setIcon(
375
+ QIcon(os.path.join(self.script_dir, "img/plus-round-line-icon.png")))
344
376
  self.add_sub_action_entry_action.setToolTip("Add sub action")
345
377
  self.add_sub_action_entry_action.triggered.connect(self.add_sub_action)
346
378
  self.add_sub_action_entry_action.setEnabled(False)
@@ -351,6 +383,7 @@ class MainWindow(ActionsWindow, LogManager):
351
383
  toolbar.addAction(self.run_job_action)
352
384
  toolbar.addAction(self.run_all_jobs_action)
353
385
 
386
+ # pylint: disable=C0103
354
387
  def contextMenuEvent(self, event):
355
388
  item = self.job_list.itemAt(self.job_list.viewport().mapFrom(self, event.pos()))
356
389
  current_action = None
@@ -362,7 +395,7 @@ class MainWindow(ActionsWindow, LogManager):
362
395
  if item:
363
396
  index = self.action_list.row(item)
364
397
  self.action_list.setCurrentRow(index)
365
- job_row, action_row, pos = self.get_action_at(index)
398
+ _job_row, _action_row, pos = self.get_action_at(index)
366
399
  current_action = pos.action if not pos.is_sub_action else pos.sub_action
367
400
  if current_action:
368
401
  menu = QMenu(self)
@@ -375,7 +408,8 @@ class MainWindow(ActionsWindow, LogManager):
375
408
  menu.addAction(edit_config_action)
376
409
  menu.addSeparator()
377
410
  self.current_action_working_path, name = get_action_working_path(current_action)
378
- if self.current_action_working_path != '' and os.path.exists(self.current_action_working_path):
411
+ if self.current_action_working_path != '' and \
412
+ os.path.exists(self.current_action_working_path):
379
413
  action_name = "Browse Working Path" + (f" > {name}" if name != '' else '')
380
414
  self.browse_working_path_action = QAction(action_name)
381
415
  self.browse_working_path_action.triggered.connect(self.browse_working_path_path)
@@ -383,7 +417,8 @@ class MainWindow(ActionsWindow, LogManager):
383
417
  ip, name = get_action_input_path(current_action)
384
418
  if ip != '':
385
419
  ips = ip.split(constants.PATH_SEPARATOR)
386
- self.current_action_input_path = constants.PATH_SEPARATOR.join([f"{self.current_action_working_path}/{ip}" for ip in ips])
420
+ self.current_action_input_path = constants.PATH_SEPARATOR.join(
421
+ [f"{self.current_action_working_path}/{ip}" for ip in ips])
387
422
  p_exists = False
388
423
  for p in self.current_action_input_path.split(constants.PATH_SEPARATOR):
389
424
  if os.path.exists(p):
@@ -410,20 +445,25 @@ class MainWindow(ActionsWindow, LogManager):
410
445
  if len(retouch_path) > 0:
411
446
  menu.addSeparator()
412
447
  self.job_retouch_path_action = QAction("Retouch path")
413
- self.job_retouch_path_action.triggered.connect(lambda job: self.run_retouch_path(current_action, retouch_path))
448
+ self.job_retouch_path_action.triggered.connect(
449
+ lambda job: self.run_retouch_path(current_action, retouch_path))
414
450
  menu.addAction(self.job_retouch_path_action)
415
451
  menu.exec(event.globalPos())
452
+ # pylint: enable=C0103
416
453
 
417
454
  def get_icon(self, icon):
418
455
  return QIcon(os.path.join(self.script_dir, f"img/{icon}.png"))
419
456
 
420
457
  def get_retouch_path(self, job):
421
458
  frames_path = [get_action_output_path(action)[0]
422
- for action in job.sub_actions if action.type_name == constants.ACTION_COMBO]
459
+ for action in job.sub_actions
460
+ if action.type_name == constants.ACTION_COMBO]
423
461
  bunches_path = [get_action_output_path(action)[0]
424
- for action in job.sub_actions if action.type_name == constants.ACTION_FOCUSSTACKBUNCH]
462
+ for action in job.sub_actions
463
+ if action.type_name == constants.ACTION_FOCUSSTACKBUNCH]
425
464
  stack_path = [get_action_output_path(action)[0]
426
- for action in job.sub_actions if action.type_name == constants.ACTION_FOCUSSTACK]
465
+ for action in job.sub_actions
466
+ if action.type_name == constants.ACTION_FOCUSSTACK]
427
467
  if len(bunches_path) > 0:
428
468
  stack_path += [bunches_path[0]]
429
469
  elif len(frames_path) > 0:
@@ -434,7 +474,7 @@ class MainWindow(ActionsWindow, LogManager):
434
474
  stack_path = [f"{wp}/{s}" for s in stack_path]
435
475
  return stack_path
436
476
 
437
- def run_retouch_path(self, job, retouch_path):
477
+ def run_retouch_path(self, _job, retouch_path):
438
478
  self.retouch_callback(retouch_path)
439
479
 
440
480
  def browse_path(self, path):
@@ -444,9 +484,9 @@ class MainWindow(ActionsWindow, LogManager):
444
484
  if running_under_windows():
445
485
  os.startfile(os.path.normpath(p))
446
486
  elif running_under_macos():
447
- subprocess.run(['open', p])
487
+ subprocess.run(['open', p], check=True)
448
488
  else:
449
- subprocess.run(['xdg-open', p])
489
+ subprocess.run(['xdg-open', p], check=True)
450
490
 
451
491
  def browse_working_path_path(self):
452
492
  self.browse_path(self.current_action_working_path)
@@ -501,13 +541,14 @@ class MainWindow(ActionsWindow, LogManager):
501
541
  w = self.tab_widget.widget(i)
502
542
  if w.id_str() == id_str:
503
543
  return i, w
544
+ return None, None
504
545
 
505
546
  def get_tab_at_position(self, id_str):
506
- i, w = self.get_tab_and_position(id_str)
547
+ _i, w = self.get_tab_and_position(id_str)
507
548
  return w
508
549
 
509
550
  def get_tab_position(self, id_str):
510
- i, w = self.get_tab_and_position(id_str)
551
+ i, _w = self.get_tab_and_position(id_str)
511
552
  return i
512
553
 
513
554
  def do_handle_end_message(self, status, id_str, message):
@@ -581,11 +622,14 @@ class MainWindow(ActionsWindow, LogManager):
581
622
  self.start_thread(worker)
582
623
  self._workers.append(worker)
583
624
  else:
584
- QMessageBox.warning(self, "Can't run Job", "Job " + job.params["name"] + " is disabled.")
625
+ QMessageBox.warning(
626
+ self, "Can't run Job",
627
+ "Job " + job.params["name"] + " is disabled.")
585
628
  return
586
629
 
587
630
  def run_all_jobs(self):
588
- labels = [[(self.action_text(a), a.enabled() and job.enabled()) for a in job.sub_actions] for job in self.project.jobs]
631
+ labels = [[(self.action_text(a), a.enabled() and
632
+ job.enabled()) for a in job.sub_actions] for job in self.project.jobs]
589
633
  project_name = ".".join(self.current_file_name().split(".")[:-1])
590
634
  if project_name == '':
591
635
  project_name = '[new]'