shinestacker 1.4.0__py3-none-any.whl → 1.5.1__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 CHANGED
@@ -1 +1 @@
1
- __version__ = '1.4.0'
1
+ __version__ = '1.5.1'
@@ -0,0 +1,23 @@
1
+ # pylint: disable=C0114, C0116
2
+
3
+ def add_project_arguments(parser):
4
+ parser.add_argument('-x', '--expert', action='store_true', help='''
5
+ expert options are visible by default.
6
+ ''')
7
+
8
+
9
+ def add_retouch_arguments(parser):
10
+ parser.add_argument('-p', '--path', nargs='?', help='''
11
+ import frames from one or more directories.
12
+ Multiple directories can be specified separated by ';'.
13
+ ''')
14
+ view_group = parser.add_mutually_exclusive_group()
15
+ view_group.add_argument('-v1', '--view-overlaid', action='store_true', help='''
16
+ set overlaid view.
17
+ ''')
18
+ view_group.add_argument('-v2', '--view-side-by-side', action='store_true', help='''
19
+ set side-by-side view.
20
+ ''')
21
+ view_group.add_argument('-v3', '--view-top-bottom', action='store_true', help='''
22
+ set top-bottom view.
23
+ ''')
shinestacker/app/main.py CHANGED
@@ -1,4 +1,4 @@
1
- # pylint: disable=C0114, C0115, C0116, C0413, E0611, R0903, E1121, W0201
1
+ # pylint: disable=C0114, C0115, C0116, C0413, E0611, R0903, E1121, W0201, R0915, R0912
2
2
  import sys
3
3
  import os
4
4
  import logging
@@ -20,6 +20,7 @@ from shinestacker.app.gui_utils import (
20
20
  disable_macos_special_menu_items, fill_app_menu, set_css_style)
21
21
  from shinestacker.app.help_menu import add_help_action
22
22
  from shinestacker.app.open_frames import open_frames
23
+ from .args import add_project_arguments, add_retouch_arguments
23
24
 
24
25
 
25
26
  class SelectionDialog(QDialog):
@@ -102,7 +103,7 @@ class MainApp(QMainWindow):
102
103
  file_menu = action.menu()
103
104
  break
104
105
  if file_menu is not None:
105
- import_action = QAction("Import From Current Project", self)
106
+ import_action = QAction("Import from Current Project", self)
106
107
  import_action.triggered.connect(self.import_from_project)
107
108
  file_menu.addAction(import_action)
108
109
  else:
@@ -211,19 +212,21 @@ if a single file is specified, it can be either a project or an image.
211
212
  Multiple frames can be specified as a list of files.
212
213
  Multiple files can be specified separated by ';'.
213
214
  ''')
214
- parser.add_argument('-p', '--path', nargs='?', help='''
215
- import frames from one or more directories.
216
- Multiple directories can be specified separated by ';'.
215
+ app_group = parser.add_mutually_exclusive_group()
216
+ app_group.add_argument('-j', '--project', action='store_true', help='''
217
+ open project window at startup instead of project windows (default).
217
218
  ''')
