Bladeren bron

Validate label text when adding/editing

The current implementation in the Dialog feels like a hack.

Fix bug where canceling the dialog with Esc accepted the value.
Map escape to Undo close.

There is also a bug in the validation of label editing, when focus is
lost with something like TAB.
Michael Pitidis 13 jaren geleden
bovenliggende
commit
5b51852d46
3 gewijzigde bestanden met toevoegingen van 50 en 7 verwijderingen
  1. 21 4
      labelDialog.py
  2. 26 3
      labelme.py
  3. 3 0
      lib.py

+ 21 - 4
labelDialog.py

@@ -2,10 +2,13 @@
 from PyQt4.QtGui import *
 from PyQt4.QtCore import *
 
-from lib import newButton
+from lib import newButton, labelValidator
 
 BB = QDialogButtonBox
 
+# FIXME:
+# - Use the validator when accepting the dialog.
+
 class LabelDialog(QDialog):
     OK, UNDO, DELETE = range(3)
 
@@ -14,6 +17,8 @@ class LabelDialog(QDialog):
         self.action = self.OK
         self.edit = QLineEdit()
         self.edit.setText(text)
+        self.edit.setValidator(labelValidator())
+        self.edit.editingFinished.connect(self.postProcess)
         layout = QHBoxLayout()
         layout.addWidget(self.edit)
         delete = newButton('Delete', icon='delete', slot=self.delete)
@@ -22,16 +27,17 @@ class LabelDialog(QDialog):
         bb.addButton(BB.Ok)
         bb.addButton(undo, BB.RejectRole)
         bb.addButton(delete, BB.RejectRole)
-        bb.accepted.connect(self.accept)
-        bb.rejected.connect(self.reject)
+        bb.accepted.connect(self.validate)
         layout.addWidget(bb)
         self.setLayout(layout)
 
     def undo(self):
         self.action = self.UNDO
+        self.reject()
 
     def delete(self):
         self.action = self.DELETE
+        self.reject()
 
     def text(self):
         return self.edit.text()
@@ -42,5 +48,16 @@ class LabelDialog(QDialog):
         self.edit.setText(u"Enter label")
         self.edit.setSelection(0, len(self.text()))
         self.edit.setFocus(Qt.PopupFocusReason)
-        return self.OK if self.exec_() else self.action
+        return self.OK if self.exec_() == QDialog.Accepted else self.action
+
+    def validate(self):
+        if self.edit.text().trimmed():
+            self.accept()
+
+    def postProcess(self):
+        self.edit.setText(self.edit.text().trimmed())
+
+    def keyPressEvent(self, ev):
+        if ev.key() == Qt.Key_Escape:
+            self.undo()
 

+ 26 - 3
labelme.py

@@ -16,7 +16,7 @@ from PyQt4.QtCore import *
 
 import resources
 
-from lib import newAction, addActions
+from lib import newAction, addActions, labelValidator
 from shape import Shape
 from canvas import Canvas
 from zoomWidget import ZoomWidget
@@ -26,12 +26,16 @@ from labelFile import LabelFile
 
 __appname__ = 'labelme'
 
+# FIXME
+# - [low] Label validation/postprocessing breaks with TAB.
+
 # TODO:
-# - Zoom is too "steppy".
 # - Add a new column in list widget with checkbox to show/hide shape.
 # - Make sure the `save' action is disabled when no labels are
 #   present in the image, e.g. when all of them are deleted.
-# - Prevent the user from entering/editing empty labels.
+# - [easy] Add button to Hide/Show all labels.
+# - Zoom is too "steppy".
+
 
 ### Utility functions and classes.
 
@@ -72,6 +76,7 @@ class MainWindow(QMainWindow, WindowMixin):
         self.dock.setWidget(self.labelList)
         self.zoom_widget = ZoomWidget()
 
+        self.labelList.setItemDelegate(LabelDelegate())
         self.labelList.itemActivated.connect(self.highlightLabel)
         # Connect to itemChanged to detect checkbox changes.
         self.labelList.itemChanged.connect(self.labelItemChanged)
@@ -380,6 +385,24 @@ class MainWindow(QMainWindow, WindowMixin):
         self.canvas.deleteSelected()
 
 
+class LabelDelegate(QItemDelegate):
+    def __init__(self, parent=None):
+        super(LabelDelegate, self).__init__(parent)
+        self.validator = labelValidator()
+
+    # FIXME: Validation and trimming are completely broken if the
+    # user navigates away from the editor with something like TAB.
+    def createEditor(self, parent, option, index):
+        """Make sure the user cannot enter empty labels.
+        Also remove trailing whitespace."""
+        edit = super(LabelDelegate, self).createEditor(parent, option, index)
+        if isinstance(edit, QLineEdit):
+            edit.setValidator(self.validator)
+            def strip():
+                edit.setText(edit.text().trimmed())
+            edit.editingFinished.connect(strip)
+        return edit
+
 class Settings(object):
     """Convenience dict-like wrapper around QSettings."""
     def __init__(self, types=None):

+ 3 - 0
lib.py

@@ -39,3 +39,6 @@ def addActions(widget, actions):
         else:
             widget.addAction(action)
 
+def labelValidator():
+    return QRegExpValidator(QRegExp(r'^[^ \t].+'), None)
+