Sfoglia il codice sorgente

Merge branch 'experiment/modes'

Michael Pitidis 13 anni fa
parent
commit
fec310a3d5
5 ha cambiato i file con 244 aggiunte e 102 eliminazioni
  1. 79 49
      canvas.py
  2. 1 0
      labelDialog.py
  3. 154 51
      labelme.py
  4. 1 0
      resources.qrc
  5. 9 2
      shape.py

+ 79 - 49
canvas.py

@@ -24,21 +24,20 @@ class Canvas(QWidget):
     shapeMoved = pyqtSignal()
     drawingPolygon = pyqtSignal(bool)
 
-    SELECT, EDIT = range(2)
+    CREATE, EDIT = range(2)
 
     epsilon = 11.0
 
     def __init__(self, *args, **kwargs):
         super(Canvas, self).__init__(*args, **kwargs)
         # Initialise local state.
-        self.mode = self.SELECT
+        self.mode = self.EDIT
         self.shapes = []
         self.current = None
         self.selectedShape=None # save the selected shape here
         self.selectedShapeCopy=None
         self.lineColor = QColor(0, 0, 255)
         self.line = Shape(line_color=self.lineColor)
-        self.mouseButtonIsPressed=False #when it is true and shape is selected , move the shape with the mouse move event
         self.prevPoint = QPointF()
         self.offsets = QPointF(), QPointF()
         self.scale = 1.0
@@ -46,8 +45,8 @@ class Canvas(QWidget):
         self.visible = {}
         self._hideBackround = False
         self.hideBackround = False
-        self.highlightedShape = None
-        self._nearest = None
+        self.hShape = None
+        self.hVertex = None
         self._painter = QPainter()
         self._cursor = CURSOR_DEFAULT
         # Menus:
@@ -68,11 +67,23 @@ class Canvas(QWidget):
     def isVisible(self, shape):
         return self.visible.get(shape, True)
 
+    def drawing(self):
+        return self.mode == self.CREATE
+
     def editing(self):
         return self.mode == self.EDIT
 
     def setEditing(self, value=True):
-        self.mode = self.EDIT if value else self.SELECT
+        self.mode = self.EDIT if value else self.CREATE
+        if not value: # Create
+            self.unHighlight()
+            self.deSelectShape()
+
+    def unHighlight(self):
+        self.hVertex = self.hShape = None
+
+    def selectedVertex(self):
+        return self.hVertex is not None
 
     def mouseMoveEvent(self, ev):
         """Update line with last point and current coordinates."""
@@ -81,7 +92,7 @@ class Canvas(QWidget):
         self.restoreCursor()
 
         # Polygon drawing.
-        if self.editing():
+        if self.drawing():
             self.overrideCursor(CURSOR_DRAW)
             if self.current:
                 color = self.lineColor
@@ -112,10 +123,11 @@ class Canvas(QWidget):
                 self.repaint()
             return
 
-        # Polygon moving.
+        # Polygon/Vertex moving.
         if Qt.LeftButton & ev.buttons():
-            if self._nearest and self.prevPoint:
+            if self.selectedVertex():
                 self.boundedMoveVertex(pos)
+                self.shapeMoved.emit()
                 self.repaint()
             elif self.selectedShape and self.prevPoint:
                 self.overrideCursor(CURSOR_MOVE)
@@ -124,39 +136,42 @@ class Canvas(QWidget):
                 self.repaint()
             return
 
-        # Just hovering over the canvas:
-        # Update tooltip value and fill topmost shape.
+        # Just hovering over the canvas, 2 posibilities:
+        # - Highlight shapes
+        # - Highlight vertex
+        # Update shape/vertex fill and tooltip value accordingly.
         self.setToolTip("Image")
