shinestacker 0.5.0__py3-none-any.whl → 1.0.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 (57) hide show
  1. shinestacker/_version.py +1 -1
  2. shinestacker/algorithms/align.py +4 -12
  3. shinestacker/algorithms/balance.py +11 -9
  4. shinestacker/algorithms/depth_map.py +0 -30
  5. shinestacker/algorithms/utils.py +10 -0
  6. shinestacker/algorithms/vignetting.py +116 -70
  7. shinestacker/app/about_dialog.py +37 -16
  8. shinestacker/app/gui_utils.py +1 -1
  9. shinestacker/app/help_menu.py +1 -1
  10. shinestacker/app/main.py +2 -2
  11. shinestacker/app/project.py +2 -2
  12. shinestacker/config/constants.py +4 -1
  13. shinestacker/config/gui_constants.py +3 -4
  14. shinestacker/gui/action_config.py +5 -561
  15. shinestacker/gui/action_config_dialog.py +567 -0
  16. shinestacker/gui/base_form_dialog.py +18 -0
  17. shinestacker/gui/colors.py +5 -6
  18. shinestacker/gui/gui_logging.py +0 -1
  19. shinestacker/gui/gui_run.py +54 -106
  20. shinestacker/gui/ico/shinestacker.icns +0 -0
  21. shinestacker/gui/ico/shinestacker.ico +0 -0
  22. shinestacker/gui/ico/shinestacker.png +0 -0
  23. shinestacker/gui/ico/shinestacker.svg +60 -0
  24. shinestacker/gui/main_window.py +275 -371
  25. shinestacker/gui/menu_manager.py +236 -0
  26. shinestacker/gui/new_project.py +75 -20
  27. shinestacker/gui/project_converter.py +6 -6
  28. shinestacker/gui/project_editor.py +248 -165
  29. shinestacker/gui/project_model.py +2 -7
  30. shinestacker/gui/tab_widget.py +81 -0
  31. shinestacker/gui/time_progress_bar.py +95 -0
  32. shinestacker/retouch/base_filter.py +173 -40
  33. shinestacker/retouch/brush_preview.py +0 -10
  34. shinestacker/retouch/brush_tool.py +2 -5
  35. shinestacker/retouch/denoise_filter.py +5 -44
  36. shinestacker/retouch/exif_data.py +10 -13
  37. shinestacker/retouch/file_loader.py +1 -1
  38. shinestacker/retouch/filter_manager.py +1 -4
  39. shinestacker/retouch/image_editor_ui.py +318 -40
  40. shinestacker/retouch/image_viewer.py +34 -11
  41. shinestacker/retouch/io_gui_handler.py +34 -30
  42. shinestacker/retouch/layer_collection.py +2 -0
  43. shinestacker/retouch/shortcuts_help.py +12 -0
  44. shinestacker/retouch/unsharp_mask_filter.py +10 -10
  45. shinestacker/retouch/vignetting_filter.py +69 -0
  46. shinestacker/retouch/white_balance_filter.py +46 -14
  47. {shinestacker-0.5.0.dist-info → shinestacker-1.0.0.dist-info}/METADATA +14 -2
  48. shinestacker-1.0.0.dist-info/RECORD +90 -0
  49. shinestacker/app/app_config.py +0 -22
  50. shinestacker/gui/actions_window.py +0 -266
  51. shinestacker/retouch/image_editor.py +0 -197
  52. shinestacker/retouch/image_filters.py +0 -69
  53. shinestacker-0.5.0.dist-info/RECORD +0 -87
  54. {shinestacker-0.5.0.dist-info → shinestacker-1.0.0.dist-info}/WHEEL +0 -0
  55. {shinestacker-0.5.0.dist-info → shinestacker-1.0.0.dist-info}/entry_points.txt +0 -0
  56. {shinestacker-0.5.0.dist-info → shinestacker-1.0.0.dist-info}/licenses/LICENSE +0 -0
  57. {shinestacker-0.5.0.dist-info → shinestacker-1.0.0.dist-info}/top_level.txt +0 -0
@@ -1,22 +1,19 @@
1
- # pylint: disable=C0114, C0115, C0116, R0904, R1702, R0917, R0913, R0902, E0611, E1131
1
+ # pylint: disable=C0114, C0115, C0116, R0904, R1702, R0917, R0913, R0902, E0611, E1131, E1121
2
2
  import os