218
- parser.add_argument('-r', '--retouch', action='store_true', help='''
219
+ app_group.add_argument('-r', '--retouch', action='store_true', help='''
219
220
  open retouch window at startup instead of project windows.
220
221
  ''')
221
- parser.add_argument('-x', '--expert', action='store_true', help='''
222
- expert options are visible by default.
223
- ''')
222
+ add_project_arguments(parser)
223
+ add_retouch_arguments(parser)
224
224
  args = vars(parser.parse_args(sys.argv[1:]))
225
225
  filename = args['filename']
226
226
  path = args['path']
227
+ if filename and path:
228
+ print("can't specify both arguments --filename and --path", file=sys.stderr)
229
+ sys.exit(1)
227
230
  setup_logging(console_level=logging.DEBUG, file_level=logging.DEBUG, disable_console=True)
228
231
  app = Application(sys.argv)
229
232
  if config.DONT_USE_NATIVE_MENU:
@@ -239,6 +242,12 @@ expert options are visible by default.
239
242
  main_app.activateWindow()
240
243
  if args['expert']:
241
244
  main_app.project_window.set_expert_options()
245
+ if args['view_overlaid']:
246
+ main_app.retouch_window.set_strategy('overlaid')
247
+ elif args['view_side_by_side']:
248
+ main_app.retouch_window.set_strategy('sidebyside')
249
+ elif args['view_top_bottom']:
250
+ main_app.retouch_window.set_strategy('topbottom')
242
251
  if filename:
243
252
  filenames = filename.split(';')
244
253
  filename = filenames[0]
@@ -17,6 +17,7 @@ from shinestacker.gui.main_window import MainWindow
17
17
  from shinestacker.app.gui_utils import (
18
18
  disable_macos_special_menu_items, fill_app_menu, set_css_style)
19
19
  from shinestacker.app.help_menu import add_help_action
20
+ from .args import add_project_arguments
20
21
 
21
22
 
22
23
  class ProjectApp(MainWindow):
@@ -52,9 +53,7 @@ def main():
52
53
  parser.add_argument('-f', '--filename', nargs='?', help='''
53
54
  project filename.
54
55
  ''')
55
- parser.add_argument('-x', '--expert', action='store_true', help='''
56
- expert options are visible by default.
57
- ''')
56
+ add_project_arguments(parser)
58
57
  args = vars(parser.parse_args(sys.argv[1:]))
59
58
  setup_logging(console_level=logging.DEBUG, file_level=logging.DEBUG, disable_console=True)
60
59
  app = Application(sys.argv)
@@ -13,6 +13,7 @@ from shinestacker.app.gui_utils import (
13
13
  disable_macos_special_menu_items, fill_app_menu, set_css_style)
14
14
  from shinestacker.app.help_menu import add_help_action
15
15
  from shinestacker.app.open_frames import open_frames
16
+ from .args import add_retouch_arguments
16
17
 
17
18
 
18
19
  class RetouchApp(ImageEditorUI):
@@ -44,10 +45,7 @@ def main():
44
45
  import frames from files.
45
46
  Multiple files can be specified separated by ';'.
46
47
  ''')
47
- parser.add_argument('-p', '--path', nargs='?', help='''
48
- import frames from one or more directories.
49
- Multiple directories can be specified separated by ';'.
50
- ''')
48
+ add_retouch_arguments(parser)
51
49
  args = vars(parser.parse_args(sys.argv[1:]))
52
50
  filename = args['filename']
53
51
  path = args['path']
@@ -65,6 +63,12 @@ Multiple directories can be specified separated by ';'.
65
63
  editor = RetouchApp()
66
64
  app.editor = editor
67
65
  editor.show()
66
+ if args['view_overlaid']:
67
+ editor.set_strategy('overlaid')
68
+ elif args['view_side_by_side']:
69
+ editor.set_strategy('sidebyside')
70
+ elif args['view_top_bottom']:
71
+ editor.set_strategy('topbottom')
68
72
  open_frames(editor, filename, path)
69
73
  sys.exit(app.exec())
70
74
 
@@ -26,7 +26,7 @@ class _GuiConstants:
26
26
  'outer': (255, 0, 0, 200),
27
27
  'inner': (255, 0, 0, 150),
28
28
  'gradient_end': (255, 0, 0, 0),
29
- 'pen': (255, 0, 0, 150),
29
+ 'pen': (255, 0, 0, 200),
30
30
  'preview': (255, 180, 180),
31
31
  'cursor_inner': (255, 0, 0, 120),
32
32
  'preview_inner': (255, 255, 255, 150)
@@ -55,7 +55,7 @@ class _GuiConstants:
55
55
  DEFAULT_BRUSH_OPACITY = 100
56
56
  DEFAULT_BRUSH_FLOW = 100
