浏览代码

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 年之前
父节点
当前提交
35c296ef94
共有 3 个文件被更改,包括 48 次插入24 次删除
  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._hideBackround = False
         self.hideBackround = False
+        # Menus:
+        self.menus = (QMenu(), QMenu())
         # Set widget options.
         self.setMouseTracking(True)
         self.setFocusPolicy(Qt.WheelFocus)
@@ -53,13 +55,13 @@ class Canvas(QWidget):
 
         # Polygon copy moving.
         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:
                 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()
             return
 
@@ -81,7 +83,7 @@ class Canvas(QWidget):
 
         # Polygon moving.
         elif Qt.LeftButton & ev.buttons() and self.selectedShape and self.prevPoint:
-            self.boundedMoveShape(pos)
+            self.boundedMoveShape(self.selectedShape, pos)
             self.repaint()
             return
 
@@ -116,6 +118,26 @@ class Canvas(QWidget):
             self.prevPoint = pos
             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):
         self.hideBackround = value
         if self.selectedShape:
@@ -158,7 +180,7 @@ class Canvas(QWidget):
         y2 = (rect.y() + rect.height()) - point.y()
         self.offsets = QPointF(x1, y1), QPointF(x2, y2)
 
-    def boundedMoveShape(self, pos):
+    def boundedMoveShape(self, shape, pos):
         if self.outOfPixmap(pos):
             return # No need to move
         o1 = pos + self.offsets[0]
@@ -173,7 +195,7 @@ 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)
-        self.selectedShape.moveBy(pos - self.prevPoint)
+        shape.moveBy(pos - self.prevPoint)
         self.prevPoint = pos
 
     def deSelectShape(self):

+ 13 - 16
labelme.py

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

+ 5 - 0
lib.py

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