Skip to content

Commit 7dac6fa

Browse files
committed
Merge branch 'developer_1'
added custom_delegets.py to check an duplicate list in listViewWidget, It is being called in CustomLabelWidget class, When user will click on any item in list for edit, it will check duplicate entry, if exits it will show a msg & revert back to origional state. - 2. in main.py two changes: a) enable edit, save, clear, delete btn when save directory loaded and it find an label for corresponding image_index. b) added one function refresh list to directly reflect the updated_modified_object list from by user from CustomLabelWidget Class. -3 in list_widget.py added custom_delegets as attribute in CustomLabelWidget class and refresh_list method in CustomObjectListWidget class
2 parents 5404c5f + ddff12a commit 7dac6fa

File tree

3 files changed

+173
-22
lines changed

3 files changed

+173
-22
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
from PyQt5.QtWidgets import QStyledItemDelegate, QMessageBox, QLineEdit
2+
from PyQt5.QtCore import Qt
3+
4+
"""
5+
CustomDelegate: Now checks for duplicates in the QStringList
6+
Model and handles committing data appropriately.
7+
8+
The setModelData method iterates through the string list to
9+
check for duplicates and updates the model if the new text is valid.
10+
CustomObjectListWidget: Uses QStringListModel for the list data.
11+
12+
add_item: Appends new items to the list.
13+
This setup integrates QStringListModel with the custom delegate,
14+
allowing you to edit list items and ensure that no duplicates are added.
15+
16+
"""
17+
18+
class CustomDelegate(QStyledItemDelegate):
19+
def __init__(self, parent=None):
20+
super().__init__(parent)
21+
self.parent_view = parent
22+
23+
def createEditor(self, parent, option, index):
24+
"""
25+
Create and return a new editor widget for the item.
26+
27+
Args:
28+
parent (QWidget): The parent widget.
29+
option (QStyleOptionViewItem): The option for the item.
30+
index (QModelIndex): The index of the item.
31+
32+
Returns:
33+
QLineEdit: The editor widget.
34+
"""
35+
editor = QLineEdit(parent)
36+
return editor
37+
38+
def setEditorData(self, editor, index):
39+
"""
40+
Set the data for the editor widget.
41+
42+
Args:
43+
editor (QWidget): The editor widget.
44+
index (QModelIndex): The index of the item.
45+
"""
46+
data = index.model().data(index, Qt.EditRole)
47+
editor.setText(data)
48+
49+
def setModelData(self, editor, model, index):
50+
"""
51+
Retrieve the data from the editor widget and set it in the model.
52+
53+
Args:
54+
editor (QWidget): The editor widget.
55+
model (QAbstractItemModel): The model.
56+
index (QModelIndex): The index of the item.
57+
"""
58+
new_text = editor.text()
59+
# Check if the new text already exists in the model
60+
for row in range(model.rowCount()):
61+
item_text = model.data(model.index(row, 0), Qt.DisplayRole)
62+
if item_text == new_text and model.index(row, 0) != index:
63+
QMessageBox.warning(self.parent_view, 'Validation Error', 'The entered text already exists in the list.')
64+
return
65+
66+
model.setData(index, new_text, Qt.EditRole)
67+
68+
def commitData(self, editor):
69+
"""
70+
Validate and commit the edited data.
71+
72+
Args:
73+
editor (QWidget): The editor widget.
74+
"""
75+
super().commitData(editor)

labelvim/labelvim/widgets/list_widgets.py

Lines changed: 82 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
from PyQt5 import QtWidgets
2-
from PyQt5.QtCore import QStringListModel, Qt, QRect, pyqtSignal
2+
from PyQt5.QtCore import QStringListModel, Qt, QRect, pyqtSignal, QModelIndex, pyqtSlot
33
from PyQt5.QtGui import QStandardItemModel, QStandardItem
44
from PyQt5.QtWidgets import QInputDialog, QMessageBox, QMenu, QAction
55

66
# External imports
77
from labelvim.utils.config import ANNOTATION_TYPE, OBJECT_LIST_ACTION
8+
from labelvim.widgets.custom_delegets import CustomDelegate
89
from enum import Enum
910