-        previous = self.highlightedShape
-        for shape in reversed(self.shapes):
-            if self.isVisible(shape):
-                v = shape.nearestVertex(pos, self.epsilon)
-                if v is not None:
-                    self._nearest = (v, shape)
-                    shape.highlightVertex(v, shape.MOVE_VERTEX)
-                    self.highlightedShape = shape
-                    self.overrideCursor(CURSOR_POINT)
-                    self.setStatusTip("Click & drag to move point")
-                    break
-                if shape.containsPoint(pos):
-                    self.setToolTip("Object '%s'" % shape.label)
-                    self.highlightedShape = shape
-                    self.overrideCursor(CURSOR_GRAB)
-                    break
-        else:
-            if self.highlightedShape:
-                self.highlightedShape.highlightClear()
-            self.highlightedShape = None
-            self._nearest = None
-
-        if previous != self.highlightedShape:
-            # Try to minimise repaints.
-            self.update()
+        for shape in reversed([s for s in self.shapes if self.isVisible(s)]):
+            # Look for a nearby vertex to highlight. If that fails,
+            # check if we happen to be inside a shape.
+            index = shape.nearestVertex(pos, self.epsilon)
+            if index is not None:
+                self.hVertex, self.hShape = index, shape
+                shape.highlightVertex(index, shape.MOVE_VERTEX)
+                self.overrideCursor(CURSOR_POINT)
+                self.setToolTip("Click & drag to move point")
+                self.setStatusTip(self.toolTip())
+                self.update()
+                break
+            elif shape.containsPoint(pos):
+                if self.selectedVertex():
+                    self.hShape.highlightClear()
+                self.hVertex, self.hShape = None, shape
+                self.setToolTip("Click & drag to move shape '%s'" % shape.label)
+                self.setStatusTip(self.toolTip())
+                self.overrideCursor(CURSOR_GRAB)
+                self.update()
+                break
+        else: # Nothing found, clear highlights, reset state.
+            if self.hShape:
+                self.hShape.highlightClear()
+                self.update()
+            self.hVertex, self.hShape = None, None
 
     def mousePressEvent(self, ev):
         pos = self.transformPos(ev.posF())
         if ev.button() == Qt.LeftButton:
-            if self.editing():
+            if self.drawing():
                 if self.current:
                     self.current.addPoint(self.line[1])
                     self.line[0] = self.current[-1]
@@ -173,7 +188,7 @@ class Canvas(QWidget):
                 self.selectShapePoint(pos)
                 self.prevPoint = pos
                 self.repaint()
-        elif ev.button() == Qt.RightButton and not self.editing():
+        elif ev.button() == Qt.RightButton and self.editing():
             self.selectShapePoint(pos)
             self.prevPoint = pos
             self.repaint()
@@ -218,7 +233,7 @@ class Canvas(QWidget):
         self._hideBackround = self.hideBackround if enable else False
 
     def mouseDoubleClickEvent(self, ev):
-        if self.current and self.editing():
+        if self.current and self.drawing():
             # Shapes need to have at least 3 vertices.
             if len(self.current) < 4:
                 return
@@ -240,9 +255,9 @@ class Canvas(QWidget):
     def selectShapePoint(self, point):
         """Select the first shape created which contains this point."""
         self.deSelectShape()
-        if self._nearest:
-            i, s = self._nearest
-            s.highlightVertex(i, s.MOVE_VERTEX)
+        if self.selectedVertex(): # A vertex is marked for selection.
+            index, shape = self.hVertex, self.hShape
+            shape.highlightVertex(index, shape.MOVE_VERTEX)
             return
         for shape in reversed(self.shapes):
             if self.isVisible(shape) and shape.containsPoint(point):
@@ -262,15 +277,15 @@ class Canvas(QWidget):
         self.offsets = QPointF(x1, y1), QPointF(x2, y2)
 
     def boundedMoveVertex(self, pos):
-        i, shape = self._nearest
-        point = shape[i]
+        index, shape = self.hVertex, self.hShape
+        point = shape[index]
         if self.outOfPixmap(pos):
             pos = self.intersectionPoint(point, pos)
-        shape.moveVertexBy(i, pos - point)
+        shape.moveVertexBy(index, pos - point)
 
     def boundedMoveShape(self, shape, pos):
         if self.outOfPixmap(pos):
-            return # No need to move
+            return False # No need to move
         o1 = pos + self.offsets[0]
         if self.outOfPixmap(o1):
             pos -= QPointF(min(0, o1.x()), min(0, o1.y()))
@@ -283,8 +298,12 @@ class Canvas(QWidget):
         # a bit "shaky" when nearing the border and allows it to
         # go outside of the shape's area for some reason. XXX
         #self.calculateOffsets(self.selectedShape, pos)
-        shape.moveBy(pos - self.prevPoint)
-        self.prevPoint = pos
+        dp = pos - self.prevPoint
+        if dp:
+            shape.moveBy(dp)
+            self.prevPoint = pos
+            return True
+        return False
 
     def deSelectShape(self):
         if self.selectedShape:
