Przeglądaj źródła

Add pop-up menu when moving shape with right click

The user can then pick to copy or move the shape. When moving with the
right click a shadow copy of the shape is created and moved.
Michael Pitidis 13 lat temu
rodzic
commit
35c296ef94
3 zmienionych plików z 48 dodań i 24 usunięć
  1. 30 8
      canvas.py
  2. 13 16
      labelme.py
  3. 5 0
      lib.py

+ 30 - 8
canvas.py

@@ -34,6 +34,8 @@ class Canvas(QWidget):
         self.visible = {}
         self.visible = {}
         self._hideBackround = False
         self._hideBackround = False
         self.hideBackround = False
         self.hideBackround = False
+        # Menus:
+        self.menus = (QMenu(), QMenu())
         # Set widget options.
         # Set widget options.
         self.setMouseTracking(True)
         self.setMouseTracking(True)
         self.setFocusPolicy(Qt.WheelFocus)
         self.setFocusPolicy(Qt.WheelFocus)
@@ -53,13 +55,13 @@ class Canvas(QWidget):
 
 
         # Polygon copy moving.
         # Polygon copy moving.
         if Qt.RightButton & ev.buttons():
         if Qt.RightButton & ev.buttons():
-            if self.selectedShapeCopy:
-                if self.prevPoint:
-                    self.selectedShapeCopy.moveBy(pos - self.prevPoint)
-                    self.repaint()
-                self.prevPoint = pos
+            if self.selectedShapeCopy and self.prevPoint:
+                self.boundedMoveShape(self.selectedShapeCopy, pos)
+                self.repaint()
             elif self.selectedShape:
             elif self.selectedShape:
                 self.selectedShapeCopy = self.selectedShape.copy()
                 self.selectedShapeCopy = self.selectedShape.copy()
+                self.selectedShapeCopy.line_color = QColor(255, 0, 0, 64)
+                self.selectedShapeCopy.fill_color = QColor(0, 255, 0, 64)
                 self.repaint()
                 self.repaint()
             return
             return
 
 
@@ -81,7 +83,7 @@ class Canvas(QWidget):
 
 
         # Polygon moving.
         # Polygon moving.
         elif Qt.LeftButton & ev.buttons() and self.selectedShape and self.prevPoint:
         elif Qt.LeftButton & ev.buttons() and self.selectedShape and self.prevPoint:
-            self.boundedMoveShape(pos)
+            self.boundedMoveShape(self.selectedShape, pos)
             self.repaint()
             self.repaint()
             return
             return
 
 
@@ -116,6 +118,26 @@ class Canvas(QWidget):
             self.prevPoint = pos
             self.prevPoint = pos
             self.repaint()
             self.repaint()
 
 
+    def mouseReleaseEvent(self, ev):
+        pos = self.transformPos(ev.posF())
+        if ev.button() == Qt.RightButton:
+            menu = self.menus[bool(self.selectedShapeCopy)]
+            menu.exec_(self.mapToGlobal(ev.pos()))
+
+    def endMove(self, copy=False):
+        assert self.selectedShape and self.selectedShapeCopy
+        shape = self.selectedShapeCopy
+        del shape.fill_color
+        del shape.line_color
+        if copy:
+            self.shapes.append(shape)
+            self.selectedShape = shape
+        else:
+            shape.label = self.selectedShape.label
+            self.deleteSelected()
+            self.shapes.append(shape)
+        self.selectedShapeCopy = None
+
     def hideBackroundShapes(self, value):
     def hideBackroundShapes(self, value):
         self.hideBackround = value
         self.hideBackround = value
         if self.selectedShape:
         if self.selectedShape:
@@ -158,7 +180,7 @@ class Canvas(QWidget):
         y2 = (rect.y() + rect.height()) - point.y()
         y2 = (rect.y() + rect.height()) - point.y()
         self.offsets = QPointF(x1, y1), QPointF(x2, y2)
         self.offsets = QPointF(x1, y1), QPointF(x2, y2)
 
 
-    def boundedMoveShape(self, pos):
+    def boundedMoveShape(self, shape, pos):
         if self.outOfPixmap(pos):
         if self.outOfPixmap(pos):
             return # No need to move
             return # No need to move
         o1 = pos + self.offsets[0]
         o1 = pos + self.offsets[0]
@@ -173,7 +195,7 @@ class Canvas(QWidget):
         # a bit "shaky" when nearing the border and allows it to
         # a bit "shaky" when nearing the border and allows it to
         # go outside of the shape's area for some reason. XXX
         # go outside of the shape's area for some reason. XXX
         #self.calculateOffsets(self.selectedShape, pos)
         #self.calculateOffsets(self.selectedShape, pos)
-        self.selectedShape.moveBy(pos - self.prevPoint)
+        shape.moveBy(pos - self.prevPoint)
         self.prevPoint = pos
         self.prevPoint = pos
 
 
     def deSelectShape(self):
     def deSelectShape(self):

+ 13 - 16
labelme.py