57
57
  BRUSH_SIZES = {
58
- 'default': 50,
58
+ 'default': 100,
59
59
  'min': 5,
60
60
  'mid': 50,
61
61
  'max': 1000
@@ -66,6 +66,11 @@ class _GuiConstants:
66
66
  ZOOM_IN_FACTOR = 1.10
67
67
  ZOOM_OUT_FACTOR = 1 / ZOOM_IN_FACTOR
68
68
 
69
+ ROTATE_LABEL = "Rotate"
70
+ ROTATE_90_CW_LABEL = f"{ROTATE_LABEL} 90° Clockwise"
71
+ ROTATE_90_CCW_LABEL = f"{ROTATE_LABEL} 90° Anticlockwise"
72
+ ROTATE_180_LABEL = f"{ROTATE_LABEL} 180°"
73
+
69
74
  def calculate_gamma(self):
70
75
  if self.BRUSH_SIZES['mid'] <= self.BRUSH_SIZES['min'] or self.BRUSH_SIZES['max'] <= 0:
71
76
  return 1.0
@@ -108,17 +108,19 @@ class NewProjectDialog(BaseFormDialog):
108
108
  step2_layout.addRow("Vignetting correction:", self.vignetting_correction)
109
109
  step2_layout.addRow(
110
110
  # f" {constants.ACTION_ICONS[constants.ACTION_ALIGNFRAMES]} "
111
- "Align layers:", self.align_frames)
111
+ "Align frames:", self.align_frames)
112
112
  step2_layout.addRow(
113
113
  # f" {constants.ACTION_ICONS[constants.ACTION_BALANCEFRAMES]} "
114
- "Balance layers:", self.balance_frames)
114
+ "Balance frames:", self.balance_frames)
115
115
  step2_layout.addRow(
116
116
  # f" {constants.ACTION_ICONS[constants.ACTION_FOCUSSTACKBUNCH]} "
117
- "Bunch stack:", self.bunch_stack)
118
- step2_layout.addRow("Bunch frames:", self.bunch_frames)
119
- step2_layout.addRow("Bunch overlap:", self.bunch_overlap)
117
+ "Create bunches:", self.bunch_stack)
118
+ self.bunch_stack.setToolTip("Combine multiple frames into fewer, high-quality "
119
+ "composite frames for easier retouching")
120
+ step2_layout.addRow("Frames per bunch:", self.bunch_frames)
121
+ step2_layout.addRow("Overlap between bunches:", self.bunch_overlap)
120
122
  self.bunches_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
121
- step2_layout.addRow("Number of bunches: ", self.bunches_label)
123
+ step2_layout.addRow("Number of resulting bunches: ", self.bunches_label)
122
124
  if self.expert():
123
125
  step2_layout.addRow(
124
126
  f" {constants.ACTION_ICONS[constants.ACTION_FOCUSSTACK]} "
@@ -133,14 +135,14 @@ class NewProjectDialog(BaseFormDialog):
133
135
  if self.expert():
134
136
  step2_layout.addRow(
135
137
  f" {constants.ACTION_ICONS[constants.ACTION_MULTILAYER]} "
136
- "Save multi layer TIFF:", self.multi_layer)
138
+ "Export as multilayer TIFF:", self.multi_layer)
137
139
  step2_group.setLayout(step2_layout)
138
140
  self.form_layout.addRow(step2_group)
139
141
  step3_group = QGroupBox("3) Confirm")
140
142
  step3_layout = QVBoxLayout()
141
143
  step3_layout.setContentsMargins(15, 0, 15, 15)
142
144
  step3_layout.addWidget(
143
- QLabel("Click 🆗 to confirm and prepare the job."))
145
+ QLabel("Click 🆗 to create project with these settings."))
144
146
  step3_layout.addWidget(
145
147
  QLabel("Select: <b>View</b> > <b>Expert options</b> for advanced configuration."))
146
148
  step3_group.setLayout(step3_layout)
@@ -149,6 +151,7 @@ class NewProjectDialog(BaseFormDialog):
149
151
  step4_layout = QHBoxLayout()
150
152
  step4_layout.setContentsMargins(15, 0, 15, 15)
151
153
  step4_layout.addWidget(QLabel("Press ▶️ to run your job."))
154
+ step4_layout.addStretch()
152
155
  icon_path = f"{os.path.dirname(__file__)}/ico/shinestacker.png"
153
156
  app_icon = QIcon(icon_path)
154
157
  icon_pixmap = app_icon.pixmap(80, 80)
@@ -293,12 +296,12 @@ class NewProjectDialog(BaseFormDialog):
293
296
  "Processing may require a significant amount "
294
297
  "of memory or I/O buffering.\n\n"
295
298
  "Continue anyway?")
