imagebaker 0.0.41__py3-none-any.whl → 0.0.48__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.
Files changed (42) hide show
  1. imagebaker/__init__.py +1 -1
  2. imagebaker/core/__init__.py +0 -0
  3. imagebaker/core/configs/__init__.py +1 -0
  4. imagebaker/core/configs/configs.py +156 -0
  5. imagebaker/core/defs/__init__.py +1 -0
  6. imagebaker/core/defs/defs.py +258 -0
  7. imagebaker/core/plugins/__init__.py +0 -0
  8. imagebaker/core/plugins/base_plugin.py +39 -0
  9. imagebaker/core/plugins/cosine_plugin.py +39 -0
  10. imagebaker/layers/__init__.py +3 -0
  11. imagebaker/layers/annotable_layer.py +847 -0
  12. imagebaker/layers/base_layer.py +724 -0
  13. imagebaker/layers/canvas_layer.py +1007 -0
  14. imagebaker/list_views/__init__.py +3 -0
  15. imagebaker/list_views/annotation_list.py +203 -0
  16. imagebaker/list_views/canvas_list.py +185 -0
  17. imagebaker/list_views/image_list.py +138 -0
  18. imagebaker/list_views/layer_list.py +390 -0
  19. imagebaker/list_views/layer_settings.py +219 -0
  20. imagebaker/models/__init__.py +0 -0
  21. imagebaker/models/base_model.py +150 -0
  22. imagebaker/tabs/__init__.py +2 -0
  23. imagebaker/tabs/baker_tab.py +496 -0
  24. imagebaker/tabs/layerify_tab.py +837 -0
  25. imagebaker/utils/__init__.py +0 -0
  26. imagebaker/utils/image.py +105 -0
  27. imagebaker/utils/state_utils.py +92 -0
  28. imagebaker/utils/transform_mask.py +107 -0
  29. imagebaker/window/__init__.py +1 -0
  30. imagebaker/window/app.py +136 -0
  31. imagebaker/window/main_window.py +181 -0
  32. imagebaker/workers/__init__.py +3 -0
  33. imagebaker/workers/baker_worker.py +247 -0
  34. imagebaker/workers/layerify_worker.py +91 -0
  35. imagebaker/workers/model_worker.py +54 -0
  36. {imagebaker-0.0.41.dist-info → imagebaker-0.0.48.dist-info}/METADATA +6 -6
  37. imagebaker-0.0.48.dist-info/RECORD +41 -0
  38. {imagebaker-0.0.41.dist-info → imagebaker-0.0.48.dist-info}/WHEEL +1 -1
  39. imagebaker-0.0.41.dist-info/RECORD +0 -7
  40. {imagebaker-0.0.41.dist-info/licenses → imagebaker-0.0.48.dist-info}/LICENSE +0 -0
  41. {imagebaker-0.0.41.dist-info → imagebaker-0.0.48.dist-info}/entry_points.txt +0 -0
  42. {imagebaker-0.0.41.dist-info → imagebaker-0.0.48.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,390 @@
1
+ # TODO: Use Signal and Slot for communication instead of passing objects around
2
+ from PySide6.QtCore import Qt, Signal
3
+ from PySide6.QtWidgets import (
4
+ QApplication,
5
+ QWidget,
6
+ QVBoxLayout,
7
+ QHBoxLayout,
8
+ QPushButton,
9
+ QLabel,
10
+ QMessageBox,
11
+ QDockWidget,
12
+ QListWidget,
13
+ QListWidgetItem,
14
+ QAbstractItemView,
15
+ QCheckBox,
16
+ QDialog,
17
+ )
18
+
19
+ # from imagebaker.tab_views import BaseLayer
20
+ from imagebaker.layers.canvas_layer import CanvasLayer as Canvas
21
+ from .layer_settings import LayerSettings
22
+ from imagebaker import logger
23
+ from imagebaker.layers.base_layer import BaseLayer
24
+
25
+
26
+ class LayerList(QDockWidget):
27
+
28
+ layersSelected = Signal(list)
29
+ messageSignal = Signal(str)
30
+
31
+ def __init__(
32
+ self,
33
+ canvas: Canvas,
34
+ layer_settings: LayerSettings,
35
+ parent=None,
36
+ ):
37
+ super().__init__("Layers", parent)
38
+ self.canvas = canvas
39
+ self.layers: list[BaseLayer] = []
40
+ self.layer_settings = layer_settings
41
+ self.init_ui()
42
+
43
+ def init_ui(self):
44
+ logger.info("Initializing LayerList")
45
+ main_widget = QWidget()
46
+ main_layout = QVBoxLayout(main_widget)
47
+ self.setMinimumWidth(150)
48
+
49
+ # Create list widget for layers
50
+ self.list_widget = QListWidget()
51
+ self.list_widget.setSelectionMode(QListWidget.MultiSelection)
52
+
53
+ # Enable drag and drop in the list widget
54
+ self.list_widget.setDragEnabled(True)
55
+ self.list_widget.setAcceptDrops(True)
56
+ self.list_widget.setDropIndicatorShown(True)
57
+ self.list_widget.setDragDropMode(QAbstractItemView.InternalMove)
58
+
59
+ # Connect signals
60
+ self.list_widget.itemClicked.connect(self.on_item_clicked)
61
+ self.list_widget.model().rowsMoved.connect(self.on_rows_moved)
62
+ self.list_widget.keyPressEvent = self.list_key_press_event
63
+
64
+ # Add list and buttons to main layout
65
+ main_layout.addWidget(self.list_widget)
66
+ # main_layout.addWidget(delete_button)
67
+
68
+ # Set main widget
69
+ self.setWidget(main_widget)
70
+ self.setFeatures(
71
+ QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable
72
+ )
73
+
74
+ def clear_layers(self):
75
+ """Clear all layers from the list"""
76
+ self.layers.clear()
77
+ self.update_list()
78
+
79
+ def on_rows_moved(self, parent, start, end, destination, row):
80
+ """Handle rows being moved in the list widget via drag and drop"""
81
+ # Calculate the source and destination indices
82
+ source_index = start
83
+ dest_index = row
84
+
85
+ # If moving down, we need to adjust the destination index
86
+ if dest_index > source_index:
87
+ dest_index -= 1
88
+
89
+ # Reorder the layers accordingly
90
+ if 0 <= source_index < len(self.layers) and 0 <= dest_index < len(self.layers):
91
+ # Move the layer in our internal list
92
+ layer = self.layers.pop(source_index)
93
+ self.layers.insert(dest_index, layer)
94
+ layer.order = dest_index
95
+
96
+ # Update the canvas with the new layer order
97
+ self.canvas.layers = self.layers
98
+ self.canvas.update()
99
+
100
+ # Update the UI
101
+ self.update_list()
102
+ logger.info(
103
+ f"BaseLayer: {layer.layer_name} moved from {source_index} to {dest_index}"
104
+ )
105
+
106
+ def update_list(self):
107
+ """Update the list widget with current layers"""
108
+ # Remember current selection
109
+ selected_row = self.list_widget.currentRow()
110
+
111
+ # Clear the list
112
+ self.list_widget.clear()
113
+ selected_layer = None
114
+
115
+ if not self.canvas:
116
+ # No canvas, show dialog and return
117
+ logger.warning("No canvas found")
118
+ QDialog.critical(
119
+ self,
120
+ "Error",
121
+ "No canvas found. Please create a canvas first.",
122
+ QDialog.StandardButton.Ok,
123
+ )
124
+
125
+ # Add all layers to the list
126
+ for idx, layer in enumerate(self.layers):
127
+ if layer.selected:
128
+ selected_layer = layer
129
+
130
+ # Get annotation info
131
+ ann = layer.annotations[0]
132
+ thumbnail = layer.get_thumbnail(annotation=ann)
133
+
134
+ # Create widget for this layer item
135
+ widget = QWidget()
136
+ layout = QHBoxLayout(widget)
137
+ layout.setContentsMargins(5, 5, 5, 5)
138
+
139
+ # Add thumbnail
140
+ thumbnail_label = QLabel()
141
+ thumbnail_label.setPixmap(thumbnail)
142
+ layout.addWidget(thumbnail_label)
143
+
144
+ # Add text info
145
+ text_container = QWidget()
146
+ text_layout = QVBoxLayout(text_container)
147
+
148
+ # Main label
149
+ main_label = QLabel(ann.label)
150
+ text_color = "#666" if not layer.visible else "black"
151
+ if layer.selected:
152
+ text_color = "blue"
153
+ main_label.setStyleSheet(f"font-weight: bold; color: {text_color};")
154
+ text_layout.addWidget(main_label)
155
+
156
+ # Add secondary info if available
157
+ secondary_text = []
158
+ score_text = (
159
+ f"{ann.annotator}: {ann.score:.2f}"
160
+ if ann.score is not None
161
+ else ann.annotator
162
+ )
163
+ secondary_text.append(score_text)
164
+ short_path = ann.file_path.stem
165
+ secondary_text.append(f"<span style='color:#666;'>{short_path}</span>")
166
+
167
+ if secondary_text:
168
+ info_color = "#888" if not layer.visible else "#444"
169
+ info_label = QLabel("<br>".join(secondary_text))
170
+ info_label.setStyleSheet(f"color: {info_color}; font-size: 10px;")
171
+ info_label.setTextFormat(Qt.RichText)
172
+ text_layout.addWidget(info_label)
173
+
174
+ text_layout.addStretch()
175
+ layout.addWidget(text_container)
176
+ layout.addStretch()
177
+
178
+ # Add control buttons
179
+ btn_container = QWidget()
180
+ btn_layout = QHBoxLayout(btn_container)
181
+
182
+ # Visibility button
183
+ vis_btn = QPushButton("👀" if layer.visible else "👁️")
184
+ vis_btn.setMaximumWidth(self.canvas.config.normal_draw_config.button_width)
185
+ vis_btn.setCheckable(True)
186
+ vis_btn.setChecked(layer.visible)
187
+ vis_btn.setToolTip("Visible" if layer.visible else "Hidden")
188
+
189
+ # Store layer index for button callbacks
190
+ vis_btn.setProperty("layer_index", idx)
191
+ vis_btn.clicked.connect(
192
+ lambda checked, btn=vis_btn: self.toggle_visibility(
193
+ btn.property("layer_index")
194
+ )
195
+ )
196
+ btn_layout.addWidget(vis_btn)
197
+
198
+ # Delete button
199
+ del_btn = QPushButton("🗑️")
200
+ del_btn.setMaximumWidth(self.canvas.config.normal_draw_config.button_width)
201
+ del_btn.setToolTip("Delete annotation")
202
+ del_btn.setProperty("layer_index", idx)
203
+ del_btn.clicked.connect(
204
+ lambda checked=False, idx=idx: self.confirm_delete_layer(idx)
205
+ )
206
+ btn_layout.addWidget(del_btn)
207
+
208
+ # a checkbox to toggle layer annotation export
209
+ export_checkbox = QCheckBox()
210
+ export_checkbox.setChecked(layer.allow_annotation_export)
211
+ export_checkbox.setToolTip("Toggle annotation export")
212
+ export_checkbox.setProperty("layer_index", idx)
213
+ export_checkbox.stateChanged.connect(
214
+ lambda state, idx=idx: self.toggle_annotation_export(idx, state)
215
+ )
216
+ btn_layout.addWidget(export_checkbox)
217
+
218
+ layout.addWidget(btn_container)
219
+
220
+ # Add item to list widget
221
+ item = QListWidgetItem()
222
+ item.setSizeHint(widget.sizeHint())
223
+ self.list_widget.addItem(item)
224
+ self.list_widget.setItemWidget(item, widget)
225
+
226
+ # Restore selection if possible
227
+ if selected_row >= 0 and selected_row < self.list_widget.count():
228
+ self.list_widget.setCurrentRow(selected_row)
229
+
230
+ # Update layer settings panel
231
+ if selected_layer:
232
+ self.layer_settings.set_selected_layer(selected_layer)
233
+ else:
234
+ self.layer_settings.set_selected_layer(None)
235
+ self.update()
236
+
237
+ def toggle_annotation_export(self, index, state):
238
+ """Toggle annotation export for a layer by index"""
239
+ if 0 <= index < len(self.layers):
240
+ layer = self.layers[index]
241
+ layer.allow_annotation_export = not layer.allow_annotation_export
242
+ logger.info(
243
+ f"BaseLayer annotation export toggled: {layer.layer_name}, {layer.allow_annotation_export}"
244
+ )
245
+ self.update_list()
246
+ layer.update()
247
+ self.canvas.update()
248
+
249
+ def on_item_clicked(self, item):
250
+ """Handle layer selection with:
251
+ - Left click only: Toggle clicked layer and deselect others
252
+ - Ctrl+Left click: Toggle clicked layer only (keep others selected)"""
253
+ modifiers = QApplication.keyboardModifiers()
254
+ current_row = self.list_widget.row(item)
255
+
256
+ if 0 <= current_row < len(self.layers):
257
+ current_layer = self.layers[current_row]
258
+
259
+ if modifiers & Qt.ControlModifier:
260
+ # Ctrl+Click: Toggle just this layer's selection
261
+ current_layer.selected = not current_layer.selected
262
+ selected_layers = [layer for layer in self.layers if layer.selected]
263
+ else:
264
+ # Normal click: Toggle this layer and deselect all others
265
+ was_selected = current_layer.selected
266
+ for layer in self.layers:
267
+ layer.selected = False
268
+ current_layer.selected = not was_selected # Toggle
269
+ selected_layers = [current_layer] if current_layer.selected else []
270
+
271
+ # Update UI
272
+ self.layersSelected.emit(selected_layers)
273
+ self.layer_settings.set_selected_layer(
274
+ selected_layers[0] if selected_layers else None
275
+ )
276
+ self.canvas.update()
277
+ self.update_list()
278
+
279
+ logger.info(f"Selected layers: {[l.layer_name for l in selected_layers]}")
280
+
281
+ def on_layer_selected(self, indices):
282
+ """Select multiple layers by indices"""
283
+ selected_layers = []
284
+ for i, layer in enumerate(self.layers):
285
+ if i in indices:
286
+ layer.selected = True
287
+ selected_layers.append(layer)
288
+ else:
289
+ layer.selected = False
290
+
291
+ # Emit the selected layers
292
+ self.layersSelected.emit(selected_layers)
293
+
294
+ # Update UI
295
+ self.layer_settings.set_selected_layer(
296
+ selected_layers[0] if selected_layers else None
297
+ )
298
+ self.canvas.update()
299
+ self.update_list()
300
+
301
+ def confirm_delete_layer(self, index):
302
+ """Show confirmation dialog before deleting a layer"""
303
+ if 0 <= index < len(self.layers):
304
+ msg_box = QMessageBox()
305
+ msg_box.setIcon(QMessageBox.Question)
306
+ msg_box.setWindowTitle("Confirm Deletion")
307
+ msg_box.setText("Are you sure you want to delete this layer?")
308
+ msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
309
+ msg_box.setDefaultButton(QMessageBox.No)
310
+
311
+ result = msg_box.exec_()
312
+ if result == QMessageBox.Yes:
313
+ self.delete_layer(index)
314
+
315
+ def list_key_press_event(self, event):
316
+ """Handle key press events in the list widget"""
317
+ if event.key() == Qt.Key_Delete:
318
+ current_row = self.list_widget.currentRow()
319
+ if current_row >= 0:
320
+ self.confirm_delete_layer(current_row)
321
+ else:
322
+ # Pass other key events to the parent class
323
+ QListWidget.keyPressEvent(self.list_widget, event)
324
+
325
+ def delete_selected_layer(self):
326
+ """Delete the currently selected layer with confirmation"""
327
+ current_row = self.list_widget.currentRow()
328
+ if current_row >= 0:
329
+ self.confirm_delete_layer(current_row)
330
+
331
+ def delete_layer(self, index):
332
+ """Delete a layer by index"""
333
+ if 0 <= index < len(self.layers):
334
+ logger.info(f"Deleting layer: {self.layers[index].layer_name}")
335
+ del self.layers[index]
336
+ self.update_list()
337
+ self.canvas.layers = self.layers
338
+ self.canvas.update()
339
+ logger.info(f"BaseLayer deleted: {index}")
340
+
341
+ def toggle_visibility(self, index):
342
+ """Toggle visibility of a layer by index"""
343
+ if 0 <= index < len(self.layers):
344
+ layer = self.layers[index]
345
+ layer.visible = not layer.visible
346
+ logger.info(
347
+ f"BaseLayer visibility toggled: {layer.layer_name}, {layer.visible}"
348
+ )
349
+ self.update_list()
350
+ self.canvas.update()
351
+
352
+ def add_layer(self, layer: BaseLayer = None):
353
+ """Add a new layer to the list"""
354
+ if layer is None:
355
+ return
356
+ self.layers.append(layer)
357
+ self.update_list()
358
+ self.canvas.layers = self.layers
359
+ # self.canvas.update()
360
+
361
+ def select_layer(self, layer):
362
+ """Select a specific layer"""
363
+ logger.info(f"Selecting layer: {layer.layer_name}")
364
+ self.update_list()
365
+
366
+ def get_selected_layers(self):
367
+ """Returns list of currently selected BaseLayer objects"""
368
+ selected_items = self.list_widget.selectedItems()
369
+ return [
370
+ self.layers[self.list_widget.row(item)]
371
+ for item in selected_items
372
+ if 0 <= self.list_widget.row(item) < len(self.layers)
373
+ ]
374
+
375
+ def keyPressEvent(self, event):
376
+ """Handle key presses."""
377
+ self.selected_layer = self.canvas.selected_layer
378
+ if self.selected_layer is None:
379
+ return
380
+ if event.key() == Qt.Key_Delete:
381
+ self.canvas.delete_layer()
382
+ elif event.key() == Qt.Key_Escape:
383
+ self.canvas.selected_layer = None
384
+ elif event.modifiers() & Qt.ControlModifier and event.key() == Qt.Key_C:
385
+ self.canvas.copy_layer()
386
+ elif event.modifiers() & Qt.ControlModifier and event.key() == Qt.Key_V:
387
+ self.canvas.paste_layer()
388
+ elif event.modifiers() & Qt.ControlModifier and event.key() == Qt.Key_Z:
389
+ # self.selected_layer.undo()
390
+ self.messageSignal.emit("Undo not implemented yet")
@@ -0,0 +1,219 @@
1
+ from PySide6.QtCore import Qt, Signal
2
+ from PySide6.QtWidgets import (
3
+ QWidget,
4
+ QVBoxLayout,
5
+ QHBoxLayout,
6
+ QPushButton,
7
+ QLabel,
8
+ QSizePolicy,
9
+ QDockWidget,
10
+ QSlider,
11
+ )
12
+
13
+ from imagebaker.core.defs import LayerState
14
+
15
+ from imagebaker.layers.base_layer import BaseLayer
16
+ from imagebaker import logger
17
+
18
+
19
+ class LayerSettings(QDockWidget):
20
+ layerState = Signal(LayerState)
21
+ messageSignal = Signal(str)
22
+
23
+ def __init__(self, parent=None, max_xpos=1000, max_ypos=1000, max_scale=100):
24
+ super().__init__("BaseLayer Settings", parent)
25
+ self.selected_layer: BaseLayer = None
26
+
27
+ self._disable_updates = False
28
+ self.last_updated_time = 0
29
+ self.max_xpos = max_xpos
30
+ self.max_ypos = max_ypos
31
+ self.max_scale = max_scale
32
+ self.init_ui()
33
+ self.setFeatures(
34
+ QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable
35
+ )
36
+ self.update_sliders()
37
+
38
+ def init_ui(self):
39
+ """Initialize the UI elements."""
40
+ logger.info("Initializing LayerSettings")
41
+ self.widget = QWidget()
42
+ self.setWidget(self.widget)
43
+ self.main_layout = QVBoxLayout(self.widget)
44
+ self.main_layout.setContentsMargins(10, 10, 10, 10) # Add some padding
45
+ self.main_layout.setSpacing(10)
46
+
47
+ # BaseLayer layer_name label
48
+ self.layer_name_label = QLabel("No BaseLayer Selected")
49
+ self.layer_name_label.setAlignment(Qt.AlignCenter)
50
+ self.layer_name_label.setStyleSheet("font-weight: bold; font-size: 14px;")
51
+ self.main_layout.addWidget(self.layer_name_label)
52
+
53
+ # Opacity slider
54
+ self.opacity_slider = self.create_slider("Opacity:", 0, 255, 255, 1)
55
+ self.main_layout.addWidget(self.opacity_slider["widget"])
56
+ self.x_slider = self.create_slider("X:", -self.max_xpos, self.max_xpos, 0, 1)
57
+ self.main_layout.addWidget(self.x_slider["widget"])
58
+ self.y_slider = self.create_slider("Y:", -self.max_ypos, self.max_ypos, 0, 1)
59
+ self.main_layout.addWidget(self.y_slider["widget"])
60
+ self.scale_x_slider = self.create_slider(
61
+ "Scale X:", -self.max_scale, self.max_scale, 100, 100
62
+ ) # 1-500 becomes 0.01-5.0
63
+ self.main_layout.addWidget(self.scale_x_slider["widget"])
64
+ self.scale_y_slider = self.create_slider(
65
+ "Scale Y:", -self.max_scale, self.max_scale, 100, 100
66
+ )
67
+ self.main_layout.addWidget(self.scale_y_slider["widget"])
68
+ self.rotation_slider = self.create_slider("Rotation:", 0, 360, 0, 1)
69
+ self.main_layout.addWidget(self.rotation_slider["widget"])
70
+
71
+ # Add stretch to push content to the top
72
+ self.main_layout.addStretch()
73
+
74
+ # Ensure the dock widget resizes properly
75
+ self.setMinimumWidth(250) # Minimum width for usability
76
+ self.widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
77
+
78
+ def create_slider(self, label, min_val, max_val, default, scale_factor=1):
79
+ """Create a slider with a label and value display."""
80
+ container = QWidget()
81
+ layout = QHBoxLayout(container)
82
+ layout.setContentsMargins(0, 0, 0, 0) # Remove inner margins
83
+
84
+ # Label
85
+ lbl = QLabel(label)
86
+ lbl.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
87
+
88
+ # Slider
89
+ slider = QSlider(Qt.Horizontal)
90
+ slider.setRange(min_val, max_val)
91
+ slider.setValue(default)
92
+ slider.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
93
+
94
+ # Value label
95
+ value_lbl = QLabel(f"{default / scale_factor:.1f}")
96
+ value_lbl.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
97
+
98
+ # Connect slider to value label (still updates during drag)
99
+ slider.valueChanged.connect(
100
+ lambda v: value_lbl.setText(f"{v / scale_factor:.1f}")
101
+ )
102
+
103
+ # Only update layer on slider release
104
+ slider.sliderReleased.connect(self.on_slider_released)
105
+
106
+ # Add widgets to layout
107
+ layout.addWidget(lbl)
108
+ layout.addWidget(slider)
109
+ layout.addWidget(value_lbl)
110
+
111
+ return {
112
+ "widget": container,
113
+ "slider": slider,
114
+ "label": value_lbl,
115
+ "scale_factor": scale_factor,
116
+ }
117
+
118
+ def on_slider_released(self):
119
+ """Update layer only when slider is released"""
120
+ if self._disable_updates or not self.selected_layer:
121
+ return
122
+
123
+ sender = self.sender() # Get which slider was released
124
+ value = sender.value()
125
+
126
+ try:
127
+ self._disable_updates = True
128
+
129
+ if sender == self.opacity_slider["slider"]:
130
+ self.selected_layer.opacity = value
131
+ elif sender == self.x_slider["slider"]:
132
+ self.selected_layer.position.setX(value / self.x_slider["scale_factor"])
133
+ elif sender == self.y_slider["slider"]:
134
+ self.selected_layer.position.setY(value / self.y_slider["scale_factor"])
135
+ elif sender == self.scale_x_slider["slider"]:
136
+ self.selected_layer.scale_x = value / 100.0
137
+ elif sender == self.scale_y_slider["slider"]:
138
+ self.selected_layer.scale_y = value / 100.0
139
+ elif sender == self.rotation_slider["slider"]:
140
+ self.selected_layer.rotation = value
141
+
142
+ self.selected_layer.update() # Trigger a repaint
143
+
144
+ finally:
145
+ self._disable_updates = False
146
+
147
+ def emit_bake_settings(self):
148
+ """Emit the bake settings signal."""
149
+ bake_settings = LayerState(
150
+ layer_id=self.selected_layer.id,
151
+ order=self.selected_layer.order,
152
+ layer_name=self.selected_layer.layer_name,
153
+ position=self.selected_layer.position,
154
+ rotation=self.selected_layer.rotation,
155
+ scale_x=self.selected_layer.scale_x,
156
+ scale_y=self.selected_layer.scale_y,
157
+ )
158
+ logger.info(f"Storing state {bake_settings}")
159
+ self.messageSignal.emit(f"Stored state for {bake_settings.layer_name}")
160
+ self.layerState.emit(bake_settings)
161
+
162
+ def set_selected_layer(self, layer):
163
+ """Set the currently selected layer."""
164
+ self.selected_layer = layer
165
+ self.update_sliders()
166
+
167
+ def update_sliders(self):
168
+ """Update slider values based on the selected layer."""
169
+ self.widget.setEnabled(False)
170
+ if self._disable_updates or not self.selected_layer:
171
+ return
172
+
173
+ try:
174
+ self._disable_updates = True
175
+
176
+ if self.selected_layer.selected:
177
+ self.widget.setEnabled(True)
178
+ self.layer_name_label.setText(
179
+ f"BaseLayer: {self.selected_layer.layer_name}"
180
+ )
181
+ new_max_xpos = self.selected_layer.config.max_xpos
182
+ new_max_ypos = self.selected_layer.config.max_ypos
183
+
184
+ if new_max_xpos - abs(self.selected_layer.position.x()) < 50:
185
+ new_max_xpos = abs(self.selected_layer.position.x()) + 50
186
+ if new_max_ypos - abs(self.selected_layer.position.y()) < 50:
187
+ new_max_ypos = abs(self.selected_layer.position.y()) + 50
188
+
189
+ # Update slider ranges
190
+ self.x_slider["slider"].setRange(
191
+ -new_max_xpos,
192
+ new_max_xpos,
193
+ )
194
+ self.y_slider["slider"].setRange(
195
+ -new_max_ypos,
196
+ new_max_ypos,
197
+ )
198
+
199
+ # Update slider values
200
+ self.opacity_slider["slider"].setValue(
201
+ int(self.selected_layer.opacity) # Scale back to 0-255
202
+ )
203
+ self.x_slider["slider"].setValue(int(self.selected_layer.position.x()))
204
+ self.y_slider["slider"].setValue(int(self.selected_layer.position.y()))
205
+ self.scale_x_slider["slider"].setValue(
206
+ int(self.selected_layer.scale_x * 100)
207
+ )
208
+ self.scale_y_slider["slider"].setValue(
209
+ int(self.selected_layer.scale_y * 100)
210
+ )
211
+ self.rotation_slider["slider"].setValue(
212
+ int(self.selected_layer.rotation)
213
+ )
214
+ else:
215
+ self.widget.setEnabled(False)
216
+ self.layer_name_label.setText("No BaseLayer")
217
+ finally:
218
+ self._disable_updates = False
219
+ self.update()
File without changes