@@ -309,8 +328,19 @@ class Canvas(QWidget):
             self.shapes.append(shape)
             shape.selected = True
             self.selectedShape = shape
+            self.boundedShiftShape(shape)
             return shape
 
+    def boundedShiftShape(self, shape):
+        # Try to move in one direction, and if it fails in another.
+        # Give up if both fail.
+        point = shape[0]
+        offset = QPointF(2.0, 2.0)
+        self.calculateOffsets(shape, point)
+        self.prevPoint = point
+        if not self.boundedMoveShape(shape, point - offset):
+            self.boundedMoveShape(shape, point + offset)
+
     def paintEvent(self, event):
         if not self.pixmap:
             return super(Canvas, self).paintEvent(event)
@@ -328,7 +358,7 @@ class Canvas(QWidget):
         Shape.scale = self.scale
         for shape in self.shapes:
             if (shape.selected or not self._hideBackround) and self.isVisible(shape):
-                shape.fill = shape.selected or self.highlightedShape == shape
+                shape.fill = shape.selected or shape == self.hShape
                 shape.paint(p)
         if self.current:
             self.current.paint(p)

+ 1 - 0
labelDialog.py

@@ -12,6 +12,7 @@ class LabelDialog(QDialog):
 
     def __init__(self, text='', parent=None):
         super(LabelDialog, self).__init__(parent)
+        self.setWindowTitle("Enter object's label")
         self.action = self.OK
         self.edit = QLineEdit()
         self.edit.setText(text)

+ 154 - 51
labelme.py

@@ -32,9 +32,8 @@ __appname__ = 'labelme'
 #   alternate files. Either keep enabled, or add "Save As" button.
 
 # TODO:
-# - [high] Draw mode is confusing in current implementation.
 # - [high] Deselect shape when clicking and already selected(?)
-# - [high] More sensible shortcuts (e.g. Ctrl+C to copy).
+# - [high] Sanitize shortcuts between beginner/advanced mode.
 # - [high] Figure out WhatsThis for help.
 # - [medium] Zoom should keep the image centered.
 # - [medium] Add undo button for vertex addition.
@@ -42,8 +41,6 @@ __appname__ = 'labelme'
 # - [low,maybe] Open images with drag & drop.
 # - [low,maybe] Preview images on file dialogs.
 # - [low,maybe] Sortable label list.
-# - [extra] Add beginner/advanced mode, where different settings are set for
-#   the application, e.g. closable labels, different toolbuttons etc.
 # - Zoom is too "steppy".
 
 
@@ -78,6 +75,7 @@ class MainWindow(QMainWindow, WindowMixin):
         self.dirty = False
 
         self._noSelectionSlot = False
+        self._beginner = True
 
         # Main widgets and related state.
         self.labelDialog = LabelDialog(parent=self)
@@ -92,9 +90,20 @@ class MainWindow(QMainWindow, WindowMixin):
         # Connect to itemChanged to detect checkbox changes.
         self.labelList.itemChanged.connect(self.labelItemChanged)
 
+        listLayout = QVBoxLayout()
+        listLayout.setContentsMargins(0, 0, 0, 0)
+        listLayout.addWidget(self.labelList)
+        self.editButton = QToolButton()
+        self.editButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
+        self.labelListContainer = QWidget()
+        self.labelListContainer.setLayout(listLayout)
+        listLayout.addWidget(self.editButton)#, 0, Qt.AlignCenter)
+        listLayout.addWidget(self.labelList)
+
+
         self.dock = QDockWidget(u'Polygon Labels', self)
         self.dock.setObjectName(u'Labels')
-        self.dock.setWidget(self.labelList)
+        self.dock.setWidget(self.labelListContainer)
 
         self.zoomWidget = ZoomWidget()
         self.colorDialog = ColorDialog(parent=self)
@@ -119,6 +128,9 @@ class MainWindow(QMainWindow, WindowMixin):
 
         self.setCentralWidget(scroll)
         self.addDockWidget(Qt.RightDockWidgetArea, self.dock)
+        self.dockFeatures = QDockWidget.DockWidgetClosable\
+                          | QDockWidget.DockWidgetFloatable
+        self.dock.setFeatures(self.dock.features() ^ self.dockFeatures)
 
         # Actions
         action = partial(newAction, self)
