lazylabel-gui 1.1.1__py3-none-any.whl → 1.1.3__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 (35) hide show
  1. lazylabel/__init__.py +1 -1
  2. lazylabel/config/__init__.py +3 -3
  3. lazylabel/config/hotkeys.py +96 -58
  4. lazylabel/config/paths.py +8 -9
  5. lazylabel/config/settings.py +15 -16
  6. lazylabel/core/__init__.py +3 -3
  7. lazylabel/core/file_manager.py +49 -33
  8. lazylabel/core/model_manager.py +9 -11
  9. lazylabel/core/segment_manager.py +21 -22
  10. lazylabel/main.py +1 -0
  11. lazylabel/models/__init__.py +1 -1
  12. lazylabel/models/sam_model.py +24 -19
  13. lazylabel/ui/__init__.py +3 -3
  14. lazylabel/ui/control_panel.py +21 -19
  15. lazylabel/ui/editable_vertex.py +16 -3
  16. lazylabel/ui/hotkey_dialog.py +125 -93
  17. lazylabel/ui/hoverable_polygon_item.py +1 -2
  18. lazylabel/ui/main_window.py +290 -49
  19. lazylabel/ui/photo_viewer.py +4 -7
  20. lazylabel/ui/reorderable_class_table.py +2 -3
  21. lazylabel/ui/right_panel.py +15 -16
  22. lazylabel/ui/widgets/__init__.py +1 -1
  23. lazylabel/ui/widgets/adjustments_widget.py +22 -21
  24. lazylabel/ui/widgets/model_selection_widget.py +28 -21
  25. lazylabel/ui/widgets/settings_widget.py +35 -28
  26. lazylabel/ui/widgets/status_bar.py +2 -2
  27. lazylabel/utils/__init__.py +2 -2
  28. lazylabel/utils/custom_file_system_model.py +3 -2
  29. {lazylabel_gui-1.1.1.dist-info → lazylabel_gui-1.1.3.dist-info}/METADATA +48 -2
  30. lazylabel_gui-1.1.3.dist-info/RECORD +37 -0
  31. lazylabel_gui-1.1.1.dist-info/RECORD +0 -37
  32. {lazylabel_gui-1.1.1.dist-info → lazylabel_gui-1.1.3.dist-info}/WHEEL +0 -0
  33. {lazylabel_gui-1.1.1.dist-info → lazylabel_gui-1.1.3.dist-info}/entry_points.txt +0 -0
  34. {lazylabel_gui-1.1.1.dist-info → lazylabel_gui-1.1.3.dist-info}/licenses/LICENSE +0 -0
  35. {lazylabel_gui-1.1.1.dist-info → lazylabel_gui-1.1.3.dist-info}/top_level.txt +0 -0
@@ -1,48 +1,46 @@
1
1
  """Main application window."""
2
2
 
3
3
  import os
4
- import numpy as np
4
+
5
5
  import cv2
6
+ import numpy as np
7
+ from PyQt6.QtCore import QModelIndex, QPointF, Qt, QTimer, pyqtSignal
8
+ from PyQt6.QtGui import (
9
+ QBrush,
10
+ QColor,
11
+ QIcon,
12
+ QKeySequence,
13
+ QPen,
14
+ QPixmap,
15
+ QPolygonF,
16
+ QShortcut,
17
+ )
6
18
  from PyQt6.QtWidgets import (
7
- QMainWindow,
8
- QWidget,
9
- QHBoxLayout,
10
- QVBoxLayout,
11
- QFileDialog,
12
19
  QApplication,
20
+ QDialog,
21
+ QFileDialog,
13
22
  QGraphicsEllipseItem,
14
23
  QGraphicsLineItem,
15
24
  QGraphicsPolygonItem,
25
+ QMainWindow,
26
+ QSplitter,
16
27
  QTableWidgetItem,
17
28
  QTableWidgetSelectionRange,
18
- QHeaderView,
19
- QSplitter,
20
- QDialog,
21
- )
22
- from PyQt6.QtGui import (
23
- QIcon,
24
- QKeySequence,
25
- QShortcut,
26
- QPixmap,
27
- QColor,
28
- QPen,
29
- QBrush,
30
- QPolygonF,
31
- QImage,
29
+ QVBoxLayout,
30
+ QWidget,
32
31
  )