1011
class CustomListViewWidget(QtWidgets.QListView):
@@ -62,7 +63,7 @@ def set_label_list(self, label_list=[]):
6263
Args:
6364
label_list (list, optional): A list of label names to display. Defaults to an empty list.
6465
"""
65-
print(f"Label List in set label list: {label_list}")
66+
# print(f"Label List in set label list: {label_list}")
6667
if isinstance(label_list, list) and len(label_list) > 0:
6768
self.model.clear() # Clear the model before updating
6869
self.label_list = label_list
@@ -176,8 +177,8 @@ def mousePressEvent(self, event):
176177
event (QMouseEvent): The mouse event object.
177178
"""
178179
index = self.indexAt(event.pos())
179-
print(f"Index: {index.row()}")
180-
print(f"model row count {self.model.rowCount()}")
180+
# print(f"Index: {index.row()}")
181+
# print(f"model row count {self.model.rowCount()}")
181182
if index.isValid():
182183
item = self.model.itemFromIndex(index)
183184
print(f"Item: {item.text()}")
@@ -230,6 +231,8 @@ def __init__(self, parent=None):
230231
# Set the geometry, model
231232
self.set_geometry()
232233
self.set_model()
234+
self.delegate = CustomDelegate(self)
235+
self.setItemDelegate(self.delegate)
233236
# Set the annotation type to None
234237
self.annotation_type = "Rectangle"
235238
# Connect the signals
@@ -387,6 +390,7 @@ def edit_label(self, index):
387390
# Start editing the item at the specified index
388391
self.edit(index)
389392

393+
390394
def on_data_changed(self, topLeft, bottomRight, roles):
391395
"""
392396
Slot that is triggered when the data in the model is changed.
@@ -438,7 +442,7 @@ def __init__(self, parent=None):
438442
self.set_label_list(self.label_list)
439443
# Connect the signals
440444
self.object_list_slot_receiver.connect(self.__receiver_action) # Connect the signal to update the label list
441-
# self.model.dataChanged.connect(self.on_data_changed) # Connect the dataChanged signal
445+
# self.model.dataChanged.connect(self.handle_data_changed) # Connect the dataChanged signal
442446

443447
def set_geometry(self):
444448
"""
@@ -455,7 +459,7 @@ def set_model(self):
455459
self.setModel(self.model)
456460
self.setUpdatesEnabled(True)
457461
# # Set the edit triggers to NoEditTriggers
458-
# self.setEditTriggers(QtWidgets.QListView.NoEditTriggers)
462+
self.setEditTriggers(QtWidgets.QListView.NoEditTriggers)
459463

460464
def __receiver_action(self, data: any, action: Enum):
461465
data = data[0]
@@ -493,15 +497,15 @@ def set_label_list(self, category_id: list = [], object_id: list = []):
493497
label_list (list, optional): A list of label names to display. Defaults to an empty list.
494498
"""
495499
# Check if the label list is a non-empty list
496-
if isinstance(category_id, list) and len(category_id) > 0 and isinstance(object_id, list) and len(object_id) > 0:
500+
# if isinstance(category_id, list) and len(category_id) > 0 and isinstance(object_id, list) and len(object_id) > 0:
497501
# Clear the model before updating
498-
self.clear_list()
499-
# Update the label list and model
500-
self.category_id = category_id
501-
self.object_id = object_id
502-
object_list = [f"{self.label_list[id]}" for id in category_id]
503-
# update the model
504-
self.model.setStringList(object_list)
502+
self.clear_list()
503+
# Update the label list and model
504+
self.category_id = category_id
505+
self.object_id = object_id
506+
object_list = [f"{self.label_list[id]}" for id in category_id]
507+
# update the model
508+
self.model.setStringList(object_list)
505509

506510
def clear_list(self):
507511
"""
@@ -534,6 +538,7 @@ def remove_label(self, data: list):
534538
Args:
535539
label (str): The label to remove.
536540
"""
541+
print(f"Data To Be remoed: {data}")
537542
category_id = [label["category_id"] for label in data]
538543
object_id = [label['id'] for label in data]
539544
self.object = {label['id']: label['category_id'] for label in data}
@@ -555,6 +560,14 @@ def edit_label(self, object_id, category_id):
555560
object_list = [f"{self.label_list[id]}" for id in self.category_id]
556561
self.model.setStringList(object_list)
557562

563+
def refresh_list(self, label_list: list):
564+
"""
565+
Refresh the list view
566+
"""
567+
self.label_list = label_list
568+
object_list = [f"{self.label_list[id]}" for id in self.category_id]
569+
self.model.setStringList(object_list)
570+
558571
def on_item_clicked(self, index):
559572
"""
560573
Emits the signal with the currently selected item's text.
@@ -563,8 +576,8 @@ def on_item_clicked(self, index):
563576
index (QModelIndex): The index of the current item.
564577
"""
565578
# Emit the signal with the selected item's text
566-
print(f"Index: {index.row()}")
567-
print(f"Item: {self.model.data(index, Qt.DisplayRole)}")
579+
# print(f"Index: {index.row()}")
580+
# print(f"Item: {self.model.data(index, Qt.DisplayRole)}")
568581
id = self.object_id[index.row()]
569582
self.object_selection_notification_slot.emit(id)
570583

@@ -580,4 +593,56 @@ def mousePressEvent(self, event) -> None:
580593
print("Clearing selection")
581594
self.object_selection_notification_slot.emit(-1)
582595
# Call the parent class's mousePressEvent to handle normal clicks
583-
super().mousePressEvent(event)
596+
super().mousePressEvent(event)
597+
598+
# def contextMenuEvent(self, event):
599+
# """
600+
# Handles the context menu event, providing an option to edit the selected item.
601+
602+
# Args:
603+
# event (QContextMenuEvent): The context menu event object.
604+
# """
605+
# # Get the index at the mouse position
606+
# index = self.indexAt(event.pos())
607+
# # Check if the index is valid
608+
# if index.isValid():
609+
# # Create a context menu
610+
# menu = QMenu(self)
611+
# # Create an action to edit the item
612+
# edit_action = QAction('Edit', self)
613+
# # Connect the action to the edit_label method
614+
# edit_action.triggered.connect(lambda: self.edit_label_at_index(index))
615+
# # Add the action to the menu
616+
# menu.addAction(edit_action)
617+
# # Show the context menu at the mouse position
618+
# menu.exec_(event.globalPos())
619+
620+
# def edit_label_at_index(self, index):
621+
# """
622+
# Triggers inline editing for the selected item.
623+
624+
# Args:
625+
# index (QModelIndex): The index of the item to be edited.
626+
# """
627+
# # Start editing the item at the specified index
628+
# self.edit(index)
629+
630+
# @pyqtSlot(QModelIndex, QModelIndex)
631+
# def handle_data_changed(self, top_left: QModelIndex, bottom_right: QModelIndex):
632+
# """
633+
# Slot to handle data changes in the list view.
634+
635+
# Args:
636+
# top_left (QModelIndex): The top-left index of the changed data.
637+
# bottom_right (QModelIndex): The bottom-right index of the changed data.
638+
# """
639+
# row = top_left.row()
640+
# # get index of the item and old data
641+
642+
643+
644+
645+
646+
# new_data = self.model.data(top_left, Qt.EditRole)
647+
# print(f"Item at row {row} was changed to '{new_data}'")
648+
# # Optionally, you can update your data model or UI here