3
3
  from dataclasses import dataclass
4
- from PySide6.QtWidgets import (QMainWindow, QListWidget, QMessageBox,
4
+ from PySide6.QtWidgets import (QListWidget, QMessageBox,
5
5
  QDialog, QListWidgetItem, QLabel)
6
- from PySide6.QtCore import Qt
6
+ from PySide6.QtCore import Qt, QObject, Signal
7
7
  from .. config.constants import constants
8
8
  from .colors import ColorPalette
9
- from .action_config import ActionConfig, ActionConfigDialog
10
- from .project_model import get_action_input_path, get_action_output_path
11
-
12
- INDENT_SPACE = "   ↪   "
13
- CLONE_POSTFIX = " (clone)"
9
+ from .action_config_dialog import ActionConfigDialog
10
+ from .project_model import ActionConfig, get_action_input_path, get_action_output_path
14
11
 
15
12
 
16
13
  @dataclass
17
14
  class ActionPosition:
18
15
  actions: list
19
- sub_actions: list | None
16
+ sub_actions: list
20
17
  action_index: int
21
18
  sub_action_index: int = -1
22
19
 
@@ -77,20 +74,81 @@ def new_row_after_clone(job, action_row, is_sub_action, cloned):
77
74
  for action in job.sub_actions[:job.sub_actions.index(cloned)])
78
75
 
79
76
 
80
- class ProjectEditor(QMainWindow):
77
+ class ProjectUndoManager:
81
78
  def __init__(self):
82
- super().__init__()
79
+ self._undo_buffer = []
80
+
81
+ def add(self, item):
82
+ self._undo_buffer.append(item)
83
+
84
+ def pop(self):
85
+ return self._undo_buffer.pop()
86
+
87
+ def filled(self):
88
+ return len(self._undo_buffer) != 0
89
+
90
+
91
+ class ProjectEditor(QObject):
92
+ INDENT_SPACE = "   ↪   "
93
+ CLONE_POSTFIX = " (clone)"
94
+
95
+ modified_signal = Signal(int)
96
+ select_signal = Signal()
97
+ refresh_ui_signal = Signal(int, int)
98
+ enable_delete_action_signal = Signal(bool)
99
+
100
+ def __init__(self, parent=None):
101
+ super().__init__(parent)
102
+ self._undo_manager = ProjectUndoManager()
103
+ self._modified = False
104
+ self._project = None
83
105
  self._copy_buffer = None
84
- self.project_buffer = []
85
- self.job_list = QListWidget()
86
- self.action_list = QListWidget()
87
- self.project = None
88
- self.job_list_model = None
89
- self.expert_options = False
90
- self.script_dir = os.path.dirname(__file__)
91
- self.dialog = None
92
106
  self._current_file_path = ''
93
- self._modified_project = False
107
+ self._job_list = QListWidget()
108
+ self._action_list = QListWidget()
109
+ self.dialog = None
110
+
111
+ def add_undo(self, item):
112
+ self._undo_manager.add(item)
113
+
114
+ def pop_undo(self):
115
+ return self._undo_manager.pop()
116
+
117
+ def filled_undo(self):
118
+ return self._undo_manager.filled()
119
+
120
+ def mark_as_modified(self, modified=True):
121
+ self._modified = modified
122
+ if modified:
123
+ self.add_undo(self._project.clone())
124
+ self.modified_signal.emit(modified)
125
+
126
+ def modified(self):
127
+ return self._modified
128
+
129
+ def set_project(self, project):
130
+ self._project = project
131
+
132
+ def project(self):
133
+ return self._project
134
+
135
+ def project_jobs(self):
136
+ return self._project.jobs
137
+
138
+ def add_job_to_project(self, job):
139
+ self._project.jobs.append(job)
140
+
141
+ def num_project_jobs(self):
142
+ return len(self.project().jobs)
143
+
144
+ def copy_buffer(self):
145
+ return self._copy_buffer
146
+
147
+ def set_copy_buffer(self, item):
148
+ self._copy_buffer = item
149
+
150
+ def has_copy_buffer(self):
151
+ return self._copy_buffer is not None
94
152
 
