Skip to content

Commit

Permalink
Undo shape edit by Ctrl+Z
Browse files Browse the repository at this point in the history
  • Loading branch information
wkentaro committed Apr 23, 2018
1 parent 225f24a commit 3f95cd7
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 16 deletions.
53 changes: 39 additions & 14 deletions labelme/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def dropEvent(self, event):
if self.canvas is None:
raise RuntimeError('self.canvas must be set beforehand.')
self.parent.setDirty()
self.canvas.shapes = self.shapes
self.canvas.loadShapes(shapes)

@property
def shapes(self):
Expand Down Expand Up @@ -270,9 +270,12 @@ def __init__(self, filename=None, output=None, store_data=True,
'Create a duplicate of the selected polygon',
enabled=False)
undoLastPoint = action('Undo last point', self.canvas.undoLastPoint,
shortcuts['undo_last_point'], 'undoLastPoint',
shortcuts['undo_last_point'], 'undo',
'Undo last drawn point', enabled=False)

undo = action('Undo', self.undoShapeEdit, shortcuts['undo'], 'undo',
'Undo last add and edit of shape', enabled=False)

hideAll = action('&Hide\nPolygons',
functools.partial(self.togglePolygons, False),
icon='eye', tip='Hide all polygons', enabled=False)
Expand Down Expand Up @@ -349,20 +352,20 @@ def __init__(self, filename=None, output=None, store_data=True,
save=save, saveAs=saveAs, open=open_, close=close,
lineColor=color1, fillColor=color2,
delete=delete, edit=edit, copy=copy,
undoLastPoint=undoLastPoint,
undoLastPoint=undoLastPoint, undo=undo,
createMode=createMode, editMode=editMode,
shapeLineColor=shapeLineColor, shapeFillColor=shapeFillColor,
zoom=zoom, zoomIn=zoomIn, zoomOut=zoomOut, zoomOrg=zoomOrg,
fitWindow=fitWindow, fitWidth=fitWidth,
zoomActions=zoomActions,
fileMenuActions=(open_, opendir, save, saveAs, close, quit),
tool=(),
editMenu=(edit, copy, delete, None, undoLastPoint,
editMenu=(edit, copy, delete, None, undo, undoLastPoint,
None, color1, color2),
menu=(
createMode, editMode, edit, copy,
delete, shapeLineColor, shapeFillColor,
undoLastPoint,
undo, undoLastPoint,
),
onLoadActive=(close, createMode, editMode),
onShapesPresent=(saveAs, hideAll, showAll),
Expand Down Expand Up @@ -397,7 +400,7 @@ def __init__(self, filename=None, output=None, store_data=True,
self.tools = self.toolbar('Tools')
self.actions.tool = (
open_, opendir, openNextImg, openPrevImg, save,
None, createMode, copy, delete, editMode, None,
None, createMode, copy, delete, editMode, undo, None,
zoomIn, zoom, zoomOut, fitWindow, fitWidth)

self.statusBar().showMessage('%s started.' % __appname__)
Expand Down Expand Up @@ -522,6 +525,7 @@ def setDirty(self):
return
self.dirty = True
self.actions.save.setEnabled(True)
self.actions.undo.setEnabled(self.canvas.isShapeRestorable)
title = __appname__
if self.filename is not None:
title = '{} - {}*'.format(title, self.filename)
Expand Down Expand Up @@ -573,6 +577,13 @@ def addRecentFile(self, filename):

# Callbacks

def undoShapeEdit(self):
self.canvas.restoreShape()
self.labelList.clear()
self.uniqLabelList.clear()
self.loadShapes(self.canvas.shapes)
self.actions.undo.setEnabled(self.canvas.isShapeRestorable)

def tutorial(self):
url = 'https://github.com/wkentaro/labelme/tree/master/examples/tutorial' # NOQA
webbrowser.open(url)
Expand All @@ -584,6 +595,7 @@ def toggleDrawingSensitive(self, drawing=True):
"""
self.actions.editMode.setEnabled(not drawing)
self.actions.undoLastPoint.setEnabled(drawing)
self.actions.undo.setEnabled(not drawing)

def toggleDrawMode(self, edit=True):
self.canvas.setEditing(edit)
Expand Down Expand Up @@ -698,6 +710,11 @@ def remLabel(self, shape):
item = self.labelList.get_item_from_shape(shape)
self.labelList.takeItem(self.labelList.row(item))

def loadShapes(self, shapes):
for shape in shapes:
self.addLabel(shape)
self.canvas.loadShapes(shapes)

def loadLabels(self, shapes):
s = []
for label, points, line_color, fill_color in shapes:
Expand All @@ -706,12 +723,11 @@ def loadLabels(self, shapes):
shape.addPoint(QtCore.QPointF(x, y))
shape.close()
s.append(shape)
self.addLabel(shape)
if line_color:
shape.line_color = QtGui.QColor(*line_color)
if fill_color:
shape.fill_color = QtGui.QColor(*fill_color)
self.canvas.loadShapes(s)
self.loadShapes(s)

def saveLabels(self, filename):
lf = LabelFile()
Expand Down Expand Up @@ -780,9 +796,12 @@ def newShape(self):
text = None
if text is None:
self.canvas.undoLastLine()
self.canvas.shapesBackups.pop()
else:
self.addLabel(self.canvas.setLastLabel(text))
self.actions.editMode.setEnabled(True)
self.actions.undoLastPoint.setEnabled(False)
self.actions.undo.setEnabled(True)
self.setDirty()

def scrollRequest(self, delta, orientation):
Expand Down Expand Up @@ -835,6 +854,13 @@ def togglePolygons(self, value):

def loadFile(self, filename=None):
"""Load the specified file, or the last opened file if None."""
# changing fileListWidget loads file
if (filename in self.imageList and
self.fileListWidget.currentRow() !=
self.imageList.index(filename)):
self.fileListWidget.setCurrentRow(self.imageList.index(filename))
return

self.resetState()
self.canvas.setEnabled(False)
if filename is None:
Expand Down Expand Up @@ -904,8 +930,6 @@ def loadFile(self, filename=None):
self.addRecentFile(self.filename)
self.toggleActions(True)
self.status("Loaded %s" % os.path.basename(str(filename)))
if filename in self.imageList:
self.fileListWidget.setCurrentRow(self.imageList.index(filename))
return True

def resizeEvent(self, event):
Expand Down Expand Up @@ -977,7 +1001,7 @@ def openPrevImg(self, _value=False):
if filename:
self.loadFile(filename)

def openNextImg(self, _value=False):
def openNextImg(self, _value=False, load=True):
if not self.mayContinue():
return

Expand All @@ -991,9 +1015,10 @@ def openNextImg(self, _value=False):
currIndex = self.imageList.index(self.filename)
if currIndex + 1 < len(self.imageList):
filename = self.imageList[currIndex + 1]
self.filename = filename

if filename:
self.loadFile(filename)
if self.filename and load:
self.loadFile(self.filename)

def openFile(self, _value=False):
if not self.mayContinue():
Expand Down Expand Up @@ -1188,7 +1213,7 @@ def importDirImages(self, dirpath):
for imgPath in self.scanAllImages(dirpath):
item = QtWidgets.QListWidgetItem(imgPath)
self.fileListWidget.addItem(item)
self.openNextImg()
self.openNextImg(load=False)

def scanAllImages(self, folderPath):
extensions = ['.%s' % fmt.data().decode("ascii").lower()
Expand Down
40 changes: 38 additions & 2 deletions labelme/canvas.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def __init__(self, *args, **kwargs):
# Initialise local state.
self.mode = self.EDIT
self.shapes = []
self.shapesBackups = []
self.current = None
self.selectedShape = None # save the selected shape here
self.selectedShapeCopy = None
Expand All @@ -63,6 +64,29 @@ def __init__(self, *args, **kwargs):
self.setMouseTracking(True)
self.setFocusPolicy(QtCore.Qt.WheelFocus)

def storeShapes(self):
shapesBackup = []
for shape in self.shapes:
shapesBackup.append(shape.copy())
if len(self.shapesBackups) >= 10:
self.shapesBackups = self.shapesBackups[-9:]
self.shapesBackups.append(shapesBackup)

@property
def isShapeRestorable(self):
if len(self.shapesBackups) < 2:
return False
return True

def restoreShape(self):
if not self.isShapeRestorable:
return
self.shapesBackups.pop() # latest
shapesBackup = self.shapesBackups.pop()
self.shapes = shapesBackup
self.storeShapes()
self.repaint()

def enterEvent(self, ev):
self.overrideCursor(self._cursor)

Expand Down Expand Up @@ -140,16 +164,17 @@ def mouseMoveEvent(self, ev):
return

# Polygon/Vertex moving.
self.movingShape = False
if QtCore.Qt.LeftButton & ev.buttons():
if self.selectedVertex():
self.boundedMoveVertex(pos)
self.shapeMoved.emit()
self.repaint()
self.movingShape = True
elif self.selectedShape and self.prevPoint:
self.overrideCursor(CURSOR_MOVE)
self.boundedMoveShape(self.selectedShape, pos)
self.shapeMoved.emit()
self.repaint()
self.movingShape = True
return

# Just hovering over the canvas, 2 posibilities:
Expand Down Expand Up @@ -230,6 +255,9 @@ def mouseReleaseEvent(self, ev):
self.repaint()
elif ev.button() == QtCore.Qt.LeftButton and self.selectedShape:
self.overrideCursor(CURSOR_GRAB)
if self.movingShape:
self.storeShapes()
self.shapeMoved.emit()

def endMove(self, copy=False):
assert self.selectedShape and self.selectedShapeCopy
Expand All @@ -245,6 +273,7 @@ def endMove(self, copy=False):
shape.label = self.selectedShape.label
self.deleteSelected()
self.shapes.append(shape)
self.storeShapes()
self.selectedShapeCopy = None

def hideBackroundShapes(self, value):
Expand Down Expand Up @@ -341,6 +370,7 @@ def deleteSelected(self):
if self.selectedShape:
shape = self.selectedShape
self.shapes.remove(self.selectedShape)
self.storeShapes()
self.selectedShape = None
self.update()
return shape
Expand All @@ -350,6 +380,7 @@ def copySelectedShape(self):
shape = self.selectedShape.copy()
self.deSelectShape()
self.shapes.append(shape)
self.storeShapes()
shape.selected = True
self.selectedShape = shape
self.boundedShiftShape(shape)
Expand Down Expand Up @@ -417,6 +448,7 @@ def finalise(self):
assert self.current
self.current.close()
self.shapes.append(self.current)
self.storeShapes()
self.current = None
self.setHiding(False)
self.newShape.emit()
Expand Down Expand Up @@ -529,6 +561,8 @@ def keyPressEvent(self, ev):
def setLastLabel(self, text):
assert text
self.shapes[-1].label = text
self.shapesBackups.pop()
self.storeShapes()
return self.shapes[-1]

def undoLastLine(self):
Expand Down Expand Up @@ -556,6 +590,7 @@ def loadPixmap(self, pixmap):

def loadShapes(self, shapes):
self.shapes = list(shapes)
self.storeShapes()
self.current = None
self.repaint()

Expand All @@ -574,4 +609,5 @@ def restoreCursor(self):
def resetState(self):
self.restoreCursor()
self.pixmap = None
self.shapesBackups = []
self.update()
1 change: 1 addition & 0 deletions labelme/config/default_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ shortcuts:
edit_polygon: Ctrl+J
delete_polygon: Delete
duplicate_polygon: Ctrl+D
undo: Ctrl+Z
undo_last_point: [Ctrl+Z, Backspace]
edit_label: Ctrl+E
edit_line_color: Ctrl+L
Expand Down

0 comments on commit 3f95cd7

Please sign in to comment.