Browse Source

Merge label edit dialogs into one

Disable listwidget editing altogether.
Michael Pitidis 13 years ago
parent
commit
c42d6fbc0c
2 changed files with 53 additions and 45 deletions
  1. 14 45
      labelme.py
  2. 39 0
      simpleLabelDialog.py

+ 14 - 45
labelme.py

@@ -30,7 +30,6 @@ __appname__ = 'labelme'
 # - [medium] Set max zoom value to something big enough for FitWidth/Window
 # - [medium] Set max zoom value to something big enough for FitWidth/Window
 # - [medium] Disabling the save button prevents the user from saving to
 # - [medium] Disabling the save button prevents the user from saving to
 #   alternate files. Either keep enabled, or add "Save As" button.
 #   alternate files. Either keep enabled, or add "Save As" button.
-# - [low] Label validation/postprocessing breaks with TAB.
 
 
 # TODO:
 # TODO:
 # - [medium] Zoom should keep the image centered.
 # - [medium] Zoom should keep the image centered.
@@ -39,10 +38,8 @@ __appname__ = 'labelme'
 # - [high] Escape should cancel editing mode if no point in canvas.
 # - [high] Escape should cancel editing mode if no point in canvas.
 # - [medium] Maybe have separate colors for different shapes, and
 # - [medium] Maybe have separate colors for different shapes, and
 #   color the background in the label list accordingly (kostas).
 #   color the background in the label list accordingly (kostas).
-# - [medium] Highlight label list on shape selection and vice-verca.
 # - [medium] Add undo button for vertex addition.
 # - [medium] Add undo button for vertex addition.
 # - [medium,maybe] Support vertex moving.
 # - [medium,maybe] Support vertex moving.
-# - [low,easy] Add button to Hide/Show all labels.
 # - [low,maybe] Open images with drag & drop.
 # - [low,maybe] Open images with drag & drop.
 # - [low,maybe] Preview images on file dialogs.
 # - [low,maybe] Preview images on file dialogs.
 # - [low,maybe] Sortable label list.
 # - [low,maybe] Sortable label list.
@@ -96,8 +93,8 @@ class MainWindow(QMainWindow, WindowMixin):
         self.colorDialog = ColorDialog(parent=self)
         self.colorDialog = ColorDialog(parent=self)
         self.simpleLabelDialog = SimpleLabelDialog(parent=self)
         self.simpleLabelDialog = SimpleLabelDialog(parent=self)
 
 
-        self.labelList.setItemDelegate(LabelDelegate())
         self.labelList.itemActivated.connect(self.highlightLabel)
         self.labelList.itemActivated.connect(self.highlightLabel)
+        self.labelList.itemDoubleClicked.connect(self.editLabel)
         # Connect to itemChanged to detect checkbox changes.
         # Connect to itemChanged to detect checkbox changes.
         self.labelList.itemChanged.connect(self.labelItemChanged)
         self.labelList.itemChanged.connect(self.labelItemChanged)
         self.labelList.itemSelectionChanged.connect(self.labelSelectionChanged)
         self.labelList.itemSelectionChanged.connect(self.labelSelectionChanged)
@@ -165,13 +162,13 @@ class MainWindow(QMainWindow, WindowMixin):
             self.FIT_WIDTH: self.scaleFitWidth,
             self.FIT_WIDTH: self.scaleFitWidth,
         }
         }
 
 
-        editLabelCanvas = action('&Edit Label', self.editLabelCanvas,
+        edit = action('&Edit Label', self.editLabel,
                 'Ctrl+E', 'edit', u'Modify the label of the selected polygon',
                 'Ctrl+E', 'edit', u'Modify the label of the selected polygon',
                 enabled=False)
                 enabled=False)
 
 
 
 
         # Custom context menu for the canvas widget:
         # Custom context menu for the canvas widget:
-        addActions(self.canvas.menus[0], (label, copy, delete, editLabelCanvas))
+        addActions(self.canvas.menus[0], (label, edit, copy, delete))
         addActions(self.canvas.menus[1], (
         addActions(self.canvas.menus[1], (
             action('&Copy here', self.copyShape),
             action('&Copy here', self.copyShape),
             action('&Move here', self.moveShape)))
             action('&Move here', self.moveShape)))
@@ -179,22 +176,18 @@ class MainWindow(QMainWindow, WindowMixin):
         labels = self.dock.toggleViewAction()
         labels = self.dock.toggleViewAction()
         labels.setShortcut('Ctrl+L')
         labels.setShortcut('Ctrl+L')
 
 
-        # Context menu for the label
-        editLabelList = action('&Edit Label', self.editLabelList,
-                'Ctrl+Return', 'edit', u'Modify this label',
-                enabled=False)
-        lmenu = QMenu()
-        addActions(lmenu, (editLabelList, delete))
+        # Lavel list context menu.
+        labelMenu = QMenu()
+        addActions(labelMenu, (edit, delete))
         self.labelList.setContextMenuPolicy(Qt.CustomContextMenu)
         self.labelList.setContextMenuPolicy(Qt.CustomContextMenu)
         self.labelList.customContextMenuRequested.connect(self.popLabelListMenu)
         self.labelList.customContextMenuRequested.connect(self.popLabelListMenu)