95
153
  def current_file_path(self):
96
154
  return self._current_file_path
@@ -111,8 +169,59 @@ class ProjectEditor(QMainWindow):
111
169
  self._current_file_path = os.path.abspath(path)
112
170
  os.chdir(self.current_file_directory())
113
171
 
114
- def set_project(self, project):
115
- self.project = project
172
+ def project_job(self, index):
173
+ return self._project.jobs[index]
174
+
175
+ def job_list(self):
176
+ return self._job_list
177
+
178
+ def action_list(self):
179
+ return self._action_list
180
+
181
+ def current_job_index(self):
182
+ return self._job_list.currentRow()
183
+
184
+ def current_action_index(self):
185
+ return self._action_list.currentRow()
186
+
187
+ def set_current_job(self, index):
188
+ self._job_list.setCurrentRow(index)
189
+
190
+ def set_current_action(self, index):
191
+ self._action_list.setCurrentRow(index)
192
+
193
+ def job_list_count(self):
194
+ return self._job_list.count()
195
+
196
+ def action_list_count(self):
197
+ return self._action_list.count()
198
+
199
+ def job_list_item(self, index):
200
+ return self._job_list.item(index)
201
+
202
+ def action_list_item(self, index):
203
+ return self._action_list.item(index)
204
+
205
+ def job_list_has_focus(self):
206
+ return self._job_list.hasFocus()
207
+
208
+ def action_list_has_focus(self):
209
+ return self._action_list.hasFocus()
210
+
211
+ def take_job(self, index):
212
+ return self._job_list.takeItem(index)
213
+
214
+ def clear_job_list(self):
215
+ self._job_list.clear()
216
+
217
+ def clear_action_list(self):
218
+ self._action_list.clear()
219
+
220
+ def num_selected_jobs(self):
221
+ return len(self._job_list.selectedItems())
222
+
223
+ def num_selected_actions(self):
224
+ return len(self._action_list.selectedItems())
116
225
 
117
226
  def job_text(self, job, long_name=False, html=False):
118
227
  txt = f"{job.params.get('name', '(job)')}"
@@ -135,7 +244,7 @@ class ProjectEditor(QMainWindow):
135
244
  }
136
245
  ico = icon_map.get(action.type_name, '')
137
246
  if is_sub_action and indent:
138
- txt = INDENT_SPACE
247
+ txt = self.INDENT_SPACE
139
248
  if ico == '':
140
249
  ico = '🟣'
141
250
  else:
@@ -152,22 +261,22 @@ class ProjectEditor(QMainWindow):
152
261
  if long_name and not is_sub_action else "]")
153
262
 
154
263
  def get_job_at(self, index):
155
- return None if index < 0 else self.project.jobs[index]
264
+ return None if index < 0 else self.project_job(index)
156
265
 
157
266
  def get_current_job(self):
158
- return self.get_job_at(self.job_list.currentRow())
267
+ return self.get_job_at(self.current_job_index())
159
268
 
160
269
  def get_current_action(self):
161
- return self.get_action_at(self.action_list.currentRow())
270
+ return self.get_action_at(self.current_action_index())
162
271
 
163
272
  def get_action_at(self, action_row):
164
- job_row = self.job_list.currentRow()
273
+ job_row = self.current_job_index()
165
274
  if job_row < 0 or action_row < 0:
166
275
  return (job_row, action_row, None)
167
276
  action, sub_action, sub_action_index = self.find_action_position(job_row, action_row)
168
277
  if not action:
169
278
  return (job_row, action_row, None)
170
- job = self.project.jobs[job_row]
279
+ job = self.project_job(job_row)
171
280
  if sub_action:
172
281
  return (job_row, action_row,
173
282
  ActionPosition(job.sub_actions, action.sub_actions,
@@ -176,9 +285,9 @@ class ProjectEditor(QMainWindow):
176
285
  ActionPosition(job.sub_actions, None, job.sub_actions.index(action)))
177
286
 
178
287
  def find_action_position(self, job_index, ui_index):
179
- if not 0 <= job_index < len(self.project.jobs):
288
+ if not 0 <= job_index < self.num_project_jobs():
180
289
  return (None, None, -1)
181
- actions = self.project.jobs[job_index].sub_actions
290
+ actions = self.project_job(job_index).sub_actions
182
291
  counter = -1
183
292
  for action in actions:
184
293
  counter += 1
@@ -190,19 +299,16 @@ class ProjectEditor(QMainWindow):
190
299
  return (action, sub_action, sub_action_index)
191
300
  return (None, None, -1)
192
301
 
193
- def refresh_ui(self, job_row=-1, action_row=-1):
194
- pass
195
-
196
302
  def shift_job(self, delta):
197
- job_index = self.job_list.currentRow()
303
+ job_index = self.current_job_index()
198
304
  if job_index < 0:
199
305
  return
200
306
  new_index = job_index + delta
201
- if 0 <= new_index < len(self.project.jobs):
202
- jobs = self.project.jobs
307
+ if 0 <= new_index < self.num_project_jobs():
308
+ jobs = self.project_jobs()
203
309
  self.mark_as_modified()
204
310
  jobs.insert(new_index, jobs.pop(job_index))
205
- self.refresh_ui(new_index, -1)
311
+ self.refresh_ui_signal.emit(new_index, -1)
206
312
 
207
313
  def shift_action(self, delta):
208
314
  job_row, action_row, pos = self.get_current_action()
@@ -218,70 +324,70 @@ class ProjectEditor(QMainWindow):
218
324
  self.mark_as_modified()
219
325
  pos.sub_actions.insert(new_index, pos.sub_actions.pop(pos.sub_action_index))
220
326
  new_row = new_row_after_insert(action_row, pos, delta)
221
- self.refresh_ui(job_row, new_row)
327
+ self.refresh_ui_signal.emit(job_row, new_row)
222
328
 
223
329
  def move_element_up(self):
224
- if self.job_list.hasFocus():
330
+ if self.job_list_has_focus():
225
331
  self.shift_job(-1)
226
- elif self.action_list.hasFocus():
332
+ elif self.action_list_has_focus():
227
333
  self.shift_action(-1)
228
334
 
229
335
  def move_element_down(self):
230
- if self.job_list.hasFocus():
336
+ if self.job_list_has_focus():
231
337
  self.shift_job(+1)
232
- elif self.action_list.hasFocus():
338
+ elif self.action_list_has_focus():
233
339
  self.shift_action(+1)
234
340
 
235
341
  def clone_job(self):
236
- job_index = self.job_list.currentRow()
237
- if 0 <= job_index < len(self.project.jobs):
238
- job_clone = self.project.jobs[job_index].clone(CLONE_POSTFIX)
342
+ job_index = self.current_job_index()
343
+ if 0 <= job_index < self.num_project_jobs():
344
+ job_clone = self.project_job(job_index).clone(self.CLONE_POSTFIX)
239
345
  new_job_index = job_index + 1
240
346
  self.mark_as_modified()
241
- self.project.jobs.insert(new_job_index, job_clone)
242
- self.job_list.setCurrentRow(new_job_index)
243
- self.action_list.setCurrentRow(new_job_index)
244
- self.refresh_ui(new_job_index, -1)
347
+ self.project_jobs().insert(new_job_index, job_clone)
348
+ self.set_current_job(new_job_index)
349
+ self.set_current_action(new_job_index)
350
+ self.refresh_ui_signal.emit(new_job_index, -1)
245
351
 
246
352
  def clone_action(self):
247
353
  job_row, action_row, pos = self.get_current_action()
248
354
  if not pos.actions:
249
355
  return
250
356
  self.mark_as_modified()
251
- job = self.project.jobs[job_row]
357
+ job = self.project_job(job_row)
252
358
  if pos.is_sub_action:
253
- cloned = pos.sub_action.clone(CLONE_POSTFIX)
359
+ cloned = pos.sub_action.clone(self.CLONE_POSTFIX)
254
360
  pos.sub_actions.insert(pos.sub_action_index + 1, cloned)
255
361
  else:
256
- cloned = pos.action.clone(CLONE_POSTFIX)
362
+ cloned = pos.action.clone(self.CLONE_POSTFIX)
257
363
  job.sub_actions.insert(pos.action_index + 1, cloned)
258
364
  new_row = new_row_after_clone(job, action_row, pos.is_sub_action, cloned)
259
- self.refresh_ui(job_row, new_row)
365
+ self.refresh_ui_signal.emit(job_row, new_row)
260
366
 
261
367
  def clone_element(self):
262
- if self.job_list.hasFocus():
368
+ if self.job_list_has_focus():
263
369
  self.clone_job()
264
- elif self.action_list.hasFocus():
370
+ elif self.action_list_has_focus():
265
371
  self.clone_action()
266
372
 
267
373
  def delete_job(self, confirm=True):
268
- current_index = self.job_list.currentRow()
269
- if 0 <= current_index < len(self.project.jobs):
374
+ current_index = self.current_job_index()
375
+ if 0 <= current_index < self.num_project_jobs():
270
376
  if confirm:
271
377
  reply = QMessageBox.question(
272
- self, "Confirm Delete",
378
+ self.parent(), "Confirm Delete",
273
379
  "Are you sure you want to delete job "
274
- f"'{self.project.jobs[current_index].params.get('name', '')}'?",
380
+ f"'{self.project_job(current_index).params.get('name', '')}'?",
275
381
  QMessageBox.Yes | QMessageBox.No
276
382
  )
277
383
  else:
278
384
  reply = None
279
385
  if not confirm or reply == QMessageBox.Yes:
280
- self.job_list.takeItem(current_index)
386
+ self.take_job(current_index)
281
387
  self.mark_as_modified()
282
- current_job = self.project.jobs.pop(current_index)
283
- self.action_list.clear()
284
- self.refresh_ui()
388
+ current_job = self.project_jobs().pop(current_index)
389
+ self.clear_action_list()
390
+ self.refresh_ui_signal.emit(-1, -1)
285
391
  return current_job
286
392
  return None
287
393
 
@@ -291,7 +397,7 @@ class ProjectEditor(QMainWindow):
291
397
  current_action = pos.action if not pos.is_sub_action else pos.sub_action
292
398
  if confirm:
293
399
  reply = QMessageBox.question(
294
- self,
400
+ self.parent(),
295
401
  "Confirm Delete",
296
402
  "Are you sure you want to delete action "
297
403
  f"'{self.action_text(current_action, pos.is_sub_action, indent=False)}'?",
@@ -304,55 +410,53 @@ class ProjectEditor(QMainWindow):
304
410
  if pos.is_sub_action:
305
411
  pos.action.pop_sub_action(pos.sub_action_index)
306
412
  else:
307
- self.project.jobs[job_row].pop_sub_action(pos.action_index)
413
+ self.project_job(job_row).pop_sub_action(pos.action_index)
308
414
  new_row = new_row_after_delete(action_row, pos)
309
- self.refresh_ui(job_row, new_row)
415
+ self.refresh_ui_signal.emit(job_row, new_row)
310
416
  return current_action
311
417
  return None
312
418
 
313
419
  def delete_element(self, confirm=True):
314
- if self.job_list.hasFocus():
420
+ if self.job_list_has_focus():
315
421
  element = self.delete_job(confirm)
316
- elif self.action_list.hasFocus():
422
+ elif self.action_list_has_focus():
317
423
  element = self.delete_action(confirm)
318
424
  else:
319
425
  element = None
320
- if self.job_list.count() > 0:
321
- self.delete_element_action.setEnabled(True)
322
426
  return element
323
427
 
324
428
  def action_config_dialog(self, action):
325
- return ActionConfigDialog(action, self.current_file_directory(), self)
429
+ return ActionConfigDialog(action, self.current_file_directory(), self.parent())
326
430
 
327
431
  def add_job(self):
328
432
  job_action = ActionConfig("Job")
329
433
  self.dialog = self.action_config_dialog(job_action)
330
434
  if self.dialog.exec() == QDialog.Accepted:
331
435
  self.mark_as_modified()
332
- self.project.jobs.append(job_action)
333
- self.add_list_item(self.job_list, job_action, False)
334
- self.job_list.setCurrentRow(self.job_list.count() - 1)
335
- self.job_list.item(self.job_list.count() - 1).setSelected(True)
336
- self.refresh_ui()
337
-
338
- def add_action(self, type_name=False):
339
- current_index = self.job_list.currentRow()
436
+ self.project_jobs().append(job_action)
437
+ self.add_list_item(self.job_list(), job_action, False)
438
+ self.set_current_job(self.job_list_count() - 1)
439
+ self.job_list_item(self.job_list_count() - 1).setSelected(True)
440
+ self.refresh_ui_signal.emit(-1, -1)
441
+
442
+ def add_action(self, type_name):
443
+ current_index = self.current_job_index()
340
444
  if current_index < 0:
341
- if len(self.project.jobs) > 0:
342
- QMessageBox.warning(self, "No Job Selected", "Please select a job first.")
445
+ if self.num_project_jobs() > 0:
446
+ QMessageBox.warning(self.parent(),
447
+ "No Job Selected", "Please select a job first.")
343
448
  else:
344
- QMessageBox.warning(self, "No Job Added", "Please add a job first.")
449
+ QMessageBox.warning(self.parent(),
450
+ "No Job Added", "Please add a job first.")
345
451
  return
346
- if type_name is False:
347
- type_name = self.action_selector.currentText()
348
452
  action = ActionConfig(type_name)
349
453
  action.parent = self.get_current_job()
350
454
  self.dialog = self.action_config_dialog(action)
351
455
  if self.dialog.exec() == QDialog.Accepted:
352
456
  self.mark_as_modified()
353
- self.project.jobs[current_index].add_sub_action(action)
354
- self.add_list_item(self.action_list, action, False)
355
- self.delete_element_action.setEnabled(False)
457
+ self.project_job(current_index).add_sub_action(action)
458
+ self.add_list_item(self.action_list(), action, False)
459
+ self.enable_delete_action_signal.emit(False)
356
460
 
357
461
  def add_list_item(self, widget_list, action, is_sub_action):
358
462
  if action.type_name == constants.ACTION_JOB:
@@ -369,13 +473,13 @@ class ProjectEditor(QMainWindow):
369
473
  label = QLabel(html_text)
370
474
  widget_list.setItemWidget(item, label)
371
475
 
372
- def add_sub_action(self, type_name=False):
373
- current_job_index = self.job_list.currentRow()
374
- current_action_index = self.action_list.currentRow()
476
+ def add_sub_action(self, type_name):
477
+ current_job_index = self.current_job_index()
478
+ current_action_index = self.current_action_index()
375
479
  if current_job_index < 0 or current_action_index < 0 or \
376
- current_job_index >= len(self.project.jobs):
480
+ current_job_index >= self.num_project_jobs():
377
481
  return
378
- job = self.project.jobs[current_job_index]
482
+ job = self.project_job(current_job_index)
379
483
  action = None
380
484
  action_counter = -1
381
485
  for act in job.sub_actions:
@@ -386,96 +490,93 @@ class ProjectEditor(QMainWindow):
386
490
  action_counter += len(act.sub_actions)
387
491
  if not action or action.type_name != constants.ACTION_COMBO:
388
492
  return
389
- if type_name is False:
390
- type_name = self.sub_action_selector.currentText()
391
493
  sub_action = ActionConfig(type_name)
392
494
  self.dialog = self.action_config_dialog(sub_action)
393
495
  if self.dialog.exec() == QDialog.Accepted:
394
496
  self.mark_as_modified()
395
497
  action.add_sub_action(sub_action)
396
498
  self.on_job_selected(current_job_index)
397
- self.action_list.setCurrentRow(current_action_index)
499
+ self.set_current_action(current_action_index)
398
500
 
399
501
  def copy_job(self):
400
- current_index = self.job_list.currentRow()
401
- if 0 <= current_index < len(self.project.jobs):
402
- self._copy_buffer = self.project.jobs[current_index].clone()
502
+ current_index = self.current_job_index()
503
+ if 0 <= current_index < self.num_project_jobs():
504
+ self.set_copy_buffer(self.project_job(current_index).clone())
403
505
 
404
506
  def copy_action(self):
405
507
  _job_row, _action_row, pos = self.get_current_action()
406
508
  if pos.actions is not None:
407
- self._copy_buffer = pos.sub_action.clone() if pos.is_sub_action else pos.action.clone()
509
+ self.set_copy_buffer(pos.sub_action.clone()
510
+ if pos.is_sub_action else pos.action.clone())
408
511
 
409
512
  def copy_element(self):
410
- if self.job_list.hasFocus():
513
+ if self.job_list_has_focus():
411
514
  self.copy_job()
412
- elif self.action_list.hasFocus():
515
+ elif self.action_list_has_focus():
413
516
  self.copy_action()
414
517
 
415
518
  def paste_job(self):
416
- if self._copy_buffer.type_name != constants.ACTION_JOB:
519
+ if self.copy_buffer().type_name != constants.ACTION_JOB:
417
520
  return
418
- job_index = self.job_list.currentRow()
419
- if 0 <= job_index < len(self.project.jobs):
521
+ job_index = self.current_job_index()
522
+ if 0 <= job_index < self.num_project_jobs():
420
523
  new_job_index = job_index
421
524
  self.mark_as_modified()
422
- self.project.jobs.insert(new_job_index, self._copy_buffer)
423
- self.job_list.setCurrentRow(new_job_index)
424
- self.action_list.setCurrentRow(new_job_index)
425
- self.refresh_ui(new_job_index, -1)
525
+ self.project_jobs().insert(new_job_index, self.copy_buffer())
526
+ self.set_current_job(new_job_index)
527
+ self.set_current_action(new_job_index)
528
+ self.refresh_ui_signal.emit(new_job_index, -1)
426
529
 
427
530
  def paste_action(self):
428
531
  job_row, action_row, pos = self.get_current_action()
429
532
  if pos.actions is not None:
430
533
  if not pos.is_sub_action:
431
- if self._copy_buffer.type_name not in constants.ACTION_TYPES:
534
+ if self.copy_buffer().type_name not in constants.ACTION_TYPES:
432
535
  return
433
536
  self.mark_as_modified()
434
- pos.actions.insert(pos.action_index, self._copy_buffer)
537
+ pos.actions.insert(pos.action_index, self.copy_buffer())
435
538
  else:
436
539
  if pos.action.type_name != constants.ACTION_COMBO or \
437
- self._copy_buffer.type_name not in constants.SUB_ACTION_TYPES:
540
+ self.copy_buffer().type_name not in constants.SUB_ACTION_TYPES:
438
541
  return
439
542
  self.mark_as_modified()
440
- pos.sub_actions.insert(pos.sub_action_index, self._copy_buffer)
543
+ pos.sub_actions.insert(pos.sub_action_index, self.copy_buffer())
441
544
  new_row = new_row_after_paste(action_row, pos)
442
- self.refresh_ui(job_row, new_row)
545
+ self.refresh_ui_signal.emit(job_row, new_row)
443
546
 
444
547
  def paste_element(self):
445
- if self._copy_buffer is None:
446
- return
447
- if self.job_list.hasFocus():
448
- self.paste_job()
449
- elif self.action_list.hasFocus():
450
- self.paste_action()
548
+ if self.has_copy_buffer():
549
+ if self.job_list_has_focus():
550
+ self.paste_job()
551
+ elif self.action_list_has_focus():
552
+ self.paste_action()
451
553
 
452
554
  def cut_element(self):
453
- self._copy_buffer = self.delete_element(False)
555
+ self.set_copy_buffer(self.delete_element(False))
454
556
 
455
557
  def undo(self):
456
- job_row = self.job_list.currentRow()
457
- action_row = self.action_list.currentRow()
458
- if len(self.project_buffer) > 0:
459
- self.set_project(self.project_buffer.pop())
460
- self.refresh_ui()
461
- len_jobs = len(self.project.jobs)
558
+ job_row = self.current_job_index()
559
+ action_row = self.current_action_index()
560
+ if self.filled_undo():
561
+ self.set_project(self.pop_undo())
562
+ self.refresh_ui_signal.emit(-1, -1)
563
+ len_jobs = self.num_project_jobs()
462
564
  if len_jobs > 0:
463
- if job_row >= len_jobs:
464
- job_row = len_jobs - 1
465
- self.job_list.setCurrentRow(job_row)
466
- len_actions = self.action_list.count()
565
+ job_row = min(job_row, len_jobs - 1)
566
+ self.set_current_job(job_row)
567
+ len_actions = self.action_list_count()
467
568
  if len_actions > 0:
468
569
  action_row = min(action_row, len_actions)
469
- self.action_list.setCurrentRow(action_row)
570
+ self.set_current_action(action_row)
470
571
 
471
572
  def set_enabled(self, enabled):
472
573
  current_action = None
473
- if self.job_list.hasFocus():
474
- job_row = self.job_list.currentRow()
475
- if 0 <= job_row < len(self.project.jobs):
476
- current_action = self.project.jobs[job_row]
574
+ if self.job_list_has_focus():
575
+ job_row = self.current_job_index()
576
+ if 0 <= job_row < self.num_project_jobs():
577
+ current_action = self.project_job(job_row)
477
578
  action_row = -1
478
- elif self.action_list.hasFocus():
579
+ elif self.action_list_has_focus():
479
580
  job_row, action_row, pos = self.get_current_action()
480
581
  current_action = pos.sub_action if pos.is_sub_action else pos.action
481
582
  else:
@@ -484,7 +585,7 @@ class ProjectEditor(QMainWindow):
484
585
  if current_action.enabled() != enabled:
485
586
  self.mark_as_modified()
486
587
  current_action.set_enabled(enabled)
487
- self.refresh_ui(job_row, action_row)
588
+ self.refresh_ui_signal.emit(job_row, action_row)
488
589
 
489
590
  def enable(self):
490
591
  self.set_enabled(True)
@@ -494,11 +595,11 @@ class ProjectEditor(QMainWindow):
494
595
 
495
596
  def set_enabled_all(self, enable=True):
496
597
  self.mark_as_modified()
497
- job_row = self.job_list.currentRow()
498
- action_row = self.action_list.currentRow()
499
- for j in self.project.jobs:
598
+ job_row = self.current_job_index()
599
+ action_row = self.current_action_index()
600
+ for j in self.project_jobs():
500
601
  j.set_enabled_all(enable)
501
- self.refresh_ui(job_row, action_row)
602
+ self.refresh_ui_signal.emit(job_row, action_row)
502
603
 
503
604
  def enable_all(self):
504
605
  self.set_enabled_all(True)
@@ -507,15 +608,15 @@ class ProjectEditor(QMainWindow):
507
608
  self.set_enabled_all(False)
508
609
 
509
610
  def on_job_selected(self, index):
510
- self.action_list.clear()
511
- if 0 <= index < len(self.project.jobs):
512
- job = self.project.jobs[index]
611
+ self.clear_action_list()
612
+ if 0 <= index < self.num_project_jobs():
613
+ job = self.project_job(index)
513
614
  for action in job.sub_actions:
514
- self.add_list_item(self.action_list, action, False)
615
+ self.add_list_item(self.action_list(), action, False)
515
616
  if len(action.sub_actions) > 0:
516
617
  for sub_action in action.sub_actions:
517
- self.add_list_item(self.action_list, sub_action, True)
518
- self.update_delete_action_state()
618
+ self.add_list_item(self.action_list(), sub_action, True)
619
+ self.select_signal.emit()
519
620
 
520
621
  def get_current_action_at(self, job, action_index):
521
622
  action_counter = -1
@@ -537,21 +638,3 @@ class ProjectEditor(QMainWindow):
537
638
  break
538
639
 
539
640
  return current_action, is_sub_action
540
-
541
- def update_delete_action_state(self):
542
- has_job_selected = len(self.job_list.selectedItems()) > 0
543
- has_action_selected = len(self.action_list.selectedItems()) > 0
544
- self.delete_element_action.setEnabled(has_job_selected or has_action_selected)
545
- if has_action_selected and has_job_selected:
546
- job_index = self.job_list.currentRow()
547
- if job_index >= len(self.project.jobs):
548
- job_index = len(self.project.jobs) - 1
549
- action_index = self.action_list.currentRow()
550
- if job_index >= 0:
551
- job = self.project.jobs[job_index]
552
- current_action, is_sub_action = self.get_current_action_at(job, action_index)
553
- enable_sub_actions = current_action is not None and \
554
- not is_sub_action and current_action.type_name == constants.ACTION_COMBO
555
- self.set_enabled_sub_actions_gui(enable_sub_actions)
556
- else:
557
- self.set_enabled_sub_actions_gui(False)