296
- msg.setInformativeText("You may consider to split the processing "
297
- " using a bunch stack to reduce memory usage.\n\n"
298
- '✅ Check the option "Bunch stack".\n\n'
299
- "➡️ Check expert options for the stacking algorithm."
300
- 'Go to "View" > "Expert Options".'
301
- )
299
+ msg.setInformativeText('You may consider creating "bunches" to reduce '
300
+ "the number of frames for retouching.\n\n"
301
+ '✅ Check "Create bunches" to combine frames '
302
+ "into manageable composites.\n\n"
303
+ "➡️ Check expert options for the stacking algorithm.\n\n"
304
+ 'Go to "View" > "Expert Options".')
302
305
  msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
303
306
  msg.setDefaultButton(QMessageBox.Cancel)
304
307
  if msg.exec_() != QMessageBox.Ok:
@@ -34,7 +34,6 @@ class BaseFilter(ABC):
34
34
  def run_with_preview(self, **kwargs):
35
35
  if self.editor.has_no_master_layer():
36
36
  return
37
-
38
37
  self.editor.copy_master_layer()
39
38
  dlg = QDialog(self.editor)
40
39
  layout = QVBoxLayout(dlg)
@@ -143,15 +142,14 @@ class BaseFilter(ABC):
143
142
  h, w = self.editor.master_layer().shape[:2]
144
143
  except Exception:
145
144
  h, w = self.editor.master_layer_copy().shape[:2]
146
- if hasattr(self.editor, "undo_manager"):
147
- try:
148
- self.editor.undo_manager.extend_undo_area(0, 0, w, h)
149
- self.editor.undo_manager.save_undo_state(
150
- self.editor.master_layer_copy(),
151
- self.name
152
- )
153
- except Exception:
154
- pass
145
+ try:
146
+ self.editor.undo_manager.extend_undo_area(0, 0, w, h)
147
+ self.editor.undo_manager.save_undo_state(
148
+ self.editor.master_layer_copy(),
149
+ self.name
150
+ )
151
+ except Exception:
152
+ pass
155
153
  final_img = self.apply(self.editor.master_layer_copy(), *params)
156
154
  self.editor.set_master_layer(final_img)
157
155
  self.editor.copy_master_layer()
@@ -1,4 +1,4 @@
1
- # pylint: disable=C0114, C0115, C0116, E0611, R0913, R0917, R0914, W0718
1
+ # pylint: disable=C0114, C0115, C0116, E0611, R0913, R0917, R0914, W0718, R0915
2
2
  import traceback
3
3
  import numpy as np
4
4
  from PySide6.QtWidgets import QGraphicsPixmapItem
@@ -72,38 +72,52 @@ class BrushPreviewItem(QGraphicsPixmapItem, LayerCollectionHandler):
72
72
  self.hide()
73
73
  return
74
74
  radius = size // 2
75
- x = int(scene_pos.x() - radius + 0.5)
76
- y = int(scene_pos.y() - radius)
75
+ x_center = int(scene_pos.x() + 0.5)
76
+ y_center = int(scene_pos.y() + 0.5)
77
+ x = x_center - radius
78
+ y = y_center - radius
77
79
  w = h = size
78
80
  if not self.valid_current_layer_idx():
79
81
  self.hide()
80
82
  return
81
- layer_area = self.get_layer_area(self.current_layer(), x, y, w, h)
82
- master_area = self.get_layer_area(self.master_layer(), x, y, w, h)
83
+ height, width = self.current_layer().shape[:2]
84
+ visible_x = max(0, x)
85
+ visible_y = max(0, y)
86
+ visible_w = min(width, x + w) - visible_x
87
+ visible_h = min(height, y + h) - visible_y
88
+ if visible_w <= 0 or visible_h <= 0:
89
+ self.hide()
90
+ return
91
+ layer_area = self.get_layer_area(
92
+ self.current_layer(), visible_x, visible_y, visible_w, visible_h)
93
+ master_area = self.get_layer_area(
94
+ self.master_layer(), visible_x, visible_y, visible_w, visible_h)
83
95
  if layer_area is None or master_area is None:
84
96
  self.hide()
85
97
  return
86
- height, width = self.current_layer().shape[:2]
87
98
  full_mask = create_brush_mask(size=size, hardness_percent=self.brush.hardness,
88
99
  opacity_percent=self.brush.opacity)[:, :, np.newaxis]