@@ -134,18 +146,31 @@ class MainWindow(QMainWindow, WindowMixin):
                 'Ctrl+L', 'color', u'Choose polygon line color')
         color2 = action('Polygon &Fill Color', self.chooseColor2,
                 'Ctrl+Shift+F', 'color', u'Choose polygon fill color')
-        mode = action('Draw &Polygon', self.toggleDrawing,
-                'Ctrl+N', 'new', u'Start drawing polygons',
-                checkable=True, enabled=False)
+
+        createMode = action('Create\n&Polygons', self.setCreateMode,
+                'Ctrl+N', 'new', u'Start drawing polygons', enabled=False)
+        editMode = action('&Edit\nPolygons', self.setEditMode,
+                'Ctrl+E', 'edit', u'Move and edit polygons', enabled=False)
+
+        create = action('Create &Polygon', self.createShape,
+                'Ctrl+N', 'new', u'Draw a new polygon')
+        delete = action('&Delete Polygon', self.deleteSelectedShape,
+                ['Ctrl+D', 'Delete'], 'delete', u'Delete', enabled=False)
         copy = action('&Copy Polygon', self.copySelectedShape,
                 'Ctrl+C', 'copy', u'Create a duplicate of the selected polygon',
                 enabled=False)
-        delete = action('&Delete Polygon', self.deleteSelectedShape,
-                ['Ctrl+D', 'Delete'], 'delete', u'Delete', enabled=False)
-        hide = action('&Hide Polygons', self.hideLabelsToggle,
-                'Ctrl+H', 'hide', u'Hide all polygons',
+
+        advancedMode = action('&Advanced Mode', self.toggleAdvancedMode,
+                'Ctrl+Shift+A', 'expert', u'Switch to advanced mode',
                 checkable=True)
 
+        hideAll = action('&Hide Polygons', partial(self.togglePolygons, False),
+                'Ctrl+H', 'hide', u'Hide all polygons',
+                enabled=False)
+        showAll = action('&Show Polygons', partial(self.togglePolygons, True),
+                'Ctrl+A', 'hide', u'Show all polygons',
+                enabled=False)
+
         zoom = QWidgetAction(self)
         zoom.setDefaultWidget(self.zoomWidget)
         self.zoomWidget.setWhatsThis(
@@ -179,24 +204,15 @@ class MainWindow(QMainWindow, WindowMixin):
         edit = action('&Edit Label', self.editLabel,
                 'Ctrl+E', 'edit', u'Modify the label of the selected polygon',
                 enabled=False)
+        self.editButton.setDefaultAction(edit)
 
-        shapeLineColor = action('Current &Line Color', self.chshapeLineColor,
+        shapeLineColor = action('Shape &Line Color', self.chshapeLineColor,
                 icon='color', tip=u'Change the line color for this specific shape',
                 enabled=False)
-        shapeFillColor = action('Current &Fill Color', self.chshapeFillColor,
+        shapeFillColor = action('Shape &Fill Color', self.chshapeFillColor,
                 icon='color', tip=u'Change the fill color for this specific shape',
                 enabled=False)
 
-        # Custom context menu for the canvas widget:
-        addActions(self.canvas.menus[0], (mode, edit, copy, delete))
-
-        addActions(self.canvas.menus[0], (
-            mode, edit, copy, delete,
-            shapeLineColor, shapeFillColor))
-        addActions(self.canvas.menus[1], (
-            action('&Copy here', self.copyShape),
-            action('&Move here', self.moveShape)))
-
         labels = self.dock.toggleViewAction()
         labels.setText('Show/Hide Label Panel')
         labels.setShortcut('Ctrl+Shift+L')
@@ -212,30 +228,48 @@ class MainWindow(QMainWindow, WindowMixin):
         # Store actions for further handling.
         self.actions = struct(save=save, open=open, close=close,
                 lineColor=color1, fillColor=color2,
-                mode=mode, delete=delete, edit=edit, copy=copy,
+                create=create, delete=delete, edit=edit, copy=copy,
+                createMode=createMode, editMode=editMode, advancedMode=advancedMode,
                 shapeLineColor=shapeLineColor, shapeFillColor=shapeFillColor,
                 zoom=zoom, zoomIn=zoomIn, zoomOut=zoomOut, zoomOrg=zoomOrg,
                 fitWindow=fitWindow, fitWidth=fitWidth,
                 zoomActions=zoomActions,
-                fileMenuActions=(open,save,close,quit))
+                fileMenuActions=(open,save,close,quit),
+                beginner=(), advanced=(),
+                beginnerContext=(create, edit, copy, delete),
+                advancedContext=(createMode, editMode, edit, copy,
+                    delete, shapeLineColor, shapeFillColor),
+                onLoadActive=(close, create, createMode, editMode),
+                onShapesPresent=(hideAll, showAll))
 
         self.menus = struct(
                 file=self.menu('&File'),
                 edit=self.menu('&Polygons'),
                 view=self.menu('&View'),
                 labelList=labelMenu)
-        addActions(self.menus.edit, (mode, color1, color2))
+        addActions(self.menus.edit, (color1, color2))
         addActions(self.menus.view, (
-            labels, None,
+            labels, advancedMode, None,
+            hideAll, showAll, None,
             zoomIn, zoomOut, zoomOrg, None,
             fitWindow, fitWidth))
         self.menus.file.aboutToShow.connect(self.updateFileMenu)
 
+        # Custom context menu for the canvas widget:
+        addActions(self.canvas.menus[0], self.actions.beginnerContext)
+        addActions(self.canvas.menus[1], (
+            action('&Copy here', self.copyShape),
+            action('&Move here', self.moveShape)))
+
         self.tools = self.toolbar('Tools')
-        addActions(self.tools, (
+        self.actions.beginner = (
+            open, save, None, create, delete, None,
+            zoomIn, zoom, zoomOut, fitWindow, fitWidth)
+
+        self.actions.advanced = (
             open, save, None,
-            mode, delete, hide, None,
-            zoomIn, zoom, zoomOut, fitWindow, fitWidth))
+            createMode, editMode, copy, delete, None,
+            hideAll, showAll)
 
         self.statusBar().showMessage('%s started.' % __appname__)
         self.statusBar().show()
@@ -275,6 +309,10 @@ class MainWindow(QMainWindow, WindowMixin):
         Shape.line_color = self.lineColor
         Shape.fill_color = self.fillColor
 
+        if settings.get('advanced', QVariant()).toBool():
+            self.actions.advancedMode.setChecked(True)
+            self.toggleAdvancedMode()
+
         # Populate the File menu dynamically.
         self.updateFileMenu()
         # Since loading the file may take some time, make sure it runs in the background.
@@ -283,12 +321,46 @@ class MainWindow(QMainWindow, WindowMixin):
         # Callbacks:
         self.zoomWidget.valueChanged.connect(self.paintCanvas)
 
+        self.populateModeActions()
+
         #self.firstStart = True
         #if self.firstStart:
         #    QWhatsThis.enterWhatsThisMode()
 
     ## Support Functions ##
 
+    def noShapes(self):
+        return not self.itemsToShapes
+
+    def toggleAdvancedMode(self, value=True):
+        self._beginner = not value
+        self.populateModeActions()
+        self.editButton.setVisible(not value)
+        if value:
+            self.actions.createMode.setEnabled(True)
+            self.actions.editMode.setEnabled(True)
+            self.dock.setFeatures(self.dock.features() | self.dockFeatures)
+        else:
+            self.dock.setFeatures(self.dock.features() ^ self.dockFeatures)
+
+    def populateModeActions(self):
+        if self.beginner():
+            tool, menu = self.actions.beginner, self.actions.beginnerContext
+        else:
+            tool, menu = self.actions.advanced, self.actions.advancedContext
+        self.tools.clear()
+        addActions(self.tools, tool)
+        self.canvas.menus[0].clear()
+        addActions(self.canvas.menus[0], menu)
+
+    def setBeginner(self):
+        self.tools.clear()
+        addActions(self.tools, self.actions.beginner)
+
+    def setAdvanced(self):
+        self.tools.clear()
+        addActions(self.tools, self.actions.advanced)
+
     def setDirty(self):
         self.dirty = True
         self.actions.save.setEnabled(True)
@@ -296,14 +368,15 @@ class MainWindow(QMainWindow, WindowMixin):
     def setClean(self):
         self.dirty = False
         self.actions.save.setEnabled(False)
-        self.actions.mode.setEnabled(True)
+        self.actions.create.setEnabled(True)
+        # XXX
 
     def toggleActions(self, value=True):
         """Enable/Disable widgets which depend on an opened image."""
         for z in self.actions.zoomActions:
             z.setEnabled(value)
-        self.actions.close.setEnabled(value)
-        self.actions.mode.setEnabled(value)
+        for action in self.actions.onLoadActive:
+            action.setEnabled(value)
 
     def queueEvent(self, function):
         QTimer.singleShot(0, function)
@@ -333,7 +406,34 @@ class MainWindow(QMainWindow, WindowMixin):
             self.recentFiles.pop()
         self.recentFiles.insert(0, filename)
 
+    def beginner(self):
+        return self._beginner
+
+    def advanced(self):
+        return not self.beginner()
+
     ## Callbacks ##
+    def createShape(self):
+        assert self.beginner()
+        self.canvas.setEditing(False)
+
+    def toggleDrawingSensitive(self, drawing=True):
+        """In the middle of drawing, toggling between modes should be disabled."""
+        self.actions.createMode.setEnabled(not drawing)
+        self.actions.editMode.setEnabled(not drawing)
+
+    def toggleDrawMode(self, edit=True):
+        self.canvas.setEditing(edit)
+        self.actions.createMode.setEnabled(edit)
+        self.actions.editMode.setEnabled(not edit)
+
+    def setCreateMode(self):
+        assert self.advanced()
+        self.toggleDrawMode(False)
+
+    def setEditMode(self):
+        assert self.advanced()
+        self.toggleDrawMode(True)
 
     def updateFileMenu(self):
         current = self.filename
@@ -356,7 +456,7 @@ class MainWindow(QMainWindow, WindowMixin):
         self.menus.labelList.exec_(self.labelList.mapToGlobal(point))
 
     def editLabel(self, item=None):
-        if self.canvas.editing():
+        if not self.canvas.editing():
             return
         item = item if item else self.currentItem()
         text = self.simpleLabelDialog.popUp(item.text())
@@ -387,10 +487,14 @@ class MainWindow(QMainWindow, WindowMixin):
         self.itemsToShapes[item] = shape
         self.shapesToItems[shape] = item
         self.labelList.addItem(item)
+        for action in self.actions.onShapesPresent:
+            action.setEnabled(True)
 
     def remLabel(self, shape):
-        item = self.shapesToItems.get(shape, None)
+        item = self.shapesToItems[shape]
         self.labelList.takeItem(self.labelList.row(item))
+        del self.shapesToItems[shape]
+        del self.itemsToShapes[item]
 
     def loadLabels(self, shapes):
         s = []
@@ -398,6 +502,7 @@ class MainWindow(QMainWindow, WindowMixin):
             shape = Shape(label=label)
             for x, y in points:
                 shape.addPoint(QPointF(x, y))
+            shape.addPoint(shape[0]) # Close shape
             s.append(shape)
             self.addLabel(shape)
             if line_color:
@@ -433,7 +538,7 @@ class MainWindow(QMainWindow, WindowMixin):
 
     def labelSelectionChanged(self):
         item = self.currentItem()
-        if item and not self.canvas.editing():
+        if item and self.canvas.editing():
             self._noSelectionSlot = True
             self.canvas.selectShape(self.itemsToShapes[item])
 
@@ -452,10 +557,14 @@ class MainWindow(QMainWindow, WindowMixin):
 
         position MUST be in global coordinates.
         """
-        action = self.labelDialog.popUp(text="Enter object's name", position=position)
+        action = self.labelDialog.popUp(position=position)
         if action == self.labelDialog.OK:
             self.addLabel(self.canvas.setLastLabel(self.labelDialog.text()))
-            self.actions.mode.setEnabled(True)
+            if self.beginner(): # Switch to edit mode.
+                self.canvas.setEditing(True)
+                self.actions.create.setEnabled(True)
+            else:
+                self.actions.editMode.setEnabled(True)
             self.setDirty()
         elif action == self.labelDialog.UNDO:
             self.canvas.undoLastLine()
@@ -495,10 +604,9 @@ class MainWindow(QMainWindow, WindowMixin):
         self.zoomMode = self.FIT_WIDTH if value else self.MANUAL_ZOOM
         self.adjustScale()
 
-    def hideLabelsToggle(self, value):
-        #self.canvas.hideBackroundShapes(value)
+    def togglePolygons(self, value):
         for item, shape in self.itemsToShapes.iteritems():
-            item.setCheckState(Qt.Unchecked if value else Qt.Checked)
+            item.setCheckState(Qt.Checked if value else Qt.Unchecked)
 
     def loadFile(self, filename=None):
         """Load the specified file, or the last opened file if None."""
@@ -591,6 +699,7 @@ class MainWindow(QMainWindow, WindowMixin):
         s['line/color'] = self.lineColor
         s['fill/color'] = self.fillColor
         s['recentFiles'] = self.recentFiles
+        s['advanced'] = not self._beginner
         # ask the use for where to save the labels
         #s['window/geometry'] = self.saveGeometry()
 
@@ -665,21 +774,15 @@ class MainWindow(QMainWindow, WindowMixin):
             self.canvas.update()
             self.setDirty()
 
-    def toggleDrawing(self, value=True):
-        if value:
-            self.canvas.deSelectShape()
-        self.canvas.setEditing(value)
-
-    def toggleDrawingSensitive(self, drawing=True):
-        """In the middle of drawing, toggling between modes should be disabled."""
-        self.actions.mode.setEnabled(not drawing)
-
     def deleteSelectedShape(self):
         yes, no = QMessageBox.Yes, QMessageBox.No
         msg = u'You are about to permanently delete this polygon, proceed anyway?'
         if yes == QMessageBox.warning(self, u'Attention', msg, yes|no):
             self.remLabel(self.canvas.deleteSelected())
             self.setDirty()
+            if self.noShapes():
+                for action in self.actions.onShapesPresent:
+                    action.setEnabled(False)
 
     def chshapeLineColor(self):
         color = self.colorDialog.getColor(self.lineColor, u'Choose line color',

+ 1 - 0
resources.qrc

@@ -1,6 +1,7 @@
 <!DOCTYPE RCC><RCC version="1.0">
 <qresource>
 
+<file alias="expert">icons/expert2.png</file>
 <file alias="done">icons/done.svg</file>
 <file alias="file">icons/file.png</file>
 <file alias="labels">icons/labels.svg</file>

+ 9 - 2
shape.py

@@ -13,7 +13,8 @@ DEFAULT_LINE_COLOR = QColor(0, 255, 0, 128)
 DEFAULT_FILL_COLOR = QColor(255, 0, 0, 128)
 DEFAULT_SELECT_LINE_COLOR = QColor(255, 255, 255)
 DEFAULT_SELECT_FILL_COLOR = QColor(0, 128, 255, 155)
-DEFAULT_VERTEX_FILL_COLOR = QColor(255, 0, 0)
+DEFAULT_VERTEX_FILL_COLOR = QColor(0, 255, 0, 255)
+DEFAULT_HVERTEX_FILL_COLOR = QColor(255, 0, 0)
 
 class Shape(object):
     P_SQUARE, P_ROUND = range(2)
@@ -27,6 +28,7 @@ class Shape(object):
     select_line_color = DEFAULT_SELECT_LINE_COLOR
     select_fill_color = DEFAULT_SELECT_FILL_COLOR
     vertex_fill_color = DEFAULT_VERTEX_FILL_COLOR
+    hvertex_fill_color = DEFAULT_HVERTEX_FILL_COLOR
     point_type = P_ROUND
     point_size = 8
     scale = 1.0
@@ -41,7 +43,7 @@ class Shape(object):
         self._highlightMode = self.NEAR_VERTEX
         self._highlightSettings = {
             self.NEAR_VERTEX: (4, self.P_ROUND),
-            self.MOVE_VERTEX: (2, self.P_SQUARE),
+            self.MOVE_VERTEX: (1.5, self.P_SQUARE),
             }
 
         self._closed = False
@@ -106,6 +108,10 @@ class Shape(object):
         if i == self._highlightIndex:
             size, shape = self._highlightSettings[self._highlightMode]
             d *= size
+        if self._highlightIndex is not None:
+            self.vertex_fill_color = self.hvertex_fill_color
+        else:
+            self.vertex_fill_color = Shape.vertex_fill_color
         if shape == self.P_SQUARE:
             path.addRect(point.x() - d/2, point.y() - d/2, d, d)
         elif shape == self.P_ROUND:
@@ -149,6 +155,7 @@ class Shape(object):
         shape.points= [p for p in self.points]
         shape.fill = self.fill
         shape.selected = self.selected
+        shape._closed = self._closed
         return shape
 
     def __len__(self):