labelvim/main.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,10 @@ def __save_directory(self):
191191

192192
# Emit the annotation data to the display
193193
self.Display.annotation_data_slot_receiver.emit(self.annotaion_data['annotations'])
194-
194+
self.SaveBtn.setEnabled(True)
195+
self.DeleteAnnotationBtn.setEnabled(True)
196+
self.EditObjectBtn.setEnabled(True)
197+
self.ClearAnnotationBtn.setEnabled(True)
195198
def __delete_file(self):
196199
"""
197200
Deletes the currently selected file from the list. Removes the file from
@@ -353,10 +356,16 @@ def __create_object(self):
353356

354357
# Enable the save button
355358
self.SaveBtn.setEnabled(True)
359+
self.DeleteAnnotationBtn.setEnabled(True)
360+
self.EditObjectBtn.setEnabled(True)
356361
self.ClearAnnotationBtn.setEnabled(True)
357362
else:
358363
# Display a message dialog if save directory is not selected
359364
self.msg_dialog("Save Directory Not Selected", "Please select the save directory first.")
365+
self.SaveBtn.setEnabled(False)
366+
self.DeleteAnnotationBtn.setEnabled(False)
367+
self.EditObjectBtn.setEnabled(False)
368+
self.ClearAnnotationBtn.setEnabled(False)
360369

361370
def __edit_object(self):
362371
print("Edit Object")
@@ -439,16 +448,18 @@ def msg_dialog(self, title, msg):
439448

440449
## Signal and Slot
441450
def update_label_list_to_Display(self, label_list):
442-
print(f"update_label_list: {label_list}")
451+
# print(f"update_label_list: {label_list}")
443452
self.label_list_manager.update(label_list)
444453
self.Display.update_label_list_slot_receiver.emit(label_list)
445-
self.ObjectLabelListWidget.label_list = label_list # Update the label list in the object list widget
454+
self.ObjectLabelListWidget.refresh_list(label_list) # Update the label list in the object list widget
455+
# self.ObjectLabelListWidget.label_list = label_list
446456

447457
def update_label_list_to_Label_Widget(self, label_list):
448-
print(f"update_label_list: {label_list}")
458+
# print(f"update_label_list: {label_list}")
449459
self.label_list_manager.update(label_list)
450460
self.LabelWidget.update_label_list_slot_receiver.emit(label_list)
451-
self.ObjectLabelListWidget.label_list = label_list # Update the label list in the object list widget
461+
self.ObjectLabelListWidget.refresh_list(label_list) # Update the label list in the object list widget
462+
# self.ObjectLabelListWidget.label_list = label_list # Update the label list in the object list widget
452463

453464
def update_data_to_ObjectListWidget(self, data, action):
454465
print(f"update_data_to_ObjectListWidget: {data}")

0 commit comments

Comments
 (0)