89
- mask_x_start = max(0, -x) if x < 0 else 0
90
- mask_y_start = max(0, -y) if y < 0 else 0
91
- mask_x_end = size - (max(0, (x + w) - width)) if (x + w) > width else size
92
- mask_y_end = size - (max(0, (y + h) - height)) if (y + h) > height else size
100
+ mask_x_start = max(0, -x)
101
+ mask_y_start = max(0, -y)
102
+ mask_x_end = mask_x_start + visible_w
103
+ mask_y_end = mask_y_start + visible_h
93
104
  mask_area = full_mask[mask_y_start:mask_y_end, mask_x_start:mask_x_end]
94
105
  area = (layer_area * mask_area + master_area * (1 - mask_area)) * 255.0
95
106
  area = area.astype(np.uint8)
96
107
  qimage = QImage(area.data, area.shape[1], area.shape[0],
97
108
  area.strides[0], QImage.Format_RGB888)
98
- mask = QPixmap(w, h)
109
+ mask = QPixmap(visible_w, visible_h)
99
110
  mask.fill(Qt.transparent)
100
111
  painter = QPainter(mask)
101
112
  painter.setPen(Qt.NoPen)
102
113
  painter.setBrush(Qt.black)
103
- painter.drawEllipse(0, 0, w, h)
114
+ center_x_in_visible = x_center - visible_x
115
+ center_y_in_visible = y_center - visible_y
116
+ painter.drawEllipse(
117
+ center_x_in_visible - radius, center_y_in_visible - radius, size, size)
104
118
  painter.end()
105
119
  pixmap = QPixmap.fromImage(qimage)
106
- final_pixmap = QPixmap(w, h)
120
+ final_pixmap = QPixmap(visible_w, visible_h)
107
121
  final_pixmap.fill(Qt.transparent)
108
122
  painter = QPainter(final_pixmap)
109
123
  painter.drawPixmap(0, 0, pixmap)
@@ -111,8 +125,7 @@ class BrushPreviewItem(QGraphicsPixmapItem, LayerCollectionHandler):
111
125
  painter.drawPixmap(0, 0, mask)
112
126
  painter.end()
113
127
  self.setPixmap(final_pixmap)
114
- x_start, y_start = max(0, x), max(0, y)
115
- self.setPos(x_start, y_start)
128
+ self.setPos(visible_x, visible_y)
116
129
  self.show()
117
130
  except Exception:
118
131
  traceback.print_exc()
@@ -25,7 +25,6 @@ class ClickableLabel(QLabel):
25
25
 
26
26
  class DisplayManager(QObject, LayerCollectionHandler):
27
27
  status_message_requested = Signal(str)
28
- cursor_preview_state_changed = Signal(bool)
29
28
 
30
29
  def __init__(self, layer_collection, image_viewer, master_thumbnail_label,
31
30
  thumbnail_list, parent=None):
@@ -35,7 +34,6 @@ class DisplayManager(QObject, LayerCollectionHandler):
35
34
  self.master_thumbnail_label = master_thumbnail_label
36
35
  self.thumbnail_list = thumbnail_list
37
36
  self.view_mode = 'master'
38
- self.temp_view_individual = False
39
37
  self.needs_update = False
40
38
  self.update_timer = QTimer()
41
39
  self.update_timer.setInterval(gui_constants.PAINT_REFRESH_TIMER)
@@ -47,21 +45,10 @@ class DisplayManager(QObject, LayerCollectionHandler):
47
45
  self.refresh_master_view()
48
46
  self.needs_update = False
49
47
 
50
- def refresh_master_view(self):
51
- if self.has_no_master_layer():
52
- return
53
- self.image_viewer.update_master_display()
54
- self.update_master_thumbnail()
55
- self.image_viewer.refresh_display()
56
-
57
- def refresh_current_view(self):
58
- if self.number_of_layers() == 0:
59
- return
60
- self.image_viewer.update_current_display()
61
- self.image_viewer.refresh_display()
62
-
63
48
  def create_thumbnail(self, layer):