33
- from PyQt6.QtCore import Qt, QTimer, QModelIndex, QPointF, pyqtSignal
34
32
 
33
+ from ..config import HotkeyManager, Paths, Settings
34
+ from ..core import FileManager, ModelManager, SegmentManager
35
+ from ..utils import CustomFileSystemModel, mask_to_pixmap
35
36
  from .control_panel import ControlPanel
36
- from .right_panel import RightPanel
37
- from .photo_viewer import PhotoViewer
38
- from .hoverable_polygon_item import HoverablePolygonItem
39
- from .hoverable_pixelmap_item import HoverablePixmapItem
40
37
  from .editable_vertex import EditableVertexItem
41
- from .numeric_table_widget_item import NumericTableWidgetItem
42
- from ..core import SegmentManager, ModelManager, FileManager
43
- from ..config import Settings, Paths, HotkeyManager
44
- from ..utils import CustomFileSystemModel, mask_to_pixmap
45
38
  from .hotkey_dialog import HotkeyDialog
39
+ from .hoverable_pixelmap_item import HoverablePixmapItem
40
+ from .hoverable_polygon_item import HoverablePolygonItem
41
+ from .numeric_table_widget_item import NumericTableWidgetItem
42
+ from .photo_viewer import PhotoViewer
43
+ from .right_panel import RightPanel
46
44
  from .widgets import StatusBar
47
45
 
48
46
 
@@ -126,6 +124,8 @@ class MainWindow(QMainWindow):
126
124
  None,
127
125
  {},
128
126
  )
127
+ self.action_history = []
128
+ self.redo_history = []
129
129
 
130
130
  # Update state flags to prevent recursion
131
131
  self._updating_lists = False
@@ -311,6 +311,7 @@ class MainWindow(QMainWindow):
311
311
  "delete_segments_alt": self._delete_selected_segments,
312
312
  "merge_segments": self._handle_merge_press,
313
313
  "undo": self._undo_last_action,
314
+ "redo": self._redo_last_action,
314
315
  "select_all": lambda: self.right_panel.select_all_segments(),
315
316
  "save_segment": self._handle_space_press,
316
317
  "save_output": self._handle_enter_press,
@@ -346,6 +347,7 @@ class MainWindow(QMainWindow):
346
347
  self.control_panel.set_annotation_size(
347
348
  int(self.settings.annotation_size_multiplier * 10)
348
349
  )
350
+ self.control_panel.set_join_threshold(self.settings.polygon_join_threshold)
349
351
  # Set initial mode based on model availability
350
352
  if self.model_manager.is_model_available():
351
353
  self.set_sam_mode()
@@ -795,7 +797,7 @@ class MainWindow(QMainWindow):
795
797
  def _display_all_segments(self):
796
798
  """Display all segments on the viewer."""
797
799
  # Clear existing segment items
798
- for i, items in self.segment_items.items():
800
+ for _i, items in self.segment_items.items():
799
801
  for item in items:
800
802
  if item.scene():
801
803
  self.viewer.scene().removeItem(item)
@@ -868,13 +870,21 @@ class MainWindow(QMainWindow):
868
870
  self.positive_points, self.negative_points
869
871
  )
870
872
  if mask is not None:
871
- self.segment_manager.add_segment(
873
+ new_segment = {
874
+ "mask": mask,
875
+ "type": "SAM",
876
+ "vertices": None,
877
+ }
878
+ self.segment_manager.add_segment(new_segment)
879
+ # Record the action for undo
880
+ self.action_history.append(
872
881
  {
873
- "mask": mask,
874
- "type": "SAM",
875
- "vertices": None,
882
+ "type": "add_segment",
883
+ "segment_index": len(self.segment_manager.segments) - 1,
876
884
  }
877
885
  )
886
+ # Clear redo history when a new action is performed
887
+ self.redo_history.clear()
878
888
  self.clear_all_points()
879
889
  self._update_all_lists()
880
890
 
@@ -883,13 +893,22 @@ class MainWindow(QMainWindow):
883
893
  if len(self.polygon_points) < 3:
884
894
  return
885
895
 
886
- self.segment_manager.add_segment(
896
+ new_segment = {
897
+ "vertices": list(self.polygon_points),
898
+ "type": "Polygon",
899
+ "mask": None,
900
+ }
901
+ self.segment_manager.add_segment(new_segment)
902
+ # Record the action for undo
903
+ self.action_history.append(
887
904
  {
888
- "vertices": list(self.polygon_points),
889
- "type": "Polygon",
890
- "mask": None,
905
+ "type": "add_segment",
906
+ "segment_index": len(self.segment_manager.segments) - 1,
891
907
  }
892
908
  )
909
+ # Clear redo history when a new action is performed
910
+ self.redo_history.clear()
911
+
893
912
  self.polygon_points.clear()
894
913
  self.clear_all_points()
895
914
  self._update_all_lists()
@@ -980,9 +999,176 @@ class MainWindow(QMainWindow):
980
999
  self.right_panel.clear_selections()
981
1000
 
982
1001
  def _undo_last_action(self):
983
- """Undo last action."""
984
- # Implementation would go here
985
- pass
1002
+ """Undo the last action recorded in the history."""
1003
+ if not self.action_history:
1004
+ self._show_notification("Nothing to undo.")
1005
+ return
1006
+
1007
+ last_action = self.action_history.pop()
1008
+ action_type = last_action.get("type")
1009
+
1010
+ # Save to redo history before undoing
1011
+ self.redo_history.append(last_action)
1012
+
1013
+ if action_type == "add_segment":
1014
+ segment_index = last_action.get("segment_index")
1015
+ if segment_index is not None and 0 <= segment_index < len(
1016
+ self.segment_manager.segments
1017
+ ):
1018
+ # Store the segment data for redo
1019
+ last_action["segment_data"] = self.segment_manager.segments[
1020
+ segment_index
1021
+ ].copy()
1022
+
1023
+ # Remove the segment that was added
1024
+ self.segment_manager.delete_segments([segment_index])
1025
+ self.right_panel.clear_selections() # Clear selection to prevent phantom highlights
1026
+ self._update_all_lists()
1027
+ self._show_notification("Undid: Add Segment")
1028
+ elif action_type == "add_point":
1029
+ point_type = last_action.get("point_type")
1030
+ point_item = last_action.get("point_item")
1031
+ point_list = (
1032
+ self.positive_points
1033
+ if point_type == "positive"
1034
+ else self.negative_points
1035
+ )
1036
+ if point_list:
1037
+ point_list.pop()
1038
+ if point_item in self.point_items:
1039
+ self.point_items.remove(point_item)
1040
+ self.viewer.scene().removeItem(point_item)
1041
+ self._update_segmentation()
1042
+ self._show_notification("Undid: Add Point")
1043
+ elif action_type == "add_polygon_point":
1044
+ dot_item = last_action.get("dot_item")
1045
+ if self.polygon_points:
1046
+ self.polygon_points.pop()
1047
+ if dot_item in self.polygon_preview_items:
1048
+ self.polygon_preview_items.remove(dot_item)
1049
+ self.viewer.scene().removeItem(dot_item)
1050
+ self._draw_polygon_preview()
1051
+ self._show_notification("Undid: Add Polygon Point")
1052
+ elif action_type == "move_polygon":
1053
+ initial_vertices = last_action.get("initial_vertices")
1054
+ for i, vertices in initial_vertices.items():
1055
+ self.segment_manager.segments[i]["vertices"] = vertices
1056
+ self._update_polygon_item(i)
1057
+ self._display_edit_handles()
1058
+ self._highlight_selected_segments()
1059
+ self._show_notification("Undid: Move Polygon")
1060
+ elif action_type == "move_vertex":
1061
+ segment_index = last_action.get("segment_index")
1062
+ vertex_index = last_action.get("vertex_index")
1063
+ old_pos = last_action.get("old_pos")
1064
+ self.segment_manager.segments[segment_index]["vertices"][vertex_index] = (
1065
+ old_pos
1066
+ )
1067
+ self._update_polygon_item(segment_index)
1068
+ self._display_edit_handles()
1069
+ self._highlight_selected_segments()
1070
+ self._show_notification("Undid: Move Vertex")
1071
+
1072
+ # Add more undo logic for other action types here in the future
1073
+ else:
1074
+ self._show_warning_notification(
1075
+ f"Undo for action '{action_type}' not implemented."
1076
+ )
1077
+ # Remove from redo history if we couldn't undo it
1078
+ self.redo_history.pop()
1079
+
1080
+ def _redo_last_action(self):
1081
+ """Redo the last undone action."""
1082
+ if not self.redo_history:
1083
+ self._show_notification("Nothing to redo.")
1084
+ return
1085
+
1086
+ last_action = self.redo_history.pop()
1087
+ action_type = last_action.get("type")
1088
+
1089
+ # Add back to action history for potential future undo
1090
+ self.action_history.append(last_action)
1091
+
1092
+ if action_type == "add_segment":
1093
+ # Restore the segment that was removed
1094
+ if "segment_data" in last_action:
1095
+ segment_data = last_action["segment_data"]
1096
+ self.segment_manager.add_segment(segment_data)
1097
+ self._update_all_lists()
1098
+ self._show_notification("Redid: Add Segment")
1099
+ else:
1100
+ # If we don't have the segment data (shouldn't happen), we can't redo
1101
+ self._show_warning_notification("Cannot redo: Missing segment data")
1102
+ self.action_history.pop() # Remove from action history
1103
+ elif action_type == "add_point":
1104
+ point_type = last_action.get("point_type")
1105
+ point_coords = last_action.get("point_coords")
1106
+ if point_coords:
1107
+ pos = QPointF(point_coords[0], point_coords[1])
1108
+ self._add_point(pos, positive=(point_type == "positive"))
1109
+ self._update_segmentation()
1110
+ self._show_notification("Redid: Add Point")
1111
+ else:
1112
+ self._show_warning_notification(
1113
+ "Cannot redo: Missing point coordinates"
1114
+ )
1115
+ self.action_history.pop()
1116
+ elif action_type == "add_polygon_point":
1117
+ point_coords = last_action.get("point_coords")
1118
+ if point_coords:
1119
+ self._handle_polygon_click(point_coords)
1120
+ self._show_notification("Redid: Add Polygon Point")
1121
+ else:
1122
+ self._show_warning_notification(
1123
+ "Cannot redo: Missing polygon point coordinates"
1124
+ )
1125
+ self.action_history.pop()
1126
+ elif action_type == "move_polygon":
1127
+ final_vertices = last_action.get("final_vertices")
1128
+ if final_vertices:
1129
+ for i, vertices in final_vertices.items():
1130
+ if i < len(self.segment_manager.segments):
1131
+ self.segment_manager.segments[i]["vertices"] = [
1132
+ QPointF(v.x(), v.y()) for v in vertices
1133
+ ]
1134
+ self._update_polygon_item(i)
1135
+ self._display_edit_handles()
1136
+ self._highlight_selected_segments()
1137
+ self._show_notification("Redid: Move Polygon")
1138
+ else:
1139
+ self._show_warning_notification("Cannot redo: Missing final vertices")
1140
+ self.action_history.pop()
1141
+ elif action_type == "move_vertex":
1142
+ segment_index = last_action.get("segment_index")
1143
+ vertex_index = last_action.get("vertex_index")
1144
+ new_pos = last_action.get("new_pos")
1145
+ if (
1146
+ segment_index is not None
1147
+ and vertex_index is not None
1148
+ and new_pos is not None
1149
+ ):
1150
+ if segment_index < len(self.segment_manager.segments):
1151
+ self.segment_manager.segments[segment_index]["vertices"][
1152
+ vertex_index
1153
+ ] = new_pos
1154
+ self._update_polygon_item(segment_index)
1155
+ self._display_edit_handles()
1156
+ self._highlight_selected_segments()
1157
+ self._show_notification("Redid: Move Vertex")
1158
+ else:
1159
+ self._show_warning_notification(
1160
+ "Cannot redo: Segment no longer exists"
1161
+ )
1162
+ self.action_history.pop()
1163
+ else:
1164
+ self._show_warning_notification("Cannot redo: Missing vertex data")
1165
+ self.action_history.pop()
1166
+ else:
1167
+ self._show_warning_notification(
1168
+ f"Redo for action '{action_type}' not implemented."
1169
+ )
1170
+ # Remove from action history if we couldn't redo it
1171
+ self.action_history.pop()
986
1172
 
987
1173
  def clear_all_points(self):
988
1174
  """Clear all temporary points."""
@@ -1091,6 +1277,8 @@ class MainWindow(QMainWindow):
1091
1277
  self.viewer.scene().removeItem(item)
1092
1278
  self.segment_items.clear()
1093
1279
  self.highlight_items.clear()
1280
+ self.action_history.clear()
1281
+ self.redo_history.clear()
1094
1282
 
1095
1283
  def _scene_mouse_press(self, event):
1096
1284
  """Handle mouse press events in the scene."""
@@ -1113,7 +1301,9 @@ class MainWindow(QMainWindow):
1113
1301
  self.drag_start_pos = pos
1114
1302
  selected_indices = self.right_panel.get_selected_segment_indices()
1115
1303
  self.drag_initial_vertices = {
1116
- i: list(self.segment_manager.segments[i]["vertices"])
1304
+ i: [
1305
+ QPointF(p) for p in self.segment_manager.segments[i]["vertices"]
1306
+ ]
1117
1307
  for i in selected_indices
1118
1308
  if self.segment_manager.segments[i].get("type") == "Polygon"
1119
1309
  }
@@ -1148,9 +1338,8 @@ class MainWindow(QMainWindow):
1148
1338
  elif self.mode == "polygon":
1149
1339
  if event.button() == Qt.MouseButton.LeftButton:
1150
1340
  self._handle_polygon_click(pos)
1151
- elif self.mode == "selection":
1152
- if event.button() == Qt.MouseButton.LeftButton:
1153
- self._handle_segment_selection_click(pos)
1341
+ elif self.mode == "selection" and event.button() == Qt.MouseButton.LeftButton:
1342
+ self._handle_segment_selection_click(pos)
1154
1343
 
1155
1344
  def _scene_mouse_move(self, event):
1156
1345
  """Handle mouse move events in the scene."""
@@ -1171,6 +1360,22 @@ class MainWindow(QMainWindow):
1171
1360
  def _scene_mouse_release(self, event):
1172
1361
  """Handle mouse release events in the scene."""
1173
1362
  if self.mode == "edit" and self.is_dragging_polygon:
1363
+ # Record the action for undo
1364
+ final_vertices = {
1365
+ i: list(self.segment_manager.segments[i]["vertices"])
1366
+ for i in self.drag_initial_vertices
1367
+ }
1368
+ self.action_history.append(
1369
+ {
1370
+ "type": "move_polygon",
1371
+ "initial_vertices": {
1372
+ k: list(v) for k, v in self.drag_initial_vertices.items()
1373
+ },
1374
+ "final_vertices": final_vertices,
1375
+ }
1376
+ )
1377
+ # Clear redo history when a new action is performed
1378
+ self.redo_history.clear()
1174
1379
  self.is_dragging_polygon = False
1175
1380
  self.drag_initial_vertices.clear()
1176
1381
  event.accept()
@@ -1202,6 +1407,18 @@ class MainWindow(QMainWindow):
1202
1407
  self.viewer.scene().addItem(point_item)
1203
1408
  self.point_items.append(point_item)
1204
1409
 
1410
+ # Record the action for undo
1411
+ self.action_history.append(
1412
+ {
1413
+ "type": "add_point",
1414
+ "point_type": "positive" if positive else "negative",
1415
+ "point_coords": [int(pos.x()), int(pos.y())],
1416
+ "point_item": point_item,
1417
+ }
1418
+ )
1419
+ # Clear redo history when a new action is performed
1420
+ self.redo_history.clear()
1421
+
1205
1422
  def _update_segmentation(self):
1206
1423
  """Update SAM segmentation preview."""
1207
1424
  if hasattr(self, "preview_mask_item") and self.preview_mask_item:
@@ -1250,6 +1467,17 @@ class MainWindow(QMainWindow):
1250
1467
  # Update polygon preview
1251
1468
  self._draw_polygon_preview()
1252
1469
 
1470
+ # Record the action for undo
1471
+ self.action_history.append(
1472
+ {
1473
+ "type": "add_polygon_point",
1474
+ "point_coords": pos,
1475
+ "dot_item": dot,
1476
+ }
1477
+ )
1478
+ # Clear redo history when a new action is performed
1479
+ self.redo_history.clear()
1480
+
1253
1481
  def _draw_polygon_preview(self):
1254
1482
  """Draw polygon preview lines and fill."""
1255
1483
  # Remove old preview lines and polygons (keep dots)
@@ -1371,13 +1599,26 @@ class MainWindow(QMainWindow):
1371
1599
  self.viewer.scene().removeItem(h)
1372
1600
  self.edit_handles = []
1373
1601
 
1374
- def update_vertex_pos(self, segment_index, vertex_index, new_pos):
1602
+ def update_vertex_pos(self, segment_index, vertex_index, new_pos, record_undo=True):
1375
1603
  """Update the position of a vertex in a polygon segment."""
1376
1604
  seg = self.segment_manager.segments[segment_index]
1377
1605
  if seg.get("type") == "Polygon":
1378
- seg["vertices"][
1379
- vertex_index
1380
- ] = new_pos # new_pos is already the correct scene coordinate
1606
+ old_pos = seg["vertices"][vertex_index]
1607
+ if record_undo:
1608
+ self.action_history.append(
1609
+ {
1610
+ "type": "move_vertex",
1611
+ "segment_index": segment_index,
1612
+ "vertex_index": vertex_index,
1613
+ "old_pos": old_pos,
1614
+ "new_pos": new_pos,
1615
+ }
1616
+ )
1617
+ # Clear redo history when a new action is performed
1618
+ self.redo_history.clear()
1619
+ seg["vertices"][vertex_index] = (
1620
+ new_pos # new_pos is already the correct scene coordinate
1621
+ )
1381
1622
  self._update_polygon_item(segment_index)
1382
1623
  self._highlight_selected_segments() # Keep the highlight in sync with the new shape
1383
1624
 
@@ -1,6 +1,6 @@
1
- from PyQt6.QtCore import Qt, QRectF
2
- from PyQt6.QtGui import QPixmap, QCursor
3
- from PyQt6.QtWidgets import QGraphicsView, QGraphicsScene, QGraphicsPixmapItem
1
+ from PyQt6.QtCore import QRectF, Qt
2
+ from PyQt6.QtGui import QCursor, QPixmap
3
+ from PyQt6.QtWidgets import QGraphicsPixmapItem, QGraphicsScene, QGraphicsView
4
4
 
5
5
 
6
6
  class PhotoViewer(QGraphicsView):
@@ -47,8 +47,5 @@ class PhotoViewer(QGraphicsView):
47
47
 
48
48
  def wheelEvent(self, event):
49
49
  if not self._pixmap_item.pixmap().isNull():
50
- if event.angleDelta().y() > 0:
51
- factor = 1.25
52
- else:
53
- factor = 0.8
50
+ factor = 1.25 if event.angleDelta().y() > 0 else 0.8
54
51
  self.scale(factor, factor)
@@ -1,5 +1,4 @@
1
- from PyQt6.QtWidgets import QTableWidget, QAbstractItemView
2
- from PyQt6.QtCore import Qt
1
+ from PyQt6.QtWidgets import QAbstractItemView, QTableWidget
3
2
 
4
3
 
5
4
  class ReorderableClassTable(QTableWidget):
@@ -31,7 +30,7 @@ class ReorderableClassTable(QTableWidget):
31
30
  drop_row = self.rowCount()
32
31
 
33
32
  selected_rows = sorted(
34
- list({index.row() for index in self.selectedIndexes()}), reverse=True
33
+ {index.row() for index in self.selectedIndexes()}, reverse=True
35
34
  )
36
35
 
37
36
  dragged_rows_data = []
@@ -1,23 +1,20 @@
1
1
  """Right panel with file explorer and segment management."""
2
2
 
3
+ from PyQt6.QtCore import Qt, pyqtSignal
3
4
  from PyQt6.QtWidgets import (
4
- QWidget,
5
- QVBoxLayout,
6
- QPushButton,
7
- QLabel,
5
+ QComboBox,
8
6
  QHBoxLayout,
7
+ QHeaderView,
8
+ QLabel,
9
+ QPushButton,
10
+ QSplitter,
9
11
  QTableWidget,
10
12
  QTreeView,
11
- QComboBox,
12
- QSplitter,
13
- QSpacerItem,
14
- QHeaderView,
13
+ QVBoxLayout,
14
+ QWidget,
15
15
  )
16
- from PyQt6.QtCore import Qt, pyqtSignal
17
- from PyQt6.QtGui import QBrush, QColor
18
16
 
19
17
  from .reorderable_class_table import ReorderableClassTable
20
- from .numeric_table_widget_item import NumericTableWidgetItem
21
18
 
22
19
 
23
20
  class RightPanel(QWidget):
@@ -191,10 +188,12 @@ class RightPanel(QWidget):
191
188
 
192
189
  def mouseDoubleClickEvent(self, event):
193
190
  """Handle double-click to expand collapsed panel."""
194
- if self.width() < 50: # If panel is collapsed
195
- # Request expansion by calling parent method
196
- if self.parent() and hasattr(self.parent(), "_expand_right_panel"):
197
- self.parent()._expand_right_panel()
191
+ if (
192
+ self.width() < 50
193
+ and self.parent()
194
+ and hasattr(self.parent(), "_expand_right_panel")
195
+ ):
196
+ self.parent()._expand_right_panel()
198
197
  super().mouseDoubleClickEvent(event)
199
198
 
200
199
  def _handle_class_alias_change(self, item):
@@ -269,7 +268,7 @@ class RightPanel(QWidget):
269
268
  def get_selected_segment_indices(self):
270
269
  """Get indices of selected segments."""
271
270
  selected_items = self.segment_table.selectedItems()
272
- selected_rows = sorted(list({item.row() for item in selected_items}))
271
+ selected_rows = sorted({item.row() for item in selected_items})
273
272
  return [
274
273
  self.segment_table.item(row, 0).data(Qt.ItemDataRole.UserRole)
275
274
  for row in selected_rows
@@ -1,8 +1,8 @@
1
1
  """UI widgets for LazyLabel."""
2
2
 
3
+ from .adjustments_widget import AdjustmentsWidget
3
4
  from .model_selection_widget import ModelSelectionWidget
4
5
  from .settings_widget import SettingsWidget
5
- from .adjustments_widget import AdjustmentsWidget
6
6
  from .status_bar import StatusBar
7
7
 
8
8
  __all__ = ["ModelSelectionWidget", "SettingsWidget", "AdjustmentsWidget", "StatusBar"]