@@ -13,7 +13,7 @@ from PyQt4.QtCore import *
 
 
 import resources
 import resources
 
 
-from lib import newAction, addActions, labelValidator
+from lib import struct, newAction, addActions, labelValidator
 from shape import Shape
 from shape import Shape
 from canvas import Canvas
 from canvas import Canvas
 from zoomWidget import ZoomWidget
 from zoomWidget import ZoomWidget
@@ -24,11 +24,11 @@ from labelFile import LabelFile
 __appname__ = 'labelme'
 __appname__ = 'labelme'
 
 
 # FIXME
 # FIXME
-# - Add copy/move menu when dragging with right click.
 # - [low] Label validation/postprocessing breaks with TAB.
 # - [low] Label validation/postprocessing breaks with TAB.
 
 
 # TODO:
 # TODO:
 # - [easy] Add button to Hide/Show all labels.
 # - [easy] Add button to Hide/Show all labels.
+# - [maybe] Open images with drag & drop.
 # - Zoom is too "steppy".
 # - Zoom is too "steppy".
 
 
 
 
@@ -78,8 +78,6 @@ class MainWindow(QMainWindow, WindowMixin):
         self.labelList.itemChanged.connect(self.labelItemChanged)
         self.labelList.itemChanged.connect(self.labelItemChanged)
 
 
         self.canvas = Canvas()
         self.canvas = Canvas()
-        #self.canvas.setAlignment(Qt.AlignCenter)
-
         self.canvas.zoomRequest.connect(self.zoomRequest)
         self.canvas.zoomRequest.connect(self.zoomRequest)
 
 
         scroll = QScrollArea()
         scroll = QScrollArea()
@@ -118,10 +116,10 @@ class MainWindow(QMainWindow, WindowMixin):
                 checkable=True)
                 checkable=True)
 
 
         # Custom context menu for the canvas widget:
         # Custom context menu for the canvas widget:
-        self.popMenu = QMenu(self)
-        addActions(self.popMenu, (label, copy, delete))
-        self.canvas.setContextMenuPolicy(Qt.CustomContextMenu)
-        self.canvas.customContextMenuRequested.connect(self.popContextMenu)
+        addActions(self.canvas.menus[0], (label, copy, delete))
+        addActions(self.canvas.menus[1], (
+            action('&Copy here', self.copyShape),
+            action('&Move here', self.moveShape)))
 
 
         labels = self.dock.toggleViewAction()
         labels = self.dock.toggleViewAction()
         labels.setShortcut('Ctrl+L')
         labels.setShortcut('Ctrl+L')
@@ -196,9 +194,6 @@ class MainWindow(QMainWindow, WindowMixin):
         self.actions.delete.setEnabled(selected)
         self.actions.delete.setEnabled(selected)
         self.actions.copy.setEnabled(selected)
         self.actions.copy.setEnabled(selected)
 
 
-    def popContextMenu(self, point):
-        self.popMenu.exec_(self.canvas.mapToGlobal(point))
-
     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 | Qt.ItemIsEditable)
@@ -358,7 +353,6 @@ class MainWindow(QMainWindow, WindowMixin):
         s['window/state'] = self.saveState()
         s['window/state'] = self.saveState()
         s['line/color'] = self.color
         s['line/color'] = self.color
         # ask the use for where to save the labels
         # ask the use for where to save the labels
-       
 
 
         #s['window/geometry'] = self.saveGeometry()
         #s['window/geometry'] = self.saveGeometry()
     def updateFileMenu(self):
     def updateFileMenu(self):
@@ -410,6 +404,13 @@ class MainWindow(QMainWindow, WindowMixin):
     def deleteSelectedShape(self):
     def deleteSelectedShape(self):
         self.remLabel(self.canvas.deleteSelected())
         self.remLabel(self.canvas.deleteSelected())
 
 
+    def copyShape(self):
+        self.canvas.endMove(copy=True)
+        self.addLabel(self.canvas.selectedShape)
+
+    def moveShape(self):
+        self.canvas.endMove(copy=False)
+
 
 
 class LabelDelegate(QItemDelegate):
 class LabelDelegate(QItemDelegate):
     def __init__(self, parent=None):
     def __init__(self, parent=None):
@@ -466,10 +467,6 @@ def read(filename, default=None):
     except:
     except:
         return default
         return default
 
 
-class struct(object):
-    def __init__(self, **kwargs):
-        self.__dict__.update(kwargs)
-
 
 
 def main(argv):
 def main(argv):
     """Standard boilerplate Qt application code."""
     """Standard boilerplate Qt application code."""

+ 5 - 0
lib.py

@@ -43,3 +43,8 @@ def addActions(widget, actions):
 def labelValidator():
 def labelValidator():
     return QRegExpValidator(QRegExp(r'^[^ \t].+'), None)
     return QRegExpValidator(QRegExp(r'^[^ \t].+'), None)
 
 
+
+class struct(object):
+    def __init__(self, **kwargs):
+        self.__dict__.update(kwargs)
+