64
49
  source_layer = (layer // 256).astype(np.uint8) if layer.dtype == np.uint16 else layer
50
+ if not source_layer.flags.c_contiguous:
51
+ source_layer = np.ascontiguousarray(source_layer)
65
52
  height, width = source_layer.shape[:2]
66
53
  if layer.ndim == 3 and source_layer.shape[-1] == 3:
67
54
  qimg = QImage(source_layer.data, width, height, 3 * width, QImage.Format_RGB888)
@@ -157,48 +144,61 @@ class DisplayManager(QObject, LayerCollectionHandler):
157
144
  self.thumbnail_list.scrollToItem(
158
145
  self.thumbnail_list.item(index), QAbstractItemView.PositionAtCenter)
159
146
 
147
+ def _master_refresh_and_thumb(self):
148
+ self.image_viewer.show_master()
149
+ self.refresh_master_view()
150
+ self.thumbnail_highlight = gui_constants.THUMB_LO_COLOR
151
+ self.highlight_thumbnail(self.current_layer_idx())
152
+
153
+ def _current_refresh_and_thumb(self):
154
+ self.image_viewer.show_current()
155
+ self.refresh_current_view()
156
+ self.thumbnail_highlight = gui_constants.THUMB_HI_COLOR
157
+ self.highlight_thumbnail(self.current_layer_idx())
158
+
160
159
  def set_view_master(self):
161
160
  if self.has_no_master_layer():
162
161
  return
163
162
  self.view_mode = 'master'
164
- self.temp_view_individual = False
165
- self.refresh_master_view()
166
- self.thumbnail_highlight = gui_constants.THUMB_LO_COLOR
167
- self.highlight_thumbnail(self.current_layer_idx())
163
+ self._master_refresh_and_thumb()
168
164
  self.status_message_requested.emit("View mode: Master")
169
- self.cursor_preview_state_changed.emit(True)
170
165
 
171
166
  def set_view_individual(self):
172
167
  if self.has_no_master_layer():
173
168
  return
174
169
  self.view_mode = 'individual'
175
- self.temp_view_individual = False
176
- self.refresh_current_view()
177
- self.thumbnail_highlight = gui_constants.THUMB_HI_COLOR
178
- self.highlight_thumbnail(self.current_layer_idx())
170
+ self._current_refresh_and_thumb()
179
171
  self.status_message_requested.emit("View mode: Individual layers")
180
- self.cursor_preview_state_changed.emit(False)
172
+
173
+ def refresh_master_view(self):
174
+ if self.has_no_master_layer():
175
+ return
176
+ self.image_viewer.update_master_display()
177
+ self.image_viewer.refresh_display()
178
+ self.update_master_thumbnail()
179
+
180
+ def refresh_current_view(self):
181
+ if self.number_of_layers() == 0:
182
+ return
183
+ self.image_viewer.update_current_display()
184
+ self.image_viewer.refresh_display()
181
185
 
182
186
  def start_temp_view(self):
183
- if not self.temp_view_individual and self.view_mode == 'master':
184
- self.temp_view_individual = True
185
- self.image_viewer.update_brush_cursor()
186
- self.thumbnail_highlight = gui_constants.THUMB_HI_COLOR
187
- self.highlight_thumbnail(self.current_layer_idx())
188
- self.image_viewer.show_current()
189
- self.refresh_current_view()
190
- self.status_message_requested.emit("Temporary view: Individual layer (hold X)")
187
+ if self.view_mode == 'master':
188
+ self._current_refresh_and_thumb()
189
+ self.status_message_requested.emit("Temporary view: Individual layer")
190
+ else:
191
+ self._master_refresh_and_thumb()
192
+ self.image_viewer.strategy.brush_preview.hide()
193
+ self.status_message_requested.emit("Temporary view: Master")
191
194
 
192
195
  def end_temp_view(self):
193
- if self.temp_view_individual:
194
- self.temp_view_individual = False
195
- self.image_viewer.update_brush_cursor()
196
- self.thumbnail_highlight = gui_constants.THUMB_LO_COLOR
197
- self.highlight_thumbnail(self.current_layer_idx())
198
- self.image_viewer.show_master()
199
- self.refresh_master_view()
196
+ if self.view_mode == 'master':
197
+ self._master_refresh_and_thumb()
200
198
  self.status_message_requested.emit("View mode: Master")
201
- self.cursor_preview_state_changed.emit(True)
199
+ else:
200
+ self._current_refresh_and_thumb()
201
+ self.status_message_requested.emit("View: Individual layer")
202
202
 
203
203
  def allow_cursor_preview(self):
204
- return self.view_mode == 'master' and not self.temp_view_individual
204
+ return self.view_mode == 'master'