-        # Add the action to the main window, effectively making its shortcut glogal!
-        self.addAction(editLabelList)
+        # Add the action to the main window, so that its shortcut is global.
+        self.addAction(edit)
 
 
         # Store actions for further handling.
         # Store actions for further handling.
         self.actions = struct(save=save, open=open, close=close,
         self.actions = struct(save=save, open=open, close=close,
                 lineColor=color1, fillColor=color2,
                 lineColor=color1, fillColor=color2,
-                label=label, delete=delete, copy=copy,
-                editLabelList=editLabelList, editLabelCanvas=editLabelCanvas,
+                label=label, delete=delete, edit=edit, copy=copy,
                 zoom=zoom, zoomIn=zoomIn, zoomOut=zoomOut, zoomOrg=zoomOrg,
                 zoom=zoom, zoomIn=zoomIn, zoomOut=zoomOut, zoomOrg=zoomOrg,
                 fitWindow=fitWindow, fitWidth=fitWidth)
                 fitWindow=fitWindow, fitWidth=fitWidth)
 
 
@@ -202,7 +195,7 @@ class MainWindow(QMainWindow, WindowMixin):
                 file=self.menu('&File'),
                 file=self.menu('&File'),
                 edit=self.menu('&Polygons'),
                 edit=self.menu('&Polygons'),
                 view=self.menu('&View'),
                 view=self.menu('&View'),
-                labelList=lmenu)
+                labelList=labelMenu)
         addActions(self.menus.file, (open, save, close, quit))
         addActions(self.menus.file, (open, save, close, quit))
         addActions(self.menus.edit, (label, color1, color2))
         addActions(self.menus.edit, (label, color1, color2))
         addActions(self.menus.view, (
         addActions(self.menus.view, (
@@ -299,17 +292,12 @@ class MainWindow(QMainWindow, WindowMixin):
     def popLabelListMenu(self, point):
     def popLabelListMenu(self, point):
         self.menus.labelList.exec_(self.labelList.mapToGlobal(point))
         self.menus.labelList.exec_(self.labelList.mapToGlobal(point))
 
 
-    def editLabelCanvas(self):
-        item = self.currentItem()
+    def editLabel(self, item=None):
+        item = item if item else self.currentItem()
         text = self.simpleLabelDialog.popUp(item.text())
         text = self.simpleLabelDialog.popUp(item.text())
         if text is not None:
         if text is not None:
             item.setText(text)
             item.setText(text)
 
 
-    def editLabelList(self):
-        items = self.labelList.selectedItems()
-        if items:
-            self.labelList.editItem(items[0])
-
     # React to canvas signals.
     # React to canvas signals.
     def shapeSelectionChanged(self, selected=False):
     def shapeSelectionChanged(self, selected=False):
         if self._noSelectionSlot:
         if self._noSelectionSlot:
@@ -322,12 +310,11 @@ class MainWindow(QMainWindow, WindowMixin):
                 self.labelList.clearSelection()
                 self.labelList.clearSelection()
         self.actions.delete.setEnabled(selected)
         self.actions.delete.setEnabled(selected)
         self.actions.copy.setEnabled(selected)
         self.actions.copy.setEnabled(selected)
-        self.actions.editLabelList.setEnabled(selected)
-        self.actions.editLabelCanvas.setEnabled(selected)
+        self.actions.edit.setEnabled(selected)
 
 
     def addLabel(self, shape):
     def addLabel(self, shape):
         item = QListWidgetItem(shape.label)
         item = QListWidgetItem(shape.label)
-        item.setFlags(item.flags() | Qt.ItemIsUserCheckable | Qt.ItemIsEditable)
+        item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
         item.setCheckState(Qt.Checked)
         item.setCheckState(Qt.Checked)
         self.labels[item] = shape
         self.labels[item] = shape
         self.items[shape] = item
         self.items[shape] = item
@@ -623,24 +610,6 @@ class MainWindow(QMainWindow, WindowMixin):
         self.setDirty()
         self.setDirty()
 
 
 
 
-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):
 class Settings(object):
     """Convenience dict-like wrapper around QSettings."""
     """Convenience dict-like wrapper around QSettings."""
     def __init__(self, types=None):
     def __init__(self, types=None):

+ 39 - 0
simpleLabelDialog.py

@@ -0,0 +1,39 @@
+
+from PyQt4.QtGui import *
+from PyQt4.QtCore import *
+
+from lib import newButton, labelValidator
+
+BB = QDialogButtonBox
+
+class SimpleLabelDialog(QDialog):
+
+    def __init__(self, text='', parent=None):
+        super(SimpleLabelDialog, self).__init__(parent)
+        self.edit = QLineEdit()
+        self.edit.setText(text)
+        self.edit.setValidator(labelValidator())
+        self.edit.editingFinished.connect(self.postProcess)
+        layout = QVBoxLayout()
+        layout.addWidget(self.edit)
+        bb = BB(BB.Ok | BB.Cancel, Qt.Horizontal, self)
+        bb.accepted.connect(self.validate)
+        bb.rejected.connect(self.reject)
+        layout.addWidget(bb)
+        self.setLayout(layout)
+
+    def validate(self):
+        if self.edit.text().trimmed():
+            self.accept()
+
+    def postProcess(self):
+        self.edit.setText(self.edit.text().trimmed())
+
+    def popUp(self, text='', pos=None):
+        self.edit.setText(text)
+        self.edit.setSelection(0, len(text))
+        self.edit.setFocus(Qt.PopupFocusReason)
+        if pos is not None:
+            self.move(pos)
+        return self.edit.text() if self.exec_() else None
+